backend/models/postgis/organisation.py (130 lines of code) (raw):
from slugify import slugify
from backend import db
from backend.models.dtos.organisation_dto import (
OrganisationDTO,
NewOrganisationDTO,
OrganisationManagerDTO,
)
from backend.models.postgis.user import User
from backend.models.postgis.campaign import Campaign, campaign_organisations
from backend.models.postgis.utils import NotFound
from backend.models.postgis.statuses import OrganisationType
# Secondary table defining many-to-many relationship between organisations and managers
organisation_managers = db.Table(
"organisation_managers",
db.metadata,
db.Column(
"organisation_id", db.Integer, db.ForeignKey("organisations.id"), nullable=False
),
db.Column("user_id", db.BigInteger, db.ForeignKey("users.id"), nullable=False),
db.UniqueConstraint("organisation_id", "user_id", name="organisation_user_key"),
)
class InvalidRoleException(Exception):
pass
class Organisation(db.Model):
""" Describes an Organisation """
__tablename__ = "organisations"
# Columns
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(512), nullable=False, unique=True)
slug = db.Column(db.String(255), nullable=False, unique=True)
logo = db.Column(db.String) # URL of a logo
description = db.Column(db.String)
url = db.Column(db.String)
type = db.Column(db.Integer, default=OrganisationType.FREE.value, nullable=False)
subscription_tier = db.Column(db.Integer)
managers = db.relationship(
User,
secondary=organisation_managers,
backref=db.backref("organisations", lazy="joined"),
)
campaign = db.relationship(
Campaign, secondary=campaign_organisations, backref="organisation"
)
def create(self):
""" Creates and saves the current model to the DB """
db.session.add(self)
db.session.commit()
@classmethod
def create_from_dto(cls, new_organisation_dto: NewOrganisationDTO):
""" Creates a new organisation from a DTO """
new_org = cls()
new_org.name = new_organisation_dto.name
new_org.slug = new_organisation_dto.slug or slugify(new_organisation_dto.name)
new_org.logo = new_organisation_dto.logo
new_org.description = new_organisation_dto.description
new_org.url = new_organisation_dto.url
new_org.type = OrganisationType[new_organisation_dto.type].value
new_org.subscription_tier = new_organisation_dto.subscription_tier
for manager in new_organisation_dto.managers:
user = User.get_by_username(manager)
if user is None:
raise NotFound(f"User {manager} Not Found")
new_org.managers.append(user)
new_org.create()
return new_org
def update(self, organisation_dto: OrganisationDTO):
""" Updates Organisation from DTO """
for attr, value in organisation_dto.items():
if attr == "type" and value is not None:
value = OrganisationType[organisation_dto.type].value
if attr == "managers":
continue
try:
is_field_nullable = self.__table__.columns[attr].nullable
if is_field_nullable and value is not None:
setattr(self, attr, value)
elif value is not None:
setattr(self, attr, value)
except KeyError:
continue
if organisation_dto.managers:
self.managers = []
# Need to handle this in the loop so we can take care of NotFound users
for manager in organisation_dto.managers:
new_manager = User.get_by_username(manager)
if new_manager is None:
raise NotFound(f"User {manager} Not Found")
self.managers.append(new_manager)
db.session.commit()
def delete(self):
""" Deletes the current model from the DB """
db.session.delete(self)
db.session.commit()
def can_be_deleted(self) -> bool:
""" An Organisation can be deleted if it doesn't have any projects or teams """
return len(self.projects) == 0 and len(self.teams) == 0
@staticmethod
def get(organisation_id: int):
"""
Gets specified organisation by id
:param organisation_id: organisation ID in scope
:return: Organisation if found otherwise None
"""
return Organisation.query.get(organisation_id)
@staticmethod
def get_organisation_by_name(organisation_name: str):
"""Get organisation by name
:param organisation_name: name of organisation
:return: Organisation if found else None
"""
return Organisation.query.filter_by(name=organisation_name).first()
@staticmethod
def get_organisation_name_by_id(organisation_id: int):
"""Get organisation name by id
:param organisation_id:
:return: Organisation name
"""
return Organisation.query.get(organisation_id).name
@staticmethod
def get_all_organisations():
""" Gets all organisations"""
return Organisation.query.order_by(Organisation.name).all()
@staticmethod
def get_organisations_managed_by_user(user_id: int):
""" Gets organisations a user can manage """
query_results = (
Organisation.query.join(organisation_managers)
.filter(
(organisation_managers.c.organisation_id == Organisation.id)
& (organisation_managers.c.user_id == user_id)
)
.order_by(Organisation.name)
.all()
)
return query_results
def as_dto(self, omit_managers=False):
""" Returns a dto for an organisation """
organisation_dto = OrganisationDTO()
organisation_dto.organisation_id = self.id
organisation_dto.name = self.name
organisation_dto.slug = self.slug
organisation_dto.logo = self.logo
organisation_dto.description = self.description
organisation_dto.url = self.url
organisation_dto.managers = []
organisation_dto.type = OrganisationType(self.type).name
organisation_dto.subscription_tier = self.subscription_tier
if omit_managers:
return organisation_dto
for manager in self.managers:
org_manager_dto = OrganisationManagerDTO()
org_manager_dto.username = manager.username
org_manager_dto.picture_url = manager.picture_url
organisation_dto.managers.append(org_manager_dto)
return organisation_dto