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

OpenRecords v3.5 - Develop #578

Open
wants to merge 60 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
e7a3395
Fixed open data compliance report missing referrer header error
johnyu95 Jan 27, 2023
a9daa6f
Updated google translate styles
johnyu95 Feb 22, 2023
78da8ff
Fixed super user toggle bug
johnyu95 Mar 2, 2023
7898277
Upgraded bootstrap and javascript dependencies
johnyu95 Mar 9, 2023
7a5a593
Updated FDNY agency instructions
johnyu95 Mar 14, 2023
3f18940
Updated jQuery to 3.6.4
johnyu95 Mar 15, 2023
cb921c4
QA fixes
johnyu95 Mar 20, 2023
3384e70
Merge pull request #562 from CityOfNewYork/johnyu95-dependencies-upgrade
johnyu95 Mar 20, 2023
c5aee6b
Updated FDNY custom forms
johnyu95 Mar 29, 2023
3ad8a64
Added Juneteenth to holiday calendar
johnyu95 Apr 7, 2023
1dc43cf
Removed noreferrer from open data report
johnyu95 Apr 12, 2023
6c5769c
Added Full Fire Marshal Report to FDNY custom forms
johnyu95 Jun 7, 2023
88e0bc5
Updated Open Data Compliance Report to filter on Response date instea…
johnyu95 Jun 7, 2023
ec9f38c
Merge pull request #564 from CityOfNewYork/johnyu95-full-fire-marshal…
johnyu95 Jun 12, 2023
3974fc0
Update _generate_signature method to use HMAC-SHA256
johnyu95 Jun 13, 2023
4987fd0
Disable submit button on submission for Contact the Agency form
johnyu95 Jun 27, 2023
faf17ff
Added validation for form name when submitting custom request forms
johnyu95 Aug 14, 2023
c070b49
Updated recaptcha implementation
johnyu95 Sep 15, 2023
05828c1
Added try/except to recaptcha response verification
johnyu95 Sep 19, 2023
6256f55
Merge pull request #566 from CityOfNewYork/johnyu95-recaptcha
johnyu95 Sep 20, 2023
3f82981
Merge pull request #565 from CityOfNewYork/johnyu95-update-generate-s…
johnyu95 Sep 20, 2023
516e251
Optimized update request statuses job
johnyu95 Oct 3, 2023
bf7124b
Updated sorting and exception handling for request statuses job
johnyu95 Oct 5, 2023
25db3be
Removed debugging code
johnyu95 Oct 5, 2023
c632d8c
Updated due soon requests queries to sort by id
johnyu95 Oct 6, 2023
6faf098
Additional logging for recaptcha errors
johnyu95 Oct 16, 2023
547020c
Merge pull request #567 from CityOfNewYork/johnyu95-fix-update-reques…
zgary Oct 16, 2023
9311867
Removed recaptcha for agency users
johnyu95 Oct 30, 2023
bde8d95
Merge pull request #568 from CityOfNewYork/johnyu95-remove-agency-rec…
johnyu95 Oct 31, 2023
481e1fc
Added is_active column to custom request forms table
johnyu95 Nov 8, 2023
970e9ae
Fixed issue with custom forms not loading consistently
johnyu95 Nov 8, 2023
8d8cd25
Merge pull request #569 from CityOfNewYork/johnyu95-fix-custom-forms-…
zgary Nov 8, 2023
a1c44e3
Updated FDNY agency instructions
johnyu95 Nov 13, 2023
092cf33
Fixed typos in FDNY agency instructions
johnyu95 Nov 13, 2023
5d5f765
Added validation to check that custom request form metadata exists
johnyu95 Nov 16, 2023
4a2f3fe
Updated backend validation for custom request forms on submission
johnyu95 Nov 20, 2023
3b107a0
Merge pull request #571 from CityOfNewYork/johnyu95-fix-custom-forms-…
johnyu95 Nov 20, 2023
9294c70
Added error logger for contact the agency functionality
johnyu95 Dec 15, 2023
646fcbd
Updated sender and reply to emails for Contact the Agency feature
johnyu95 Dec 22, 2023
ddb9ee0
Updated FDNY agency instructions and reverted custom request form names
johnyu95 Jan 16, 2024
eef921c
Updated footer year
johnyu95 Jan 17, 2024
1b75e0b
Updated es_update function for Requests
johnyu95 Jan 19, 2024
ea02b36
Fixed typo in FDNY custom request form name
johnyu95 Jan 19, 2024
74b09d1
Updates to FDNY agency instructions
johnyu95 Jan 22, 2024
976aa2f
Merge pull request #572 from CityOfNewYork/johnyu95-contact-the-agenc…
johnyu95 Jan 22, 2024
aa7bbae
Merge pull request #573 from CityOfNewYork/johnyu95-contact-the-agenc…
johnyu95 Jan 22, 2024
cd1bdf6
Merge pull request #570 from CityOfNewYork/johnyu95-fdny-updates
johnyu95 Jan 22, 2024
4e2b291
Added new denial and closing reasons
johnyu95 Jan 30, 2024
c3fbf09
Added missing citation to reason
johnyu95 Jan 30, 2024
b9f387c
Updated FDNY agency instructions
johnyu95 Jan 31, 2024
b17a859
Updated logic for contact emails
johnyu95 Feb 15, 2024
2eca4a1
Updated values for NYPD letter signature in agency JSON
johnyu95 Feb 26, 2024
a9ba3e9
Updated About page text
johnyu95 Mar 4, 2024
3a536fc
Merge pull request #574 from CityOfNewYork/johnyu95-new-reasons
zgary Mar 11, 2024
25c48de
Merge pull request #575 from CityOfNewYork/johnyu95-contact-the-agenc…
zgary Mar 11, 2024
634ffbd
Merge pull request #576 from CityOfNewYork/johnyu95-update-nypd-lette…
zgary Mar 11, 2024
9f6d077
Merge pull request #577 from CityOfNewYork/johnyu95-about-page-update
zgary Mar 11, 2024
7609acc
Merge branch 'develop' into main-merge
johnyu95 Apr 1, 2024
1eae68e
Merged with main
johnyu95 Apr 1, 2024
52f7b88
Fixed typo in models.py
johnyu95 Apr 1, 2024
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
6 changes: 4 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ LDAP_BASE_DN=<LDAP SEARCH BASE>
USE_LOCAL_AUTH=<USE LOCAL AUTHENTICATION>

# ReCaptcha
RECAPTCHA_SITE_KEY_V3=<SITE KEY>
RECAPTCHA_SECRET_KEY_V3=<SECRET KEY>
RECAPTCHA_ENABLED=
RECAPTCHA_PUBLIC_KEY=
RECAPTCHA_PRIVATE_KEY=
RECAPTCHA_THRESHOLD=

# Sentry
SENTRY_DSN=<SENTRY DSN>
Expand Down
17 changes: 11 additions & 6 deletions app/agency/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,7 @@ def get_custom_request_form_options(agency_ein):
CustomRequestForms.repeatable,
CustomRequestForms.category,
CustomRequestForms.minimum_required).filter_by(
agency_ein=agency_ein).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.id)).all()
# Convert the results of with_entities back to tuple format so that jsonify can be used
custom_request_forms = [tuple(form) for form in custom_request_forms]
agency_ein=agency_ein, is_active=True).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.order)).all()
return jsonify(custom_request_forms), 200


Expand Down Expand Up @@ -177,12 +175,19 @@ def get_request_types(agency_ein):
if current_user.is_agency_active(agency_ein):
agency = Agencies.query.filter_by(ein=agency_ein).one_or_none()
if agency is not None and agency.agency_features['custom_request_forms']['enabled']:
request_types = [
active_request_types = [
(custom_request_form.form_name, custom_request_form.form_name)
for custom_request_form in CustomRequestForms.query.filter_by(
agency_ein=agency_ein
).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.id)).all()
agency_ein=agency_ein, is_active=True
).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.order)).all()
]
inactive_request_types = [
(custom_request_form.form_name, "(Inactive) " + custom_request_form.form_name)
for custom_request_form in CustomRequestForms.query.filter_by(
agency_ein=agency_ein, is_active=False
).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.order)).all()
]
request_types = active_request_types + inactive_request_types
request_types.insert(0, ("", "All"))
return jsonify({"request_types": request_types}), 200

Expand Down
126 changes: 78 additions & 48 deletions app/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
from psycopg2 import OperationalError
from sqlalchemy.exc import SQLAlchemyError

from app import calendar, db, store
from app.constants import OPENRECORDS_DL_EMAIL, request_status
from app import calendar, sentry, store, db
from app.constants import OPENRECORDS_DL_EMAIL, request_status, determination_type
from app.constants.event_type import EMAIL_NOTIFICATION_SENT, REQ_STATUS_CHANGED
from app.constants.response_privacy import PRIVATE
from app.lib.db_utils import create_object, update_object
from app.lib.email_utils import send_email
from app.models import Agencies, Emails, Events, Requests, Users
from app.models import Agencies, Emails, Events, Requests, Responses, Determinations, Users

# NOTE: (For Future Reference)
# If we find ourselves in need of a request context, app.test_request_context() might come in handy.
Expand Down Expand Up @@ -45,81 +45,104 @@ def _update_request_statuses():
due_soon_date = calendar.addbusdays(
now, current_app.config['DUE_SOON_DAYS_THRESHOLD']
).replace(hour=23, minute=59, second=59) # the entire day
request_errors = []

agencies = Agencies.query.with_entities(Agencies.ein).filter_by(is_active=True).all()
for agency_ein, in agencies:
# Overdue requests
requests_overdue = Requests.query.filter(
Requests.due_date < now,
Requests.status != request_status.CLOSED,
Requests.agency_ein == agency_ein
).order_by(
Requests.due_date.asc()
Requests.id.asc()
).all()

# Query for all acknowledged overdue requests
agency_requests_overdue = Requests.query.join(Responses, Determinations).filter(
Requests.due_date < now,
Requests.status != request_status.CLOSED,
Requests.agency_ein == agency_ein,
Determinations.dtype == determination_type.ACKNOWLEDGMENT
).order_by(
Requests.id.asc()
).all()

# Get the difference for all unacknowledged overdue requests
agency_acknowledgments_overdue = list(set(requests_overdue) - set(agency_requests_overdue))
agency_acknowledgments_overdue.sort(key=lambda x: x.id)

# Due soon requests
requests_due_soon = Requests.query.filter(
Requests.due_date > now,
Requests.due_date <= due_soon_date,
Requests.status != request_status.CLOSED,
Requests.agency_ein == agency_ein
).order_by(
Requests.due_date.asc()
Requests.id.asc()
).all()

# Query for all acknowledged due soon requests
agency_requests_due_soon = Requests.query.join(Responses, Determinations).filter(
Requests.due_date > now,
Requests.due_date <= due_soon_date,
Requests.status != request_status.CLOSED,
Requests.agency_ein == agency_ein,
Determinations.dtype == determination_type.ACKNOWLEDGMENT
).order_by(
Requests.id.asc()
).all()

# Get the difference for all unacknowledged due soon requests
agency_acknowledgments_due_soon = list(set(requests_due_soon) - set(agency_requests_due_soon))
agency_acknowledgments_due_soon.sort(key=lambda x: x.id)

if not requests_overdue and not requests_due_soon:
continue

agency_requests_overdue = []
agency_acknowledgments_overdue = []
agency_requests_due_soon = []
agency_acknowledgments_due_soon = []

# OVERDUE
for request in requests_overdue:

if request.was_acknowledged:
agency_requests_overdue.append(request)
else:
agency_acknowledgments_overdue.append(request)

if request.status != request_status.OVERDUE:
create_object(
Events(
request.id,
user_guid=None,
type_=REQ_STATUS_CHANGED,
previous_value={"status": request.status},
new_value={"status": request_status.OVERDUE},
response_id=None,
try:
update_object(
{"status": request_status.OVERDUE},
Requests,
request.id)
create_object(
Events(
request.id,
user_guid=None,
type_=REQ_STATUS_CHANGED,
previous_value={"status": request.status},
new_value={"status": request_status.OVERDUE},
response_id=None,
)
)
)
update_object(
{"status": request_status.OVERDUE},
Requests,
request.id)
except Exception:
request_errors.append(
(request.id, traceback.format_exc().replace("\n", "<br/>").replace(" ", "&nbsp;")))

# DUE SOON
for request in requests_due_soon:

if request.was_acknowledged:
agency_requests_due_soon.append(request)
else:
agency_acknowledgments_due_soon.append(request)

if request.status != request_status.DUE_SOON:
create_object(
Events(
request.id,
user_guid=None,
type_=REQ_STATUS_CHANGED,
previous_value={"status": request.status},
new_value={"status": request_status.DUE_SOON},
response_id=None,
try:
update_object(
{"status": request_status.DUE_SOON},
Requests,
request.id)
create_object(
Events(
request.id,
user_guid=None,
type_=REQ_STATUS_CHANGED,
previous_value={"status": request.status},
new_value={"status": request_status.DUE_SOON},
response_id=None,
)
)
)
update_object(
{"status": request_status.DUE_SOON},
Requests,
request.id)
except Exception:
request_errors.append(
(request.id, traceback.format_exc().replace("\n", "<br/>").replace(" ", "&nbsp;")))

# mail to agency admins for each agency
user_emails = list(set(admin.notification_email or admin.email for admin
Expand Down Expand Up @@ -161,6 +184,13 @@ def _update_request_statuses():
timestamp=datetime.utcnow()
)
)
send_email(
'Update Request Statuses Job Finished',
to=[OPENRECORDS_DL_EMAIL],
template='email_templates/email_update_request_statuses_job_finished',
timestamp=str(datetime.utcnow()),
request_errors=request_errors
)


@app.task(autoretry_for=(OperationalError, SQLAlchemyError,), retry_kwargs={'max_retries': 5}, retry_backoff=True)
Expand Down
9 changes: 9 additions & 0 deletions app/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ def _populate(self, year):
elif year >= 1888:
self[date(year, 5, 30)] = "Memorial Day"

# Juneteenth Day
if year > 2021:
name = "Juneteenth Day"
self[date(year, 6, 19)] = name
if self.observed and date(year, 6, 19).weekday() == 5:
self[date(year, 6, 19) + rd(days=-1)] = name + " (Observed)"
elif self.observed and date(year, 6, 19).weekday() == 6:
self[date(year, 6, 19) + rd(days=+1)] = name + " (Observed)"

# Independence Day
if year > 1870:
name = "Independence Day"
Expand Down
4 changes: 2 additions & 2 deletions app/lib/email_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def send_async_email(msg):
current_app.logger.exception("Failed to Send Email {} : {}".format(msg, e))


def send_contact_email(subject, recipients, body, sender):
msg = Message(subject, recipients, body, sender=sender)
def send_contact_email(subject, recipients, body, sender, reply_to):
msg = Message(subject, recipients, body, sender=sender, reply_to=reply_to)
send_async_email.delay(msg)


Expand Down
Loading