Merge pull request #2896 from alphagov/remove-domains-yml

Use organisations from database rather than YAML file
This commit is contained in:
Chris Hill-Scott
2019-04-12 16:37:15 +01:00
committed by GitHub
35 changed files with 438 additions and 5294 deletions

27
app/models/__init__.py Normal file
View File

@@ -0,0 +1,27 @@
from flask import abort
class JSONModel():
ALLOWED_PROPERTIES = set()
def __init__(self, _dict):
# in the case of a bad request _dict may be `None`
self._dict = _dict or {}
def __bool__(self):
return self._dict != {}
def __getattr__(self, attr):
if attr in self.ALLOWED_PROPERTIES:
return self._dict[attr]
raise AttributeError('`{}` is not a {} attribute'.format(
attr,
self.__class__.__name__.lower(),
))
def _get_by_id(self, things, id):
try:
return next(thing for thing in things if thing['id'] == str(id))
except StopIteration:
abort(404)

134
app/models/organisation.py Normal file
View File

@@ -0,0 +1,134 @@
from flask import Markup, abort
from app.models import JSONModel
class Organisation(JSONModel):
ALLOWED_PROPERTIES = {
'id',
'name',
'active',
'crown',
'organisation_type',
'letter_branding_id',
'email_branding_id',
'agreement_signed',
'agreement_signed_at',
'agreement_signed_by_id',
'agreement_signed_version',
'domains',
}
def __init__(self, _dict):
super().__init__(_dict)
if self._dict == {}:
self.name, self.crown, self.agreement_signed = None, None, None
def as_human_readable(self, fallback_domain):
if 'dwp.' in ''.join(self.domains):
return 'DWP - Requires OED approval'
if self.agreement_signed:
return 'Yes, on behalf of {}'.format(self.name)
elif self.name:
return '{} (organisation is {}, {})'.format(
{
False: 'No',
None: 'Cant tell',
}.get(self.agreement_signed),
self.name,
{
True: 'a crown body',
False: 'a non-crown body',
None: 'crown status unknown',
}.get(self.crown),
)
else:
return 'Cant tell (domain is {})'.format(fallback_domain)
def as_info_for_branding_request(self, fallback_domain):
return self.name or 'Cant tell (domain is {})'.format(fallback_domain)
@property
def as_jinja_template(self):
if self.crown is None:
return 'agreement-choose'
if self.agreement_signed:
return 'agreement-signed'
return 'agreement'
def as_terms_of_use_paragraph(self, **kwargs):
return Markup(self._as_terms_of_use_paragraph(**kwargs))
def _as_terms_of_use_paragraph(self, terms_link, download_link, support_link, signed_in):
if not signed_in:
return ((
'{} <a href="{}">Sign in</a> to download a copy '
'or find out if one is already in place.'
).format(self._acceptance_required, terms_link))
if self.agreement_signed is None:
return ((
'{} <a href="{}">Download the agreement</a> or '
'<a href="{}">contact us</a> to find out if we already '
'have one in place with your organisation.'
).format(self._acceptance_required, download_link, support_link))
if self.agreement_signed is False:
return ((
'{} <a href="{}">Download a copy</a>.'
).format(self._acceptance_required, download_link))
return (
'Your organisation ({}) has already accepted the '
'GOV.UK&nbsp;Notify data sharing and financial '
'agreement.'.format(self.name)
)
def as_pricing_paragraph(self, **kwargs):
return Markup(self._as_pricing_paragraph(**kwargs))
def _as_pricing_paragraph(self, pricing_link, download_link, support_link, signed_in):
if not signed_in:
return ((
'<a href="{}">Sign in</a> to download a copy or find '
'out if one is already in place with your organisation.'
).format(pricing_link))
if self.agreement_signed is None:
return ((
'<a href="{}">Download the agreement</a> or '
'<a href="{}">contact us</a> to find out if we already '
'have one in place with your organisation.'
).format(download_link, support_link))
return (
'<a href="{}">Download the agreement</a> '
'({} {}).'.format(
download_link,
self.name,
{
True: 'has already accepted it',
False: 'hasnt accepted it yet'
}.get(self.agreement_signed)
)
)
@property
def _acceptance_required(self):
return (
'Your organisation {} must also accept our data sharing '
'and financial agreement.'.format(
'({})'.format(self.name) if self.name else '',
)
)
@property
def crown_status_or_404(self):
if self.crown is None:
abort(404)
return self.crown

View File

@@ -2,6 +2,8 @@ from flask import abort, current_app
from notifications_utils.field import Field
from werkzeug.utils import cached_property
from app.models import JSONModel
from app.models.organisation import Organisation
from app.notify_client.api_key_api_client import api_key_api_client
from app.notify_client.billing_api_client import billing_api_client
from app.notify_client.email_branding_client import email_branding_client
@@ -18,7 +20,7 @@ from app.notify_client.user_api_client import user_api_client
from app.utils import get_default_sms_sender
class Service():
class Service(JSONModel):
ALLOWED_PROPERTIES = {
'active',
@@ -50,25 +52,12 @@ class Service():
)
def __init__(self, _dict):
# in the case of a bad request current service may be `None`
self._dict = _dict or {}
super().__init__(_dict)
if 'permissions' not in self._dict:
self.permissions = {'email', 'sms', 'letter'}
def __bool__(self):
return self._dict != {}
def __getattr__(self, attr):
if attr in self.ALLOWED_PROPERTIES:
return self._dict[attr]
raise AttributeError('`{}` is not a service attribute'.format(attr))
def _get_by_id(self, things, id):
try:
return next(thing for thing in things if thing['id'] == str(id))
except StopIteration:
abort(404)
def update(self, **kwargs):
return service_api_client.update_service(self.id, **kwargs)
@@ -405,8 +394,10 @@ class Service():
return None
@cached_property
def organisation_name(self):
return organisations_client.get_service_organisation(self.id).get('name', None)
def organisation(self):
return Organisation(
organisations_client.get_service_organisation(self.id)
)
@cached_property
def inbound_number(self):
@@ -555,3 +546,30 @@ class Service():
def get_api_key(self, id):
return self._get_by_id(self.api_keys, id)
@property
def request_to_go_live_tags(self):
return list(self._get_request_to_go_live_tags())
def _get_request_to_go_live_tags(self):
BASE = 'notify_request_to_go_live'
yield BASE
if self.go_live_checklist_completed and self.organisation.agreement_signed:
yield BASE + '_complete'
return
for test, tag in (
(True, ''),
(not self.volumes, '_volumes'),
(not self.go_live_checklist_completed, '_checklist'),
(not self.organisation.agreement_signed, '_mou'),
(self.needs_to_add_email_reply_to_address, '_email_reply_to'),
(not self.has_team_members, '_team_member'),
(not self.has_templates, '_template_content'),
(self.needs_to_change_sms_sender, '_sms_sender'),
):
if test:
yield BASE + '_incomplete' + tag

View File

@@ -2,7 +2,10 @@ from itertools import chain
from flask import abort, request, session
from flask_login import AnonymousUserMixin, UserMixin
from werkzeug.utils import cached_property
from app.models.organisation import Organisation
from app.notify_client.organisations_api_client import organisations_client
from app.utils import is_gov_user
roles = {
@@ -192,6 +195,16 @@ class User(UserMixin):
def is_locked(self):
return self.failed_login_count >= self.max_failed_login_count
@property
def email_domain(self):
return self.email_address.split('@')[-1]
@cached_property
def default_organisation(self):
return Organisation(
organisations_client.get_organisation_by_domain(self.email_domain)
)
def serialize(self):
dct = {
"id": self.id,
@@ -322,3 +335,7 @@ class AnonymousUser(AnonymousUserMixin):
# set the anonymous user so that if a new browser hits us we don't error http://stackoverflow.com/a/19275188
def logged_in_elsewhere(self):
return False
@property
def default_organisation(self):
return Organisation(None)