def test_project_with_unknown_mapping_type_is_invalid(self):

        project_dto = ProjectDTO()
        project_dto.mapping_types = ["BAD", "DATA"]

        # Act / Assert
        with self.assertRaises(DataError):
            project_dto.validate()
    def _validate_allowed_users(project_dto: ProjectDTO):
        """ Ensures that all usernames are known and returns their user ids """
        if len(project_dto.allowed_usernames) == 0:
            raise ProjectAdminServiceError(
                "Must have at least one allowed user on a private project")

        try:
            allowed_users = []
            for username in project_dto.allowed_usernames:
                user = UserService.get_user_by_username(username)
                allowed_users.append(user)

            # Dynamically attach the user object to the DTO for more efficient persistence
            project_dto.allowed_users = allowed_users
        except NotFound:
            raise ProjectAdminServiceError(
                f"allowedUsers contains an unknown username {user}")
예제 #3
0
    def _get_project_and_base_dto(self):
        """ Populates a project DTO with properties common to all roles """
        base_dto = ProjectDTO()
        base_dto.project_id = self.id
        base_dto.project_status = ProjectStatus(self.status).name
        base_dto.default_locale = self.default_locale
        base_dto.project_priority = ProjectPriority(self.priority).name
        base_dto.area_of_interest = self.get_aoi_geometry_as_geojson()
        base_dto.aoi_bbox = shape(base_dto.area_of_interest).bounds
        base_dto.mapping_permission = MappingPermission(
            self.mapping_permission).name
        base_dto.validation_permission = ValidationPermission(
            self.validation_permission).name
        base_dto.enforce_random_task_selection = self.enforce_random_task_selection
        base_dto.private = self.private
        base_dto.mapper_level = MappingLevel(self.mapper_level).name
        base_dto.changeset_comment = self.changeset_comment
        base_dto.osmcha_filter_id = self.osmcha_filter_id
        base_dto.due_date = self.due_date
        base_dto.imagery = self.imagery
        base_dto.josm_preset = self.josm_preset
        base_dto.id_presets = self.id_presets
        base_dto.country_tag = self.country
        base_dto.organisation_id = self.organisation_id
        base_dto.license_id = self.license_id
        base_dto.created = self.created
        base_dto.last_updated = self.last_updated
        base_dto.author = User.get_by_id(self.author_id).username
        base_dto.active_mappers = Project.get_active_mappers(self.id)
        base_dto.task_creation_mode = TaskCreationMode(
            self.task_creation_mode).name
        base_dto.percent_mapped = Project.calculate_tasks_percent(
            "mapped",
            self.total_tasks,
            self.tasks_mapped,
            self.tasks_validated,
            self.tasks_bad_imagery,
        )
        base_dto.percent_validated = Project.calculate_tasks_percent(
            "validated",
            self.total_tasks,
            self.tasks_mapped,
            self.tasks_validated,
            self.tasks_bad_imagery,
        )
        base_dto.percent_bad_imagery = Project.calculate_tasks_percent(
            "bad_imagery",
            self.total_tasks,
            self.tasks_mapped,
            self.tasks_validated,
            self.tasks_bad_imagery,
        )

        base_dto.project_teams = [
            ProjectTeamDTO(
                dict(
                    team_id=t.team.id,
                    team_name=t.team.name,
                    role=TeamRoles(t.role).name,
                )) for t in self.teams
        ]

        if self.custom_editor:
            base_dto.custom_editor = self.custom_editor.as_dto()

        if self.private:
            # If project is private it should have a list of allowed users
            allowed_usernames = []
            for user in self.allowed_users:
                allowed_usernames.append(user.username)
            base_dto.allowed_usernames = allowed_usernames

        if self.mapping_types:
            mapping_types = []
            for mapping_type in self.mapping_types:
                mapping_types.append(MappingTypes(mapping_type).name)

            base_dto.mapping_types = mapping_types

        if self.campaign:
            base_dto.campaigns = [i.as_dto() for i in self.campaign]

        if self.mapping_editors:
            mapping_editors = []
            for mapping_editor in self.mapping_editors:
                mapping_editors.append(Editors(mapping_editor).name)

            base_dto.mapping_editors = mapping_editors

        if self.validation_editors:
            validation_editors = []
            for validation_editor in self.validation_editors:
                validation_editors.append(Editors(validation_editor).name)

            base_dto.validation_editors = validation_editors

        if self.priority_areas:
            geojson_areas = []
            for priority_area in self.priority_areas:
                geojson_areas.append(priority_area.get_as_geojson())

            base_dto.priority_areas = geojson_areas

        base_dto.interests = [
            InterestDTO(dict(id=i.id, name=i.name)) for i in self.interests
        ]

        return self, base_dto
예제 #4
0
    def patch(self, project_id):
        """
        Updates a Tasking-Manager project
        ---
        tags:
            - projects
        produces:
            - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              required: true
              type: string
              default: Token sessionTokenHere==
            - name: project_id
              in: path
              description: Unique project ID
              required: true
              type: integer
              default: 1
            - in: body
              name: body
              required: true
              description: JSON object for updating an existing project
              schema:
                properties:
                    projectStatus:
                        type: string
                        default: DRAFT
                    projectPriority:
                        type: string
                        default: MEDIUM
                    defaultLocale:
                        type: string
                        default: en
                    mapperLevel:
                        type: string
                        default: BEGINNER
                    validation_permission:
                        type: string
                        default: ANY
                    mapping_permission:
                        type: string
                        default: ANY
                    private:
                        type: boolean
                        default: false
                    changesetComment:
                        type: string
                        default: hotosm-project-1
                    dueDate:
                        type: date
                        default: "2017-04-11T12:38:49"
                    imagery:
                        type: string
                        default: http//www.bing.com/maps/
                    josmPreset:
                        type: string
                        default: josm preset goes here
                    mappingTypes:
                        type: array
                        items:
                            type: string
                        default: [BUILDINGS, ROADS]
                    mappingEditors:
                        type: array
                        items:
                            type: string
                        default: [ID, JOSM, POTLATCH_2, FIELD_PAPERS]
                    validationEditors:
                        type: array
                        items:
                            type: string
                        default: [ID, JOSM, POTLATCH_2, FIELD_PAPERS]
                    campaign:
                        type: string
                        default: malaria
                    organisation:
                        type: integer
                        default: 1
                    countryTag:
                          type: array
                          items:
                              type: string
                          default: []
                    licenseId:
                        type: integer
                        default: 1
                        description: Id of imagery license associated with the project
                    allowedUsernames:
                        type: array
                        items:
                            type: string
                        default: ["Iain Hunter", LindaA1]
                    priorityAreas:
                        type: array
                        items:
                            schema:
                                $ref: "#/definitions/GeoJsonPolygon"
                    projectInfoLocales:
                        type: array
                        items:
                            schema:
                                $ref: "#/definitions/ProjectInfo"
                    taskCreationMode:
                        type: integer
                        default: GRID
        responses:
            200:
                description: Project updated
            400:
                description: Client Error - Invalid Request
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            404:
                description: Project not found
            500:
                description: Internal Server Error
        """
        authenticated_user_id = token_auth.current_user()
        try:
            ProjectAdminService.is_user_action_permitted_on_project(
                authenticated_user_id, project_id
            )
        except ValueError as e:
            error_msg = f"ProjectsRestAPI PATCH: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            project_dto = ProjectDTO(request.get_json())
            project_dto.project_id = project_id
            project_dto.validate()
        except DataError as e:
            current_app.logger.error(f"Error validating request: {str(e)}")
            return {"Error": "Unable to update project"}, 400

        try:
            ProjectAdminService.update_project(project_dto, authenticated_user_id)
            return {"Status": "Updated"}, 200
        except InvalidGeoJson as e:
            return {"Invalid GeoJson": str(e)}, 400
        except NotFound as e:
            return {"Error": str(e) or "Project Not Found"}, 404
        except Exception as e:
            error_msg = f"ProjectsRestAPI PATCH - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to update project"}, 500