示例#1
0
    def set_counters_after_undo(project_id: int, user_id: int, current_state: TaskStatus, undo_state: TaskStatus):
        """ Resets counters after a user undoes their task"""
        project = ProjectService.get_project_by_id(project_id)
        user = UserService.get_user_by_id(user_id)

        # This is best endeavours to reset the stats and may have missed some edge cases, hopefully majority of
        # cases will be Mapped to Ready
        if current_state == TaskStatus.MAPPED and undo_state == TaskStatus.READY:
            project.tasks_mapped -= 1
            user.tasks_mapped -= 1
        if current_state == TaskStatus.MAPPED and undo_state == TaskStatus.INVALIDATED:
            user.tasks_mapped -= 1
            project.tasks_mapped -= 1
        elif current_state == TaskStatus.BADIMAGERY and undo_state == TaskStatus.READY:
            project.tasks_bad_imagery -= 1
        elif current_state == TaskStatus.BADIMAGERY and undo_state == TaskStatus.MAPPED:
            project.tasks_mapped += 1
            project.tasks_bad_imagery -= 1
        elif current_state == TaskStatus.BADIMAGERY and undo_state == TaskStatus.INVALIDATED:
            project.tasks_bad_imagery -= 1
        elif current_state == TaskStatus.INVALIDATED and undo_state == TaskStatus.MAPPED:
            user.tasks_invalidated -= 1
            project.tasks_mapped += 1
        elif current_state == TaskStatus.INVALIDATED and undo_state == TaskStatus.VALIDATED:
            user.tasks_invalidated -= 1
            project.tasks_validated += 1
        elif current_state == TaskStatus.VALIDATED and undo_state == TaskStatus.MAPPED:
            user.tasks_validated -= 1
            project.tasks_validated -= 1
        elif current_state == TaskStatus.VALIDATED and undo_state == TaskStatus.BADIMAGERY:
            user.tasks_validated -= 1
            project.tasks_validated -= 1
    def update_stats_after_task_state_change(
        project_id: int,
        user_id: int,
        last_state: TaskStatus,
        new_state: TaskStatus,
        action="change",
    ):
        """ Update stats when a task has had a state change """

        if new_state in [
                TaskStatus.READY,
                TaskStatus.LOCKED_FOR_VALIDATION,
                TaskStatus.LOCKED_FOR_MAPPING,
        ]:
            return  # No stats to record for these states

        project = ProjectService.get_project_by_id(project_id)
        user = UserService.get_user_by_id(user_id)

        project, user = StatsService._update_tasks_stats(
            project, user, last_state, new_state, action)
        UserService.upsert_mapped_projects(user_id, project_id)
        project.last_updated = timestamp()

        # Transaction will be saved when task is saved
        return project, user
示例#3
0
    def update_stats_after_task_state_change(project_id: int, user_id: int,
                                             new_state: TaskStatus,
                                             task_id: int):
        """ Update stats when a task has had a state change """
        if new_state in [
                TaskStatus.READY, TaskStatus.LOCKED_FOR_VALIDATION,
                TaskStatus.LOCKED_FOR_MAPPING
        ]:
            return  # No stats to record for these states

        project = ProjectService.get_project_by_id(project_id)
        user = UserService.get_user_by_id(user_id)

        if new_state == TaskStatus.MAPPED:
            StatsService._set_counters_after_mapping(project, user)
        elif new_state == TaskStatus.INVALIDATED:
            StatsService._set_counters_after_invalidated(
                task_id, project, user)
        elif new_state == TaskStatus.VALIDATED:
            StatsService._set_counters_after_validated(project, user)
        elif new_state == TaskStatus.BADIMAGERY:
            StatsService._set_counters_after_bad_imagery(project)

        UserService.upsert_mapped_projects(user_id, project_id)
        project.last_updated = timestamp()

        # Transaction will be saved when task is saved
        return project, user
    def update_project_stats(project_id: int):
        project = ProjectService.get_project_by_id(project_id)
        tasks = Task.query.filter(Task.project_id == project_id)

        project.total_tasks = tasks.count()
        project.tasks_mapped = tasks.filter(
            Task.task_status == TaskStatus.MAPPED.value).count()
        project.tasks_validated = tasks.filter(
            Task.task_status == TaskStatus.VALIDATED.value).count()
        project.tasks_bad_imagery = tasks.filter(
            Task.task_status == TaskStatus.BADIMAGERY.value).count()
        project.save()
    def reset_all_badimagery(project_id: int, user_id: int):
        """ Marks all bad imagery tasks ready for mapping """
        badimagery_tasks = Task.query.filter(Task.task_status == TaskStatus.BADIMAGERY.value).all()

        for task in badimagery_tasks:
            task.lock_task_for_mapping(user_id)
            task.unlock_task(user_id, new_state=TaskStatus.READY)

        # Reset bad imagery counter
        project = ProjectService.get_project_by_id(project_id)
        project.tasks_bad_imagery = 0
        project.save()
示例#6
0
    def invalidate_all_tasks(project_id: int, user_id: int):
        """ Invalidates all mapped tasks on a project"""
        mapped_tasks = Task.query.filter(
            Task.project_id == project_id, ~Task.task_status.in_(
                [TaskStatus.READY.value, TaskStatus.BADIMAGERY.value])).all()
        for task in mapped_tasks:
            if TaskStatus(task.task_status) != TaskStatus.LOCKED_FOR_MAPPING:
                # Only lock tasks that are not already locked to avoid double lock issue.
                task.lock_task_for_validating(user_id)
            task.unlock_task(user_id, new_state=TaskStatus.INVALIDATED)

        # Reset counters
        project = ProjectService.get_project_by_id(project_id)
        project.tasks_mapped = 0
        project.tasks_validated = 0
        project.save()
    def map_all_tasks(project_id: int, user_id: int):
        """ Marks all tasks on a project as mapped """
        tasks_to_map = Task.query.filter(Task.project_id == project_id,
                                         Task.task_status.notin_([TaskStatus.BADIMAGERY.value,
                                                                  TaskStatus.MAPPED.value,
                                                                  TaskStatus.VALIDATED.value])).all()

        for task in tasks_to_map:
            if TaskStatus(task.task_status) not in [TaskStatus.LOCKED_FOR_MAPPING, TaskStatus.LOCKED_FOR_VALIDATION]:
                # Only lock tasks that are not already locked to avoid double lock issue
                task.lock_task_for_mapping(user_id)

            task.unlock_task(user_id, new_state=TaskStatus.MAPPED)

        # Set counters to fully mapped
        project = ProjectService.get_project_by_id(project_id)
        project.tasks_mapped = (project.total_tasks - project.tasks_bad_imagery)
        project.save()
示例#8
0
    def validate_all_tasks(project_id: int, user_id: int):
        """ Validates all mapped tasks on a project"""
        tasks_to_validate = Task.query.filter(
            Task.project_id == project_id,
            Task.task_status != TaskStatus.BADIMAGERY.value).all()

        for task in tasks_to_validate:
            task.mapped_by = user_id  # Ensure we set mapped by value
            task.lock_task_for_validating(user_id)
            task.unlock_task(user_id, new_state=TaskStatus.VALIDATED)

        # Set counters to fully mapped and validated
        project = ProjectService.get_project_by_id(project_id)
        project.tasks_mapped = (project.total_tasks -
                                project.tasks_bad_imagery)
        project.tasks_validated = (project.total_tasks -
                                   project.tasks_bad_imagery)
        project.save()
示例#9
0
    def get(self, project_id: int, annotation_type: str = None):
        """
        Get all task annotations for a project
        ---
        tags:
            - task annotations
        produces:
            - application/json
        parameters:
            - name: project_id
              in: path
              description: The ID of the project
              required: true
              type: integer
            - name: annotation_type
              in: path
              description: The type of annotation to fetch
              required: false
              type: string
        responses:
            200:
                description: Project Annotations
            404:
                description: Project or annotations not found
            500:
                description: Internal Server Error
        """
        try:
            project = ProjectService.get_project_by_id(project_id)
        except NotFound as e:
            current_app.logger.error(f'Error validating project: {str(e)}')
            return {"Error": "Project not found"}, 404

        try:
            if annotation_type:
                annotations = TaskAnnotation.get_task_annotations_by_project_id_type(
                    project_id, annotation_type)
            else:
                annotations = TaskAnnotation.get_task_annotations_by_project_id(
                    project_id)
            return annotations.to_primitive(), 200
        except NotFound:
            return {"Error": "Annotations not found"}, 404
    def validate_all_tasks(project_id: int, user_id: int):
        """ Validates all mapped tasks on a project"""
        tasks_to_validate = Task.query.filter(
            Task.project_id == project_id,
            Task.task_status != TaskStatus.BADIMAGERY.value).all()

        for task in tasks_to_validate:
            task.mapped_by = task.mapped_by or user_id  # Ensure we set mapped by value
            if TaskStatus(task.task_status) not in [
                    TaskStatus.LOCKED_FOR_MAPPING,
                    TaskStatus.LOCKED_FOR_VALIDATION
            ]:
                # Only lock tasks that are not already locked to avoid double lock issue
                task.lock_task_for_validating(user_id)

            task.unlock_task(user_id, new_state=TaskStatus.VALIDATED)

        # Set counters to fully mapped and validated
        project = ProjectService.get_project_by_id(project_id)
        project.tasks_mapped = (project.total_tasks -
                                project.tasks_bad_imagery)
        project.tasks_validated = project.total_tasks
        project.save()
    def test_project_service_raises_error_if_project_not_found(
            self, mock_project):
        mock_project.return_value = None

        with self.assertRaises(NotFound):
            ProjectService.get_project_by_id(123)
示例#12
0
    def post(self, project_id: int, annotation_type: str):
        """
        Store new task annotations for tasks of a project
        ---
        tags:
            - task annotations
        produces:
            - application/json
        parameters:
            - in: header
              name: Content-Type
              description: Content type for post body
              required: true
              type: string
              default: application/json
            - name: project_id
              in: path
              description: The unique project ID
              required: true
              type: integer
            - name: annotation_type
              in: path
              description: Annotation type
              required: true
              type: string
            - name: Application-Token
              in: header
              description: Application token registered with TM
              required: true
              type: string
            - in: body
              name: body
              required: true
              description: JSON object for creating draft project
              schema:
                projectId:
                    type: integer
                    required: true
                annotationType:
                    type: string
                    required: true
                tasks:
                    type: array
                    required: true
                    items:
                        schema:
                            taskId:
                                type: integer
                                required: true
                            annotationSource:
                                type: string
                            annotationMarkdown:
                                type: string
                            properties:
                                description: JSON object with properties
        responses:
            200:
                description: Project updated
            400:
                description: Client Error - Invalid Request
            404:
                description: Project or task not found
            500:
                description: Internal Server Error
        """

        if 'Application-Token' in request.headers:
            application_token = request.headers['Application-Token']
            try:
                is_valid_token = ApplicationService.check_token(
                    application_token)
            except NotFound as e:
                current_app.logger.error(f'Invalid token')
                return {"error": "Invalid token"}, 500
        else:
            current_app.logger.error(f'No token supplied')
            return {"error": "No token supplied"}, 500

        try:
            annotations = request.get_json() or {}
        except DataError as e:
            current_app.logger.error(f'Error validating request: {str(e)}')

        try:
            project = ProjectService.get_project_by_id(project_id)
        except NotFound as e:
            current_app.logger.error(f'Error validating project: {str(e)}')

        task_ids = [t['taskId'] for t in annotations['tasks']]

        # check if task ids are valid
        tasks = Task.get_tasks(project_id, task_ids)
        tasks_ids_db = [t.id for t in tasks]
        if (len(task_ids) != len(tasks_ids_db)):
            return {"error": 'Invalid task id'}, 500

        for annotation in annotations['tasks']:
            try:
                TaskAnnotationsService.add_or_update_annotation(
                    annotation, project_id, annotation_type)
            except DataError as e:
                current_app.logger.error(
                    f'Error creating annotations: {str(e)}')
                return {"Error": "Error creating annotations"}, 500

        return project_id, 200