mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-07 14:28:24 -04:00
Get info about organisations from database table
This is the first step of replacing the `domains.yml` file. In order to replicate the same functionality we get from the `domains.yml` file and its associated code this commit adds a `Organisation` model. This model copies a lot of methods from the `AgreementInfo` class which wrapped the `domains.yml` file. It factors out some stuff that would otherwise be duplicated between the `Organisation` and `Service` model, in such a way that could be reused for making other models in the future. This commit doesn’t change other parts of the code to make use of this new model yet – that will come in subsequent commits.
This commit is contained in:
27
app/models/__init__.py
Normal file
27
app/models/__init__.py
Normal 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)
|
||||||
140
app/models/organisation.py
Normal file
140
app/models/organisation.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def crown_status(self):
|
||||||
|
return self.crown
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_human_readable(self):
|
||||||
|
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.owner:
|
||||||
|
return '{} (organisation is {}, {})'.format(
|
||||||
|
{
|
||||||
|
False: 'No',
|
||||||
|
None: 'Can’t tell',
|
||||||
|
}.get(self.agreement_signed),
|
||||||
|
self.name,
|
||||||
|
{
|
||||||
|
True: 'a crown body',
|
||||||
|
False: 'a non-crown body',
|
||||||
|
None: 'crown status unknown',
|
||||||
|
}.get(self.crown_status),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return 'Can’t tell (domain is {})'.format(self._domain)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_info_for_branding_request(self):
|
||||||
|
return self.owner or 'Can’t tell (domain is {})'.format(self._domain)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_jinja_template(self):
|
||||||
|
if self.crown_status 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 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: 'hasn’t 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_status is None:
|
||||||
|
abort(404)
|
||||||
|
return self.crown_status
|
||||||
@@ -2,6 +2,8 @@ from flask import abort, current_app
|
|||||||
from notifications_utils.field import Field
|
from notifications_utils.field import Field
|
||||||
from werkzeug.utils import cached_property
|
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.api_key_api_client import api_key_api_client
|
||||||
from app.notify_client.billing_api_client import billing_api_client
|
from app.notify_client.billing_api_client import billing_api_client
|
||||||
from app.notify_client.email_branding_client import email_branding_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
|
from app.utils import get_default_sms_sender
|
||||||
|
|
||||||
|
|
||||||
class Service():
|
class Service(JSONModel):
|
||||||
|
|
||||||
ALLOWED_PROPERTIES = {
|
ALLOWED_PROPERTIES = {
|
||||||
'active',
|
'active',
|
||||||
@@ -50,25 +52,12 @@ class Service():
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, _dict):
|
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:
|
if 'permissions' not in self._dict:
|
||||||
self.permissions = {'email', 'sms', 'letter'}
|
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):
|
def update(self, **kwargs):
|
||||||
return service_api_client.update_service(self.id, **kwargs)
|
return service_api_client.update_service(self.id, **kwargs)
|
||||||
|
|
||||||
@@ -402,8 +391,14 @@ class Service():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
def organisation(self):
|
||||||
|
return Organisation(
|
||||||
|
organisations_client.get_service_organisation(self.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
def organisation_name(self):
|
def organisation_name(self):
|
||||||
return organisations_client.get_service_organisation(self.id).get('name', None)
|
return self.organisation.name
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def inbound_number(self):
|
def inbound_number(self):
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ from itertools import chain
|
|||||||
|
|
||||||
from flask import abort, request, session
|
from flask import abort, request, session
|
||||||
from flask_login import AnonymousUserMixin, UserMixin
|
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
|
from app.utils import is_gov_user
|
||||||
|
|
||||||
roles = {
|
roles = {
|
||||||
@@ -192,6 +195,16 @@ class User(UserMixin):
|
|||||||
def is_locked(self):
|
def is_locked(self):
|
||||||
return self.failed_login_count >= self.max_failed_login_count
|
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):
|
def serialize(self):
|
||||||
dct = {
|
dct = {
|
||||||
"id": self.id,
|
"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
|
# 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):
|
def logged_in_elsewhere(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_organisation(self):
|
||||||
|
return Organisation(None)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from notifications_python_client.errors import HTTPError
|
||||||
|
|
||||||
from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache
|
from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +11,16 @@ class OrganisationsClient(NotifyAdminAPIClient):
|
|||||||
def get_organisation(self, org_id):
|
def get_organisation(self, org_id):
|
||||||
return self.get(url='/organisations/{}'.format(org_id))
|
return self.get(url='/organisations/{}'.format(org_id))
|
||||||
|
|
||||||
|
def get_organisation_by_domain(self, domain):
|
||||||
|
try:
|
||||||
|
return self.get(
|
||||||
|
url='/organisations/by-domain?domain={}'.format(domain),
|
||||||
|
)
|
||||||
|
except HTTPError as error:
|
||||||
|
if error.status_code == 404:
|
||||||
|
return None
|
||||||
|
raise error
|
||||||
|
|
||||||
def create_organisation(self, name):
|
def create_organisation(self, name):
|
||||||
data = {
|
data = {
|
||||||
"name": name
|
"name": name
|
||||||
|
|||||||
Reference in New Issue
Block a user