Пример #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_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)
Пример #3
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)
Пример #4
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)
Пример #5
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)
    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)
    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)
Пример #8
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)
Пример #9
0
    def get(self, project_id):
        """
        Get tasks as OSM XML
        ---
        tags:
            - mapping
        produces:
            - application/xml
        parameters:
            - name: project_id
              in: path
              description: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - in: query
              name: tasks
              type: string
              description: List of tasks; leave blank to retrieve all
              default: 1,2
            - in: query
              name: as_file
              type: boolean
              description: Set to true if file download preferred
              default: False
        responses:
            200:
                description: OSM XML
            400:
                description: Client Error
            404:
                description: No mapped tasks
            500:
                description: Internal Server Error
        """
        try:
            tasks = request.args.get('tasks') if request.args.get(
                'tasks') else None
            as_file = strtobool(request.args.get(
                'as_file')) if request.args.get('as_file') else False

            xml = MappingService.generate_osm_xml(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}.osm')

            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'Task as OSM API - unhandled error: {str(e)}'
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Пример #10
0
    def get(self, project_id, task_id):
        """
        Get task for mapping
        ---
        tags:
            - mapping
        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: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: The 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'Task GET API - unhandled error: {str(e)}'
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Пример #11
0
 def post(self, project_id, task_id):
     """
     Get task for mapping
     ---
     tags:
         - mapping
     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: The ID of the project the task is associated with
           required: true
           type: integer
           default: 1
         - name: task_id
           in: path
           description: The 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,
                                            tm.authenticated_user_id,
                                            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": error_msg}, 500
Пример #12
0
    def get(self, project_id):
        """
        Get tasks as GPX
        ---
        tags:
            - mapping
        produces:
            - application/xml
        parameters:
            - name: project_id
              in: path
              description: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - in: query
              name: tasks
              type: string
              required: true
              description: List of tasks required
              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
            if tasks is None:
                return {"Error": 'No tasks supplied in querystring'}, 400

            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": "No mapped tasks"}, 404
        except Exception as e:
            error_msg = f'Task Lock API - unhandled error: {str(e)}'
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Пример #13
0
 def post(self, project_id):
     """
     Mark all bad imagery tasks ready for mapping
     ---
     tags:
         - project-admin
     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: The 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
         500:
             description: Internal Server Error
     """
     try:
         MappingService.reset_all_badimagery(project_id,
                                             tm.authenticated_user_id)
         return {
             "Success": "All bad imagery tasks marked ready for mapping"
         }, 200
     except Exception as e:
         error_msg = f'Project GET - unhandled error: {str(e)}'
         current_app.logger.critical(error_msg)
         return {"error": error_msg}, 500
Пример #14
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')
Пример #15
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)
Пример #16
0
    def test_task_is_not_undoable_if_last_change_not_made_by_you(self, last_action):
        # 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
        is_undoable = MappingService._is_task_undoable(1, task)

        # Assert
        self.assertFalse(is_undoable)
    def test_osm_xml_file_generated_correctly(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, '1,2')

        # Covert 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')
Пример #18
0
    def test_gpx_xml_file_generated_correctly(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, '1,2')

        # Covert 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, '76c8cebb99729481d46734463386bad3')
    def test_gpx_xml_file_generated_correctly(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, '1,2', timestamp)

        # Covert 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, '97c4274c013964091974916ffee07846')
    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')
Пример #21
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()
        f = open('/home/enelson/test.xml', 'w')
        f.write(osm_xml_str)
        f.close()

        # Assert
        self.assertEqual(osm_hash, 'eafd0760a0d372e2ab139e25a2d300f1')
Пример #22
0
    def get(self, project_id):
        """
        Get tasks as OSM XML
        ---
        tags:
            - mapping
        produces:
            - application/xml
        parameters:
            - name: project_id
              in: path
              description: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - in: query
              name: tasks
              type: string
              required: true
              description: List of tasks required
              default: 1,2
        responses:
            200:
                description: OSM XML
            400:
                description: Client Error
            404:
                description: No mapped tasks
            500:
                description: Internal Server Error
        """
        tasks = request.args.get('tasks')
        if tasks is None:
            return {"Error": 'No tasks supplied in querystring'}, 400

        xml = MappingService.generate_osm_xml(project_id, tasks)
        return Response(xml, mimetype='text/xml', status=200)
Пример #23
0
    def post(self, project_id, task_id):
        """
        Locks the task for mapping
        ---
        tags:
            - mapping
        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: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: The 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 = tm.authenticated_user_id
            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 str(e), 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": error_msg}, 500
Пример #24
0
    def post(self, project_id, task_id):
        """
        Unlocks the task after mapping completed
        ---
        tags:
            - mapping
        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: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: The 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:
            mapped_task = MappedTaskDTO(request.get_json())
            mapped_task.user_id = tm.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 str(e), 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 as e:
            return {"Error": str(e)}, 403
        except Exception as e:
            error_msg = f'Task Lock API - unhandled error: {str(e)}'
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
        finally:
            # Refresh mapper level after mapping
            UserService.check_and_update_mapper_level(tm.authenticated_user_id)
Пример #25
0
    def post(self, project_id, task_id):
        """
        Unlock task that is locked for mapping resetting it to it's last status
        ---
        tags:
            - mapping
        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: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: The 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 = tm.authenticated_user_id
            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 str(e), 400

        try:
            task = MappingService.stop_mapping_task(stop_task)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except MappingServiceError as e:
            return {"Error": str(e)}, 403
        except Exception as e:
            error_msg = f'Task Lock API - unhandled error: {str(e)}'
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Пример #26
0
    def post(self, project_id, task_id):
        """
        Adds a comment to the task outside of mapping/validation
        ---
        tags:
            - mapping
        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: The ID of the project the task is associated with
              required: true
              type: integer
              default: 1
            - name: task_id
              in: path
              description: The 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 str(e), 400

        try:
            task = MappingService.add_task_comment(task_comment)
            return task.to_primitive(), 200
        except NotFound:
            return {"Error": "Task Not Found"}, 404
        except MappingServiceError as e:
            return {"Error": str(e)}, 403
        except Exception as e:
            error_msg = f'Task Comment API - unhandled error: {str(e)}'
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Пример #27
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)