From 5fd10622912fb98c08e6206eec64ba78cae64b89 Mon Sep 17 00:00:00 2001 From: abandoned-prototype <41744410+abandoned-prototype@users.noreply.github.com> Date: Sun, 28 Feb 2021 15:46:00 -0600 Subject: [PATCH] simplify and streamline officer filtering (#858) --- OpenOversight/app/config.py | 1 + OpenOversight/app/main/views.py | 19 ++-- OpenOversight/app/models.py | 20 ++--- OpenOversight/app/templates/list_officer.html | 2 +- .../app/templates/tagger_gallery.html | 4 +- OpenOversight/app/utils.py | 88 +++++++++---------- .../routes/test_officer_and_department.py | 12 +-- OpenOversight/tests/test_utils.py | 4 +- 8 files changed, 77 insertions(+), 73 deletions(-) diff --git a/OpenOversight/app/config.py b/OpenOversight/app/config.py index 1e2077b20..8e2636fb5 100644 --- a/OpenOversight/app/config.py +++ b/OpenOversight/app/config.py @@ -50,6 +50,7 @@ def init_app(app): class DevelopmentConfig(BaseConfig): DEBUG = True + SQLALCHEMY_ECHO = True SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI') NUM_OFFICERS = 15000 SITEMAP_URL_SCHEME = 'http' diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py index 43fdf0e05..90d95eca6 100644 --- a/OpenOversight/app/main/views.py +++ b/OpenOversight/app/main/views.py @@ -5,6 +5,7 @@ import re from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm import selectinload import sys from traceback import format_exc @@ -540,15 +541,23 @@ def list_officer(department_id, page=1, race=[], gender=[], rank=[], min_age='16 form_data['gender'] = request.args.getlist('gender') unit_choices = [(unit.id, unit.descrip) for unit in Unit.query.filter_by(department_id=department_id).order_by(Unit.descrip.asc()).all()] - rank_choices = [jc[0] for jc in db.session.query(Job.job_title, Job.order).filter_by(department_id=department_id, is_sworn_officer=True).order_by(Job.order).all()] + rank_choices = [jc[0] for jc in db.session.query(Job.job_title, Job.order).filter_by(department_id=department_id).order_by(Job.order).all()] if request.args.get('rank') and all(rank in rank_choices for rank in request.args.getlist('rank')): form_data['rank'] = request.args.getlist('rank') - officers = filter_by_form(form_data, Officer.query, department_id).filter(Officer.department_id == department_id).order_by(Officer.last_name, Officer.first_name, Officer.id).paginate(page, OFFICERS_PER_PAGE, False) + officers = filter_by_form( + form_data, Officer.query, department_id + ).filter(Officer.department_id == department_id) + officers = officers.options(selectinload(Officer.face)) + officers = officers.order_by(Officer.last_name, Officer.first_name, Officer.id) + officers = officers.paginate(page, OFFICERS_PER_PAGE, False) for officer in officers.items: - officer_face = officer.face.order_by(Face.featured.desc()).first() - if officer_face: - officer.image = officer_face.image.filepath + officer_face = sorted(officer.face, key=lambda x: x.featured) + + # could do some extra work to not lazy load images but load them all together + # but we would want to ensure to only load the first picture of each officer + if officer_face and officer_face[0].image: + officer.image = officer_face[0].image.filepath choices = { 'race': RACE_CHOICES, diff --git a/OpenOversight/app/models.py b/OpenOversight/app/models.py index 67ca37c18..3b1731530 100644 --- a/OpenOversight/app/models.py +++ b/OpenOversight/app/models.py @@ -1,4 +1,5 @@ import re +from datetime import date from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy.model import DefaultMeta @@ -103,15 +104,14 @@ class Officer(BaseModel): birth_year = db.Column(db.Integer, index=True, unique=False, nullable=True) assignments = db.relationship('Assignment', backref='officer', lazy='dynamic') assignments_lazy = db.relationship('Assignment') - face = db.relationship('Face', backref='officer', lazy='dynamic') + face = db.relationship('Face', backref='officer') department_id = db.Column(db.Integer, db.ForeignKey('departments.id')) department = db.relationship('Department', backref='officers') unique_internal_identifier = db.Column(db.String(50), index=True, unique=True, nullable=True) - # we don't expect to pull up officers via link often so we make it lazy. + links = db.relationship( 'Link', secondary=officer_links, - lazy='subquery', backref=db.backref('officers', lazy=True)) notes = db.relationship('Note', back_populates='officer', order_by='Note.date_created') descriptions = db.relationship('Description', back_populates='officer', order_by='Description.date_created') @@ -147,18 +147,12 @@ def gender_label(self): return label def job_title(self): - if self.assignments.all(): - return self.assignments\ - .order_by(self.assignments[0].__table__.c.star_date.desc())\ - .first()\ - .job.job_title + if self.assignments_lazy: + return max(self.assignments_lazy, key=lambda x: x.star_date or date.min).job.job_title def badge_number(self): - if self.assignments.all(): - return self.assignments\ - .order_by(self.assignments[0].__table__.c.star_date.desc())\ - .first()\ - .star_no + if self.assignments_lazy: + return max(self.assignments_lazy, key=lambda x: x.star_date or date.min).star_no def __repr__(self): if self.unique_internal_identifier: diff --git a/OpenOversight/app/templates/list_officer.html b/OpenOversight/app/templates/list_officer.html index cbcbf039b..5d11e1a28 100644 --- a/OpenOversight/app/templates/list_officer.html +++ b/OpenOversight/app/templates/list_officer.html @@ -163,7 +163,7 @@

Gender
{{ officer.gender_label()|default('Unknown') }}
Number of Photos
-
{{ officer.face.count() }}
+
{{ officer.face | count }}
diff --git a/OpenOversight/app/templates/tagger_gallery.html b/OpenOversight/app/templates/tagger_gallery.html index c98b2475d..76136b522 100644 --- a/OpenOversight/app/templates/tagger_gallery.html +++ b/OpenOversight/app/templates/tagger_gallery.html @@ -23,10 +23,10 @@

Matching Officers