2017-04-20 11:52:00 +01:00
|
|
|
|
import time
|
2016-01-28 11:42:13 +00:00
|
|
|
|
import uuid
|
2016-03-31 15:57:50 +01:00
|
|
|
|
import datetime
|
2017-05-04 17:09:04 +01:00
|
|
|
|
from flask import url_for, current_app
|
2016-09-22 11:56:26 +01:00
|
|
|
|
|
2017-05-22 17:25:58 +01:00
|
|
|
|
from sqlalchemy.ext.associationproxy import association_proxy
|
2017-05-04 17:09:04 +01:00
|
|
|
|
from sqlalchemy.ext.hybrid import hybrid_property
|
2016-04-25 16:12:46 +01:00
|
|
|
|
from sqlalchemy.dialects.postgresql import (
|
|
|
|
|
|
UUID,
|
|
|
|
|
|
JSON
|
|
|
|
|
|
)
|
2016-11-24 10:33:38 +00:00
|
|
|
|
from sqlalchemy import UniqueConstraint, and_
|
2016-08-09 13:07:48 +01:00
|
|
|
|
from sqlalchemy.orm import foreign, remote
|
2016-09-22 11:56:26 +01:00
|
|
|
|
from notifications_utils.recipients import (
|
|
|
|
|
|
validate_email_address,
|
|
|
|
|
|
validate_phone_number,
|
|
|
|
|
|
InvalidPhoneError,
|
|
|
|
|
|
InvalidEmailError
|
|
|
|
|
|
)
|
2016-03-31 15:57:50 +01:00
|
|
|
|
|
2016-01-19 11:38:29 +00:00
|
|
|
|
from app.encryption import (
|
|
|
|
|
|
hashpw,
|
|
|
|
|
|
check_hash
|
|
|
|
|
|
)
|
2016-06-20 16:23:56 +01:00
|
|
|
|
from app import (
|
|
|
|
|
|
db,
|
2016-09-07 13:44:56 +01:00
|
|
|
|
encryption,
|
2016-11-18 17:36:11 +00:00
|
|
|
|
DATETIME_FORMAT
|
|
|
|
|
|
)
|
2016-04-14 15:09:59 +01:00
|
|
|
|
|
|
|
|
|
|
from app.history_meta import Versioned
|
2017-05-24 14:52:32 +01:00
|
|
|
|
from app.utils import convert_utc_time_in_bst, convert_bst_to_utc
|
2016-04-14 15:09:59 +01:00
|
|
|
|
|
2017-05-15 12:49:46 +01:00
|
|
|
|
SMS_TYPE = 'sms'
|
|
|
|
|
|
EMAIL_TYPE = 'email'
|
|
|
|
|
|
LETTER_TYPE = 'letter'
|
|
|
|
|
|
|
|
|
|
|
|
TEMPLATE_TYPES = [SMS_TYPE, EMAIL_TYPE, LETTER_TYPE]
|
|
|
|
|
|
|
|
|
|
|
|
template_types = db.Enum(*TEMPLATE_TYPES, name='template_type')
|
|
|
|
|
|
|
|
|
|
|
|
NORMAL = 'normal'
|
|
|
|
|
|
PRIORITY = 'priority'
|
|
|
|
|
|
TEMPLATE_PROCESS_TYPE = [NORMAL, PRIORITY]
|
|
|
|
|
|
|
2016-01-07 17:31:17 +00:00
|
|
|
|
|
2016-01-08 17:51:46 +00:00
|
|
|
|
def filter_null_value_fields(obj):
|
|
|
|
|
|
return dict(
|
|
|
|
|
|
filter(lambda x: x[1] is not None, obj.items())
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-12-15 17:11:47 +00:00
|
|
|
|
class HistoryModel:
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
def from_original(cls, original):
|
|
|
|
|
|
history = cls()
|
|
|
|
|
|
history.update_from_original(original)
|
|
|
|
|
|
return history
|
|
|
|
|
|
|
|
|
|
|
|
def update_from_original(self, original):
|
|
|
|
|
|
for c in self.__table__.columns:
|
2017-05-04 17:09:04 +01:00
|
|
|
|
# in some cases, columns may have different names to their underlying db column - so only copy those
|
|
|
|
|
|
# that we can, and leave it up to subclasses to deal with any oddities/properties etc.
|
|
|
|
|
|
if hasattr(original, c.name):
|
|
|
|
|
|
setattr(self, c.name, getattr(original, c.name))
|
|
|
|
|
|
else:
|
|
|
|
|
|
current_app.logger.debug('{} has no column {} to copy from'.format(original, c.name))
|
2016-12-15 17:11:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-01-07 17:31:17 +00:00
|
|
|
|
class User(db.Model):
|
|
|
|
|
|
__tablename__ = 'users'
|
|
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2016-01-19 11:38:29 +00:00
|
|
|
|
name = db.Column(db.String, nullable=False, index=True, unique=False)
|
2016-01-07 17:31:17 +00:00
|
|
|
|
email_address = db.Column(db.String(255), nullable=False, index=True, unique=True)
|
2016-01-11 15:07:13 +00:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-04-27 10:27:05 +01:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-01-11 15:07:13 +00:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
2016-04-27 10:27:05 +01:00
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
2016-01-19 11:38:29 +00:00
|
|
|
|
_password = db.Column(db.String, index=False, unique=False, nullable=False)
|
|
|
|
|
|
mobile_number = db.Column(db.String, index=False, unique=False, nullable=False)
|
2016-06-28 11:24:08 +01:00
|
|
|
|
password_changed_at = db.Column(db.DateTime, index=False, unique=False, nullable=False,
|
|
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-01-19 11:38:29 +00:00
|
|
|
|
logged_in_at = db.Column(db.DateTime, nullable=True)
|
|
|
|
|
|
failed_login_count = db.Column(db.Integer, nullable=False, default=0)
|
|
|
|
|
|
state = db.Column(db.String, nullable=False, default='pending')
|
2016-03-17 10:37:24 +00:00
|
|
|
|
platform_admin = db.Column(db.Boolean, nullable=False, default=False)
|
2017-02-17 14:06:16 +00:00
|
|
|
|
current_session_id = db.Column(UUID(as_uuid=True), nullable=True)
|
2016-01-19 11:38:29 +00:00
|
|
|
|
|
2017-06-16 16:30:03 +01:00
|
|
|
|
services = db.relationship(
|
|
|
|
|
|
'Service',
|
|
|
|
|
|
secondary='user_to_service',
|
|
|
|
|
|
backref=db.backref('user_to_service', lazy='dynamic'))
|
|
|
|
|
|
|
2016-01-19 11:38:29 +00:00
|
|
|
|
@property
|
|
|
|
|
|
def password(self):
|
|
|
|
|
|
raise AttributeError("Password not readable")
|
|
|
|
|
|
|
|
|
|
|
|
@password.setter
|
|
|
|
|
|
def password(self, password):
|
|
|
|
|
|
self._password = hashpw(password)
|
|
|
|
|
|
|
|
|
|
|
|
def check_password(self, password):
|
|
|
|
|
|
return check_hash(password, self._password)
|
2016-01-08 17:51:46 +00:00
|
|
|
|
|
2016-01-07 17:31:17 +00:00
|
|
|
|
|
|
|
|
|
|
user_to_service = db.Table(
|
|
|
|
|
|
'user_to_service',
|
|
|
|
|
|
db.Model.metadata,
|
2016-04-08 13:34:46 +01:00
|
|
|
|
db.Column('user_id', UUID(as_uuid=True), db.ForeignKey('users.id')),
|
2016-02-25 12:11:51 +00:00
|
|
|
|
db.Column('service_id', UUID(as_uuid=True), db.ForeignKey('services.id')),
|
|
|
|
|
|
UniqueConstraint('user_id', 'service_id', name='uix_user_to_service')
|
2016-01-07 17:31:17 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2016-08-04 12:35:47 +01:00
|
|
|
|
BRANDING_GOVUK = 'govuk'
|
|
|
|
|
|
BRANDING_ORG = 'org'
|
|
|
|
|
|
BRANDING_BOTH = 'both'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BrandingTypes(db.Model):
|
|
|
|
|
|
__tablename__ = 'branding_type'
|
|
|
|
|
|
name = db.Column(db.String(255), primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-06 10:56:03 +01:00
|
|
|
|
class Organisation(db.Model):
|
2016-08-04 12:35:47 +01:00
|
|
|
|
__tablename__ = 'organisation'
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
colour = db.Column(db.String(7), nullable=True)
|
2017-07-06 10:56:03 +01:00
|
|
|
|
logo = db.Column(db.String(255), nullable=False)
|
2016-08-04 12:35:47 +01:00
|
|
|
|
name = db.Column(db.String(255), nullable=True)
|
|
|
|
|
|
|
2017-07-04 17:02:28 +01:00
|
|
|
|
@classmethod
|
|
|
|
|
|
def from_json(cls, data):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Assumption: data has been validated appropriately.
|
|
|
|
|
|
|
2017-07-06 10:56:03 +01:00
|
|
|
|
Returns a Organisation object based on the provided data.
|
2017-07-04 17:02:28 +01:00
|
|
|
|
"""
|
|
|
|
|
|
# validate json with marshmallow
|
|
|
|
|
|
fields = data.copy()
|
|
|
|
|
|
|
|
|
|
|
|
return cls(**fields)
|
|
|
|
|
|
|
2016-08-04 12:35:47 +01:00
|
|
|
|
|
2017-04-21 12:39:42 +01:00
|
|
|
|
DVLA_ORG_HM_GOVERNMENT = '001'
|
|
|
|
|
|
DVLA_ORG_LAND_REGISTRY = '500'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DVLAOrganisation(db.Model):
|
|
|
|
|
|
__tablename__ = 'dvla_organisation'
|
|
|
|
|
|
id = db.Column(db.String, primary_key=True)
|
|
|
|
|
|
name = db.Column(db.String(255), nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-05-17 14:09:18 +01:00
|
|
|
|
INTERNATIONAL_SMS_TYPE = 'international_sms'
|
2017-05-22 17:25:58 +01:00
|
|
|
|
INBOUND_SMS_TYPE = 'inbound_sms'
|
2017-05-26 15:41:14 +01:00
|
|
|
|
SCHEDULE_NOTIFICATIONS = 'schedule_notifications'
|
2017-05-17 14:09:18 +01:00
|
|
|
|
|
2017-05-26 15:41:14 +01:00
|
|
|
|
SERVICE_PERMISSION_TYPES = [EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, INBOUND_SMS_TYPE,
|
|
|
|
|
|
SCHEDULE_NOTIFICATIONS]
|
2017-05-17 14:09:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ServicePermissionTypes(db.Model):
|
|
|
|
|
|
__tablename__ = 'service_permission_types'
|
|
|
|
|
|
|
|
|
|
|
|
name = db.Column(db.String(255), primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-14 15:09:59 +01:00
|
|
|
|
class Service(db.Model, Versioned):
|
2016-01-07 17:31:17 +00:00
|
|
|
|
__tablename__ = 'services'
|
|
|
|
|
|
|
2016-02-02 14:16:08 +00:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2016-02-19 15:52:19 +00:00
|
|
|
|
name = db.Column(db.String(255), nullable=False, unique=True)
|
2016-01-11 15:07:13 +00:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-05-11 10:56:24 +01:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-01-11 15:07:13 +00:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
2016-05-11 10:56:24 +01:00
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
2016-11-10 10:38:36 +00:00
|
|
|
|
active = db.Column(db.Boolean, index=False, unique=False, nullable=False, default=True)
|
2016-04-08 16:13:10 +01:00
|
|
|
|
message_limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False)
|
2016-01-11 15:07:13 +00:00
|
|
|
|
users = db.relationship(
|
|
|
|
|
|
'User',
|
|
|
|
|
|
secondary=user_to_service,
|
|
|
|
|
|
backref=db.backref('user_to_service', lazy='dynamic'))
|
2016-01-07 17:31:17 +00:00
|
|
|
|
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
|
2016-08-04 12:35:47 +01:00
|
|
|
|
research_mode = db.Column(db.Boolean, index=False, unique=False, nullable=False, default=False)
|
2016-02-19 15:52:19 +00:00
|
|
|
|
email_from = db.Column(db.Text, index=False, unique=True, nullable=False)
|
2016-04-14 15:09:59 +01:00
|
|
|
|
created_by = db.relationship('User')
|
|
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2016-05-17 10:56:02 +01:00
|
|
|
|
reply_to_email_address = db.Column(db.Text, index=False, unique=False, nullable=True)
|
2017-02-28 11:28:39 +00:00
|
|
|
|
letter_contact_block = db.Column(db.Text, index=False, unique=False, nullable=True)
|
2017-06-01 11:00:26 +01:00
|
|
|
|
sms_sender = db.Column(db.String(11), nullable=False, default=lambda: current_app.config['FROM_NUMBER'])
|
2016-08-04 12:35:47 +01:00
|
|
|
|
organisation_id = db.Column(UUID(as_uuid=True), db.ForeignKey('organisation.id'), index=True, nullable=True)
|
|
|
|
|
|
organisation = db.relationship('Organisation')
|
2017-04-21 12:39:42 +01:00
|
|
|
|
dvla_organisation_id = db.Column(
|
|
|
|
|
|
db.String,
|
|
|
|
|
|
db.ForeignKey('dvla_organisation.id'),
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
default=DVLA_ORG_HM_GOVERNMENT
|
|
|
|
|
|
)
|
|
|
|
|
|
dvla_organisation = db.relationship('DVLAOrganisation')
|
2016-08-04 12:35:47 +01:00
|
|
|
|
branding = db.Column(
|
|
|
|
|
|
db.String(255),
|
|
|
|
|
|
db.ForeignKey('branding_type.name'),
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
default=BRANDING_GOVUK
|
|
|
|
|
|
)
|
2017-05-22 17:25:58 +01:00
|
|
|
|
|
|
|
|
|
|
association_proxy('permissions', 'service_permission_types')
|
2016-04-14 15:09:59 +01:00
|
|
|
|
|
2017-06-06 14:49:05 +01:00
|
|
|
|
@staticmethod
|
|
|
|
|
|
def free_sms_fragment_limit():
|
|
|
|
|
|
return current_app.config['FREE_SMS_TIER_FRAGMENT_COUNT']
|
|
|
|
|
|
|
2017-05-24 16:27:12 +01:00
|
|
|
|
@classmethod
|
|
|
|
|
|
def from_json(cls, data):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Assumption: data has been validated appropriately.
|
|
|
|
|
|
|
|
|
|
|
|
Returns a Service object based on the provided data. Deserialises created_by to created_by_id as marshmallow
|
|
|
|
|
|
would.
|
|
|
|
|
|
"""
|
|
|
|
|
|
# validate json with marshmallow
|
|
|
|
|
|
fields = data.copy()
|
|
|
|
|
|
|
|
|
|
|
|
fields['created_by_id'] = fields.pop('created_by')
|
|
|
|
|
|
|
|
|
|
|
|
return cls(**fields)
|
|
|
|
|
|
|
2017-05-15 12:49:46 +01:00
|
|
|
|
|
2017-05-22 17:25:58 +01:00
|
|
|
|
class ServicePermission(db.Model):
|
|
|
|
|
|
__tablename__ = "service_permissions"
|
|
|
|
|
|
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'),
|
|
|
|
|
|
primary_key=True, index=True, nullable=False)
|
|
|
|
|
|
permission = db.Column(db.String(255), db.ForeignKey('service_permission_types.name'),
|
|
|
|
|
|
index=True, primary_key=True, nullable=False)
|
|
|
|
|
|
service = db.relationship("Service")
|
|
|
|
|
|
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, nullable=False)
|
|
|
|
|
|
|
2017-05-23 14:24:07 +01:00
|
|
|
|
service_permission_types = db.relationship(
|
|
|
|
|
|
Service, backref=db.backref("permissions", cascade="all, delete-orphan"))
|
2017-05-22 17:25:58 +01:00
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
|
return '<{} has service permission: {}>'.format(self.service_id, self.permission)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-27 13:44:29 +01:00
|
|
|
|
MOBILE_TYPE = 'mobile'
|
|
|
|
|
|
EMAIL_TYPE = 'email'
|
|
|
|
|
|
|
|
|
|
|
|
WHITELIST_RECIPIENT_TYPE = [MOBILE_TYPE, EMAIL_TYPE]
|
|
|
|
|
|
whitelist_recipient_types = db.Enum(*WHITELIST_RECIPIENT_TYPE, name='recipient_type')
|
2016-01-13 11:04:13 +00:00
|
|
|
|
|
2016-09-27 14:16:35 +01:00
|
|
|
|
|
2016-09-20 15:41:53 +01:00
|
|
|
|
class ServiceWhitelist(db.Model):
|
|
|
|
|
|
__tablename__ = 'service_whitelist'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False)
|
|
|
|
|
|
service = db.relationship('Service', backref='whitelist')
|
2016-09-27 13:44:29 +01:00
|
|
|
|
recipient_type = db.Column(whitelist_recipient_types, nullable=False)
|
|
|
|
|
|
recipient = db.Column(db.String(255), nullable=False)
|
2016-09-20 15:41:53 +01:00
|
|
|
|
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
|
|
|
|
|
|
2016-09-22 11:56:26 +01:00
|
|
|
|
@classmethod
|
2016-09-27 13:44:29 +01:00
|
|
|
|
def from_string(cls, service_id, recipient_type, recipient):
|
|
|
|
|
|
instance = cls(service_id=service_id, recipient_type=recipient_type)
|
|
|
|
|
|
|
2016-09-22 11:56:26 +01:00
|
|
|
|
try:
|
2016-09-27 13:44:29 +01:00
|
|
|
|
if recipient_type == MOBILE_TYPE:
|
|
|
|
|
|
validate_phone_number(recipient)
|
|
|
|
|
|
instance.recipient = recipient
|
|
|
|
|
|
elif recipient_type == EMAIL_TYPE:
|
|
|
|
|
|
validate_email_address(recipient)
|
|
|
|
|
|
instance.recipient = recipient
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise ValueError('Invalid recipient type')
|
|
|
|
|
|
except InvalidPhoneError:
|
|
|
|
|
|
raise ValueError('Invalid whitelist: "{}"'.format(recipient))
|
2016-09-22 11:56:26 +01:00
|
|
|
|
except InvalidEmailError:
|
2016-09-27 13:44:29 +01:00
|
|
|
|
raise ValueError('Invalid whitelist: "{}"'.format(recipient))
|
|
|
|
|
|
else:
|
|
|
|
|
|
return instance
|
2016-09-22 11:56:26 +01:00
|
|
|
|
|
2016-11-25 16:58:46 +00:00
|
|
|
|
def __repr__(self):
|
|
|
|
|
|
return 'Recipient {} of type: {}'.format(self.recipient, self.recipient_type)
|
2016-09-20 15:41:53 +01:00
|
|
|
|
|
2016-09-22 17:18:52 +01:00
|
|
|
|
|
2017-06-15 11:32:51 +01:00
|
|
|
|
class ServiceInboundApi(db.Model, Versioned):
|
2017-06-13 15:27:13 +01:00
|
|
|
|
__tablename__ = 'service_inbound_api'
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2017-06-15 16:19:12 +01:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False, unique=True)
|
|
|
|
|
|
service = db.relationship('Service', backref='inbound_api')
|
2017-06-19 12:25:05 +01:00
|
|
|
|
url = db.Column(db.String(), nullable=False)
|
2017-06-19 14:32:22 +01:00
|
|
|
|
_bearer_token = db.Column("bearer_token", db.String(), nullable=False)
|
2017-06-15 11:32:51 +01:00
|
|
|
|
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, nullable=False)
|
|
|
|
|
|
updated_at = db.Column(db.DateTime, nullable=True)
|
|
|
|
|
|
updated_by = db.relationship('User')
|
|
|
|
|
|
updated_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2017-06-13 15:27:13 +01:00
|
|
|
|
|
|
|
|
|
|
@property
|
2017-06-19 14:32:22 +01:00
|
|
|
|
def bearer_token(self):
|
|
|
|
|
|
if self._bearer_token:
|
|
|
|
|
|
return encryption.decrypt(self._bearer_token)
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
@bearer_token.setter
|
|
|
|
|
|
def bearer_token(self, bearer_token):
|
|
|
|
|
|
if bearer_token:
|
|
|
|
|
|
self._bearer_token = encryption.encrypt(str(bearer_token))
|
2017-06-13 15:27:13 +01:00
|
|
|
|
|
2017-06-15 11:32:51 +01:00
|
|
|
|
def serialize(self):
|
|
|
|
|
|
return {
|
2017-06-15 16:19:12 +01:00
|
|
|
|
"id": str(self.id),
|
|
|
|
|
|
"service_id": str(self.service_id),
|
2017-06-15 11:32:51 +01:00
|
|
|
|
"url": self.url,
|
2017-06-15 16:19:12 +01:00
|
|
|
|
"updated_by_id": str(self.updated_by_id),
|
2017-06-15 11:32:51 +01:00
|
|
|
|
"created_at": self.created_at.strftime(DATETIME_FORMAT),
|
|
|
|
|
|
"updated_at": self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-13 15:27:13 +01:00
|
|
|
|
|
2016-04-20 17:25:20 +01:00
|
|
|
|
class ApiKey(db.Model, Versioned):
|
2016-04-08 13:34:46 +01:00
|
|
|
|
__tablename__ = 'api_keys'
|
2016-01-13 09:25:46 +00:00
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2016-01-19 12:07:00 +00:00
|
|
|
|
name = db.Column(db.String(255), nullable=False)
|
2017-06-19 14:32:22 +01:00
|
|
|
|
_secret = db.Column("secret", db.String(255), unique=True, nullable=False)
|
2016-02-02 14:16:08 +00:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False)
|
2016-11-10 11:07:12 +00:00
|
|
|
|
service = db.relationship('Service', backref='api_keys')
|
2016-06-23 16:45:20 +01:00
|
|
|
|
key_type = db.Column(db.String(255), db.ForeignKey('key_types.name'), index=True, nullable=False)
|
2016-01-13 09:25:46 +00:00
|
|
|
|
expiry_date = db.Column(db.DateTime)
|
2016-04-20 17:25:20 +01:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-05-11 10:56:24 +01:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-04-20 17:25:20 +01:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
2016-05-11 10:56:24 +01:00
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
2016-04-20 17:25:20 +01:00
|
|
|
|
created_by = db.relationship('User')
|
|
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2016-01-13 09:25:46 +00:00
|
|
|
|
|
2016-01-21 16:53:53 +00:00
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
UniqueConstraint('service_id', 'name', name='uix_service_to_key_name'),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2016-06-29 14:15:32 +01:00
|
|
|
|
@property
|
2017-06-19 14:32:22 +01:00
|
|
|
|
def secret(self):
|
|
|
|
|
|
if self._secret:
|
|
|
|
|
|
return encryption.decrypt(self._secret)
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
@secret.setter
|
|
|
|
|
|
def secret(self, secret):
|
|
|
|
|
|
if secret:
|
|
|
|
|
|
self._secret = encryption.encrypt(str(secret))
|
2016-06-29 14:15:32 +01:00
|
|
|
|
|
2016-01-13 09:25:46 +00:00
|
|
|
|
|
2016-06-23 16:45:20 +01:00
|
|
|
|
KEY_TYPE_NORMAL = 'normal'
|
|
|
|
|
|
KEY_TYPE_TEAM = 'team'
|
2016-07-05 10:47:47 +01:00
|
|
|
|
KEY_TYPE_TEST = 'test'
|
2016-06-23 16:45:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class KeyTypes(db.Model):
|
|
|
|
|
|
__tablename__ = 'key_types'
|
|
|
|
|
|
|
|
|
|
|
|
name = db.Column(db.String(255), primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-03-08 16:34:03 +00:00
|
|
|
|
class NotificationStatistics(db.Model):
|
|
|
|
|
|
__tablename__ = 'notification_statistics'
|
2016-03-08 15:23:19 +00:00
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2016-04-20 15:38:06 +01:00
|
|
|
|
day = db.Column(db.Date, index=True, nullable=False, unique=False, default=datetime.date.today)
|
2016-03-08 15:23:19 +00:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False)
|
|
|
|
|
|
service = db.relationship('Service', backref=db.backref('service_notification_stats', lazy='dynamic'))
|
2016-03-17 11:32:55 +00:00
|
|
|
|
emails_requested = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
emails_delivered = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
2016-04-08 16:13:10 +01:00
|
|
|
|
emails_failed = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
2016-03-17 11:32:55 +00:00
|
|
|
|
sms_requested = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
sms_delivered = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
2016-04-08 16:13:10 +01:00
|
|
|
|
sms_failed = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
2016-03-08 15:23:19 +00:00
|
|
|
|
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
UniqueConstraint('service_id', 'day', name='uix_service_to_day'),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-01-13 12:14:34 +00:00
|
|
|
|
class TemplateProcessTypes(db.Model):
|
|
|
|
|
|
__tablename__ = 'template_process_type'
|
|
|
|
|
|
name = db.Column(db.String(255), primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-02 16:23:14 +01:00
|
|
|
|
class Template(db.Model):
|
2016-01-13 11:04:13 +00:00
|
|
|
|
__tablename__ = 'templates'
|
|
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2016-01-13 11:04:13 +00:00
|
|
|
|
name = db.Column(db.String(255), nullable=False)
|
2016-06-29 11:50:54 +01:00
|
|
|
|
template_type = db.Column(template_types, nullable=False)
|
2016-01-13 11:04:13 +00:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-01-13 11:04:13 +00:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
2016-01-13 11:04:13 +00:00
|
|
|
|
content = db.Column(db.Text, index=False, unique=False, nullable=False)
|
2016-04-25 16:28:08 +01:00
|
|
|
|
archived = db.Column(db.Boolean, index=False, nullable=False, default=False)
|
2016-02-02 14:16:08 +00:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False, nullable=False)
|
2016-11-10 17:07:02 +00:00
|
|
|
|
service = db.relationship('Service', backref='templates')
|
2016-05-18 10:00:09 +01:00
|
|
|
|
subject = db.Column(db.Text, index=False, unique=False, nullable=True)
|
2016-04-25 10:38:37 +01:00
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
|
|
|
|
|
created_by = db.relationship('User')
|
2017-06-28 10:26:25 +01:00
|
|
|
|
version = db.Column(db.Integer, default=0, nullable=False)
|
|
|
|
|
|
process_type = db.Column(
|
|
|
|
|
|
db.String(255),
|
|
|
|
|
|
db.ForeignKey('template_process_type.name'),
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
default=NORMAL
|
|
|
|
|
|
)
|
2016-08-02 16:23:14 +01:00
|
|
|
|
|
2017-06-28 16:10:22 +01:00
|
|
|
|
redact_personalisation = association_proxy('template_redacted', 'redact_personalisation')
|
|
|
|
|
|
|
2016-11-18 17:36:11 +00:00
|
|
|
|
def get_link(self):
|
|
|
|
|
|
# TODO: use "/v2/" route once available
|
2016-11-28 11:13:11 +00:00
|
|
|
|
return url_for(
|
|
|
|
|
|
"template.get_template_by_id_and_service_id",
|
|
|
|
|
|
service_id=self.service_id,
|
|
|
|
|
|
template_id=self.id,
|
|
|
|
|
|
_external=True
|
|
|
|
|
|
)
|
2016-11-18 17:36:11 +00:00
|
|
|
|
|
2017-03-14 15:25:36 +00:00
|
|
|
|
def serialize(self):
|
|
|
|
|
|
serialized = {
|
2017-03-28 10:41:25 +01:00
|
|
|
|
"id": str(self.id),
|
2017-03-14 15:25:36 +00:00
|
|
|
|
"type": self.template_type,
|
|
|
|
|
|
"created_at": self.created_at.strftime(DATETIME_FORMAT),
|
|
|
|
|
|
"updated_at": self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None,
|
|
|
|
|
|
"created_by": self.created_by.email_address,
|
|
|
|
|
|
"version": self.version,
|
|
|
|
|
|
"body": self.content,
|
|
|
|
|
|
"subject": self.subject if self.template_type == EMAIL_TYPE else None
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return serialized
|
|
|
|
|
|
|
2016-08-02 16:23:14 +01:00
|
|
|
|
|
2017-06-28 10:26:25 +01:00
|
|
|
|
class TemplateRedacted(db.Model):
|
|
|
|
|
|
__tablename__ = 'template_redacted'
|
|
|
|
|
|
|
|
|
|
|
|
template_id = db.Column(UUID(as_uuid=True), db.ForeignKey('templates.id'), primary_key=True, nullable=False)
|
|
|
|
|
|
redact_personalisation = db.Column(db.Boolean, nullable=False, default=False)
|
|
|
|
|
|
updated_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
2017-07-10 14:43:46 +01:00
|
|
|
|
updated_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), nullable=False, index=True)
|
2017-06-28 10:26:25 +01:00
|
|
|
|
updated_by = db.relationship('User')
|
|
|
|
|
|
|
|
|
|
|
|
# uselist=False as this is a one-to-one relationship
|
|
|
|
|
|
template = db.relationship('Template', uselist=False, backref=db.backref('template_redacted', uselist=False))
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-02 16:23:14 +01:00
|
|
|
|
class TemplateHistory(db.Model):
|
|
|
|
|
|
__tablename__ = 'templates_history'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True)
|
|
|
|
|
|
name = db.Column(db.String(255), nullable=False)
|
|
|
|
|
|
template_type = db.Column(template_types, nullable=False)
|
|
|
|
|
|
created_at = db.Column(db.DateTime, nullable=False)
|
|
|
|
|
|
updated_at = db.Column(db.DateTime)
|
|
|
|
|
|
content = db.Column(db.Text, nullable=False)
|
|
|
|
|
|
archived = db.Column(db.Boolean, nullable=False, default=False)
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False)
|
|
|
|
|
|
service = db.relationship('Service')
|
|
|
|
|
|
subject = db.Column(db.Text)
|
|
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
|
|
|
|
|
created_by = db.relationship('User')
|
2016-10-04 10:47:34 +01:00
|
|
|
|
version = db.Column(db.Integer, primary_key=True, nullable=False)
|
2017-01-13 12:14:34 +00:00
|
|
|
|
process_type = db.Column(db.String(255),
|
|
|
|
|
|
db.ForeignKey('template_process_type.name'),
|
|
|
|
|
|
index=True,
|
2017-01-16 11:15:48 +00:00
|
|
|
|
nullable=False,
|
2017-01-13 12:14:34 +00:00
|
|
|
|
default=NORMAL)
|
2016-01-15 11:12:05 +00:00
|
|
|
|
|
2017-03-14 15:25:36 +00:00
|
|
|
|
def serialize(self):
|
|
|
|
|
|
serialized = {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"type": self.template_type,
|
|
|
|
|
|
"created_at": self.created_at.strftime(DATETIME_FORMAT),
|
|
|
|
|
|
"updated_at": self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None,
|
|
|
|
|
|
"created_by": self.created_by.email_address,
|
|
|
|
|
|
"version": self.version,
|
|
|
|
|
|
"body": self.content,
|
|
|
|
|
|
"subject": self.subject if self.template_type == EMAIL_TYPE else None
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return serialized
|
|
|
|
|
|
|
2017-02-01 09:19:32 +00:00
|
|
|
|
|
2016-04-21 11:37:38 +01:00
|
|
|
|
MMG_PROVIDER = "mmg"
|
|
|
|
|
|
FIRETEXT_PROVIDER = "firetext"
|
|
|
|
|
|
SES_PROVIDER = 'ses'
|
|
|
|
|
|
|
2016-06-06 10:50:36 +01:00
|
|
|
|
SMS_PROVIDERS = [MMG_PROVIDER, FIRETEXT_PROVIDER]
|
2016-04-28 12:01:27 +01:00
|
|
|
|
EMAIL_PROVIDERS = [SES_PROVIDER]
|
|
|
|
|
|
PROVIDERS = SMS_PROVIDERS + EMAIL_PROVIDERS
|
2016-04-21 11:37:38 +01:00
|
|
|
|
|
2016-06-29 11:50:54 +01:00
|
|
|
|
NOTIFICATION_TYPE = [EMAIL_TYPE, SMS_TYPE, LETTER_TYPE]
|
|
|
|
|
|
notification_types = db.Enum(*NOTIFICATION_TYPE, name='notification_type')
|
2016-04-21 11:37:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProviderStatistics(db.Model):
|
|
|
|
|
|
__tablename__ = 'provider_statistics'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
day = db.Column(db.Date, nullable=False)
|
2016-05-05 09:55:25 +01:00
|
|
|
|
provider_id = db.Column(UUID(as_uuid=True), db.ForeignKey('provider_details.id'), index=True, nullable=False)
|
2016-05-11 15:36:17 +01:00
|
|
|
|
provider = db.relationship(
|
2016-05-06 09:09:47 +01:00
|
|
|
|
'ProviderDetails', backref=db.backref('provider_stats', lazy='dynamic')
|
|
|
|
|
|
)
|
2016-04-21 11:37:38 +01:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False)
|
|
|
|
|
|
service = db.relationship('Service', backref=db.backref('service_provider_stats', lazy='dynamic'))
|
|
|
|
|
|
unit_count = db.Column(db.BigInteger, nullable=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProviderRates(db.Model):
|
|
|
|
|
|
__tablename__ = 'provider_rates'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
valid_from = db.Column(db.DateTime, nullable=False)
|
|
|
|
|
|
rate = db.Column(db.Numeric(), nullable=False)
|
2016-05-05 09:55:25 +01:00
|
|
|
|
provider_id = db.Column(UUID(as_uuid=True), db.ForeignKey('provider_details.id'), index=True, nullable=False)
|
2016-05-11 15:36:17 +01:00
|
|
|
|
provider = db.relationship('ProviderDetails', backref=db.backref('provider_rates', lazy='dynamic'))
|
2016-05-05 09:55:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProviderDetails(db.Model):
|
|
|
|
|
|
__tablename__ = 'provider_details'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
display_name = db.Column(db.String, nullable=False)
|
|
|
|
|
|
identifier = db.Column(db.String, nullable=False)
|
|
|
|
|
|
priority = db.Column(db.Integer, nullable=False)
|
2016-06-29 11:50:54 +01:00
|
|
|
|
notification_type = db.Column(notification_types, nullable=False)
|
2016-12-15 17:11:47 +00:00
|
|
|
|
active = db.Column(db.Boolean, default=False, nullable=False)
|
2016-12-19 16:49:56 +00:00
|
|
|
|
version = db.Column(db.Integer, default=1, nullable=False)
|
2016-12-19 17:45:46 +00:00
|
|
|
|
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
2017-03-02 17:59:02 +00:00
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=True)
|
|
|
|
|
|
created_by = db.relationship('User')
|
2017-04-25 10:36:37 +01:00
|
|
|
|
supports_international = db.Column(db.Boolean, nullable=False, default=False)
|
2016-12-15 17:11:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProviderDetailsHistory(db.Model, HistoryModel):
|
|
|
|
|
|
__tablename__ = 'provider_details_history'
|
|
|
|
|
|
|
2016-12-19 16:49:56 +00:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
2016-12-15 17:11:47 +00:00
|
|
|
|
display_name = db.Column(db.String, nullable=False)
|
|
|
|
|
|
identifier = db.Column(db.String, nullable=False)
|
|
|
|
|
|
priority = db.Column(db.Integer, nullable=False)
|
|
|
|
|
|
notification_type = db.Column(notification_types, nullable=False)
|
|
|
|
|
|
active = db.Column(db.Boolean, nullable=False)
|
2016-12-19 16:49:56 +00:00
|
|
|
|
version = db.Column(db.Integer, primary_key=True, nullable=False)
|
2016-12-19 17:45:46 +00:00
|
|
|
|
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
2017-03-02 17:59:02 +00:00
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=True)
|
|
|
|
|
|
created_by = db.relationship('User')
|
2017-04-25 10:36:37 +01:00
|
|
|
|
supports_international = db.Column(db.Boolean, nullable=False, default=False)
|
2016-04-21 11:37:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-08-24 13:34:42 +01:00
|
|
|
|
JOB_STATUS_PENDING = 'pending'
|
|
|
|
|
|
JOB_STATUS_IN_PROGRESS = 'in progress'
|
|
|
|
|
|
JOB_STATUS_FINISHED = 'finished'
|
|
|
|
|
|
JOB_STATUS_SENDING_LIMITS_EXCEEDED = 'sending limits exceeded'
|
|
|
|
|
|
JOB_STATUS_SCHEDULED = 'scheduled'
|
2016-09-01 14:31:01 +01:00
|
|
|
|
JOB_STATUS_CANCELLED = 'cancelled'
|
2017-03-10 16:33:15 +00:00
|
|
|
|
JOB_STATUS_READY_TO_SEND = 'ready to send'
|
|
|
|
|
|
JOB_STATUS_SENT_TO_DVLA = 'sent to dvla'
|
2017-04-18 11:42:48 +01:00
|
|
|
|
JOB_STATUS_ERROR = 'error'
|
2016-09-23 16:34:13 +01:00
|
|
|
|
JOB_STATUS_TYPES = [
|
|
|
|
|
|
JOB_STATUS_PENDING,
|
|
|
|
|
|
JOB_STATUS_IN_PROGRESS,
|
|
|
|
|
|
JOB_STATUS_FINISHED,
|
|
|
|
|
|
JOB_STATUS_SENDING_LIMITS_EXCEEDED,
|
|
|
|
|
|
JOB_STATUS_SCHEDULED,
|
2017-04-06 17:16:08 +01:00
|
|
|
|
JOB_STATUS_CANCELLED,
|
|
|
|
|
|
JOB_STATUS_READY_TO_SEND,
|
2017-04-18 11:42:48 +01:00
|
|
|
|
JOB_STATUS_SENT_TO_DVLA,
|
|
|
|
|
|
JOB_STATUS_ERROR
|
2016-09-23 16:34:13 +01:00
|
|
|
|
]
|
2016-08-24 13:34:42 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-08-24 14:35:22 +01:00
|
|
|
|
class JobStatus(db.Model):
|
2016-08-24 13:34:42 +01:00
|
|
|
|
__tablename__ = 'job_status'
|
|
|
|
|
|
|
|
|
|
|
|
name = db.Column(db.String(255), primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-15 11:12:05 +00:00
|
|
|
|
class Job(db.Model):
|
|
|
|
|
|
__tablename__ = 'jobs'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True)
|
|
|
|
|
|
original_file_name = db.Column(db.String, nullable=False)
|
2016-02-02 14:16:08 +00:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False, nullable=False)
|
2016-01-15 11:12:05 +00:00
|
|
|
|
service = db.relationship('Service', backref=db.backref('jobs', lazy='dynamic'))
|
2016-04-08 13:34:46 +01:00
|
|
|
|
template_id = db.Column(UUID(as_uuid=True), db.ForeignKey('templates.id'), index=True, unique=False)
|
2016-01-15 15:48:05 +00:00
|
|
|
|
template = db.relationship('Template', backref=db.backref('jobs', lazy='dynamic'))
|
2016-05-11 17:04:51 +01:00
|
|
|
|
template_version = db.Column(db.Integer, nullable=False)
|
2016-01-15 11:12:05 +00:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-01-15 11:12:05 +00:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
2016-02-22 14:56:09 +00:00
|
|
|
|
notification_count = db.Column(db.Integer, nullable=False)
|
2016-03-04 14:25:28 +00:00
|
|
|
|
notifications_sent = db.Column(db.Integer, nullable=False, default=0)
|
2016-05-23 15:44:57 +01:00
|
|
|
|
notifications_delivered = db.Column(db.Integer, nullable=False, default=0)
|
|
|
|
|
|
notifications_failed = db.Column(db.Integer, nullable=False, default=0)
|
|
|
|
|
|
|
2016-02-25 09:59:50 +00:00
|
|
|
|
processing_started = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True)
|
|
|
|
|
|
processing_finished = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True)
|
2016-04-26 16:15:34 +01:00
|
|
|
|
created_by = db.relationship('User')
|
|
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2016-08-24 13:34:42 +01:00
|
|
|
|
scheduled_for = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True)
|
2016-08-24 14:04:52 +01:00
|
|
|
|
job_status = db.Column(
|
2016-08-25 16:59:38 +01:00
|
|
|
|
db.String(255), db.ForeignKey('job_status.name'), index=True, nullable=False, default='pending'
|
2016-08-24 14:16:39 +01:00
|
|
|
|
)
|
2016-01-21 17:29:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-06-30 15:41:51 +01:00
|
|
|
|
VERIFY_CODE_TYPES = [EMAIL_TYPE, SMS_TYPE]
|
2016-02-01 10:54:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-01-21 17:29:24 +00:00
|
|
|
|
class VerifyCode(db.Model):
|
|
|
|
|
|
__tablename__ = 'verify_codes'
|
|
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
user_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2016-01-21 17:29:24 +00:00
|
|
|
|
user = db.relationship('User', backref=db.backref('verify_codes', lazy='dynamic'))
|
|
|
|
|
|
_code = db.Column(db.String, nullable=False)
|
2016-02-01 10:54:32 +00:00
|
|
|
|
code_type = db.Column(db.Enum(*VERIFY_CODE_TYPES, name='verify_code_types'),
|
|
|
|
|
|
index=False, unique=False, nullable=False)
|
2016-01-21 17:29:24 +00:00
|
|
|
|
expiry_datetime = db.Column(db.DateTime, nullable=False)
|
|
|
|
|
|
code_used = db.Column(db.Boolean, default=False)
|
|
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-01-21 17:29:24 +00:00
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
def code(self):
|
|
|
|
|
|
raise AttributeError("Code not readable")
|
|
|
|
|
|
|
|
|
|
|
|
@code.setter
|
|
|
|
|
|
def code(self, cde):
|
|
|
|
|
|
self._code = hashpw(cde)
|
|
|
|
|
|
|
|
|
|
|
|
def check_code(self, cde):
|
|
|
|
|
|
return check_hash(cde, self._code)
|
2016-02-09 12:01:17 +00:00
|
|
|
|
|
2016-09-07 13:44:56 +01:00
|
|
|
|
|
2016-07-29 16:39:51 +01:00
|
|
|
|
NOTIFICATION_CREATED = 'created'
|
|
|
|
|
|
NOTIFICATION_SENDING = 'sending'
|
2017-04-25 17:08:29 +01:00
|
|
|
|
NOTIFICATION_SENT = 'sent'
|
2016-07-29 16:39:51 +01:00
|
|
|
|
NOTIFICATION_DELIVERED = 'delivered'
|
|
|
|
|
|
NOTIFICATION_PENDING = 'pending'
|
|
|
|
|
|
NOTIFICATION_FAILED = 'failed'
|
|
|
|
|
|
NOTIFICATION_TECHNICAL_FAILURE = 'technical-failure'
|
|
|
|
|
|
NOTIFICATION_TEMPORARY_FAILURE = 'temporary-failure'
|
|
|
|
|
|
NOTIFICATION_PERMANENT_FAILURE = 'permanent-failure'
|
|
|
|
|
|
|
2016-11-25 14:55:29 +00:00
|
|
|
|
NOTIFICATION_STATUS_TYPES_FAILED = [
|
|
|
|
|
|
NOTIFICATION_TECHNICAL_FAILURE,
|
|
|
|
|
|
NOTIFICATION_TEMPORARY_FAILURE,
|
|
|
|
|
|
NOTIFICATION_PERMANENT_FAILURE,
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2016-11-25 14:53:21 +00:00
|
|
|
|
NOTIFICATION_STATUS_TYPES_COMPLETED = [
|
2017-04-25 17:08:29 +01:00
|
|
|
|
NOTIFICATION_SENT,
|
2016-11-25 14:53:21 +00:00
|
|
|
|
NOTIFICATION_DELIVERED,
|
|
|
|
|
|
NOTIFICATION_FAILED,
|
|
|
|
|
|
NOTIFICATION_TECHNICAL_FAILURE,
|
|
|
|
|
|
NOTIFICATION_TEMPORARY_FAILURE,
|
|
|
|
|
|
NOTIFICATION_PERMANENT_FAILURE,
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2017-05-12 12:19:44 +01:00
|
|
|
|
NOTIFICATION_STATUS_SUCCESS = [
|
|
|
|
|
|
NOTIFICATION_SENT,
|
|
|
|
|
|
NOTIFICATION_DELIVERED
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2016-07-29 16:39:51 +01:00
|
|
|
|
NOTIFICATION_STATUS_TYPES_BILLABLE = [
|
|
|
|
|
|
NOTIFICATION_SENDING,
|
2017-04-25 17:08:29 +01:00
|
|
|
|
NOTIFICATION_SENT,
|
2016-07-29 16:39:51 +01:00
|
|
|
|
NOTIFICATION_DELIVERED,
|
|
|
|
|
|
NOTIFICATION_FAILED,
|
|
|
|
|
|
NOTIFICATION_TECHNICAL_FAILURE,
|
|
|
|
|
|
NOTIFICATION_TEMPORARY_FAILURE,
|
2016-11-25 14:55:29 +00:00
|
|
|
|
NOTIFICATION_PERMANENT_FAILURE,
|
2016-07-29 16:39:51 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
NOTIFICATION_STATUS_TYPES = [
|
|
|
|
|
|
NOTIFICATION_CREATED,
|
|
|
|
|
|
NOTIFICATION_SENDING,
|
2017-04-25 17:08:29 +01:00
|
|
|
|
NOTIFICATION_SENT,
|
2016-07-29 16:39:51 +01:00
|
|
|
|
NOTIFICATION_DELIVERED,
|
|
|
|
|
|
NOTIFICATION_PENDING,
|
|
|
|
|
|
NOTIFICATION_FAILED,
|
|
|
|
|
|
NOTIFICATION_TECHNICAL_FAILURE,
|
|
|
|
|
|
NOTIFICATION_TEMPORARY_FAILURE,
|
2016-11-25 14:55:29 +00:00
|
|
|
|
NOTIFICATION_PERMANENT_FAILURE,
|
2016-07-29 16:39:51 +01:00
|
|
|
|
]
|
2017-05-12 12:19:44 +01:00
|
|
|
|
|
2017-05-19 16:42:47 +01:00
|
|
|
|
NOTIFICATION_STATUS_TYPES_NON_BILLABLE = list(set(NOTIFICATION_STATUS_TYPES) - set(NOTIFICATION_STATUS_TYPES_BILLABLE))
|
|
|
|
|
|
|
2016-07-07 16:30:22 +01:00
|
|
|
|
NOTIFICATION_STATUS_TYPES_ENUM = db.Enum(*NOTIFICATION_STATUS_TYPES, name='notify_status_type')
|
2016-02-09 12:01:17 +00:00
|
|
|
|
|
2016-07-11 16:48:32 +01:00
|
|
|
|
|
2017-05-04 17:09:04 +01:00
|
|
|
|
class NotificationStatusTypes(db.Model):
|
|
|
|
|
|
__tablename__ = 'notification_status_types'
|
|
|
|
|
|
|
|
|
|
|
|
name = db.Column(db.String(255), primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-09 12:01:17 +00:00
|
|
|
|
class Notification(db.Model):
|
|
|
|
|
|
__tablename__ = 'notifications'
|
|
|
|
|
|
|
2016-02-09 18:28:10 +00:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2016-02-09 12:01:17 +00:00
|
|
|
|
to = db.Column(db.String, nullable=False)
|
2017-05-23 10:43:48 +01:00
|
|
|
|
normalised_to = db.Column(db.String, nullable=True)
|
2016-02-10 11:08:24 +00:00
|
|
|
|
job_id = db.Column(UUID(as_uuid=True), db.ForeignKey('jobs.id'), index=True, unique=False)
|
2016-02-09 12:01:17 +00:00
|
|
|
|
job = db.relationship('Job', backref=db.backref('notifications', lazy='dynamic'))
|
2016-05-19 10:46:03 +01:00
|
|
|
|
job_row_number = db.Column(db.Integer, nullable=True)
|
2016-02-09 12:01:17 +00:00
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False)
|
2016-02-09 12:48:27 +00:00
|
|
|
|
service = db.relationship('Service')
|
2016-04-08 13:34:46 +01:00
|
|
|
|
template_id = db.Column(UUID(as_uuid=True), db.ForeignKey('templates.id'), index=True, unique=False)
|
2016-02-09 12:48:27 +00:00
|
|
|
|
template = db.relationship('Template')
|
2016-05-11 17:04:51 +01:00
|
|
|
|
template_version = db.Column(db.Integer, nullable=False)
|
2016-06-23 16:45:20 +01:00
|
|
|
|
api_key_id = db.Column(UUID(as_uuid=True), db.ForeignKey('api_keys.id'), index=True, unique=False)
|
|
|
|
|
|
api_key = db.relationship('ApiKey')
|
2016-07-01 16:30:45 +01:00
|
|
|
|
key_type = db.Column(db.String, db.ForeignKey('key_types.name'), index=True, unique=False, nullable=False)
|
2016-08-03 14:27:58 +01:00
|
|
|
|
billable_units = db.Column(db.Integer, nullable=False, default=0)
|
2016-08-02 12:14:42 +01:00
|
|
|
|
notification_type = db.Column(notification_types, index=True, nullable=False)
|
2016-02-09 12:01:17 +00:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
2016-08-02 12:14:42 +01:00
|
|
|
|
index=True,
|
2016-02-09 12:01:17 +00:00
|
|
|
|
unique=False,
|
2016-02-25 09:59:50 +00:00
|
|
|
|
nullable=False)
|
|
|
|
|
|
sent_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True)
|
|
|
|
|
|
sent_by = db.Column(db.String, nullable=True)
|
2016-02-09 12:01:17 +00:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
2017-07-06 14:20:24 +01:00
|
|
|
|
status = db.Column(
|
2017-05-04 17:09:04 +01:00
|
|
|
|
'notification_status',
|
|
|
|
|
|
db.String,
|
|
|
|
|
|
db.ForeignKey('notification_status_types.name'),
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
nullable=True,
|
2017-07-06 14:20:24 +01:00
|
|
|
|
default='created',
|
|
|
|
|
|
key='status' # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column
|
2017-05-04 17:09:04 +01:00
|
|
|
|
)
|
2016-03-11 09:40:35 +00:00
|
|
|
|
reference = db.Column(db.String, nullable=True, index=True)
|
2016-11-17 13:42:34 +00:00
|
|
|
|
client_reference = db.Column(db.String, index=True, nullable=True)
|
2016-06-20 16:23:56 +01:00
|
|
|
|
_personalisation = db.Column(db.String, nullable=True)
|
|
|
|
|
|
|
2016-08-10 16:57:27 +01:00
|
|
|
|
template_history = db.relationship('TemplateHistory', primaryjoin=and_(
|
2016-08-09 13:07:48 +01:00
|
|
|
|
foreign(template_id) == remote(TemplateHistory.id),
|
|
|
|
|
|
foreign(template_version) == remote(TemplateHistory.version)
|
|
|
|
|
|
))
|
|
|
|
|
|
|
2017-05-16 15:29:31 +01:00
|
|
|
|
scheduled_notification = db.relationship('ScheduledNotification', uselist=False)
|
2017-05-15 17:27:38 +01:00
|
|
|
|
|
2017-04-26 10:22:20 +01:00
|
|
|
|
client_reference = db.Column(db.String, index=True, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
international = db.Column(db.Boolean, nullable=False, default=False)
|
|
|
|
|
|
phone_prefix = db.Column(db.String, nullable=True)
|
2017-04-27 16:40:00 +01:00
|
|
|
|
rate_multiplier = db.Column(db.Float(asdecimal=False), nullable=True)
|
2017-04-26 10:22:20 +01:00
|
|
|
|
|
2017-06-13 15:33:33 +01:00
|
|
|
|
created_by = db.relationship('User')
|
|
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), nullable=True)
|
|
|
|
|
|
|
2016-06-20 16:23:56 +01:00
|
|
|
|
@property
|
|
|
|
|
|
def personalisation(self):
|
|
|
|
|
|
if self._personalisation:
|
|
|
|
|
|
return encryption.decrypt(self._personalisation)
|
2017-07-03 16:04:21 +01:00
|
|
|
|
return {}
|
2016-06-20 16:23:56 +01:00
|
|
|
|
|
|
|
|
|
|
@personalisation.setter
|
|
|
|
|
|
def personalisation(self, personalisation):
|
2017-07-03 16:04:21 +01:00
|
|
|
|
self._personalisation = encryption.encrypt(personalisation or {})
|
2016-02-23 16:21:47 +00:00
|
|
|
|
|
2016-11-18 17:36:11 +00:00
|
|
|
|
def completed_at(self):
|
2016-11-25 14:53:21 +00:00
|
|
|
|
if self.status in NOTIFICATION_STATUS_TYPES_COMPLETED:
|
2016-11-18 17:36:11 +00:00
|
|
|
|
return self.updated_at.strftime(DATETIME_FORMAT)
|
|
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
2016-11-25 14:55:29 +00:00
|
|
|
|
@staticmethod
|
|
|
|
|
|
def substitute_status(status_or_statuses):
|
|
|
|
|
|
"""
|
|
|
|
|
|
static function that takes a status or list of statuses and substitutes our new failure types if it finds
|
|
|
|
|
|
the deprecated one
|
|
|
|
|
|
|
|
|
|
|
|
> IN
|
|
|
|
|
|
'failed'
|
|
|
|
|
|
|
|
|
|
|
|
< OUT
|
|
|
|
|
|
['technical-failure', 'temporary-failure', 'permanent-failure']
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
> IN
|
|
|
|
|
|
['failed', 'created']
|
|
|
|
|
|
|
|
|
|
|
|
< OUT
|
|
|
|
|
|
['technical-failure', 'temporary-failure', 'permanent-failure', 'created']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:param status_or_statuses: a single status or list of statuses
|
|
|
|
|
|
:return: a single status or list with the current failure statuses substituted for 'failure'
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def _substitute_status_str(_status):
|
|
|
|
|
|
return NOTIFICATION_STATUS_TYPES_FAILED if _status == NOTIFICATION_FAILED else _status
|
|
|
|
|
|
|
|
|
|
|
|
def _substitute_status_seq(_statuses):
|
|
|
|
|
|
if NOTIFICATION_FAILED in _statuses:
|
|
|
|
|
|
_statuses = list(set(
|
|
|
|
|
|
NOTIFICATION_STATUS_TYPES_FAILED + [_s for _s in _statuses if _s != NOTIFICATION_FAILED]
|
|
|
|
|
|
))
|
|
|
|
|
|
return _statuses
|
|
|
|
|
|
|
|
|
|
|
|
if isinstance(status_or_statuses, str):
|
|
|
|
|
|
return _substitute_status_str(status_or_statuses)
|
|
|
|
|
|
|
|
|
|
|
|
return _substitute_status_seq(status_or_statuses)
|
|
|
|
|
|
|
2016-12-15 16:19:55 +00:00
|
|
|
|
@property
|
|
|
|
|
|
def content(self):
|
|
|
|
|
|
from app.utils import get_template_instance
|
|
|
|
|
|
template_object = get_template_instance(self.template.__dict__, self.personalisation)
|
|
|
|
|
|
return str(template_object)
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
def subject(self):
|
|
|
|
|
|
from app.utils import get_template_instance
|
|
|
|
|
|
if self.notification_type == EMAIL_TYPE:
|
|
|
|
|
|
template_object = get_template_instance(self.template.__dict__, self.personalisation)
|
|
|
|
|
|
return template_object.subject
|
|
|
|
|
|
|
2017-04-20 11:52:00 +01:00
|
|
|
|
@property
|
|
|
|
|
|
def formatted_status(self):
|
|
|
|
|
|
return {
|
|
|
|
|
|
'email': {
|
|
|
|
|
|
'failed': 'Failed',
|
|
|
|
|
|
'technical-failure': 'Technical failure',
|
|
|
|
|
|
'temporary-failure': 'Inbox not accepting messages right now',
|
|
|
|
|
|
'permanent-failure': 'Email address doesn’t exist',
|
|
|
|
|
|
'delivered': 'Delivered',
|
|
|
|
|
|
'sending': 'Sending',
|
2017-04-28 11:00:55 +01:00
|
|
|
|
'created': 'Sending',
|
|
|
|
|
|
'sent': 'Delivered'
|
2017-04-20 11:52:00 +01:00
|
|
|
|
},
|
|
|
|
|
|
'sms': {
|
|
|
|
|
|
'failed': 'Failed',
|
|
|
|
|
|
'technical-failure': 'Technical failure',
|
|
|
|
|
|
'temporary-failure': 'Phone not accepting messages right now',
|
|
|
|
|
|
'permanent-failure': 'Phone number doesn’t exist',
|
|
|
|
|
|
'delivered': 'Delivered',
|
|
|
|
|
|
'sending': 'Sending',
|
2017-04-28 11:00:55 +01:00
|
|
|
|
'created': 'Sending',
|
|
|
|
|
|
'sent': 'Sent internationally'
|
2017-04-20 11:52:00 +01:00
|
|
|
|
},
|
|
|
|
|
|
'letter': {
|
|
|
|
|
|
'failed': 'Failed',
|
|
|
|
|
|
'technical-failure': 'Technical failure',
|
|
|
|
|
|
'temporary-failure': 'Temporary failure',
|
|
|
|
|
|
'permanent-failure': 'Permanent failure',
|
|
|
|
|
|
'delivered': 'Delivered',
|
|
|
|
|
|
'sending': 'Sending',
|
2017-04-28 11:00:55 +01:00
|
|
|
|
'created': 'Sending',
|
|
|
|
|
|
'sent': 'Delivered'
|
2017-04-20 11:52:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
}[self.template.template_type].get(self.status, self.status)
|
|
|
|
|
|
|
|
|
|
|
|
def serialize_for_csv(self):
|
2017-05-17 15:06:15 +01:00
|
|
|
|
created_at_in_bst = convert_utc_time_in_bst(self.created_at)
|
2017-04-20 11:52:00 +01:00
|
|
|
|
serialized = {
|
|
|
|
|
|
"row_number": '' if self.job_row_number is None else self.job_row_number + 1,
|
|
|
|
|
|
"recipient": self.to,
|
|
|
|
|
|
"template_name": self.template.name,
|
|
|
|
|
|
"template_type": self.template.template_type,
|
|
|
|
|
|
"job_name": self.job.original_file_name if self.job else '',
|
|
|
|
|
|
"status": self.formatted_status,
|
|
|
|
|
|
"created_at": time.strftime('%A %d %B %Y at %H:%M', created_at_in_bst.timetuple())
|
|
|
|
|
|
}
|
2016-11-18 17:36:11 +00:00
|
|
|
|
|
2017-04-20 11:52:00 +01:00
|
|
|
|
return serialized
|
|
|
|
|
|
|
|
|
|
|
|
def serialize(self):
|
2016-11-18 17:36:11 +00:00
|
|
|
|
template_dict = {
|
|
|
|
|
|
'version': self.template.version,
|
|
|
|
|
|
'id': self.template.id,
|
|
|
|
|
|
'uri': self.template.get_link()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
serialized = {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"reference": self.client_reference,
|
|
|
|
|
|
"email_address": self.to if self.notification_type == EMAIL_TYPE else None,
|
|
|
|
|
|
"phone_number": self.to if self.notification_type == SMS_TYPE else None,
|
|
|
|
|
|
"line_1": None,
|
|
|
|
|
|
"line_2": None,
|
|
|
|
|
|
"line_3": None,
|
|
|
|
|
|
"line_4": None,
|
|
|
|
|
|
"line_5": None,
|
|
|
|
|
|
"line_6": None,
|
|
|
|
|
|
"postcode": None,
|
|
|
|
|
|
"type": self.notification_type,
|
|
|
|
|
|
"status": self.status,
|
|
|
|
|
|
"template": template_dict,
|
2016-12-15 16:19:55 +00:00
|
|
|
|
"body": self.content,
|
|
|
|
|
|
"subject": self.subject,
|
2016-11-18 17:36:11 +00:00
|
|
|
|
"created_at": self.created_at.strftime(DATETIME_FORMAT),
|
|
|
|
|
|
"sent_at": self.sent_at.strftime(DATETIME_FORMAT) if self.sent_at else None,
|
2017-05-15 17:27:38 +01:00
|
|
|
|
"completed_at": self.completed_at(),
|
2017-05-24 14:52:32 +01:00
|
|
|
|
"scheduled_for": convert_bst_to_utc(self.scheduled_notification.scheduled_for
|
2017-05-24 16:27:15 +01:00
|
|
|
|
).strftime(DATETIME_FORMAT) if self.scheduled_notification else None
|
2016-11-18 17:36:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return serialized
|
|
|
|
|
|
|
2016-02-23 16:21:47 +00:00
|
|
|
|
|
2016-12-15 17:11:47 +00:00
|
|
|
|
class NotificationHistory(db.Model, HistoryModel):
|
2016-07-07 16:30:22 +01:00
|
|
|
|
__tablename__ = 'notification_history'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True)
|
|
|
|
|
|
job_id = db.Column(UUID(as_uuid=True), db.ForeignKey('jobs.id'), index=True, unique=False)
|
|
|
|
|
|
job = db.relationship('Job')
|
|
|
|
|
|
job_row_number = db.Column(db.Integer, nullable=True)
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False)
|
|
|
|
|
|
service = db.relationship('Service')
|
|
|
|
|
|
template_id = db.Column(UUID(as_uuid=True), db.ForeignKey('templates.id'), index=True, unique=False)
|
|
|
|
|
|
template = db.relationship('Template')
|
|
|
|
|
|
template_version = db.Column(db.Integer, nullable=False)
|
|
|
|
|
|
api_key_id = db.Column(UUID(as_uuid=True), db.ForeignKey('api_keys.id'), index=True, unique=False)
|
|
|
|
|
|
api_key = db.relationship('ApiKey')
|
|
|
|
|
|
key_type = db.Column(db.String, db.ForeignKey('key_types.name'), index=True, unique=False, nullable=False)
|
2016-08-03 14:27:58 +01:00
|
|
|
|
billable_units = db.Column(db.Integer, nullable=False, default=0)
|
2016-08-02 12:14:42 +01:00
|
|
|
|
notification_type = db.Column(notification_types, index=True, nullable=False)
|
|
|
|
|
|
created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False)
|
2016-07-07 16:30:22 +01:00
|
|
|
|
sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
|
|
|
|
|
|
sent_by = db.Column(db.String, nullable=True)
|
|
|
|
|
|
updated_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
|
2017-07-06 14:20:24 +01:00
|
|
|
|
status = db.Column(
|
2017-05-04 17:09:04 +01:00
|
|
|
|
'notification_status',
|
|
|
|
|
|
db.String,
|
|
|
|
|
|
db.ForeignKey('notification_status_types.name'),
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
nullable=True,
|
2017-07-06 14:20:24 +01:00
|
|
|
|
default='created',
|
|
|
|
|
|
key='status' # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column
|
2017-05-04 17:09:04 +01:00
|
|
|
|
)
|
2016-07-07 16:30:22 +01:00
|
|
|
|
reference = db.Column(db.String, nullable=True, index=True)
|
2016-11-17 13:42:34 +00:00
|
|
|
|
client_reference = db.Column(db.String, nullable=True)
|
2016-07-07 16:30:22 +01:00
|
|
|
|
|
2017-04-26 10:22:20 +01:00
|
|
|
|
international = db.Column(db.Boolean, nullable=False, default=False)
|
|
|
|
|
|
phone_prefix = db.Column(db.String, nullable=True)
|
2017-04-27 15:43:57 +01:00
|
|
|
|
rate_multiplier = db.Column(db.Float(asdecimal=False), nullable=True)
|
2017-04-26 10:22:20 +01:00
|
|
|
|
|
2017-06-23 15:56:47 +01:00
|
|
|
|
created_by = db.relationship('User')
|
|
|
|
|
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), nullable=True)
|
|
|
|
|
|
|
2016-07-08 16:19:34 +01:00
|
|
|
|
@classmethod
|
2016-12-15 17:11:47 +00:00
|
|
|
|
def from_original(cls, notification):
|
|
|
|
|
|
history = super().from_original(notification)
|
2017-05-04 17:09:04 +01:00
|
|
|
|
history.status = notification.status
|
2016-08-25 11:55:38 +01:00
|
|
|
|
return history
|
2016-07-08 16:19:34 +01:00
|
|
|
|
|
2017-05-09 11:09:16 +01:00
|
|
|
|
def update_from_original(self, original):
|
|
|
|
|
|
super().update_from_original(original)
|
|
|
|
|
|
self.status = original.status
|
|
|
|
|
|
|
2016-07-07 16:30:22 +01:00
|
|
|
|
|
2016-02-23 16:21:47 +00:00
|
|
|
|
INVITED_USER_STATUS_TYPES = ['pending', 'accepted', 'cancelled']
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-05-15 12:59:44 +01:00
|
|
|
|
class ScheduledNotification(db.Model):
|
|
|
|
|
|
__tablename__ = 'scheduled_notifications'
|
|
|
|
|
|
|
2017-05-26 15:41:14 +01:00
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2017-05-15 12:59:44 +01:00
|
|
|
|
notification_id = db.Column(UUID(as_uuid=True), db.ForeignKey('notifications.id'), index=True, nullable=False)
|
2017-05-16 15:29:31 +01:00
|
|
|
|
notification = db.relationship('Notification', uselist=False)
|
2017-05-15 12:59:44 +01:00
|
|
|
|
scheduled_for = db.Column(db.DateTime, index=False, nullable=False)
|
2017-05-22 15:07:16 +01:00
|
|
|
|
pending = db.Column(db.Boolean, nullable=False, default=True)
|
2017-05-15 12:59:44 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-02-23 16:21:47 +00:00
|
|
|
|
class InvitedUser(db.Model):
|
|
|
|
|
|
__tablename__ = 'invited_users'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
email_address = db.Column(db.String(255), nullable=False)
|
2016-04-08 13:34:46 +01:00
|
|
|
|
user_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2016-02-23 16:21:47 +00:00
|
|
|
|
from_user = db.relationship('User')
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False)
|
|
|
|
|
|
service = db.relationship('Service')
|
|
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-02-23 16:21:47 +00:00
|
|
|
|
status = db.Column(
|
2016-02-24 14:01:19 +00:00
|
|
|
|
db.Enum(*INVITED_USER_STATUS_TYPES, name='invited_users_status_types'), nullable=False, default='pending')
|
2016-02-29 09:49:12 +00:00
|
|
|
|
permissions = db.Column(db.String, nullable=False)
|
|
|
|
|
|
|
|
|
|
|
|
# would like to have used properties for this but haven't found a way to make them
|
|
|
|
|
|
# play nice with marshmallow yet
|
|
|
|
|
|
def get_permissions(self):
|
|
|
|
|
|
return self.permissions.split(',')
|
2016-02-26 12:00:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-03-01 14:21:28 +00:00
|
|
|
|
# Service Permissions
|
2016-03-02 15:34:26 +00:00
|
|
|
|
MANAGE_USERS = 'manage_users'
|
2016-03-01 14:21:28 +00:00
|
|
|
|
MANAGE_TEMPLATES = 'manage_templates'
|
2016-03-02 15:34:26 +00:00
|
|
|
|
MANAGE_SETTINGS = 'manage_settings'
|
|
|
|
|
|
SEND_TEXTS = 'send_texts'
|
|
|
|
|
|
SEND_EMAILS = 'send_emails'
|
|
|
|
|
|
SEND_LETTERS = 'send_letters'
|
|
|
|
|
|
MANAGE_API_KEYS = 'manage_api_keys'
|
2016-03-17 10:37:24 +00:00
|
|
|
|
PLATFORM_ADMIN = 'platform_admin'
|
2016-03-29 15:35:34 +01:00
|
|
|
|
VIEW_ACTIVITY = 'view_activity'
|
2016-03-01 14:21:28 +00:00
|
|
|
|
|
|
|
|
|
|
# List of permissions
|
|
|
|
|
|
PERMISSION_LIST = [
|
2016-03-02 15:34:26 +00:00
|
|
|
|
MANAGE_USERS,
|
2016-03-01 17:18:46 +00:00
|
|
|
|
MANAGE_TEMPLATES,
|
2016-03-02 15:34:26 +00:00
|
|
|
|
MANAGE_SETTINGS,
|
|
|
|
|
|
SEND_TEXTS,
|
|
|
|
|
|
SEND_EMAILS,
|
|
|
|
|
|
SEND_LETTERS,
|
|
|
|
|
|
MANAGE_API_KEYS,
|
2016-03-29 15:35:34 +01:00
|
|
|
|
PLATFORM_ADMIN,
|
|
|
|
|
|
VIEW_ACTIVITY]
|
2016-03-01 14:21:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-02-26 12:00:16 +00:00
|
|
|
|
class Permission(db.Model):
|
|
|
|
|
|
__tablename__ = 'permissions'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
# Service id is optional, if the service is omitted we will assume the permission is not service specific.
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False, nullable=True)
|
|
|
|
|
|
service = db.relationship('Service')
|
2016-04-08 13:34:46 +01:00
|
|
|
|
user_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
|
2016-02-26 12:00:16 +00:00
|
|
|
|
user = db.relationship('User')
|
2016-03-01 17:18:46 +00:00
|
|
|
|
permission = db.Column(
|
|
|
|
|
|
db.Enum(*PERMISSION_LIST, name='permission_types'),
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False)
|
2016-02-26 12:00:16 +00:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
2016-03-15 09:32:43 +00:00
|
|
|
|
default=datetime.datetime.utcnow)
|
2016-02-26 12:00:16 +00:00
|
|
|
|
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
UniqueConstraint('service_id', 'user_id', 'permission', name='uix_service_user_permission'),
|
|
|
|
|
|
)
|
2016-03-31 15:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-04-27 10:27:05 +01:00
|
|
|
|
class Event(db.Model):
|
|
|
|
|
|
__tablename__ = 'events'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
event_type = db.Column(db.String(255), nullable=False)
|
|
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
default=datetime.datetime.utcnow)
|
|
|
|
|
|
data = db.Column(JSON, nullable=False)
|
2017-04-24 16:20:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-04-25 09:53:43 +01:00
|
|
|
|
class Rate(db.Model):
|
2017-04-24 16:20:03 +01:00
|
|
|
|
__tablename__ = 'rates'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
valid_from = db.Column(db.DateTime, nullable=False)
|
2017-04-27 15:43:57 +01:00
|
|
|
|
rate = db.Column(db.Float(asdecimal=False), nullable=False)
|
2017-04-24 16:20:03 +01:00
|
|
|
|
notification_type = db.Column(notification_types, index=True, nullable=False)
|
2017-05-09 11:22:05 +01:00
|
|
|
|
|
2017-05-24 08:57:11 +01:00
|
|
|
|
def __str__(self):
|
|
|
|
|
|
the_string = "{}".format(self.rate)
|
|
|
|
|
|
the_string += " {}".format(self.notification_type)
|
|
|
|
|
|
the_string += " {}".format(self.valid_from)
|
|
|
|
|
|
return the_string
|
|
|
|
|
|
|
2017-05-09 11:22:05 +01:00
|
|
|
|
|
|
|
|
|
|
class JobStatistics(db.Model):
|
|
|
|
|
|
__tablename__ = 'job_statistics'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
2017-05-12 13:18:12 +01:00
|
|
|
|
job_id = db.Column(UUID(as_uuid=True), db.ForeignKey('jobs.id'), index=True, unique=True, nullable=False)
|
2017-05-09 11:22:05 +01:00
|
|
|
|
job = db.relationship('Job', backref=db.backref('job_statistics', lazy='dynamic'))
|
|
|
|
|
|
emails_sent = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
emails_delivered = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
emails_failed = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
sms_sent = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
sms_delivered = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
sms_failed = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
letters_sent = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
|
|
|
|
|
letters_failed = db.Column(db.BigInteger, index=False, unique=False, nullable=False, default=0)
|
2017-06-07 11:15:05 +01:00
|
|
|
|
sent = db.Column(db.BigInteger, index=False, unique=False, nullable=True, default=0)
|
|
|
|
|
|
delivered = db.Column(db.BigInteger, index=False, unique=False, nullable=True, default=0)
|
|
|
|
|
|
failed = db.Column(db.BigInteger, index=False, unique=False, nullable=True, default=0)
|
2017-05-11 15:23:18 +01:00
|
|
|
|
created_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
default=datetime.datetime.utcnow)
|
2017-05-09 14:04:19 +01:00
|
|
|
|
updated_at = db.Column(
|
|
|
|
|
|
db.DateTime,
|
|
|
|
|
|
index=False,
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
onupdate=datetime.datetime.utcnow)
|
|
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
|
the_string = ""
|
|
|
|
|
|
the_string += "email sent {} email delivered {} email failed {} ".format(
|
|
|
|
|
|
self.emails_sent, self.emails_delivered, self.emails_failed
|
|
|
|
|
|
)
|
|
|
|
|
|
the_string += "sms sent {} sms delivered {} sms failed {} ".format(
|
|
|
|
|
|
self.sms_sent, self.sms_delivered, self.sms_failed
|
|
|
|
|
|
)
|
2017-05-12 12:19:44 +01:00
|
|
|
|
the_string += "letter sent {} letter failed {} ".format(
|
|
|
|
|
|
self.letters_sent, self.letters_failed
|
|
|
|
|
|
)
|
|
|
|
|
|
the_string += "job_id {} ".format(
|
|
|
|
|
|
self.job_id
|
|
|
|
|
|
)
|
|
|
|
|
|
the_string += "created at {}".format(self.created_at)
|
2017-05-09 14:04:19 +01:00
|
|
|
|
return the_string
|
2017-05-22 11:26:47 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InboundSms(db.Model):
|
|
|
|
|
|
__tablename__ = 'inbound_sms'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
|
|
|
|
|
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False)
|
|
|
|
|
|
service = db.relationship('Service', backref='inbound_sms')
|
|
|
|
|
|
|
|
|
|
|
|
notify_number = db.Column(db.String, nullable=False) # the service's number, that the msg was sent to
|
2017-07-10 14:43:46 +01:00
|
|
|
|
user_number = db.Column(db.String, nullable=False, index=True) # the end user's number, that the msg was sent from
|
2017-05-22 11:26:47 +01:00
|
|
|
|
provider_date = db.Column(db.DateTime)
|
|
|
|
|
|
provider_reference = db.Column(db.String)
|
2017-06-02 16:51:27 +01:00
|
|
|
|
provider = db.Column(db.String, nullable=False)
|
2017-05-22 11:26:47 +01:00
|
|
|
|
_content = db.Column('content', db.String, nullable=False)
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
def content(self):
|
|
|
|
|
|
return encryption.decrypt(self._content)
|
|
|
|
|
|
|
|
|
|
|
|
@content.setter
|
|
|
|
|
|
def content(self, content):
|
|
|
|
|
|
self._content = encryption.encrypt(content)
|
2017-06-02 14:47:28 +01:00
|
|
|
|
|
2017-05-31 14:49:14 +01:00
|
|
|
|
def serialize(self):
|
|
|
|
|
|
return {
|
|
|
|
|
|
'id': str(self.id),
|
|
|
|
|
|
'created_at': self.created_at.isoformat(),
|
|
|
|
|
|
'service_id': str(self.service_id),
|
|
|
|
|
|
'notify_number': self.notify_number,
|
|
|
|
|
|
'user_number': self.user_number,
|
|
|
|
|
|
'content': self.content,
|
|
|
|
|
|
'provider_date': self.provider_date and self.provider_date.isoformat(),
|
|
|
|
|
|
'provider_reference': self.provider_reference
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-02 14:47:28 +01:00
|
|
|
|
|
2017-05-31 13:34:54 +01:00
|
|
|
|
class LetterRate(db.Model):
|
|
|
|
|
|
__tablename__ = 'letter_rates'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
valid_from = valid_from = db.Column(db.DateTime, nullable=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LetterRateDetail(db.Model):
|
|
|
|
|
|
__tablename__ = 'letter_rate_details'
|
|
|
|
|
|
|
|
|
|
|
|
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
|
letter_rate_id = db.Column(UUID(as_uuid=True), db.ForeignKey('letter_rates.id'), index=True, nullable=False)
|
|
|
|
|
|
letter_rate = db.relationship('LetterRate', backref='letter_rates')
|
|
|
|
|
|
page_total = db.Column(db.Integer, nullable=False)
|
|
|
|
|
|
rate = db.Column(db.Numeric(), nullable=False)
|