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