コード例 #1
0
    def test_unlock_of_not_locked_for_mapping_raises_error(self, mock_task):
        # Arrange
        mock_task.return_value = self.task_stub

        # Act / Assert
        with self.assertRaises(MappingServiceError):
            MappingService.unlock_task_after_mapping(MagicMock())
コード例 #2
0
    def test_cant_unlock_a_task_you_dont_own(self, mock_task):
        # Arrange
        self.task_stub.task_status = TaskStatus.LOCKED_FOR_MAPPING.value
        self.task_stub.locked_by = 12
        mock_task.return_value = self.task_stub

        # Act / Assert
        with self.assertRaises(MappingServiceError):
            MappingService.unlock_task_after_mapping(self.mapped_task_dto)
コード例 #3
0
    def test_lock_task_for_mapping_raises_error_if_task_in_invalid_state(
            self, mock_task):
        # Arrange
        self.task_stub.task_status = TaskStatus.MAPPED.value
        mock_task.return_value = self.task_stub

        # Act / Assert
        with self.assertRaises(MappingServiceError):
            MappingService.lock_task_for_mapping(self.lock_task_dto)
コード例 #4
0
    def test_lock_task_for_mapping_raises_error_if_user_has_not_accepted_license(
            self, mock_task, mock_project):
        # Arrange
        mock_task.return_value = self.task_stub
        mock_project.return_value = False, MappingNotAllowed.USER_NOT_ACCEPTED_LICENSE

        # Act / Assert
        with self.assertRaises(UserLicenseError):
            MappingService.lock_task_for_mapping(self.lock_task_dto)
コード例 #5
0
    def test_map_all_sets_counters_correctly(self):
        if self.skip_tests:
            return

        # Act
        MappingService.map_all_tasks(self.test_project.id, self.test_user.id)

        # Assert
        self.assertEqual(self.test_project.tasks_mapped,
                         self.test_project.total_tasks)
コード例 #6
0
    def test_mapped_by_is_set_after_mapping_all(self):
        if self.skip_tests:
            return

        # Act
        MappingService.map_all_tasks(self.test_project.id, self.test_user.id)

        # Assert
        for task in self.test_project.tasks:
            self.assertIsNotNone(task.mapped_by)
コード例 #7
0
    def test_if_new_state_not_acceptable_raise_error(self, mock_task):
        # Arrange
        self.task_stub.task_status = TaskStatus.LOCKED_FOR_MAPPING.value
        mock_task.return_value = self.task_stub

        self.mapped_task_dto.status = TaskStatus.LOCKED_FOR_VALIDATION.name

        # Act / Assert
        with self.assertRaises(MappingServiceError):
            MappingService.unlock_task_after_mapping(self.mapped_task_dto)
コード例 #8
0
    def test_lock_task_for_mapping_raises_error_if_user_already_has_locked_task(
            self, mock_task, mock_project):
        # Arrange
        mock_task.return_value = self.task_stub
        mock_project.return_value = (
            False,
            MappingNotAllowed.USER_ALREADY_HAS_TASK_LOCKED,
        )

        # Act / Assert
        with self.assertRaises(MappingServiceError):
            MappingService.lock_task_for_mapping(self.lock_task_dto)
コード例 #9
0
    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
コード例 #10
0
    def get(self, project_id, task_id):
        """
        Get a task's metadata
        ---
        tags:
            - tasks
        produces:
            - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              required: false
              type: string
              default: Token sessionTokenHere==
            - in: header
              name: Accept-Language
              description: Language user is requesting
              type: string
              required: true
              default: en
            - name: project_id
              in: path
              description: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: Unique task ID
              required: true
              type: integer
              default: 1
        responses:
            200:
                description: Task found
            404:
                description: Task not found
            500:
                description: Internal Server Error
        """
        try:
            preferred_locale = request.environ.get("HTTP_ACCEPT_LANGUAGE")
            token = request.environ.get("HTTP_AUTHORIZATION")

            # Login isn't required here, but if we have a token we can find out if the user can undo the task
            if token:
                verify_token(token[6:])

            user_id = tm.authenticated_user_id

            task = MappingService.get_task_as_dto(task_id, project_id,
                                                  preferred_locale, user_id)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except Exception as e:
            error_msg = f"TasksRestAPI - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to fetch task"}, 500
コード例 #11
0
    def post(self, project_id):
        """
        Map all tasks on a project
        ---
        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 mapped
            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"TasksActionsMapAllAPI POST: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            MappingService.map_all_tasks(project_id, tm.authenticated_user_id)
            return {"Success": "All tasks mapped"}, 200
        except Exception as e:
            error_msg = f"TasksActionsMapAllAPI POST - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to mapall tasks"}, 500
コード例 #12
0
 def post(self, project_id, task_id):
     """
     Undo a task's mapping status
     ---
     tags:
         - tasks
     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
         - name: project_id
           in: path
           description: Project ID the task is associated with
           required: true
           type: integer
           default: 1
         - name: task_id
           in: path
           description: Unique task ID
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: Task found
         403:
             description: Forbidden
         404:
             description: Task not found
         500:
             description: Internal Server Error
     """
     try:
         preferred_locale = request.environ.get("HTTP_ACCEPT_LANGUAGE")
         task = MappingService.undo_mapping(project_id, task_id,
                                            token_auth.current_user(),
                                            preferred_locale)
         return task.to_primitive(), 200
     except NotFound:
         return {"Error": "Task Not Found"}, 404
     except MappingServiceError:
         return {"Error": "User not permitted to undo task"}, 403
     except Exception as e:
         error_msg = f"Task GET API - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": "Unable to lock task"}, 500
コード例 #13
0
    def get(self, project_id, task_id):
        """
        Get a task's metadata
        ---
        tags:
            - tasks
        produces:
            - application/json
        parameters:
            - in: header
              name: Accept-Language
              description: Language user is requesting
              type: string
              required: true
              default: en
            - name: project_id
              in: path
              description: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: Unique task ID
              required: true
              type: integer
              default: 1
        responses:
            200:
                description: Task found
            404:
                description: Task not found
            500:
                description: Internal Server Error
        """
        try:
            preferred_locale = request.environ.get("HTTP_ACCEPT_LANGUAGE")

            task = MappingService.get_task_as_dto(task_id, project_id,
                                                  preferred_locale)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except Exception as e:
            error_msg = f"TasksRestAPI - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to fetch task"}, 500
コード例 #14
0
    def test_task_is_not_undoable_if_last_change_not_made_by_you(
            self, last_action, mock_project):
        # Arrange
        task_history = TaskHistory(1, 1, 1)
        task_history.user_id = 2
        last_action.return_value = task_history

        task = Task()
        task.task_status = TaskStatus.MAPPED.value
        task.mapped_by = 1

        # Act
        mock_project.return_value = False
        is_undoable = MappingService._is_task_undoable(1, task)

        # Assert
        self.assertFalse(is_undoable)
コード例 #15
0
    def test_osm_xml_file_generated_correctly_all_tasks(self, mock_task):
        if self.skip_tests:
            return

        # Arrange
        task = Task.get(1, self.test_project.id)
        mock_task.return_value = [task]

        # Act
        osm_xml = MappingService.generate_osm_xml(1, None)

        # Convert XML into a hash that should be identical every time
        osm_xml_str = osm_xml.decode("utf-8")
        osm_hash = hashlib.md5(osm_xml_str.encode("utf-8")).hexdigest()

        # Assert
        self.assertEqual(osm_hash, "eafd0760a0d372e2ab139e25a2d300f1")
コード例 #16
0
    def test_gpx_xml_file_generated_correctly_all_tasks(self, mock_task):
        if self.skip_tests:
            return

        # Arrange
        task = Task.get(1, self.test_project.id)
        mock_task.return_value = [task]
        timestamp = datetime.date(2017, 4, 13)

        # Act
        gpx_xml = MappingService.generate_gpx(1, None, timestamp)

        # Convert XML into a hash that should be identical every time
        gpx_xml_str = gpx_xml.decode("utf-8")
        gpx_hash = hashlib.md5(gpx_xml_str.encode("utf-8")).hexdigest()

        # Assert
        self.assertEqual(gpx_hash, "b91f7361cc1d6d9433cf393609103272")
コード例 #17
0
    def test_unlock_with_status_change_sets_history(
        self,
        mock_task,
        mock_history,
        mock_update,
        mock_stats,
        mock_instructions,
        mock_state,
    ):
        # Arrange
        self.task_stub.task_status = TaskStatus.LOCKED_FOR_MAPPING.value
        mock_task.return_value = self.task_stub
        mock_state.return_value = TaskStatus.LOCKED_FOR_MAPPING

        # Act
        test_task = MappingService.unlock_task_after_mapping(self.mapped_task_dto)

        # Assert
        self.assertEqual(TaskAction.STATE_CHANGE.name, test_task.task_history[0].action)
        self.assertEqual(test_task.task_history[0].action_text, TaskStatus.MAPPED.name)
        self.assertEqual(TaskStatus.MAPPED.name, test_task.task_status)
コード例 #18
0
    def test_unlock_with_comment_sets_history(
        self,
        mock_task,
        mock_history,
        mock_update,
        mock_stats,
        mock_instructions,
        mock_state,
    ):
        # Arrange
        self.task_stub.task_status = TaskStatus.LOCKED_FOR_MAPPING.value
        self.mapped_task_dto.comment = "Test comment"
        mock_task.return_value = self.task_stub
        mock_state.return_value = TaskStatus.LOCKED_FOR_MAPPING

        # Act
        test_task = MappingService.unlock_task_after_mapping(self.mapped_task_dto)

        # Assert
        self.assertEqual(TaskAction.COMMENT.name, test_task.task_history[0].action)
        self.assertEqual(test_task.task_history[0].action_text, "Test comment")
コード例 #19
0
    def post(self, project_id, task_id):
        """
        Set a task as mapped
        ---
        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: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: Unique task ID
              required: true
              type: integer
              default: 1
            - in: body
              name: body
              required: true
              description: JSON object for unlocking a task
              schema:
                  id: TaskUpdateUnlock
                  required:
                      - status
                  properties:
                      status:
                          type: string
                          description: The new status for the task
                          default: MAPPED
                      comment:
                          type: string
                          description: Optional user comment about the task
                          default: Comment about the mapping
        responses:
            200:
                description: Task unlocked
            400:
                description: Client Error
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            404:
                description: Task not found
            500:
                description: Internal Server Error
        """
        try:
            authenticated_user_id = token_auth.current_user()
            mapped_task = MappedTaskDTO(request.get_json())
            mapped_task.user_id = authenticated_user_id
            mapped_task.task_id = task_id
            mapped_task.project_id = project_id
            mapped_task.validate()
        except DataError as e:
            current_app.logger.error(f"Error validating request: {str(e)}")
            return {"Error": "Task unlock failed"}, 400

        try:
            task = MappingService.unlock_task_after_mapping(mapped_task)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except MappingServiceError:
            return {"Error": "Task unlock failed"}, 403
        except Exception as e:
            error_msg = f"Task Lock API - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Task unlock failed"}, 500
        finally:
            # Refresh mapper level after mapping
            UserService.check_and_update_mapper_level(authenticated_user_id)
コード例 #20
0
    def post(self, project_id, task_id):
        """
        Adds a comment to the task outside of mapping/validation
        ---
        tags:
            - comments
        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: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: Unique task ID
              required: true
              type: integer
              default: 1
            - in: body
              name: body
              required: true
              description: JSON object representing the comment
              schema:
                id: TaskComment
                required:
                    - comment
                properties:
                    comment:
                        type: string
                        description: user comment about the task
        responses:
            200:
                description: Comment added
            400:
                description: Client Error
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            404:
                description: Task not found
            500:
                description: Internal Server Error
        """
        try:
            task_comment = TaskCommentDTO(request.get_json())
            task_comment.user_id = tm.authenticated_user_id
            task_comment.task_id = task_id
            task_comment.project_id = project_id
            task_comment.validate()
        except DataError as e:
            current_app.logger.error(f"Error validating request: {str(e)}")
            return {"Error": "Unable to add comment"}, 400

        try:
            task = MappingService.add_task_comment(task_comment)
            return task.to_primitive(), 201
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except MappingServiceError:
            return {"Error": "Task update failed"}, 403
        except Exception as e:
            error_msg = f"Task Comment API - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Task update failed"}, 500
コード例 #21
0
    def get(self, project_id):
        """
        Get all tasks for a project as GPX
        ---
        tags:
            - tasks
        produces:
            - application/xml
        parameters:
            - name: project_id
              in: path
              description: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - in: query
              name: tasks
              type: string
              description: List of tasks; leave blank for all
              default: 1,2
            - in: query
              name: as_file
              type: boolean
              description: Set to true if file download preferred
              default: False
        responses:
            200:
                description: GPX XML
            400:
                description: Client error
            404:
                description: No mapped tasks
            500:
                description: Internal Server Error
        """
        try:
            current_app.logger.debug("GPX Called")
            tasks = request.args.get("tasks")
            as_file = (
                strtobool(request.args.get("as_file"))
                if request.args.get("as_file")
                else False
            )

            xml = MappingService.generate_gpx(project_id, tasks)

            if as_file:
                return send_file(
                    io.BytesIO(xml),
                    mimetype="text.xml",
                    as_attachment=True,
                    attachment_filename=f"HOT-project-{project_id}.gpx",
                )

            return Response(xml, mimetype="text/xml", status=200)
        except NotFound:
            return (
                {"Error": "Not found; please check the project and task numbers."},
                404,
            )
        except Exception as e:
            error_msg = f"TasksQueriesGpxAPI - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to fetch task GPX"}, 500
コード例 #22
0
    def post(self, project_id, task_id):
        """
        Unlock a task that is locked for mapping resetting it to its last status
        ---
        tags:
            - tasks
        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
            - name: project_id
              in: path
              description: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: Unique task ID
              required: true
              type: integer
              default: 1
            - in: body
              name: body
              required: true
              description: JSON object for unlocking a task
              schema:
                  id: TaskUpdateStop
                  properties:
                      comment:
                          type: string
                          description: Optional user comment about the task
                          default: Comment about mapping done before stop
        responses:
            200:
                description: Task unlocked
            400:
                description: Client Error
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            404:
                description: Task not found
            500:
                description: Internal Server Error
        """
        try:
            stop_task = StopMappingTaskDTO(request.get_json())
            stop_task.user_id = token_auth.current_user()
            stop_task.task_id = task_id
            stop_task.project_id = project_id
            stop_task.preferred_locale = request.environ.get(
                "HTTP_ACCEPT_LANGUAGE")
            stop_task.validate()
        except DataError as e:
            current_app.logger.error(f"Error validating request: {str(e)}")
            return {"Error": "Task unlock failed"}, 400

        try:
            task = MappingService.stop_mapping_task(stop_task)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except MappingServiceError:
            return {"Error": "Task unlock failed"}, 403
        except Exception as e:
            error_msg = f"Task Lock API - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Task unlock failed"}, 500
コード例 #23
0
    def test_get_task_raises_error_if_task_not_found(self, mock_task):
        mock_task.return_value = None

        with self.assertRaises(NotFound):
            MappingService.get_task(12, 12)
コード例 #24
0
    def post(self, project_id, task_id):
        """
        Locks a task for mapping
        ---
        tags:
            - tasks
        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
            - name: project_id
              in: path
              description: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: Unique task ID
              required: true
              type: integer
              default: 1
        responses:
            200:
                description: Task locked
            400:
                description: Client Error
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            404:
                description: Task not found
            409:
                description: User has not accepted license terms of project
            500:
                description: Internal Server Error
        """
        try:
            lock_task_dto = LockTaskDTO()
            lock_task_dto.user_id = token_auth.current_user()
            lock_task_dto.project_id = project_id
            lock_task_dto.task_id = task_id
            lock_task_dto.preferred_locale = request.environ.get(
                "HTTP_ACCEPT_LANGUAGE")
            lock_task_dto.validate()
        except DataError as e:
            current_app.logger.error(f"Error validating request: {str(e)}")
            return {"Error": "Unable to lock task"}, 400

        try:
            task = MappingService.lock_task_for_mapping(lock_task_dto)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except MappingServiceError as e:
            return {"Error": str(e)}, 403
        except UserLicenseError:
            return {"Error": "User not accepted license terms"}, 409
        except Exception as e:
            error_msg = f"Task Lock API - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to lock task"}, 500