Exemple #1
0
def can_letter_job_be_cancelled(job):
    template = dao_get_template_by_id(job.template_id)
    if template.template_type != LETTER_TYPE:
        return (
            False,
            "Only letter jobs can be cancelled through this endpoint. This is not a letter job.",
        )

    notifications = Notification.query.filter(Notification.job_id == job.id).all()
    count_notifications = len(notifications)
    if job.job_status != JOB_STATUS_FINISHED or count_notifications != job.notification_count:
        return (
            False,
            "We are still processing these letters, please try again in a minute.",
        )
    count_cancellable_notifications = len([n for n in notifications if n.status in CANCELLABLE_JOB_LETTER_STATUSES])
    if count_cancellable_notifications != job.notification_count or not letter_can_be_cancelled(
        NOTIFICATION_CREATED, job.created_at
    ):
        return (
            False,
            "It’s too late to cancel sending, these letters have already been sent.",
        )

    return True, None
Exemple #2
0
def cancel_notification_for_service(service_id, notification_id):
    notification = notifications_dao.get_notification_by_id(
        notification_id, service_id)

    if not notification:
        raise InvalidRequest('Notification not found', status_code=404)
    elif notification.notification_type != LETTER_TYPE:
        raise InvalidRequest(
            'Notification cannot be cancelled - only letters can be cancelled',
            status_code=400)
    elif not letter_can_be_cancelled(notification.status,
                                     notification.created_at):
        print_day = letter_print_day(notification.created_at)

        raise InvalidRequest(
            "It’s too late to cancel this letter. Printing started {} at 5.30pm"
            .format(print_day),
            status_code=400)

    updated_notification = notifications_dao.update_notification_status_by_id(
        notification_id,
        NOTIFICATION_CANCELLED,
    )

    return jsonify(
        notification_with_template_schema.dump(updated_notification).data), 200
def get_letter_printing_statement(status, created_at):
    created_at_dt = parser.parse(created_at).replace(tzinfo=None)

    if letter_can_be_cancelled(status, created_at_dt):
        return 'Printing starts {} at 5:30pm'.format(
            printing_today_or_tomorrow())
    else:
        printed_datetime = utc_string_to_aware_gmt_datetime(
            created_at) + timedelta(hours=6, minutes=30)
        printed_date = _format_datetime_short(printed_datetime)

        return 'Printed on {}'.format(printed_date)
Exemple #4
0
def get_letter_printing_statement(status, created_at):
    created_at_dt = parser.parse(created_at).replace(tzinfo=None)
    if letter_can_be_cancelled(status, created_at_dt):
        return 'Printing starts {} at 5:30pm'.format(printing_today_or_tomorrow())
    else:
        printed_datetime = utc_string_to_aware_gmt_datetime(created_at) + timedelta(hours=6, minutes=30)
        if printed_datetime.date() == datetime.now().date():
            return 'Printed today at 5:30pm'
        elif printed_datetime.date() == datetime.now().date() - timedelta(days=1):
            return 'Printed yesterday at 5:30pm'

        printed_date = printed_datetime.strftime('%d %B').lstrip('0')

        return 'Printed on {} at 5:30pm'.format(printed_date)
Exemple #5
0
    def letter_job_can_be_cancelled(self):

        if self.template['template_type'] != 'letter':
            return False

        if any(self.uncancellable_notifications):
            return False

        if not letter_can_be_cancelled(
            'created',
            utc_string_to_aware_gmt_datetime(self.created_at).replace(tzinfo=None)
        ):
            return False

        return True
def get_letter_printing_statement(status, created_at, long_form=True):
    created_at_dt = parser.parse(created_at).replace(tzinfo=None)
    if letter_can_be_cancelled(status, created_at_dt):
        decription = 'Printing starts' if long_form else 'Printing'
        return f'{decription} {printing_today_or_tomorrow(created_at)} at 5:30pm'
    else:
        printed_datetime = utc_string_to_aware_gmt_datetime(created_at) + timedelta(hours=6, minutes=30)
        if printed_datetime.date() == datetime.now().date():
            return 'Printed today at 5:30pm'
        elif printed_datetime.date() == datetime.now().date() - timedelta(days=1):
            return 'Printed yesterday at 5:30pm'

        printed_date = printed_datetime.strftime('%d %B').lstrip('0')
        description = 'Printed on' if long_form else 'Printed'

        return f'{description} {printed_date} at 5:30pm'
Exemple #7
0
def test_letter_cannot_be_cancelled_if_1730_exactly_and_letter_created_at_or_before_1730(
        notification_created_at):
    notification_status = 'pending-virus-check'

    assert not letter_can_be_cancelled(notification_status,
                                       notification_created_at)
def get_job_partials(job, template):
    filter_args = parse_filter_args(request.args)
    filter_args['status'] = set_status_filters(filter_args)
    notifications = notification_api_client.get_notifications_for_service(
        job['service'], job['id'], status=filter_args['status'])

    if template['template_type'] == 'letter':
        # there might be no notifications if the job has only just been created and the tasks haven't run yet
        if notifications['notifications']:
            postage = notifications['notifications'][0]['postage']
        else:
            postage = template['postage']

        counts = render_template(
            'partials/jobs/count-letters.html',
            total=job.get('notification_count', 0),
            delivery_estimate=get_letter_timings(
                job['created_at'], postage=postage).earliest_delivery,
        )
    else:
        counts = render_template('partials/count.html',
                                 counts=_get_job_counts(job),
                                 status=filter_args['status'])
    service_data_retention_days = current_service.get_days_of_retention(
        template['template_type'])
    can_letter_job_be_cancelled = False
    if template["template_type"] == "letter":
        not_cancellable = [
            n for n in notifications["notifications"]
            if n["status"] not in CANCELLABLE_JOB_LETTER_STATUSES
        ]
        job_created = job["created_at"][:-6]
        if not letter_can_be_cancelled(
                "created",
                datetime.strptime(
                    job_created,
                    '%Y-%m-%dT%H:%M:%S.%f')) or len(not_cancellable) != 0:
            can_letter_job_be_cancelled = False
        else:
            can_letter_job_be_cancelled = True
    return {
        'counts':
        counts,
        'notifications':
        render_template(
            'partials/jobs/notifications.html',
            notifications=list(
                add_preview_of_content_to_notifications(
                    notifications['notifications'])),
            more_than_one_page=bool(
                notifications.get('links', {}).get('next')),
            percentage_complete=(job['notifications_requested'] /
                                 job['notification_count'] * 100),
            download_link=url_for('.view_job_csv',
                                  service_id=current_service.id,
                                  job_id=job['id'],
                                  status=request.args.get('status')),
            time_left=get_time_left(
                job['created_at'],
                service_data_retention_days=service_data_retention_days),
            job=job,
            template=template,
            template_version=job['template_version'],
        ),
        'status':
        render_template('partials/jobs/status.html',
                        job=job,
                        template_type=template["template_type"],
                        letter_print_day=get_letter_printing_statement(
                            "created", job["created_at"])),
        'can_letter_job_be_cancelled':
        can_letter_job_be_cancelled,
    }
def view_notification(service_id, notification_id):
    notification = notification_api_client.get_notification(
        service_id, str(notification_id))
    notification['template'].update(
        {'reply_to_text': notification['reply_to_text']})

    personalisation = get_all_personalisation_from_notification(notification)

    if notification['template']['is_precompiled_letter']:
        try:
            file_contents = view_letter_notification_as_preview(
                service_id, notification_id, "pdf")
            page_count = pdf_page_count(io.BytesIO(file_contents))
        except PdfReadError:
            return render_template(
                'views/notifications/invalid_precompiled_letter.html',
                created_at=notification['created_at'])
    else:
        page_count = get_page_count_for_letter(notification['template'],
                                               values=personalisation)

    if notification.get('postage'):
        notification['template']['postage'] = notification['postage']
    template = get_template(
        notification['template'],
        current_service,
        letter_preview_url=url_for(
            '.view_letter_notification_as_preview',
            service_id=service_id,
            notification_id=notification_id,
            filetype='png',
        ),
        page_count=page_count,
        show_recipient=True,
        redact_missing_personalisation=True,
    )
    template.values = personalisation
    if notification['job']:
        job = job_api_client.get_job(service_id,
                                     notification['job']['id'])['data']
    else:
        job = None

    letter_print_day = get_letter_printing_statement(
        notification['status'], notification['created_at'])

    notification_created = parser.parse(
        notification['created_at']).replace(tzinfo=None)

    show_cancel_button = notification['notification_type'] == 'letter' and \
        letter_can_be_cancelled(notification['status'], notification_created)

    if get_help_argument() or request.args.get('help') == '0':
        # help=0 is set when you’ve just sent a notification. We
        # only want to show the back link when you’ve navigated to a
        # notification, not when you’ve just sent it.
        back_link = None
    elif request.args.get('from_job'):
        back_link = url_for(
            'main.view_job',
            service_id=current_service.id,
            job_id=request.args.get('from_job'),
        )
    else:
        back_link = url_for(
            'main.view_notifications',
            service_id=current_service.id,
            message_type=template.template_type,
            status='sending,delivered,failed',
        )

    return render_template(
        'views/notifications/notification.html',
        finished=(notification['status']
                  in (DELIVERED_STATUSES + FAILURE_STATUSES)),
        notification_status=notification['status'],
        uploaded_file_name='Report',
        template=template,
        job=job,
        updates_url=url_for(".view_notification_updates",
                            service_id=service_id,
                            notification_id=notification['id'],
                            status=request.args.get('status'),
                            help=get_help_argument()),
        partials=get_single_notification_partials(notification),
        created_by=notification.get('created_by'),
        created_at=notification['created_at'],
        updated_at=notification['updated_at'],
        help=get_help_argument(),
        estimated_letter_delivery_date=get_letter_timings(
            notification['created_at'],
            postage=notification['postage']).earliest_delivery,
        notification_id=notification['id'],
        postage=notification['postage'],
        can_receive_inbound=(current_service.has_permission('inbound_sms')),
        is_precompiled_letter=notification['template']
        ['is_precompiled_letter'],
        letter_print_day=letter_print_day,
        show_cancel_button=show_cancel_button,
        sent_with_test_key=(notification.get('key_type') == KEY_TYPE_TEST),
        back_link=back_link,
    )
Exemple #10
0
def get_job_partials(job, template):
    filter_args = parse_filter_args(request.args)
    filter_args["status"] = set_status_filters(filter_args)
    notifications = notification_api_client.get_notifications_for_service(
        job["service"], job["id"], status=filter_args["status"])

    if template["template_type"] == "letter":
        # there might be no notifications if the job has only just been created and the tasks haven't run yet
        if notifications["notifications"]:
            postage = notifications["notifications"][0]["postage"]
        else:
            postage = template["postage"]

        counts = render_template(
            "partials/jobs/count-letters.html",
            total=job.get("notification_count", 0),
            delivery_estimate=get_letter_timings(
                job["created_at"], postage=postage).earliest_delivery,
        )
    else:
        counts = render_template(
            "partials/count.html",
            counts=_get_job_counts(job),
            status=filter_args["status"],
        )
    service_data_retention_days = current_service.get_days_of_retention(
        template["template_type"])
    can_letter_job_be_cancelled = False
    if template["template_type"] == "letter":
        not_cancellable = [
            n for n in notifications["notifications"]
            if n["status"] not in CANCELLABLE_JOB_LETTER_STATUSES
        ]
        job_created = job["created_at"][:-6]
        if (not letter_can_be_cancelled(
                "created",
                datetime.strptime(job_created, "%Y-%m-%dT%H:%M:%S.%f"))
                or len(not_cancellable) != 0):
            can_letter_job_be_cancelled = False
        else:
            can_letter_job_be_cancelled = True
    return {
        "counts":
        counts,
        "notifications":
        render_template(
            "partials/jobs/notifications.html",
            notifications=list(
                add_preview_of_content_to_notifications(
                    notifications["notifications"])),
            more_than_one_page=bool(
                notifications.get("links", {}).get("next")),
            percentage_complete=(job["notifications_requested"] /
                                 job["notification_count"] * 100),
            download_link=url_for(
                ".view_job_csv",
                service_id=current_service.id,
                job_id=job["id"],
                status=request.args.get("status"),
            ),
            available_until_date=get_available_until_date(
                job["created_at"],
                service_data_retention_days=service_data_retention_days,
            ),
            job=job,
            template=template,
            template_version=job["template_version"],
        ),
        "status":
        render_template(
            "partials/jobs/status.html",
            job=job,
            template=template,
            template_type=template["template_type"],
            template_version=job["template_version"],
            letter_print_day=get_letter_printing_statement(
                "created", job["created_at"]),
        ),
        "can_letter_job_be_cancelled":
        can_letter_job_be_cancelled,
    }
Exemple #11
0
def test_letter_can_be_cancelled_if_after_1730_and_letter_created_at_1730_today_or_later(
        notification_created_at):
    notification_status = 'created'

    assert letter_can_be_cancelled(notification_status,
                                   notification_created_at)
Exemple #12
0
def test_letter_cannot_be_cancelled_if_after_1730_and_letter_created_before_1730(
        notification_created_at):
    notification_status = 'created'

    assert not letter_can_be_cancelled(notification_status,
                                       notification_created_at)
def view_notification(service_id, notification_id):
    notification = notification_api_client.get_notification(service_id, str(notification_id))
    notification["template"].update({"reply_to_text": notification["reply_to_text"]})

    personalisation = get_all_personalisation_from_notification(notification)

    if notification["template"]["is_precompiled_letter"]:
        try:
            file_contents = view_letter_notification_as_preview(service_id, notification_id, "pdf")
            page_count = pdf_page_count(io.BytesIO(file_contents))
        except PdfReadError:
            return render_template(
                "views/notifications/invalid_precompiled_letter.html",
                created_at=notification["created_at"],
            )
    else:
        page_count = get_page_count_for_letter(notification["template"], values=personalisation)

    if notification.get("postage"):
        notification["template"]["postage"] = notification["postage"]
    template = get_template(
        notification["template"],
        current_service,
        letter_preview_url=url_for(
            ".view_letter_notification_as_preview",
            service_id=service_id,
            notification_id=notification_id,
            filetype="png",
        ),
        page_count=page_count,
        show_recipient=True,
        redact_missing_personalisation=True,
    )
    template.values = personalisation
    if notification["job"]:
        job = job_api_client.get_job(service_id, notification["job"]["id"])["data"]
    else:
        job = None

    letter_print_day = get_letter_printing_statement(notification["status"], notification["created_at"])

    notification_created = parser.parse(notification["created_at"]).replace(tzinfo=None)

    show_cancel_button = notification["notification_type"] == "letter" and letter_can_be_cancelled(
        notification["status"], notification_created
    )

    if get_help_argument() or request.args.get("help") == "0":
        # help=0 is set when you’ve just sent a notification. We
        # only want to show the back link when you’ve navigated to a
        # notification, not when you’ve just sent it.
        back_link = None
    elif request.args.get("from_job"):
        back_link = url_for(
            "main.view_job",
            service_id=current_service.id,
            job_id=request.args.get("from_job"),
        )
    else:
        back_link = url_for(
            "main.view_notifications",
            service_id=current_service.id,
            message_type=template.template_type,
            status="sending,delivered,failed",
        )

    return render_template(
        "views/notifications/notification.html",
        finished=(notification["status"] in (DELIVERED_STATUSES + FAILURE_STATUSES)),
        notification_status=notification["status"],
        uploaded_file_name="Report",
        template=template,
        job=job,
        updates_url=url_for(
            ".view_notification_updates",
            service_id=service_id,
            notification_id=notification["id"],
            status=request.args.get("status"),
            help=get_help_argument(),
        ),
        partials=get_single_notification_partials(notification),
        created_by=notification.get("created_by"),
        created_at=notification["created_at"],
        updated_at=notification["updated_at"],
        help=get_help_argument(),
        estimated_letter_delivery_date=get_letter_timings(
            notification["created_at"], postage=notification["postage"]
        ).earliest_delivery,
        notification_id=notification["id"],
        postage=notification["postage"],
        can_receive_inbound=(current_service.has_permission("inbound_sms")),
        is_precompiled_letter=notification["template"]["is_precompiled_letter"],
        letter_print_day=letter_print_day,
        show_cancel_button=show_cancel_button,
        sent_with_test_key=(notification.get("key_type") == KEY_TYPE_TEST),
        back_link=back_link,
        just_sent=request.args.get("just_sent"),
        attachments=get_attachments(notification, "attach").values(),
    )
Exemple #14
0
def test_letter_cannot_be_cancelled_if_letter_status_is_not_created_or_pending_virus_check(
        status):
    notification_created_at = datetime.utcnow()

    assert not letter_can_be_cancelled(status, notification_created_at)
def test_letter_can_be_cancelled_always_compares_in_bst(
        notification_created_at):
    assert letter_can_be_cancelled('created', notification_created_at)
def test_letter_cannot_be_cancelled_if_before_1730_and_letter_created_after_1730_two_days_ago(
):
    notification_status = 'created'

    assert not letter_can_be_cancelled(notification_status,
                                       datetime(2018, 7, 5, 19, 0))
def test_letter_cannot_be_cancelled_if_before_1730_and_letter_created_before_1730_yesterday(
):
    notification_status = 'created'

    assert not letter_can_be_cancelled(notification_status,
                                       datetime(2018, 7, 6, 14, 0))