Example #1
0
 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)
Example #2
0
 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),
     )
Example #3
0
 def test_set_overdue(self):
     self.__test_due_soon_or_overdue(
         request_status.OVERDUE,
         calendar.addbusdays(
             datetime.utcnow(), -1
         ).replace(microsecond=0)
     )
Example #4
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)
Example #6
0
 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
     })
Example #7
0
    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)
Example #8
0
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))
Example #9
0
 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
Example #10
0
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)
    )
Example #12
0
    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)
Example #13
0
 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
Example #14
0
 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))
Example #15
0
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)
Example #16
0
    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
Example #17
0
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()))
Example #18
0
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)
Example #20
0
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()
            )
        )
Example #21
0
 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)
Example #22
0
 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)
Example #23
0
 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)