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
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()
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()
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()
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)
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