Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filter by photo ability in officer query #965

Merged
merged 32 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2e5ea7f
Small views edits
michplunkett Jul 10, 2023
d16374a
Update email_client.py
michplunkett Jul 10, 2023
e7e2e34
Update __init__.py
michplunkett Jul 10, 2023
79c99f9
Merge branch 'fix_build' into filter_by_photo
michplunkett Jul 10, 2023
2a5113f
Update views.py
michplunkett Jul 10, 2023
b35bdb5
Update views.py
michplunkett Jul 10, 2023
e368c25
Update list_officer.html
michplunkett Jul 10, 2023
2dde1bc
Update openoversight.css
michplunkett Jul 10, 2023
87c92c8
Update views.py
michplunkett Jul 10, 2023
e989bda
Update forms.py
michplunkett Jul 10, 2023
0ee8ef7
HTML update
michplunkett Jul 10, 2023
fcdd73c
Update list_officer.html
michplunkett Jul 10, 2023
27f8ae2
Update views.py
michplunkett Jul 10, 2023
f2c317a
Update views.py
michplunkett Jul 10, 2023
c18eff8
Merge branch 'develop' into filter_by_photo
michplunkett Jul 10, 2023
297c1b1
Formatting using djlint
michplunkett Jul 11, 2023
d009908
Update list_officer.html
michplunkett Jul 11, 2023
4b498d0
Update list_officer.html
michplunkett Jul 11, 2023
bc87b28
Update test_officer_and_department.py
michplunkett Jul 11, 2023
0e8e8bf
Update test
michplunkett Jul 11, 2023
ebf3c8c
Update test_officer_and_department.py
michplunkett Jul 11, 2023
2745329
Update list_officer.html
michplunkett Jul 11, 2023
b359215
Update list_officer.html
michplunkett Jul 11, 2023
858c01f
Update forms.py
michplunkett Jul 11, 2023
6468e2d
Update forms.py
michplunkett Jul 11, 2023
515f97e
Update list_officer.html
michplunkett Jul 11, 2023
e02b42d
Update openoversight.css
michplunkett Jul 11, 2023
f4aff85
Revert "Update forms.py"
michplunkett Jul 11, 2023
8bc3a7e
Update views.py
michplunkett Jul 12, 2023
924a834
Merge branch 'develop' into filter_by_photo
michplunkett Jul 12, 2023
81339ad
Update test_officer_and_department.py
michplunkett Jul 12, 2023
a916d2b
Update test_officer_and_department.py
michplunkett Jul 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 51 additions & 27 deletions OpenOversight/app/main/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def get_officer():
jsloads = ["js/find_officer.js"]
form = FindOfficerForm()

depts_dict = [dept_choice.toCustomDict() for dept_choice in dept_choices()]
departments_dict = [dept_choice.toCustomDict() for dept_choice in dept_choices()]

if getattr(current_user, "dept_pref_rel", None):
set_dynamic_default(form.dept, current_user.dept_pref_rel)
Expand Down Expand Up @@ -177,7 +177,10 @@ def get_officer():
else:
current_app.logger.info(form.errors)
return render_template(
"input_find_officer.html", form=form, depts_dict=depts_dict, jsloads=jsloads
"input_find_officer.html",
form=form,
depts_dict=departments_dict,
jsloads=jsloads,
)


Expand Down Expand Up @@ -246,7 +249,7 @@ def officer_profile(officer_id):
except NoResultFound:
abort(HTTPStatus.NOT_FOUND)
except: # noqa: E722
exception_type, value, full_tback = sys.exc_info()
exception_type, value, full_traceback = sys.exc_info()
current_app.logger.error(
"Error finding officer: {}".format(
" ".join([str(exception_type), str(value), format_exc()])
Expand All @@ -272,7 +275,7 @@ def officer_profile(officer_id):
# Add in the placeholder image if no faces are found
face_paths = [url_for("static", filename="images/placeholder.png")]
except: # noqa: E722
exception_type, value, full_tback = sys.exc_info()
exception_type, value, full_traceback = sys.exc_info()
current_app.logger.error(
"Error loading officer profile: {}".format(
" ".join([str(exception_type), str(value), format_exc()])
Expand Down Expand Up @@ -491,7 +494,7 @@ def classify_submission(image_id, contains_cops):
flash("Updated image classification")
except: # noqa: E722
flash("Unknown error occurred")
exception_type, value, full_tback = sys.exc_info()
exception_type, value, full_traceback = sys.exc_info()
current_app.logger.error(
"Error classifying image: {}".format(
" ".join([str(exception_type), str(value), format_exc()])
Expand Down Expand Up @@ -649,8 +652,9 @@ def list_officer(
unique_internal_identifier=None,
unit=None,
current_job=None,
require_photo=False,
):
jsloads = ["js/select2.min.js", "js/list_officer.js"]
js_loads = ["js/select2.min.js", "js/list_officer.js"]

form = BrowseForm()
form.rank.query = (
Expand All @@ -670,6 +674,7 @@ def list_officer(
form_data["unit"] = unit or []
form_data["current_job"] = current_job
form_data["unique_internal_identifier"] = unique_internal_identifier
form_data["require_photo"] = require_photo

department = Department.query.filter_by(id=department_id).first()
if not department:
Expand Down Expand Up @@ -702,27 +707,30 @@ def list_officer(
for gender in genders
):
form_data["gender"] = genders
if require_photo_arg := request.args.get("require_photo"):
current_app.logger.info("SETTING THE REQUIRE_PHOTO KEY")
michplunkett marked this conversation as resolved.
Show resolved Hide resolved
form_data["require_photo"] = require_photo_arg

unit_choices = ["Not Sure"] + [
unit_selections = ["Not Sure"] + [
uc[0]
for uc in db.session.query(Unit.descrip)
.filter_by(department_id=department_id)
.order_by(Unit.descrip.asc())
.all()
]
rank_choices = [
rank_selections = [
jc[0]
for jc in db.session.query(Job.job_title, Job.order)
.filter_by(department_id=department_id)
.order_by(Job.job_title)
.all()
]
if (units := request.args.getlist("unit")) and all(
unit in unit_choices for unit in units
unit in unit_selections for unit in units
):
form_data["unit"] = units
if (ranks := request.args.getlist("rank")) and all(
rank in rank_choices for rank in ranks
rank in rank_selections for rank in ranks
):
form_data["rank"] = ranks
if current_job_arg := request.args.get("current_job"):
Expand All @@ -731,24 +739,33 @@ def list_officer(
officers = filter_by_form(form_data, Officer.query, department_id).filter(
Officer.department_id == department_id
)
officers = officers.options(selectinload(Officer.face))

# Filter officers by presence of a photo
if form_data["require_photo"]:
officers = officers.join(Face)
else:
officers = officers.options(selectinload(Officer.face))
Comment on lines +742 to +746
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Picture filtering logic.


officers = officers.order_by(Officer.last_name, Officer.first_name, Officer.id)

officers = officers.paginate(
page=page, per_page=current_app.config["OFFICERS_PER_PAGE"], error_out=False
)

for officer in officers.items:
officer_face = sorted(officer.face, key=lambda x: x.featured, reverse=True)

# 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
# Could do some extra work to not lazy load images but load them all together.
# To do that properly 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,
"gender": GENDER_CHOICES,
"rank": [(rc, rc) for rc in rank_choices],
"unit": [(uc, uc) for uc in unit_choices],
"rank": [(rc, rc) for rc in rank_selections],
"unit": [(uc, uc) for uc in unit_selections],
}

next_url = url_for(
Expand All @@ -766,6 +783,7 @@ def list_officer(
unique_internal_identifier=form_data["unique_internal_identifier"],
unit=form_data["unit"],
current_job=form_data["current_job"],
require_photo=form_data["require_photo"],
)
prev_url = url_for(
"main.list_officer",
Expand All @@ -782,6 +800,7 @@ def list_officer(
unique_internal_identifier=form_data["unique_internal_identifier"],
unit=form_data["unit"],
current_job=form_data["current_job"],
require_photo=form_data["require_photo"],
)

return render_template(
Expand All @@ -793,7 +812,7 @@ def list_officer(
choices=choices,
next_url=next_url,
prev_url=prev_url,
jsloads=jsloads,
jsloads=js_loads,
)


Expand All @@ -812,7 +831,8 @@ def get_dept_ranks(department_id=None, is_sworn_officer=None):
ranks = ranks.order_by(Job.job_title).all()
rank_list = [(rank.id, rank.job_title) for rank in ranks]
else:
ranks = Job.query.all() # Not filtering by is_sworn_officer
# Not filtering by is_sworn_officer
ranks = Job.query.all()
# Prevent duplicate ranks
rank_list = sorted(
set((rank.id, rank.job_title) for rank in ranks),
Expand Down Expand Up @@ -863,11 +883,11 @@ def add_officer():
abort(HTTPStatus.FORBIDDEN)
if form.validate_on_submit():
# Work around for WTForms limitation with boolean fields in FieldList
new_formdata = request.form.copy()
for key in new_formdata.keys():
new_form_data = request.form.copy()
for key in new_form_data.keys():
if re.fullmatch(r"salaries-\d+-is_fiscal_year", key):
new_formdata[key] = "y"
form = AddOfficerForm(new_formdata)
new_form_data[key] = "y"
form = AddOfficerForm(new_form_data)
officer = add_officer_profile(form, current_user)
flash("New Officer {} added to OpenOversight".format(officer.last_name))
return redirect(url_for("main.submit_officer_images", officer_id=officer.id))
Expand Down Expand Up @@ -1025,7 +1045,8 @@ def label_data(department_id=None, image_id=None):
department = None
if image_id:
image = Image.query.filter_by(id=image_id).one()
else: # Select a random untagged image from the entire database
else:
# Select a random untagged image from the entire database
image_query = Image.query.filter_by(contains_cops=True).filter_by(
is_tagged=False
)
Expand All @@ -1052,9 +1073,8 @@ def label_data(department_id=None, image_id=None):
flash("Invalid officer ID. Please select a valid OpenOversight ID!")
elif department and officer_exists.department_id != department_id:
flash(
"The officer is not in {}. Are you sure that is the correct OpenOversight ID?".format(
department.name
)
"The officer is not in {}. Are you sure that is the correct "
"OpenOversight ID?".format(department.name)
)
elif not existing_tag:
left = form.dataX.data
Expand Down Expand Up @@ -1389,7 +1409,8 @@ def upload(department_id, officer_id=None):
image.contains_cops = True
face = Face(
officer_id=officer_id,
# Assuming photos uploaded with an officer ID are already cropped, so we set both images to the uploaded one
# Assuming photos uploaded with an officer ID are already cropped,
# we set both images to the uploaded one
img_id=image.id,
original_image_id=image.id,
user_id=current_user.get_id(),
Expand Down Expand Up @@ -1712,7 +1733,10 @@ def dispatch_request(self, *args, **kwargs):


class OfficerLinkApi(ModelView):
"""This API only applies to links attached to officer profiles, not links attached to incidents"""
"""
This API only applies to links attached to officer profiles, not links attached to
incidents.
"""

model = Link
model_name = "link"
Expand Down
14 changes: 12 additions & 2 deletions OpenOversight/app/static/css/openoversight.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,16 @@ a > .tutorial{
max-width:80%;
}

.center-text {
text-align: center;
}

/* Carousel base class */
.carousel {
min-height: 700px;
margin-bottom: 50px;
}

/* Since positioning the image, we need to help out the caption */
.carousel-caption {
display:block;
Expand Down Expand Up @@ -500,6 +505,12 @@ tr:hover .row-actions {
content: "\e253";
}

@media all and (min-width: 768px) and (max-width: 991px) {
.filter-sidebar > .form > .btn.btn-primary:first-of-type {
margin-bottom: 5px;
}
}

Comment on lines +504 to +509
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before media query:
Screenshot 2023-07-10 at 1 48 23 PM

After media query:
Screenshot 2023-07-10 at 1 48 34 PM

.search-results .list-group-item {
border: 0;
}
Expand Down Expand Up @@ -578,8 +589,7 @@ tr:hover .row-actions {
}

.officer-face {
min-width: 200px;
min-height: 200px;
height: 300px;
Comment on lines -581 to +588
Copy link
Collaborator Author

@michplunkett michplunkett Jul 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the previous setup you got these HUGE photos that threw off the flow of the page, so I set a standard height so that the width is automatically set according to its respective aspect ratio.

}

.face-wrap {
Expand Down
Loading