def test_reset_all_tasks(self, mock_set_task_history, mock_query, mock_get_project): user_id = 123 test_project = MagicMock(spec=Project) test_project.id = 456 test_project.tasks_mapped = 2 test_project.tasks_validated = 2 test_tasks = [ MagicMock(spec=Task), MagicMock(spec=Task), MagicMock(spec=Task) ] mock_query.return_value.filter.return_value.all.return_value = test_tasks mock_get_project.return_value = test_project ProjectAdminService.reset_all_tasks(test_project.id, user_id) for test_task in test_tasks: test_task.set_task_history.assert_called() test_task.reset_task.assert_called_with(user_id) mock_get_project.assert_called_with(test_project.id) self.assertEqual(test_project.tasks_mapped, 0) self.assertEqual(test_project.tasks_validated, 0) test_project.save.assert_called()
def test_published_project_with_incomplete_default_locale_raises_error( self, mock_project, mock_user): # Arrange stub_project = Project() stub_project.status = ProjectStatus.PUBLISHED.value mock_project.return_value = stub_project locales = [] info = ProjectInfoDTO() info.locale = "en" info.name = "Test" locales.append(info) dto = ProjectDTO() dto.project_id = 1 dto.default_locale = "en" dto.project_info_locales = locales dto.project_status = ProjectStatus.PUBLISHED.name stub_admin_user = User() stub_admin_user.username = "******" stub_admin_user.role = UserRole.ADMIN.value mock_user.return_value = stub_admin_user # Act / Assert with self.assertRaises(ProjectAdminServiceError): ProjectAdminService.update_project(dto, mock_user.id)
def test_get_raises_error_if_not_found(self, mock_project): # Arrange mock_project.return_value = None # Act / Assert with self.assertRaises(NotFound): ProjectAdminService._get_project_by_id(12)
def test_attempting_to_attach_non_existant_license_raise_error( self, license_mock): # Arrange license_mock.side_effect = NotFound() with self.assertRaises(ProjectAdminServiceError): ProjectAdminService._validate_imagery_licence(1)
def test_cant_add_tasks_if_geojson_not_feature_collection(self): # Arrange invalid_feature = ( '{"coordinates": [[[[-4.0237, 56.0904], [-3.9111, 56.1715], [-3.8122, 56.098],' '[-4.0237, 56.0904]]]], "type": "MultiPolygon"}') # Act with self.assertRaises(InvalidGeoJson): ProjectAdminService._attach_tasks_to_project( MagicMock(), invalid_feature)
def delete(self, project_id, campaign_id): """ Delete a campaign for a project --- tags: - campaigns 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 - name: campaign_id in: path description: Unique campaign ID required: true type: integer default: 1 responses: 200: description: Campaign assigned successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: ProjectAdminService.is_user_action_permitted_on_project( tm.authenticated_user_id, project_id) except ValueError as e: error_msg = f"ProjectsCampaignsAPI DELETE: {str(e)}" return {"Error": error_msg}, 403 try: CampaignService.delete_project_campaign(project_id, campaign_id) return {"Success": "Campaigns Deleted"}, 200 except NotFound: return {"Error": "Campaign Not Found"}, 404 except Exception as e: error_msg = f"ProjectsCampaignsAPI DELETE - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def test_no_project_info_for_default_locale_raises_error(self): # Arrange locales = [] info = ProjectInfoDTO() info.locale = "en" info.name = "Test" locales.append(info) # Act / Assert with self.assertRaises(ProjectAdminServiceError): ProjectAdminService._validate_default_locale("it", locales)
def post(self, project_id): """ Set a project as featured --- 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 responses: 200: description: Featured projects 400: description: Bad request 403: description: Forbidden 404: description: Project not found 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() ProjectAdminService.is_user_action_permitted_on_project( authenticated_user_id, project_id) except ValueError as e: error_msg = f"FeaturedProjects POST: {str(e)}" return {"Error": error_msg}, 403 try: ProjectService.set_project_as_featured(project_id) return {"Success": True}, 200 except NotFound: return {"Error": "Project Not Found"}, 404 except ValueError as e: error_msg = f"FeaturedProjects POST: {str(e)}" return {"Error": error_msg}, 400 except Exception as e: error_msg = f"FeaturedProjects POST - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def delete(self, project_id): """ Deletes 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 responses: 200: description: Project deleted 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 404: description: Project not found 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() ProjectAdminService.is_user_action_permitted_on_project( authenticated_user_id, project_id ) except ValueError as e: error_msg = f"ProjectsRestAPI DELETE: {str(e)}" return {"Error": error_msg}, 403 try: ProjectAdminService.delete_project(project_id, authenticated_user_id) return {"Success": "Project deleted"}, 200 except ProjectAdminServiceError: return {"Error": "Project has some mapping"}, 403 except NotFound: return {"Error": "Project Not Found"}, 404 except Exception as e: error_msg = f"ProjectsRestAPI DELETE - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": "Unable to delete project"}, 500
def post(self, project_id): """ Transfers a project to a new user --- 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: username of the new owner schema: properties: username: type: string responses: 200: description: Project ownership transfered successfully 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: username = request.get_json()["username"] authenticated_user_id = token_auth.current_user() ProjectAdminService.transfer_project_to(project_id, authenticated_user_id, username) return {"Success": "Project Transfered"}, 200 except ValueError as e: return {"Error": str(e)}, 403 except Exception as e: error_msg = f"ProjectsActionsTransferAPI POST - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": "Unable to transfer project"}, 500
def post(self, project_id): """ Set all bad imagery tasks as ready for mapping --- tags: - tasks 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 responses: 200: description: All bad imagery tasks marked ready for mapping 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() ProjectAdminService.is_user_action_permitted_on_project( authenticated_user_id, project_id) except ValueError as e: error_msg = f"TasksActionsResetBadImageryAllAPI POST: {str(e)}" return {"Error": error_msg}, 403 try: MappingService.reset_all_badimagery(project_id, authenticated_user_id) return { "Success": "All bad imagery tasks marked ready for mapping" }, 200 except Exception as e: error_msg = ( f"TasksActionsResetBadImageryAllAPI POST - unhandled error: {str(e)}" ) current_app.logger.critical(error_msg) return {"Error": "Unable to reset tasks"}, 500
def get(self, project_id): """ Retrieves 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 responses: 200: description: Project found 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 404: description: Project not found 500: description: Internal Server Error """ try: ProjectAdminService.is_user_action_permitted_on_project( token_auth.current_user(), project_id ) except ValueError as e: error_msg = f"ProjectsQueriesNoTasksAPI GET: {str(e)}" return {"Error": error_msg}, 403 try: project_dto = ProjectAdminService.get_project_dto_for_admin(project_id) return project_dto.to_primitive(), 200 except NotFound: return {"Error": "Project Not 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 get_project_dto_for_mapper( project_id, current_user_id, locale="en", abbrev=False ) -> ProjectDTO: """ Get the project DTO for mappers :param project_id: ID of the Project mapper has requested :param locale: Locale the mapper has requested :raises ProjectServiceError, NotFound """ project = ProjectService.get_project_by_id(project_id) # if project is public and is not draft, we don't need to check permissions if not project.private and not project.status == ProjectStatus.DRAFT.value: return project.as_dto_for_mapping(current_user_id, locale, abbrev) is_allowed_user = True is_team_member = None is_manager_permission = False if current_user_id: is_manager_permission = ProjectAdminService.is_user_action_permitted_on_project( current_user_id, project_id ) # Draft Projects - admins, authors, org admins & team managers permitted if project.status == ProjectStatus.DRAFT.value: if not is_manager_permission: is_allowed_user = False raise ProjectServiceError("Unable to fetch project") # Private Projects - allowed_users, admins, org admins & # assigned teams (mappers, validators, project managers), authors permitted if project.private and not is_manager_permission: is_allowed_user = False if current_user_id: is_allowed_user = ( len( [ user for user in project.allowed_users if user.id == current_user_id ] ) > 0 ) if not (is_allowed_user or is_manager_permission): if current_user_id: allowed_roles = [ TeamRoles.MAPPER.value, TeamRoles.VALIDATOR.value, TeamRoles.PROJECT_MANAGER.value, ] is_team_member = TeamService.check_team_membership( project_id, allowed_roles, current_user_id ) if is_allowed_user or is_manager_permission or is_team_member: return project.as_dto_for_mapping(current_user_id, locale, abbrev) else: raise ProjectServiceError("Unable to fetch project")
def test_updating_a_private_project_with_no_allowed_users_causes_an_error( self, mock_project, mock_user): # Arrange mock_project.return_value = Project() dto = ProjectDTO() dto.private = True dto.allowed_usernames = [] stub_user = User() stub_user.username = "******" stub_user.role = UserRole.ADMIN.value mock_user.return_value = stub_user with self.assertRaises(ProjectAdminServiceError): ProjectAdminService.update_project(dto, mock_user.id)
def post(self, project_id): """ Reset all tasks on project back to ready, preserving history --- tags: - tasks 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 responses: 200: description: All tasks reset 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: ProjectAdminService.is_user_action_permitted_on_project( tm.authenticated_user_id, project_id) except ValueError as e: error_msg = f"TasksActionsResetAllAPI POST: {str(e)}" return {"Error": error_msg}, 403 try: ProjectAdminService.reset_all_tasks(project_id, tm.authenticated_user_id) return {"Success": "All tasks reset"}, 200 except Exception as e: error_msg = f"TasksActionsResetAllAPI POST - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": "Unable to reset tasks"}, 500
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 test_updating_a_project_with_different_roles(self, mock_project, mock_project2, mock_user): stub_project = Project() stub_project.status = ProjectStatus.DRAFT.value mock_project.return_value = stub_project locales = [] info = ProjectInfoDTO() info.locale = "en" info.name = "Test" locales.append(info) dto = ProjectDTO() dto.project_id = 1 dto.default_locale = "en" dto.project_status = ProjectStatus.DRAFT.name dto.project_priority = ProjectPriority.LOW.name dto.mapper_level = MappingLevel.BEGINNER.name dto.mapping_types = ["ROADS"] dto.mapping_editors = ["ID"] dto.validation_editors = ["ID"] dto.project_info_locales = locales stub_user = User() stub_user.username = "******" stub_user.role = UserRole.MAPPER.value mock_user.return_value = stub_user with self.assertRaises(ValueError) as e: ProjectAdminService.update_project(dto, mock_user.id) the_exception = e.exception self.assertTrue(isinstance(the_exception, ValueError)) # stub_project.author_id = mock_user.id stub_user.username = "******" stub_user.role = UserRole.ADMIN.value mock_user.return_value = stub_user try: ProjectAdminService.update_project(dto, mock_user.id) except ProjectAdminServiceError: self.fail("update_project raised an exception with admin role")
def test_valid_geo_json_attaches_task_to_project(self): # Arrange valid_feature_collection = json.loads( '{"features": [{"geometry": {"coordinates": [[[[-4.0237, 56.0904],' '[-3.9111, 56.1715], [-3.8122, 56.098], [-4.0237, 56.0904]]]], "type":' '"MultiPolygon"}, "properties": {"x": 2402, "y": 1736, "zoom": 12, "isSquare": true}, "type":' '"Feature"}], "type": "FeatureCollection"}') test_project = Project() # Act ProjectAdminService._attach_tasks_to_project(test_project, valid_feature_collection) # Assert self.assertEqual( 1, test_project.tasks.count(), "One task should have been attached to project", )
def test_updating_a_private_project_with_no_allowed_users( self, mock_project, mock_project2, mock_user): # Arrange mock_project.return_value = Project() dto = ProjectDTO() dto.private = True dto.allowed_usernames = [] stub_user = User() stub_user.username = "******" stub_user.role = UserRole.ADMIN.value mock_user.return_value = stub_user try: ProjectAdminService.update_project(dto, mock_user.id) except ProjectAdminServiceError: self.fail( "update_project raised an exception when setting it as private" )
def is_user_permitted_to_validate(project_id, user_id): """ Check if the user is allowed to validate on the project in scope """ if UserService.is_user_blocked(user_id): return False, ValidatingNotAllowed.USER_NOT_ON_ALLOWED_LIST project = ProjectService.get_project_by_id(project_id) if project.license_id: if not UserService.has_user_accepted_license(user_id, project.license_id): return False, ValidatingNotAllowed.USER_NOT_ACCEPTED_LICENSE validation_permission = project.validation_permission # is_admin or is_author or is_org_manager or is_manager_team is_manager_permission = False if ProjectAdminService.is_user_action_permitted_on_project(user_id, project_id): is_manager_permission = True # Draft (public/private) accessible only for is_manager_permission if ( ProjectStatus(project.status) == ProjectStatus.DRAFT and not is_manager_permission ): return False, ValidatingNotAllowed.PROJECT_NOT_PUBLISHED is_restriction = None if not is_manager_permission and validation_permission: is_restriction = ProjectService.evaluate_validation_permission( project_id, user_id, validation_permission ) tasks = Task.get_locked_tasks_for_user(user_id) if len(tasks.locked_tasks) > 0: return False, ValidatingNotAllowed.USER_ALREADY_HAS_TASK_LOCKED is_allowed_user = None if project.private and not is_manager_permission: # Check if user is in allowed user list is_allowed_user = ProjectService.is_user_in_the_allowed_list( project.allowed_users, user_id ) if is_allowed_user: return True, "User allowed to validate" if not is_manager_permission and is_restriction: return is_restriction elif project.private and not ( is_manager_permission or is_allowed_user or not is_restriction ): return False, ValidatingNotAllowed.USER_NOT_ON_ALLOWED_LIST return True, "User allowed to validate"
def is_user_permitted_to_validate(project_id, user_id): """ Check if the user is allowed to validate on the project in scope """ if UserService.is_user_blocked(user_id): return False, ValidatingNotAllowed.USER_NOT_ON_ALLOWED_LIST project = ProjectService.get_project_by_id(project_id) if project.license_id: if not UserService.has_user_accepted_license(user_id, project.license_id): return False, ValidatingNotAllowed.USER_NOT_ACCEPTED_LICENSE validation_permission = project.validation_permission is_manager_permission = False # is_admin or is_author or is_org_manager or is_manager_team if ProjectAdminService.is_user_action_permitted_on_project(user_id, project_id): is_manager_permission = True if ( ProjectStatus(project.status) != ProjectStatus.PUBLISHED and not is_manager_permission ): return False, ValidatingNotAllowed.PROJECT_NOT_PUBLISHED tasks = Task.get_locked_tasks_for_user(user_id) if len(tasks.locked_tasks) > 0: return False, ValidatingNotAllowed.USER_ALREADY_HAS_TASK_LOCKED if project.private and not is_manager_permission: # Check user is in allowed users try: next(user for user in project.allowed_users if user.id == user_id) except StopIteration: return False, ValidatingNotAllowed.USER_NOT_ON_ALLOWED_LIST is_restriction = ProjectService.evaluate_validation_permission( project_id, user_id, validation_permission ) if is_restriction: return is_restriction if project.validation_permission and not is_manager_permission: is_restriction = ProjectService.evaluate_validation_permission( project_id, user_id, validation_permission ) if is_restriction: return is_restriction return True, "User allowed to validate"
def test_complete_default_locale_raises_is_valid(self): # Arrange locales = [] info = ProjectInfoDTO() info.locale = "en" info.name = "Test" info.description = "Test Desc" info.short_description = "Short Desc" info.instructions = "Instruct" locales.append(info) # Act is_valid = ProjectAdminService._validate_default_locale("en", locales) # Assert self.assertTrue(is_valid, "Complete default locale should be valid")
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): raise ValueError("User is on read only mode") project = ProjectService.get_project_by_id(project_id) is_allowed_user = True is_manager_permission = ProjectAdminService.is_user_action_permitted_on_project( authenticated_user_id, project_id) is_team_member = False # Draft (public/private) accessible only for is_manager_permission if (ProjectStatus(project.status) == ProjectStatus.DRAFT and not is_manager_permission): raise ValueError("User not permitted to post Comment") if project.private: is_allowed_user = False if not is_manager_permission: allowed_roles = [ TeamRoles.PROJECT_MANAGER.value, TeamRoles.VALIDATOR.value, TeamRoles.MAPPER.value, ] is_team_member = TeamService.check_team_membership( project_id, allowed_roles, authenticated_user_id) if not is_team_member: is_allowed_user = (len([ user for user in project.allowed_users if user.id == authenticated_user_id ]) > 0) if is_manager_permission 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")
def is_user_permitted_to_map(project_id: int, user_id: int): """ Check if the user is allowed to map the on the project in scope """ if UserService.is_user_blocked(user_id): return False, MappingNotAllowed.USER_NOT_ON_ALLOWED_LIST project = ProjectService.get_project_by_id(project_id) mapping_permission = project.mapping_permission if ProjectStatus( project.status ) != ProjectStatus.PUBLISHED and not ProjectAdminService.is_user_action_permitted_on_project( user_id, project_id): return False, MappingNotAllowed.PROJECT_NOT_PUBLISHED tasks = Task.get_locked_tasks_for_user(user_id) if len(tasks.locked_tasks) > 0: return False, MappingNotAllowed.USER_ALREADY_HAS_TASK_LOCKED if project.private: # Check user is in allowed users try: next(user for user in project.allowed_users if user.id == user_id) except StopIteration: return False, MappingNotAllowed.USER_NOT_ON_ALLOWED_LIST is_restriction = ProjectService.evaluate_mapping_permission( project_id, user_id, mapping_permission) if is_restriction: return is_restriction if project.mapping_permission: is_restriction = ProjectService.evaluate_mapping_permission( project_id, user_id, mapping_permission) if is_restriction: return is_restriction if project.license_id: if not UserService.has_user_accepted_license( user_id, project.license_id): return False, MappingNotAllowed.USER_NOT_ACCEPTED_LICENSE return True, "User allowed to map"
def post(self, project_id, campaign_id): """ Assign a campaign for a project --- tags: - campaigns 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 - name: campaign_id in: path description: Unique campaign ID required: true type: integer default: 1 responses: 201: description: Campaign assigned successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: ProjectAdminService.is_user_action_permitted_on_project( tm.authenticated_user_id, project_id) except ValueError as e: error_msg = f"ProjectsCampaignsAPI POST: {str(e)}" return {"Error": error_msg}, 403 try: campaign_project_dto = CampaignProjectDTO() campaign_project_dto.campaign_id = campaign_id campaign_project_dto.project_id = project_id campaign_project_dto.validate() except DataError as e: current_app.logger.error(f"error validating request: {str(e)}") return str(e), 400 try: CampaignService.create_campaign_project(campaign_project_dto) message = "campaign with id {} assigned successfully for project with id {}".format( campaign_id, project_id) return ({"Success": message}, 200) except Exception as e: error_msg = f"ProjectsCampaignsAPI POST - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def post(self, project_id): """ Send message to all contributors of a 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 creating draft project schema: properties: subject: type: string default: Thanks required: true message: type: string default: Thanks for your contribution required: true responses: 200: description: Message sent successfully 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: authenticated_user_id = token_auth.current_user() message_dto = MessageDTO(request.get_json()) message_dto.from_user_id = authenticated_user_id message_dto.validate() except DataError as e: current_app.logger.error(f"Error validating request: {str(e)}") return {"Error": "Unable to send message to mappers"}, 400 try: ProjectAdminService.is_user_action_permitted_on_project( authenticated_user_id, project_id) threading.Thread( target=MessageService.send_message_to_all_contributors, args=(project_id, message_dto), ).start() return {"Success": "Messages started"}, 200 except ValueError as e: return {"Error": str(e)}, 403 except Exception as e: error_msg = f"Send message all - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": "Unable to send messages to mappers"}, 500
def post(self, project_id): """ Creates a relationship between project and interests --- tags: - interests 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 creating/updating project and interests relationships schema: properties: interests: type: array items: type: integer responses: 200: description: New project interest relationship created 400: description: Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: ProjectAdminService.is_user_action_permitted_on_project( token_auth.current_user(), project_id) except ValueError as e: error_msg = f"ProjectsActionsSetInterestsAPI POST: {str(e)}" return {"Error": error_msg}, 403 try: data = request.get_json() project_interests = InterestService.create_or_update_project_interests( project_id, data["interests"]) return project_interests.to_primitive(), 200 except NotFound: return {"Error": "Project not Found"}, 404 except Exception as e: error_msg = ( f"ProjectsActionsSetInterestsAPI POST - unhandled error: {str(e)}" ) current_app.logger.critical(error_msg) return {"Error": error_msg}, 500
def post(self): """ Creates 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== - in: body name: body required: true description: JSON object for creating draft project schema: properties: cloneFromProjectId: type: int default: 1 description: Specify this value if you want to clone a project, otherwise avoid information projectName: type: string default: HOT Project areaOfInterest: schema: properties: type: type: string default: FeatureCollection features: type: array items: schema: $ref: "#/definitions/GeoJsonFeature" tasks: schema: properties: type: type: string default: FeatureCollection features: type: array items: schema: $ref: "#/definitions/GeoJsonFeature" arbitraryTasks: type: boolean default: false responses: 201: description: Draft project created successfully 400: description: Client Error - Invalid Request 401: description: Unauthorized - Invalid credentials 403: description: Forbidden 500: description: Internal Server Error """ try: draft_project_dto = DraftProjectDTO(request.get_json()) draft_project_dto.user_id = token_auth.current_user() draft_project_dto.validate() except DataError as e: current_app.logger.error(f"error validating request: {str(e)}") return {"Error": "Unable to create project"}, 400 try: draft_project_id = ProjectAdminService.create_draft_project( draft_project_dto ) return {"projectId": draft_project_id}, 201 except ProjectAdminServiceError as e: return {"Error": str(e)}, 403 except (InvalidGeoJson, InvalidData): return {"Error": "Invalid GeoJson"}, 400 except Exception as e: error_msg = f"Project PUT - unhandled error: {str(e)}" current_app.logger.critical(error_msg) return {"Error": "Unable to create project"}, 500
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