Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
    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
Ejemplo n.º 4
0
 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
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
    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
Ejemplo n.º 11
0
 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
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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
Ejemplo n.º 15
0
 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
Ejemplo n.º 19
0
    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
Ejemplo n.º 21
0
 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")
Ejemplo n.º 22
0
    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
Ejemplo n.º 23
0
    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
Ejemplo n.º 24
0
    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
Ejemplo n.º 26
0
    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
Ejemplo n.º 27
0
    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
Ejemplo n.º 28
0
    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
Ejemplo n.º 29
0
    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)
Ejemplo n.º 30
0
    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