def test_extend_date_overdue(self): request = self.rf.create_request_as_anonymous_user( due_date=calendar.addbusdays(datetime.utcnow(), -2)) date = calendar.addbusdays(utc_to_local(request.due_date, self.tz_name), 1) due_date = process_due_date(local_to_utc(date, self.tz_name)) response = request.extend(date=date) self.__test_extension( response, determination_type.EXTENSION, str, due_date, request_status.OVERDUE, request=request)
def test_set_due_soon(self): self.__test_due_soon_or_overdue( request_status.DUE_SOON, calendar.addbusdays( datetime.utcnow(), current_app.config["DUE_SOON_DAYS_THRESHOLD"] ).replace(hour=23, minute=59, second=59, microsecond=0), )
def test_set_overdue(self): self.__test_due_soon_or_overdue( request_status.OVERDUE, calendar.addbusdays( datetime.utcnow(), -1 ).replace(microsecond=0) )
def get_following_date(date_created): """ Generates the date submitted for a request. :param date_created: date (local) the request was made :return: date submitted (local) which is the date_created rounded off to the next business day """ return calendar.addbusdays(date_created, FOLLOWING)
def get_following_date(date_created): """ Generates the date submitted for a request. :param date_created: date (local) the request was made :return: date submitted (local) which is the date_created rounded off to the next business day """ return calendar.addbusdays(date_created, FOLLOWING)
def set_agency_request_summary_privacy(self, privacy: bool): release_date = calendar.addbusdays( datetime.utcnow(), RELEASE_PUBLIC_DAYS) if not privacy else None self.__update({ "privacy": { "agency_request_summary": privacy }, "agency_request_summary_release_date": release_date })
def set_overdue(self, shift_dates=True): """ Shift request date field values, with due date set to yesterday, and sets request statues to "overdue". :param shift_dates: shift dates or only change status? """ self.__set_due_soon_or_overdue( request_status.OVERDUE, calendar.addbusdays(datetime.utcnow(), -1).replace(microsecond=0), shift_dates)
def get_due_date(date_submitted, days_until_due): """ Generates the due date for a request. :param date_submitted: date submitted which is the date_created rounded off to the next business day :param days_until_due: number of business days until a request is due :return: due date with time set to 5:00 PM EST (10:00 PM UTC) """ return process_due_date(calendar.addbusdays(date_submitted, days_until_due))
def __init__(self, request_id, privacy, date_modified=None, is_editable=False): self.request_id = request_id self.privacy = privacy self.date_modified = date_modified or datetime.utcnow() self.release_date = (calendar.addbusdays( datetime.utcnow(), RELEASE_PUBLIC_DAYS) if privacy == response_privacy.RELEASE_AND_PUBLIC else None) self.is_editable = is_editable
def get_due_date(date_submitted, days_until_due, tz_name): """ Generates the due date for a request. :param date_submitted: date submitted (local) which is the date_created rounded off to the next business day :param days_until_due: number of business days until a request is due :param tz_name: time zone name (e.g. "America/New_York") :return: due date (utc) with time set to 22:00 PM (5:00 PM EST) """ return process_due_date( local_to_utc(calendar.addbusdays(date_submitted, days_until_due), tz_name))
def get_due_date(date_submitted, days_until_due, tz_name): """ Generates the due date for a request. :param date_submitted: date submitted (local) which is the date_created rounded off to the next business day :param days_until_due: number of business days until a request is due :param tz_name: time zone name (e.g. "America/New_York") :return: due date (utc) with time set to 22:00 PM (5:00 PM EST) """ return process_due_date( local_to_utc(calendar.addbusdays(date_submitted, days_until_due), tz_name) )
def set_due_soon(self, days=current_app.config["DUE_SOON_DAYS_THRESHOLD"], shift_dates=True): """ Shift request date field values, with due date set to up to 2 days (DUE_SOON_DAYS_THRESHOLD) from now, and sets request statues to "due_soon". :param days: days until request is due (max: 2) :param shift_dates: shift dates or only change status? """ days = min(days, current_app.config["DUE_SOON_DAYS_THRESHOLD"]) self.__set_due_soon_or_overdue( request_status.DUE_SOON, calendar.addbusdays(datetime.utcnow(), days).replace(hour=23, minute=59, second=59, microsecond=0), shift_dates)
def __close(self, close_type, user, reason_ids=None): if reason_ids is None: reasons = "|".join( (r.content for r in Reasons.query.filter_by(type=close_type).order_by( func.random()).limit(random.randrange(1, 6)).all())) else: reasons = format_determination_reasons(reason_ids) self.__update({ "status": request_status.CLOSED, "agency_request_summary_release_date": calendar.addbusdays(datetime.utcnow(), RELEASE_PUBLIC_DAYS) }) response = Determinations( self.request.id, response_privacy.RELEASE_AND_PUBLIC, close_type, reasons, ) create_object(response) self.__create_event(event_type.REQ_CLOSED, response, user) return response
def test_extend_bad_due_date(self): with self.assertRaises(AssertionError): self.request.extend(date=calendar.addbusdays( utc_to_local(self.request.due_date, self.tz_name), -1))
def get_release_date(initial_date, days_until_release, tz_name): release_date = calendar.addbusdays(initial_date, days_until_release) return utc_to_local(release_date, tz_name)
def create_request( self, user, title=None, description=None, agency_request_summary=None, # TODO: agency_request_summary_release_date agency_ein=None, date_created=None, date_submitted=None, due_date=None, category=None, title_privacy=True, agency_request_summary_privacy=True, submission=None, status=request_status.OPEN, tz_name=current_app.config["APP_TIMEZONE"]): """ Create a request as the supplied user. An anonymous requester will be created if the supplied user is an agency user. :rtype: RequestWrapper """ # check due date if (date_created is not None or date_submitted is not None) and due_date is not None: def assert_date(date, date_var_str): assert ( due_date - date ).days >= 1, "due_date must be at least 1 day after " + date_var_str if date_created is not None: assert_date(date_created, "date_created") if date_submitted is not None: assert_date(date_submitted, "date_submitted") # check agency_ein if (agency_ein is not None or self.agency_ein is not None) \ and user.auth_user_type == user_type_auth.AGENCY_USER: assert (agency_ein or self.agency_ein) == user.agency_ein, \ "user's agency ein must match supplied agency ein" agency_ein = agency_ein or self.agency_ein or user.agency_ein or get_random_agency( ).ein # create dates date_created_local = utc_to_local(date_created or datetime.utcnow(), tz_name) date_submitted_local = date_submitted or get_following_date( date_created_local) due_date = due_date or get_due_date(date_submitted_local, ACKNOWLEDGMENT_DAYS_DUE, tz_name) date_created = date_created or local_to_utc(date_created_local, tz_name) date_submitted = date_submitted or local_to_utc( date_submitted_local, tz_name) # create request request = Requests( generate_request_id(agency_ein), title or fake.title(), description or fake.description(), agency_ein=agency_ein, date_created=date_created or datetime.utcnow(), date_submitted=date_submitted, due_date=due_date, category=category, privacy={ "title": title_privacy, "agency_request_summary": agency_request_summary_privacy }, submission=submission or random.choice(submission_methods.ALL), status=status, ) if agency_request_summary is not None: request.agency_request_summary = agency_request_summary if agency_request_summary_privacy is not None: request.agency_request_summary_release_date = calendar.addbusdays( datetime.utcnow(), RELEASE_PUBLIC_DAYS ) if not agency_request_summary_privacy else None create_object(request) request = RequestWrapper(request, self.agency_user) # create events timestamp = datetime.utcnow() create_object( Events(user_guid=user.guid, auth_user_type=user.auth_user_type, request_id=request.id, type_=event_type.REQ_CREATED, timestamp=timestamp, new_value=request.val_for_events)) if user.is_agency: create_object( Events(user_guid=user.guid, auth_user_type=user.auth_user_type, request_id=request.id, type_=event_type.AGENCY_REQ_CREATED, timestamp=timestamp)) # add users if user.is_public or user.is_anonymous_requester: request.add_user(user) if user.is_agency: # then create and add anonymous requester request.add_user(self.__uf.create_anonymous_user()) for admin in Agencies.query.filter_by( ein=agency_ein).one().administrators: request.add_user(admin) # create request doc now that requester is set request.es_create() return request
def update_request_statuses(): """ Update statuses for all requests that are now Due Soon or Overdue and send a notification email to agency admins listing the requests. """ with scheduler.app.app_context(): now = datetime.utcnow() due_soon_date = calendar.addbusdays( now, current_app.config['DUE_SOON_DAYS_THRESHOLD']).replace( hour=23, minute=59, second=59) # the entire day requests_overdue = Requests.query.filter( Requests.due_date < now, Requests.status != request_status.CLOSED).order_by( Requests.due_date.asc()).all() requests_due_soon = Requests.query.filter( Requests.due_date > now, Requests.due_date <= due_soon_date, Requests.status != request_status.CLOSED).order_by( Requests.due_date.asc()).all() agencies_to_requests_overdue = {} agencies_to_acknowledgments_overdue = {} agencies_to_requests_due_soon = {} agencies_to_acknowledgments_due_soon = {} def add_to_agencies_to_request_dict(req, agencies_to_request_dict): if req.agency.ein not in agencies_to_request_dict: agencies_to_request_dict[req.agency.ein] = [req] else: agencies_to_request_dict[req.agency.ein].append(req) # OVERDUE for request in requests_overdue: if request.was_acknowledged: add_to_agencies_to_request_dict(request, agencies_to_requests_overdue) else: add_to_agencies_to_request_dict( request, agencies_to_acknowledgments_overdue) if request.status != request_status.OVERDUE: update_object({"status": request_status.OVERDUE}, Requests, request.id) # DUE SOON for request in requests_due_soon: if request.was_acknowledged: add_to_agencies_to_request_dict(request, agencies_to_requests_due_soon) else: add_to_agencies_to_request_dict( request, agencies_to_acknowledgments_due_soon) if request.status != request_status.DUE_SOON: update_object({"status": request_status.DUE_SOON}, Requests, request.id) # get all possible agencies to email agency_eins = set( list(agencies_to_requests_overdue) + list(agencies_to_acknowledgments_overdue) + list(agencies_to_requests_due_soon) + list(agencies_to_acknowledgments_due_soon)) # mail to agency admins for each agency for agency_ein in agency_eins: agency_requests_overdue = agencies_to_requests_overdue.get( agency_ein, []) agency_acknowledgments_overdue = agencies_to_acknowledgments_overdue.get( agency_ein, []) agency_requests_due_soon = agencies_to_requests_due_soon.get( agency_ein, []) agency_acknowledgments_due_soon = agencies_to_acknowledgments_due_soon.get( agency_ein, []) user_emails = list( set(admin.notification_email or admin.email for admin in Agencies.query.filter_by( ein=agency_ein).one().administrators)) send_email( STATUSES_EMAIL_SUBJECT, to=user_emails, template=STATUSES_EMAIL_TEMPLATE, requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon) email = Emails( request.id, PRIVATE, to=','.join(user_emails), cc=None, bcc=None, subject=STATUSES_EMAIL_SUBJECT, body=render_template( STATUSES_EMAIL_TEMPLATE + ".html", requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon)) create_object(email) create_object( Events(request.id, user_guid=None, auth_user_type=None, type_=EMAIL_NOTIFICATION_SENT, previous_value=None, new_value=email.val_for_events, response_id=None, timestamp=datetime.utcnow()))
def _update_request_statuses(): """ Update statuses for all requests that are now Due Soon or Overdue and send a notification email to agency admins listing the requests. """ now = datetime.utcnow() due_soon_date = calendar.addbusdays( now, current_app.config['DUE_SOON_DAYS_THRESHOLD']).replace( hour=23, minute=59, second=59) # the entire day agencies = Agencies.query.with_entities( Agencies.ein).filter_by(is_active=True).all() for agency_ein, in agencies: 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()).all() 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()).all() 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, auth_user_type=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) # 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, auth_user_type=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) # mail to agency admins for each agency user_emails = list( set(admin.notification_email or admin.email for admin in Agencies.query.filter_by( ein=agency_ein).one().administrators)) send_email(STATUSES_EMAIL_SUBJECT, to=user_emails, template=STATUSES_EMAIL_TEMPLATE, requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon) email = Emails( request.id, PRIVATE, to=','.join(user_emails), cc=None, bcc=None, subject=STATUSES_EMAIL_SUBJECT, body=render_template( STATUSES_EMAIL_TEMPLATE + ".html", requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon)) create_object(email) create_object( Events(request.id, user_guid=None, auth_user_type=None, type_=EMAIL_NOTIFICATION_SENT, previous_value=None, new_value=email.val_for_events, response_id=None, timestamp=datetime.utcnow()))
def get_release_date(initial_date, days_until_release, tz_name): release_date = calendar.addbusdays(initial_date, days_until_release) return utc_to_local(release_date, tz_name)
def _update_request_statuses(): """ Update statuses for all requests that are now Due Soon or Overdue and send a notification email to agency admins listing the requests. """ now = datetime.utcnow() due_soon_date = calendar.addbusdays( now, current_app.config['DUE_SOON_DAYS_THRESHOLD'] ).replace(hour=23, minute=59, second=59) # the entire day agencies = Agencies.query.with_entities(Agencies.ein).filter_by(is_active=True).all() for agency_ein, in agencies: 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() ).all() 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() ).all() 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, ) ) update_object( {"status": request_status.OVERDUE}, Requests, request.id) # 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, ) ) update_object( {"status": request_status.DUE_SOON}, Requests, request.id) # mail to agency admins for each agency user_emails = list(set(admin.notification_email or admin.email for admin in Agencies.query.filter_by(ein=agency_ein).one().administrators)) send_email( STATUSES_EMAIL_SUBJECT, to=user_emails, template=STATUSES_EMAIL_TEMPLATE, requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon ) email = Emails( request.id, PRIVATE, to=','.join(user_emails), cc=None, bcc=None, subject=STATUSES_EMAIL_SUBJECT, body=render_template( STATUSES_EMAIL_TEMPLATE + ".html", requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon ) ) create_object(email) create_object( Events( request.id, user_guid=None, type_=EMAIL_NOTIFICATION_SENT, previous_value=None, new_value=email.val_for_events, response_id=None, timestamp=datetime.utcnow() ) )
def test_reopen(self): date = calendar.addbusdays(utc_to_local(self.request.due_date, self.tz_name), 1) response = self.request.reopen(date) due_date = process_due_date(local_to_utc(date, self.tz_name)) self.__test_extension(response, determination_type.REOPENING, None, due_date) self.assertEqual(self.request.agency_request_summary, None)
def __init__(self, response_id, expiration_date=None): self.token = self.generate_token() self.response_id = response_id self.expiration_date = expiration_date or calendar.addbusdays( datetime.utcnow(), DEFAULT_RESPONSE_TOKEN_EXPIRY_DAYS)
def test_acknowledge_date(self): date = calendar.addbusdays(datetime.now(), 100) response = self.request.acknowledge(date=date) due_date = process_due_date(local_to_utc(date, self.tz_name)) self.__test_extension(response, determination_type.ACKNOWLEDGMENT, str, due_date)