def get(self, organisation_id): """ Return statistics about projects and active tasks of an organisation --- tags: - organisations produces: - application/json parameters: - name: organisation_id in: path description: The unique organisation ID required: true type: integer default: 1 responses: 200: description: Organisation found 404: description: Organisation not found 500: description: Internal Server Error """ try: OrganisationService.get_organisation_by_id(organisation_id) organisation_dto = OrganisationService.get_organisation_stats( organisation_id) return organisation_dto.to_primitive(), 200 except NotFound: return {"Error": "Organisation Not Found"}, 404 except Exception as e: error_msg = f"Organisation GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def get_team_as_dto(team_id: int, user_id: int) -> TeamDTO: team = TeamService.get_team_by_id(team_id) if team is None: raise NotFound() team_dto = TeamDetailsDTO() team_dto.team_id = team.id team_dto.name = team.name team_dto.invite_only = team.invite_only team_dto.visibility = TeamVisibility(team.visibility).name team_dto.description = team.description team_dto.logo = team.organisation.logo team_dto.organisation = team.organisation.name team_dto.organisation_id = team.organisation.id if user_id != 0: if UserService.is_user_an_admin(user_id): team_dto.is_general_admin = True if OrganisationService.is_user_an_org_manager( team.organisation.id, user_id): team_dto.is_org_admin = True else: team_dto.is_general_admin = False team_dto.is_org_admin = False team_members = TeamService._get_team_members(team_id) for member in team_members: user = UserService.get_user_by_id(member.user_id) member_dto = TeamMembersDTO() member_dto.username = user.username member_dto.pictureUrl = user.picture_url member_dto.function = TeamMemberFunctions(member.function).name member_dto.picture_url = user.picture_url member_dto.active = member.active team_dto.members.append(member_dto) team_projects = TeamService.get_projects_by_team_id(team.id) for team_project in team_projects: project_team_dto = TeamProjectDTO() project_team_dto.project_name = team_project.name project_team_dto.project_id = team_project.project_id project_team_dto.role = TeamRoles(team_project.role).name team_dto.team_projects.append(project_team_dto) org_projects = OrganisationService.get_projects_by_organisation_id( team.organisation.id) for org_project in org_projects: org_project_dto = OrganisationProjectsDTO() org_project_dto.project_id = org_project.id org_project_dto.project_name = org_project.name team_dto.organisation_projects.append(org_project_dto) return team_dto
def create_draft_project(draft_project_dto: DraftProjectDTO) -> int: """ Validates and then persists draft projects in the DB :param draft_project_dto: Draft Project DTO with data from API :raises InvalidGeoJson :returns ID of new draft project """ user_id = draft_project_dto.user_id is_admin = UserService.is_user_an_admin(user_id) user_orgs = OrganisationService.get_organisations_managed_by_user_as_dto( user_id) is_org_manager = len(user_orgs.organisations) > 0 # First things first, we need to validate that the author_id is a PM. issue #1715 if not (is_admin or is_org_manager): user = UserService.get_user_by_id(user_id) raise (ProjectAdminServiceError( f"User {user.username} is not permitted to create project")) # If we're cloning we'll copy all the project details from the clone, otherwise create brand new project if draft_project_dto.cloneFromProjectId: draft_project = Project.clone(draft_project_dto.cloneFromProjectId, user_id) else: draft_project = Project() org = OrganisationService.get_organisation_by_id( draft_project_dto.organisation) if org is None: raise NotFound("Organisation does not exist") draft_project_dto.organisation = org draft_project.create_draft_project(draft_project_dto) draft_project.set_project_aoi(draft_project_dto) # if arbitrary_tasks requested, create tasks from aoi otherwise use tasks in DTO if draft_project_dto.has_arbitrary_tasks: tasks = GridService.tasks_from_aoi_features( draft_project_dto.area_of_interest) draft_project.task_creation_mode = TaskCreationMode.ARBITRARY.value else: tasks = draft_project_dto.tasks ProjectAdminService._attach_tasks_to_project(draft_project, tasks) if draft_project_dto.cloneFromProjectId: draft_project.save() # Update the clone else: draft_project.create() # Create the new project draft_project.set_default_changeset_comment() draft_project.set_country_info() return draft_project.id
def delete(self, organisation_id): """ Deletes an organisation --- tags: - organisations produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - name: organisation_id in: path description: The unique organisation ID required: true type: integer default: 1 responses: 200: description: Organisation deleted 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 404: description: Organisation not found 500: description: Internal Server Error """ if not OrganisationService.can_user_manage_organisation( organisation_id, token_auth.current_user() ): return {"Error": "User is not an admin for the org"}, 403 try: OrganisationService.delete_organisation(organisation_id) return {"Success": "Organisation deleted"}, 200 except OrganisationServiceError: return {"Error": "Organisation has some projects"}, 403 except NotFound: return {"Error": "Organisation Not Found"}, 404 except Exception as e: error_msg = f"Organisation DELETE - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def get_campaign_as_dto(campaign_id: int, user_id: int): """Gets the specified campaign""" campaign = CampaignService.get_campaign(campaign_id) campaign_dto = CampaignDTO() campaign_dto.id = campaign.id campaign_dto.url = campaign.url campaign_dto.name = campaign.name campaign_dto.logo = campaign.logo campaign_dto.description = campaign.description campaign_dto.organisations = [] orgs = ( Organisation.query.join(campaign_organisations) .filter(campaign_organisations.c.campaign_id == campaign.id) .all() ) for org in orgs: if user_id != 0: logged_in = OrganisationService.can_user_manage_organisation( org.id, user_id ) else: logged_in = False organisation_dto = OrganisationDTO() organisation_dto.organisation_id = org.id organisation_dto.name = org.name organisation_dto.logo = org.logo organisation_dto.url = org.url organisation_dto.is_manager = logged_in return campaign_dto
def is_user_action_permitted_on_project(authenticated_user_id: int, project_id: int) -> bool: """ Is user action permitted on project""" project = Project.get(project_id) author_id = project.author_id allowed_roles = [TeamRoles.PROJECT_MANAGER.value] is_admin = UserService.is_user_an_admin(authenticated_user_id) is_author = UserService.is_user_the_project_author( authenticated_user_id, author_id) is_org_manager = False if hasattr(project, "organisation_id") and project.organisation_id: org_id = project.organisation_id org = OrganisationService.get_organisation_by_id_as_dto( org_id, authenticated_user_id) if org.is_manager: is_org_manager = True is_manager_team = None if hasattr(project, "project_teams") and project.project_teams: teams_dto = TeamService.get_project_teams_as_dto(project_id) if teams_dto.teams: teams_allowed = [ team_dto for team_dto in teams_dto.teams if team_dto.role in allowed_roles ] user_membership = [ team_dto.team_id for team_dto in teams_allowed if TeamService.is_user_member_of_team( team_dto.team_id, authenticated_user_id) ] if user_membership: is_manager_team = True return is_admin or is_author or is_org_manager or is_manager_team
def get(self): """ Get all projects for logged in admin --- tags: - projects produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - in: header name: Accept-Language description: Language user is requesting type: string required: true default: en responses: 200: description: All mapped tasks validated 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 404: description: Admin has no projects 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto( authenticated_user_id ) if len(orgs_dto.organisations) < 1: raise ValueError("User not a project manager") except ValueError as e: error_msg = f"ProjectsQueriesOwnerAPI GET: {str(e)}" return {"Error": error_msg}, 403 try: search_dto = self.setup_search_dto() admin_projects = ProjectAdminService.get_projects_for_admin( authenticated_user_id, request.environ.get("HTTP_ACCEPT_LANGUAGE"), search_dto, ) return admin_projects.to_primitive(), 200 except NotFound: return {"Error": "No comments found"}, 404 except Exception as e: error_msg = f"Project GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def delete(self, organisation_id, campaign_id): """ Unassigns an organization from an campaign --- tags: - campaigns produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - name: organisation_id in: path description: Unique organisation ID required: true type: integer default: 1 - name: campaign_id in: path description: Unique campaign ID required: true type: integer default: 1 responses: 200: description: Organisation and campaign unassociated successfully 401: description: Unauthorized - Invalid credentials 403: description: Forbidden - users have submitted mapping 404: description: Project not found 500: description: Internal Server Error """ try: if OrganisationService.can_user_manage_organisation( organisation_id, token_auth.current_user() ): CampaignService.delete_organisation_campaign( organisation_id, campaign_id ) return ( {"Success": "Organisation and campaign unassociated successfully"}, 200, ) else: return {"Error": "User is not a manager of the organisation"}, 403 except NotFound: return {"Error": "Organisation Campaign Not Found"}, 404 except Exception as e: error_msg = f"Organisation Campaigns DELETE - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def delete(self, campaign_id): """ Deletes an existing campaign --- tags: - campaigns produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token type: string required: true default: Token sessionTokenHere== - in: header name: Accept-Language description: Language user is requesting type: string required: true default: en - name: campaign_id in: path description: Campaign ID required: true type: integer default: 1 responses: 200: description: Campaign deleted successfully 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto( token_auth.current_user() ) if len(orgs_dto.organisations) < 1: raise ValueError("User not a Org Manager") except ValueError as e: error_msg = f"CampaignsRestAPI DELETE: {str(e)}" return {"Error": error_msg}, 403 try: campaign = CampaignService.get_campaign(campaign_id) CampaignService.delete_campaign(campaign.id) return {"Success": "Campaign deleted"}, 200 except NotFound: return {"Error": "Campaign not found"}, 404 except Exception as e: error_msg = f"Campaign DELETE - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def create_campaign(campaign_dto: NewCampaignDTO): campaign = Campaign.from_dto(campaign_dto) campaign.create() if campaign_dto.organisations: for org_id in campaign_dto.organisations: organisation = OrganisationService.get_organisation_by_id( org_id) campaign.organisation.append(organisation) db.session.commit() return campaign
def get(self, organisation_id): """ Retrieves an organisation --- tags: - organisations produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token type: string default: Token sessionTokenHere== - name: organisation_id in: path description: The unique organisation ID required: true type: integer default: 1 - in: query name: omitManagerList type: boolean description: Set it to true if you don't want the managers list on the response. default: False responses: 200: description: Organisation found 401: description: Unauthorized - Invalid credentials 404: description: Organisation not found 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() if authenticated_user_id is None: user_id = 0 else: user_id = authenticated_user_id # Validate abbreviated. omit_managers = strtobool( request.args.get("omitManagerList", "false")) organisation_dto = OrganisationService.get_organisation_by_id_as_dto( organisation_id, user_id, omit_managers) return organisation_dto.to_primitive(), 200 except NotFound: return {"Error": "Organisation Not Found"}, 404 except Exception as e: error_msg = f"Organisation GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def delete(self, interest_id): """ Delete a specified interest --- tags: - interests produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - name: interest_id in: path description: Unique interest ID required: true type: integer default: 1 responses: 200: description: Interest deleted 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 404: description: Interest not found 500: description: Internal Server Error """ try: orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto( tm.authenticated_user_id ) if len(orgs_dto.organisations) < 1: raise ValueError("User not a Org Manager") except ValueError as e: error_msg = f"InterestsAllAPI DELETE: {str(e)}" return {"Error": error_msg}, 403 try: InterestService.delete(interest_id) return {"Success": "Interest deleted"}, 200 except NotFound: return {"Error": "Interest Not Found"}, 404 except Exception as e: error_msg = f"License DELETE - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def get(self, interest_id): """ Get an existing interest --- tags: - interests produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - name: interest_id in: path description: Interest ID required: true type: integer default: 1 responses: 200: description: Interest 400: description: Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto( tm.authenticated_user_id ) if len(orgs_dto.organisations) < 1: raise ValueError("User not a Org Manager") except ValueError as e: error_msg = f"InterestsRestAPI GET: {str(e)}" return {"Error": error_msg}, 403 try: interest = InterestService.get(interest_id) return interest.to_primitive(), 200 except Exception as e: error_msg = f"Interest GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def create_campaign(campaign_dto: NewCampaignDTO): campaign = Campaign.from_dto(campaign_dto) try: campaign.create() if campaign_dto.organisations: for org_id in campaign_dto.organisations: organisation = OrganisationService.get_organisation_by_id(org_id) campaign.organisation.append(organisation) db.session.commit() except IntegrityError as e: current_app.logger.info("Integrity error: {}".format(e.args[0])) raise ValueError() return campaign
def get(self, organisation_id): """ Retrieves an organisation --- tags: - organisations produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token type: string default: Token sessionTokenHere== - name: organisation_id in: path description: The unique organisation ID required: true type: integer default: 1 responses: 200: description: Organisation found 401: description: Unauthorized - Invalid credentials 404: description: Organisation not found 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() if authenticated_user_id is None: user_id = 0 else: user_id = authenticated_user_id organisation_dto = OrganisationService.get_organisation_by_id_as_dto( organisation_id, user_id) return organisation_dto.to_primitive(), 200 except NotFound: return {"Error": "Organisation Not Found"}, 404 except Exception as e: error_msg = f"Organisation GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def is_user_team_manager(team_id: int, user_id: int): # Admin manages all teams if UserService.is_user_an_admin(user_id): return True managers = TeamService._get_team_managers(team_id) for member in managers: if member.user_id == user_id: return True # Org admin manages teams attached to their org user_managed_orgs = [ org.id for org in OrganisationService.get_organisations(user_id) ] if Team.get(team_id).organisation_id in user_managed_orgs: return True return False
def delete_project(project_id: int, authenticated_user_id: int): """ Deletes project if it has no completed tasks """ project = ProjectAdminService._get_project_by_id(project_id) is_admin = UserService.is_user_an_admin(authenticated_user_id) user_orgs = OrganisationService.get_organisations_managed_by_user_as_dto( authenticated_user_id) is_org_manager = len(user_orgs.organisations) > 0 if is_admin or is_org_manager: if project.can_be_deleted(): project.delete() else: raise ProjectAdminServiceError( "Project has mapped tasks, cannot be deleted") else: raise ProjectAdminServiceError( "User does not have permissions to delete project")
def create_campaign(campaign_dto: NewCampaignDTO): """ Creates a new campaign """ campaign = Campaign.from_dto(campaign_dto) try: campaign.create() if campaign_dto.organisations: for org_id in campaign_dto.organisations: organisation = OrganisationService.get_organisation_by_id( org_id) campaign.organisation.append(organisation) db.session.commit() except IntegrityError as e: current_app.logger.info("Integrity error: {}".format(e.args[0])) if isinstance(e.orig, UniqueViolation): raise ValueError("Campaign name already exists") from e if isinstance(e.orig, NotNullViolation): raise ValueError("Campaign name cannot be null") from e return campaign
def get_team_as_dto(team_id: int, user_id: int, abbreviated: bool) -> TeamDetailsDTO: team = TeamService.get_team_by_id(team_id) if team is None: raise NotFound() team_dto = TeamDetailsDTO() team_dto.team_id = team.id team_dto.name = team.name team_dto.invite_only = team.invite_only team_dto.visibility = TeamVisibility(team.visibility).name team_dto.description = team.description team_dto.logo = team.organisation.logo team_dto.organisation = team.organisation.name team_dto.organisation_id = team.organisation.id team_dto.organisation_slug = team.organisation.slug if user_id != 0: if UserService.is_user_an_admin(user_id): team_dto.is_general_admin = True if OrganisationService.is_user_an_org_manager( team.organisation.id, user_id): team_dto.is_org_admin = True else: team_dto.is_general_admin = False team_dto.is_org_admin = False if abbreviated: return team_dto team_dto.members = [ team.as_dto_team_member(member) for member in team.members ] team_projects = TeamService.get_projects_by_team_id(team.id) team_dto.team_projects = [ team.as_dto_team_project(project) for project in team_projects ] return team_dto
def is_user_action_permitted_on_project(authenticated_user_id: int, project_id: int) -> bool: """ Is user action permitted on project""" project = Project.get(project_id) author_id = project.author_id allowed_roles = [TeamRoles.PROJECT_MANAGER.value] is_admin = UserService.is_user_an_admin(authenticated_user_id) is_author = UserService.is_user_the_project_author( authenticated_user_id, author_id) is_org_manager = False is_manager_team = False if not (is_admin or is_author): if hasattr(project, "organisation_id") and project.organisation_id: org_id = project.organisation_id is_org_manager = OrganisationService.is_user_an_org_manager( org_id, authenticated_user_id) if not is_org_manager: is_manager_team = TeamService.check_team_membership( project_id, allowed_roles, authenticated_user_id) return is_admin or is_author or is_org_manager or is_manager_team
def assert_validate_organisation(org_id: int): """ Makes sure an organisation exists """ try: OrganisationService.get_organisation_by_id(org_id) except NotFound: raise TeamServiceError(f"Organisation {org_id} does not exist")
def get_all_teams( user_id: int = None, team_name_filter: str = None, team_role_filter: str = None, member_filter: int = None, member_request_filter: int = None, manager_filter: int = None, organisation_filter: int = None, omit_members: bool = False, ) -> TeamsListDTO: query = db.session.query(Team) orgs_query = None is_admin = UserService.is_user_an_admin(user_id) if organisation_filter: orgs_query = query.filter( Team.organisation_id == organisation_filter) if manager_filter and not (manager_filter == user_id and is_admin): manager_teams = query.filter( TeamMembers.user_id == manager_filter, TeamMembers.active == True, # noqa TeamMembers.function == TeamMemberFunctions.MANAGER.value, Team.id == TeamMembers.team_id, ) manager_orgs_teams = query.filter( Team.organisation_id.in_([ org.id for org in OrganisationService.get_organisations( manager_filter) ])) query = manager_teams.union(manager_orgs_teams) if team_name_filter: query = query.filter(Team.name.ilike("%" + team_name_filter + "%"), ) if team_role_filter: try: role = TeamRoles[team_role_filter.upper()].value project_teams = (db.session.query(ProjectTeams).filter( ProjectTeams.role == role).subquery()) query = query.join(project_teams) except KeyError: pass if member_filter: team_member = (db.session.query(TeamMembers).filter( TeamMembers.user_id == member_filter, TeamMembers.active.is_(True)).subquery()) query = query.join(team_member) if member_request_filter: team_member = (db.session.query(TeamMembers).filter( TeamMembers.user_id == member_request_filter, TeamMembers.active.is_(False), ).subquery()) query = query.join(team_member) if orgs_query: query = query.union(orgs_query) teams_list_dto = TeamsListDTO() for team in query.all(): team_dto = TeamDTO() team_dto.team_id = team.id team_dto.name = team.name team_dto.invite_only = team.invite_only team_dto.visibility = TeamVisibility(team.visibility).name team_dto.description = team.description team_dto.logo = team.organisation.logo team_dto.organisation = team.organisation.name team_dto.organisation_id = team.organisation.id team_dto.members = [] is_team_member = TeamService.is_user_an_active_team_member( team.id, user_id) # Skip if members are not included if not omit_members: team_members = team.members team_dto.members = [ team.as_dto_team_member(member) for member in team_members ] if team_dto.visibility == "PRIVATE" and not is_admin: if is_team_member: teams_list_dto.teams.append(team_dto) else: teams_list_dto.teams.append(team_dto) return teams_list_dto
def post(self): """ Creates a new team --- tags: - teams produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - in: body name: body required: true description: JSON object for creating team schema: properties: name: type: string default: HOT - Mappers organisation_id: type: integer default: 1 description: type: string visibility: type: string default: PUBLIC inviteOnly: type: boolean default: false responses: 201: description: Team created successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Unauthorized - Forbidden 500: description: Internal Server Error """ user_id = token_auth.current_user() try: team_dto = NewTeamDTO(request.get_json()) team_dto.creator = user_id team_dto.validate() except DataError as e: current_app.logger.error(f"error validating request: {str(e)}") return str(e), 400 try: organisation_id = team_dto.organisation_id is_org_manager = OrganisationService.is_user_an_org_manager( organisation_id, user_id) is_admin = UserService.is_user_an_admin(user_id) if is_admin or is_org_manager: team_id = TeamService.create_team(team_dto) return {"teamId": team_id}, 201 else: error_msg = ( "Team POST - User not permitted to create team for the Organisation" ) return {"Error": error_msg}, 403 except TeamServiceError as e: return str(e), 400 except NotFound: error_msg = "Team POST - Organisation does not exist" return {"Error": error_msg}, 400 except Exception as e: error_msg = f"Team POST - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def post(self, team_id): """ Updates a team information --- tags: - teams produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - name: team_id in: path description: Unique team ID required: true type: integer default: 1 - in: body name: body required: true description: JSON object for updating a team schema: properties: name: type: string default: HOT - Mappers logo: type: string default: https://tasks.hotosm.org/assets/img/hot-tm-logo.svg members: type: array items: schema: $ref: "#/definitions/TeamMembers" organisation: type: string default: HOT description: type: string default: HOT's mapping editors inviteOnly: type: boolean default: false responses: 201: description: Team updated successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 500: description: Internal Server Error """ try: team_dto = TeamDTO(request.get_json()) team_dto.team_id = team_id team_dto.validate() authenticated_user_id = token_auth.current_user() team_details_dto = TeamService.get_team_as_dto( team_id, authenticated_user_id) org = TeamService.assert_validate_organisation( team_dto.organisation_id) TeamService.assert_validate_members(team_details_dto) if not TeamService.is_user_team_manager( team_id, authenticated_user_id ) and not OrganisationService.can_user_manage_organisation( org.id, authenticated_user_id): return { "Error": "User is not a admin or a manager for the team" }, 401 except DataError as e: current_app.logger.error(f"error validating request: {str(e)}") return str(e), 400 try: TeamService.update_team(team_dto) return {"Status": "Updated"}, 200 except NotFound as e: return {"Error": str(e)}, 404 except TeamServiceError as e: return str(e), 402 except Exception as e: error_msg = f"Team POST - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def get_all_teams( user_id: int = None, team_name_filter: str = None, team_role_filter: str = None, member_filter: int = None, member_request_filter: int = None, manager_filter: int = None, organisation_filter: int = None, omit_members: bool = False, ) -> TeamsListDTO: query = db.session.query(Team).outerjoin(TeamMembers).outerjoin( ProjectTeams) orgs_query = None is_admin = UserService.is_user_an_admin(user_id) if organisation_filter: orgs_query = query.filter( Team.organisation_id == organisation_filter) if manager_filter and not (manager_filter == user_id and is_admin): manager_teams = query.filter( TeamMembers.user_id == manager_filter, TeamMembers.active == True, # noqa TeamMembers.function == TeamMemberFunctions.MANAGER.value, ) manager_orgs_teams = query.filter( Team.organisation_id.in_([ org.id for org in OrganisationService.get_organisations( manager_filter) ])) query = manager_teams.union(manager_orgs_teams) if team_name_filter: query = query.filter(Team.name.contains(team_name_filter)) if team_role_filter: try: role = TeamRoles[team_role_filter.upper()].value query = query.filter(ProjectTeams.role == role) except KeyError: pass if member_filter: query = query.filter( TeamMembers.user_id == member_filter, TeamMembers.active == True # noqa ) if member_request_filter: query = query.filter( TeamMembers.user_id == member_request_filter, TeamMembers.active == False, # noqa ) if orgs_query: query = query.union(orgs_query) teams_list_dto = TeamsListDTO() for team in query.all(): team_dto = TeamDTO() team_dto.team_id = team.id team_dto.name = team.name team_dto.invite_only = team.invite_only team_dto.visibility = TeamVisibility(team.visibility).name team_dto.description = team.description team_dto.logo = team.organisation.logo team_dto.organisation = team.organisation.name team_dto.organisation_id = team.organisation.id team_dto.members = [] team_members = TeamService._get_team_members(team.id) is_team_manager = False is_team_member = False for member in team_members: # Skip if members are not included. if omit_members: continue user = UserService.get_user_by_id(member.user_id) member_dto = TeamMembersDTO() member_dto.username = user.username member_dto.function = TeamMemberFunctions(member.function).name if member.user_id == user_id: is_team_member = True if member_dto.function == "MANAGER": is_team_manager = True member_dto.picture_url = user.picture_url member_dto.active = member.active team_dto.members.append(member_dto) if team_dto.visibility == "PRIVATE" and not is_admin: if is_team_manager or is_team_member: teams_list_dto.teams.append(team_dto) else: teams_list_dto.teams.append(team_dto) return teams_list_dto
def get(self): """ List and search projects by bounding box --- tags: - projects produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - in: header name: Accept-Language description: Language user is requesting type: string default: en - in: query name: bbox description: comma separated list xmin, ymin, xmax, ymax type: string required: true default: 34.404,-1.034, 34.717,-0.624 - in: query name: srid description: srid of bbox coords type: integer default: 4326 - in: query name: createdByMe description: limit to projects created by authenticated user type: boolean required: true default: false responses: 200: description: ok 400: description: Client Error - Invalid Request 403: description: Forbidden 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto( authenticated_user_id ) if len(orgs_dto.organisations) < 1: raise ValueError("User not a project manager") except ValueError as e: error_msg = f"ProjectsQueriesBboxAPI GET: {str(e)}" return {"Error": error_msg}, 403 try: search_dto = ProjectSearchBBoxDTO() search_dto.bbox = map(float, request.args.get("bbox").split(",")) search_dto.input_srid = request.args.get("srid") search_dto.preferred_locale = request.environ.get("HTTP_ACCEPT_LANGUAGE") created_by_me = ( strtobool(request.args.get("createdByMe")) if request.args.get("createdByMe") else False ) if created_by_me: search_dto.project_author = authenticated_user_id search_dto.validate() except Exception as e: current_app.logger.error(f"Error validating request: {str(e)}") return {"Error": "Unable to fetch projects"}, 400 try: geojson = ProjectSearchService.get_projects_geojson(search_dto) return geojson, 200 except BBoxTooBigError: return {"Error": "Bounding Box too large"}, 403 except ProjectSearchServiceError: return {"Error": "Unable to fetch projects"}, 400 except Exception as e: error_msg = f"ProjectsQueriesBboxAPI GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": "Unable to fetch projects"}, 500
def post(self): """ Creates a new organisation --- tags: - organisations produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - in: body name: body required: true description: JSON object for creating organisation schema: properties: name: type: string default: HOT slug: type: string default: hot logo: type: string default: https://cdn.hotosm.org/tasking-manager/uploads/1588741335578_hot-logo.png url: type: string default: https://hotosm.org managers: type: array items: type: string default: [ user_1, user_2 ] responses: 201: description: Organisation created successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 402: description: Duplicate Name - Organisation name already exists 500: description: Internal Server Error """ request_user = User.get_by_id(token_auth.current_user()) if request_user.role != 1: return {"Error": "Only admin users can create organisations."}, 403 try: organisation_dto = NewOrganisationDTO(request.get_json()) if request_user.username not in organisation_dto.managers: organisation_dto.managers.append(request_user.username) organisation_dto.validate() except DataError as e: current_app.logger.error(f"error validating request: {str(e)}") return str(e), 400 try: org_id = OrganisationService.create_organisation(organisation_dto) return {"organisationId": org_id}, 201 except OrganisationServiceError as e: return str(e), 400 except Exception as e: error_msg = f"Organisation PUT - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def get(self): """ List all organisations --- tags: - organisations produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token type: string default: Token sessionTokenHere== - name: manager_user_id in: query description: Filter projects on managers with this user_id required: false type: integer - in: query name: omitManagerList type: boolean description: Set it to true if you don't want the managers list on the response. default: False responses: 200: description: Organisations found 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Unauthorized - Not allowed 404: description: Organisations not found 500: description: Internal Server Error """ # Restrict some of the parameters to some permissions authenticated_user_id = token_auth.current_user() try: manager_user_id = int(request.args.get("manager_user_id")) except Exception: manager_user_id = None if manager_user_id is not None and not authenticated_user_id: return ( { "Error": "Unauthorized - Filter by manager_user_id is not allowed to unauthenticated requests" }, 403, ) # Validate abbreviated. omit_managers = strtobool(request.args.get("omitManagerList", "false")) # Obtain organisations try: results_dto = OrganisationService.get_organisations_as_dto( manager_user_id, authenticated_user_id, omit_managers) return results_dto.to_primitive(), 200 except NotFound: return {"Error": "No organisations found"}, 404 except Exception as e: error_msg = f"Organisations GET - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def post_message( chat_dto: ChatMessageDTO, project_id: int, authenticated_user_id: int ) -> ProjectChatDTO: """ Save message to DB and return latest chat""" current_app.logger.debug("Posting Chat Message") if UserService.is_user_blocked(authenticated_user_id): return ValueError("User is on read only mode") project = ProjectService.get_project_by_id(project_id) if project.private: author_id = project.author_id allowed_roles = [ TeamRoles.PROJECT_MANAGER.value, TeamRoles.VALIDATOR.value, TeamRoles.MAPPER.value, ] is_admin = UserService.is_user_an_admin(authenticated_user_id) is_author = UserService.is_user_the_project_author( authenticated_user_id, author_id ) is_org_manager = False if hasattr(project, "organisation_id") and project.organisation_id: org_id = project.organisation_id org = OrganisationService.get_organisation_by_id_as_dto( org_id, authenticated_user_id ) if org.is_manager: is_org_manager = True is_team_member = None if hasattr(project, "project_teams") and project.project_teams: teams_dto = TeamService.get_project_teams_as_dto(project_id) if teams_dto.teams: teams_allowed = [ team_dto for team_dto in teams_dto.teams if team_dto.role in allowed_roles ] user_membership = [ team_dto.team_id for team_dto in teams_allowed if TeamService.is_user_member_of_team( team_dto.team_id, authenticated_user_id ) ] if user_membership: is_team_member = True for user in project.allowed_users: if user.id == authenticated_user_id: is_allowed_user = True break if ( is_admin or is_author or is_org_manager or is_team_member or is_allowed_user ): chat_message = ProjectChat.create_from_dto(chat_dto) MessageService.send_message_after_chat( chat_dto.user_id, chat_message.message, chat_dto.project_id ) db.session.commit() # Ensure we return latest messages after post return ProjectChat.get_messages(chat_dto.project_id, 1) else: raise ValueError("User not permitted to post Comment") else: chat_message = ProjectChat.create_from_dto(chat_dto) MessageService.send_message_after_chat( chat_dto.user_id, chat_message.message, chat_dto.project_id ) db.session.commit() # Ensure we return latest messages after post return ProjectChat.get_messages(chat_dto.project_id, 1)
def patch(self, organisation_id): """ Updates an organisation --- tags: - organisations produces: - application/json parameters: - in: header name: Authorization description: Base64 encoded session token required: true type: string default: Token sessionTokenHere== - name: organisation_id in: path description: The unique organisation ID required: true type: integer default: 1 - in: body name: body required: true description: JSON object for updating an organisation schema: properties: name: type: string default: HOT slug: type: string default: HOT logo: type: string default: https://tasks.hotosm.org/assets/img/hot-tm-logo.svg url: type: string default: https://hotosm.org managers: type: array items: type: string default: [ user_1, user_2 ] responses: 201: description: Organisation updated successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ if not OrganisationService.can_user_manage_organisation( organisation_id, token_auth.current_user()): return {"Error": "User is not an admin for the org"}, 403 try: organisation_dto = UpdateOrganisationDTO(request.get_json()) organisation_dto.organisation_id = organisation_id # Don't update organisation type and subscription_tier if request user is not an admin if User.get_by_id(token_auth.current_user()).role != 1: org = OrganisationService.get_organisation_by_id( organisation_id) organisation_dto.type = OrganisationType(org.type).name organisation_dto.subscription_tier = org.subscription_tier organisation_dto.validate() except DataError as e: current_app.logger.error(f"error validating request: {str(e)}") return str(e), 400 try: OrganisationService.update_organisation(organisation_dto) return {"Status": "Updated"}, 200 except NotFound as e: return {"Error": str(e)}, 404 except OrganisationServiceError as e: return str(e), 402 except Exception as e: error_msg = f"Organisation PATCH - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500