Files
notifications-admin/app/models/job.py

219 lines
6.1 KiB
Python
Raw Normal View History

from collections import defaultdict
from datetime import datetime
from notifications_utils.letter_timings import (
CANCELLABLE_JOB_LETTER_STATUSES,
get_letter_timings,
letter_can_be_cancelled,
)
from werkzeug.utils import cached_property
from app.models import JSONModel, ModelList
from app.notify_client.job_api_client import job_api_client
from app.notify_client.notification_api_client import notification_api_client
from app.notify_client.service_api_client import service_api_client
from app.utils import set_status_filters
class Job(JSONModel):
ALLOWED_PROPERTIES = {
'id',
'service',
'template',
'template_version',
'original_file_name',
'created_at',
'notification_count',
'job_status',
'created_by',
'scheduled_for',
}
@classmethod
def from_id(cls, job_id, service_id):
return cls(job_api_client.get_job(service_id, job_id)['data'])
@property
def status(self):
return self.job_status
@property
def cancelled(self):
return self.status == 'cancelled'
@property
def scheduled(self):
return self.status == 'scheduled'
@cached_property
def statistics(self):
results = defaultdict(int)
for outcome in self._dict['statistics']:
if outcome['status'] in [
'failed', 'technical-failure', 'temporary-failure',
'permanent-failure', 'cancelled'
]:
results['failed'] += outcome['count']
if outcome['status'] in ['sending', 'pending', 'created']:
results['sending'] += outcome['count']
if outcome['status'] in ['delivered', 'sent']:
results['delivered'] += outcome['count']
results['requested'] += outcome['count']
return results
@property
def notifications_delivered(self):
return self.statistics['delivered']
@property
def notifications_failed(self):
return self.statistics['failed']
@property
def notifications_requested(self):
return self.statistics['requested']
@property
def notifications_sent(self):
return self.notifications_delivered + self.notifications_failed
@property
def notifications_processed(self):
return self.notifications_delivered + self.notifications_failed
@property
def notifications_sending(self):
if self.scheduled:
return 0
return self.notification_count - self.notifications_sent
@property
def notifications_created(self):
return notification_api_client.get_notification_count_for_job_id(
service_id=self.service, job_id=self.id
)
@property
def still_processing(self):
return (
self.percentage_complete < 100 and self.status != 'finished'
)
@cached_property
def finished_processing(self):
return self.notification_count == self.notifications_processed
@property
def template_id(self):
return self._dict['template']
@cached_property
def template(self):
return service_api_client.get_service_template(
service_id=self.service,
template_id=self.template_id,
version=self.template_version,
)['data']
@property
def template_type(self):
return self.template['template_type']
@property
def percentage_complete(self):
return self.notifications_requested / self.notification_count * 100
@property
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', datetime.strptime(self.created_at[:-6], '%Y-%m-%dT%H:%M:%S.%f')
):
return False
return True
@cached_property
def all_notifications(self):
return self.get_notifications(set_status_filters({}))['notifications']
@property
def uncancellable_notifications(self):
return (
n for n in self.all_notifications
if n['status'] not in CANCELLABLE_JOB_LETTER_STATUSES
)
@cached_property
def postage(self):
# There might be no notifications if the job has only just been
# created and the tasks haven't run yet
try:
return self.all_notifications[0]['postage']
except IndexError:
return self.template['postage']
@property
def letter_timings(self):
return get_letter_timings(self.created_at, postage=self.postage)
@property
def failure_rate(self):
if not self.notifications_delivered:
return 100 if self.notifications_failed else 0
return (
self.notifications_failed / (
self.notifications_failed + self.notifications_delivered
) * 100
)
@property
def high_failure_rate(self):
return self.failure_rate > 30
def get_notifications(self, status):
return notification_api_client.get_notifications_for_service(
self.service, self.id, status=status,
)
def cancel(self):
if self.template_type == 'letter':
return job_api_client.cancel_letter_job(self.service, self.id)
else:
return job_api_client.cancel_job(self.service, self.id)
class ImmediateJobs(ModelList):
client = job_api_client.get_immediate_jobs
model = Job
class ScheduledJobs(ImmediateJobs):
client = job_api_client.get_scheduled_jobs
class PaginatedJobs(ImmediateJobs):
client = job_api_client.get_page_of_jobs
def __init__(self, service_id, page=None):
try:
self.current_page = int(page)
except TypeError:
self.current_page = 1
response = self.client(service_id, page=self.current_page)
self.items = response['data']
self.prev_page = response.get('links', {}).get('prev', None)
self.next_page = response.get('links', {}).get('next', None)
class PaginatedUploads(PaginatedJobs):
client = job_api_client.get_uploads