def init_counters(app):
    """ Initialise homepage counters so that users don't see 0 users on first load of application"""
    from server.services.stats_service import StatsService

    app.logger.debug('Initialising Homepage Counters')
    with app.app_context():
        StatsService.get_homepage_stats()
    def unlock_task_after_mapping(mapped_task: MappedTaskDTO) -> TaskDTO:
        """ Unlocks the task and sets the task history appropriately """
        task = MappingService.get_task_locked_by_user(mapped_task.project_id,
                                                      mapped_task.task_id,
                                                      mapped_task.user_id)

        new_state = TaskStatus[mapped_task.status.upper()]

        if new_state not in [
                TaskStatus.MAPPED, TaskStatus.BADIMAGERY, TaskStatus.READY
        ]:
            raise MappingServiceError(
                'Can only set status to MAPPED, BADIMAGERY, READY after mapping'
            )

        # Update stats around the change of state
        last_state = TaskHistory.get_last_status(mapped_task.project_id,
                                                 mapped_task.task_id, True)
        StatsService.update_stats_after_task_state_change(
            mapped_task.project_id, mapped_task.user_id, last_state, new_state)

        if mapped_task.comment:
            # Parses comment to see if any users have been @'d
            MessageService.send_message_after_comment(mapped_task.user_id,
                                                      mapped_task.comment,
                                                      task.id,
                                                      mapped_task.project_id)

        task.unlock_task(mapped_task.user_id, new_state, mapped_task.comment)

        return task.as_dto_with_instructions(mapped_task.preferred_locale)
    def unlock_tasks_after_validation(
            validated_dto: UnlockAfterValidationDTO) -> TaskDTOs:
        """
        Unlocks supplied tasks after validation
        :raises ValidatatorServiceError
        """
        validated_tasks = validated_dto.validated_tasks
        project_id = validated_dto.project_id
        user_id = validated_dto.user_id
        tasks_to_unlock = ValidatorService.get_tasks_locked_by_user(
            project_id, validated_tasks, user_id)

        # Unlock all tasks
        dtos = []
        message_sent_to = []
        for task_to_unlock in tasks_to_unlock:
            task = task_to_unlock['task']

            if task_to_unlock['comment']:
                # Parses comment to see if any users have been @'d
                MessageService.send_message_after_comment(
                    validated_dto.user_id, task_to_unlock['comment'], task.id,
                    validated_dto.project_id)
            if task_to_unlock[
                    'new_state'] == TaskStatus.VALIDATED or task_to_unlock[
                        'new_state'] == TaskStatus.INVALIDATED:
                # All mappers get a notification if their task has been validated or invalidated.
                # Only once if multiple tasks mapped
                if task.mapped_by not in message_sent_to:
                    MessageService.send_message_after_validation(
                        task_to_unlock['new_state'], validated_dto.user_id,
                        task.mapped_by, task.id, validated_dto.project_id)
                    message_sent_to.append(task.mapped_by)

                if task_to_unlock['new_state'] == TaskStatus.VALIDATED:
                    # Set last_validation_date for the mapper to current date
                    task.mapper.last_validation_date = timestamp()

            # Update stats if user setting task to a different state from previous state
            prev_status = TaskHistory.get_last_status(project_id, task.id)
            if prev_status != task_to_unlock['new_state']:
                StatsService.update_stats_after_task_state_change(
                    validated_dto.project_id, validated_dto.user_id,
                    prev_status, task_to_unlock['new_state'])
            task_mapping_issues = ValidatorService.get_task_mapping_issues(
                task_to_unlock)
            task.unlock_task(validated_dto.user_id,
                             task_to_unlock['new_state'],
                             task_to_unlock['comment'],
                             issues=task_mapping_issues)
            dtos.append(
                task.as_dto_with_instructions(validated_dto.preferred_locale))

        task_dtos = TaskDTOs()
        task_dtos.tasks = dtos

        return task_dtos
    def test_update_after_flagging_bad_imagery(self):
        # Arrange
        test_project = Project()
        test_project.tasks_bad_imagery = 0

        # Act
        StatsService._set_counters_after_bad_imagery(test_project)

        # Assert
        self.assertEqual(test_project.tasks_bad_imagery, 1)
    def test_update_after_mapping_increments_counter(self):
        # Arrange
        test_project = Project()
        test_project.tasks_mapped = 0

        test_user = User()
        test_user.tasks_mapped = 0

        # Act
        StatsService._set_counters_after_mapping(test_project, test_user)

        # Assert
        self.assertEqual(test_project.tasks_mapped, 1)
        self.assertEqual(test_user.tasks_mapped, 1)
    def undo_mapping(project_id: int, task_id: int, user_id: int, preferred_locale: str = 'en') -> TaskDTO:
        """ Allows a user to Undo the task state they updated """
        task = MappingService.get_task(task_id, project_id)

        if not MappingService._is_task_undoable(user_id, task):
            raise MappingServiceError('Undo not allowed for this user')

        current_state = TaskStatus(task.task_status)
        undo_state = TaskHistory.get_last_status(project_id, task_id, True)

        StatsService.set_counters_after_undo(project_id, user_id, current_state, undo_state)
        task.unlock_task(user_id, undo_state,
                         f'Undo state from {current_state.name} to {undo_state.name}', True)

        return task.as_dto_with_instructions(preferred_locale)
Example #7
0
    def test_update_after_mapping_increments_counter(self):
        # Arrange
        test_project = Project()
        test_project.tasks_mapped = 0

        test_user = User()
        test_user.tasks_mapped = 0

        # Act
        StatsService._update_tasks_stats(test_project, test_user,
                                         TaskStatus.READY, TaskStatus.MAPPED)

        # Assert
        self.assertEqual(test_project.tasks_mapped, 1)
        self.assertEqual(test_user.tasks_mapped, 1)
Example #8
0
    def test_update_after_flagging_bad_imagery(self):
        # Arrange
        test_project = Project()
        test_project.tasks_bad_imagery = 0

        test_user = User()
        test_user.tasks_invalidated = 0

        # Act
        StatsService._update_tasks_stats(test_project, test_user,
                                         TaskStatus.READY,
                                         TaskStatus.BADIMAGERY)

        # Assert
        self.assertEqual(test_project.tasks_bad_imagery, 1)
 def get(self, project_id):
     """
     Get all user contributions on a project
     ---
     tags:
       - stats
     produces:
       - application/json
     parameters:
         - name: project_id
           in: path
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: User contributions
         404:
             description: No contributions
         500:
             description: Internal Server Error
     """
     try:
         contributions = StatsService.get_user_contributions(project_id)
         return contributions.to_primitive(), 200
     except NotFound:
         return {"Error": "No contributions on project"}, 404
     except Exception as e:
         error_msg = f'User GET - unhandled error: {str(e)}'
         current_app.logger.critical(error_msg)
         return {"error": error_msg}, 500
    def test_update_after_invalidating_bad_imagery_task_sets_counters_correctly(self, last_status):
        # Arrange
        test_project = Project()
        test_project.tasks_bad_imagery = 1

        test_user = User()
        test_user.tasks_invalidated = 0

        last_status.return_value = TaskStatus.BADIMAGERY

        # Act
        StatsService._set_counters_after_invalidated(1, test_project, test_user)

        # Assert
        self.assertEqual(test_project.tasks_bad_imagery, 0)
        self.assertEqual(test_user.tasks_invalidated, 1)
    def test_update_after_invalidating_mapped_task_sets_counters_correctly(self, last_status):
        # Arrange
        test_project = Project()
        test_project.tasks_mapped = 1

        test_user = User()
        test_user.tasks_invalidated = 0

        last_status.return_value = TaskStatus.MAPPED

        # Act
        StatsService._set_counters_after_invalidated(1, test_project, test_user)

        # Assert
        self.assertEqual(test_project.tasks_mapped, 0)
        self.assertEqual(test_user.tasks_invalidated, 1)
Example #12
0
    def test_update_after_invalidating_validated_task_sets_counters_correctly(
            self):
        # Arrange
        test_project = Project()
        test_project.tasks_validated = 1

        test_user = User()
        test_user.tasks_invalidated = 0

        # Act
        StatsService._update_tasks_stats(test_project, test_user,
                                         TaskStatus.VALIDATED,
                                         TaskStatus.INVALIDATED)

        # Assert
        self.assertEqual(test_project.tasks_validated, 0)
        self.assertEqual(test_user.tasks_invalidated, 1)
    def unlock_tasks_after_validation(
            validated_dto: UnlockAfterValidationDTO) -> TaskDTOs:
        """
        Unlocks supplied tasks after validation
        :raises ValidatatorServiceError
        """
        validated_tasks = validated_dto.validated_tasks
        project_id = validated_dto.project_id
        user_id = validated_dto.user_id
        tasks_to_unlock = ValidatorService.get_tasks_locked_by_user(
            project_id, validated_tasks, user_id)

        # Unlock all tasks
        dtos = []
        for task_to_unlock in tasks_to_unlock:
            task = task_to_unlock['task']

            if task_to_unlock['comment']:
                # Parses comment to see if any users have been @'d
                MessageService.send_message_after_comment(
                    validated_dto.user_id, task_to_unlock['comment'], task.id,
                    validated_dto.project_id)

            if task_to_unlock['new_state'] == TaskStatus.VALIDATED:
                # All mappers get a thankyou if their task has been validated :)
                MessageService.send_message_after_validation(
                    validated_dto.user_id, task.mapped_by, task.id,
                    validated_dto.project_id)

            StatsService.update_stats_after_task_state_change(
                validated_dto.project_id, validated_dto.user_id,
                task_to_unlock['new_state'], task.id)

            task.unlock_task(validated_dto.user_id,
                             task_to_unlock['new_state'],
                             task_to_unlock['comment'])

            dtos.append(task.as_dto())

        task_dtos = TaskDTOs()
        task_dtos.tasks = dtos

        return task_dtos
    def test_homepage_stats_returns_results(self):
        if self.skip_tests:
            return

        # Act
        stats = StatsService.get_homepage_stats()

        # Assert
        self.assertGreaterEqual(stats.mappers_online, 0)
        self.assertGreater(stats.tasks_mapped, 0)
        self.assertGreater(stats.total_mappers, 0)
    def test_homepage_stats_returns_results(self):
        if self.skip_tests:
            return

        # Arrange
        test_project, test_user = create_canned_project()

        # Act
        stats = StatsService.get_homepage_stats()

        # Assert
        self.assertGreater(stats.mappers_online, 0)
        self.assertGreater(stats.tasks_mapped, 0)
        self.assertGreater(stats.total_mappers, 0)

        # Tidy up
        test_project.delete()
        test_user.delete()
 def get(self):
     """
     Get HomePage Stats
     ---
     tags:
       - stats
     produces:
       - application/json
     responses:
         200:
             description: Project stats
         500:
             description: Internal Server Error
     """
     try:
         stats = StatsService.get_homepage_stats()
         return stats.to_primitive(), 200
     except Exception as e:
         error_msg = f'Unhandled error: {str(e)}'
         current_app.logger.critical(error_msg)
         return {"error": error_msg}, 500
 def get(self, project_id):
     """
     Get user actvity on a project
     ---
     tags:
       - stats
     produces:
       - application/json
     parameters:
         - name: project_id
           in: path
           required: true
           type: integer
           default: 1
         - in: query
           name: page
           description: Page of results user requested
           type: integer
     responses:
         200:
             description: Project activity
         404:
             description: No activity
         500:
             description: Internal Server Error
     """
     try:
         page = int(
             request.args.get('page')) if request.args.get('page') else 1
         activity = StatsService.get_latest_activity(project_id, page)
         return activity.to_primitive(), 200
     except NotFound:
         return {"Error": "No activity on project"}, 404
     except Exception as e:
         error_msg = f'User GET - unhandled error: {str(e)}'
         current_app.logger.critical(error_msg)
         return {"error": error_msg}, 500
Example #18
0
def refresh_project_stats():
    print("Started updating project stats...")
    StatsService.update_all_project_stats()
    print("Project stats updated")
Example #19
0
    def test_tasks_state_representation(self):

        # Arrange
        test_project = Project()
        test_project.tasks_mapped = 0
        test_project.tasks_validated = 0
        test_project.tasks_bad_imagery = 0

        test_mapper = User()
        test_mapper.tasks_mapped = 0
        test_mapper.tasks_validated = 0
        test_mapper.tasks_invalidated = 0

        test_validator = User()
        test_validator.tasks_mapped = 0
        test_validator.tasks_validated = 0
        test_validator.tasks_invalidated = 0

        test_admin = User()
        test_admin.tasks_mapped = 0
        test_admin.tasks_validated = 0
        test_admin.tasks_invalidated = 0

        # Mapper marks task as mapped
        StatsService._update_tasks_stats(test_project, test_mapper,
                                         TaskStatus.READY, TaskStatus.MAPPED)

        # Validator marks task as bad imagery
        StatsService._update_tasks_stats(test_project, test_validator,
                                         TaskStatus.MAPPED,
                                         TaskStatus.BADIMAGERY)

        # Admin undos marking task as bad imagery
        StatsService._update_tasks_stats(test_project, test_admin,
                                         TaskStatus.BADIMAGERY,
                                         TaskStatus.MAPPED, 'undo')

        # Validator marks task as invalid
        StatsService._update_tasks_stats(test_project, test_validator,
                                         TaskStatus.MAPPED,
                                         TaskStatus.INVALIDATED)

        # Mapper marks task as mapped
        StatsService._update_tasks_stats(test_project, test_mapper,
                                         TaskStatus.INVALIDATED,
                                         TaskStatus.MAPPED)

        # Admin undos marking task as mapped (test_mapper is given to the function though, as the author of the
        # last_change - compare with MappingServer.undo_mapping() method)
        StatsService._update_tasks_stats(test_project, test_mapper,
                                         TaskStatus.MAPPED,
                                         TaskStatus.INVALIDATED, 'undo')

        # Mapper marks task as mapped
        StatsService._update_tasks_stats(test_project, test_mapper,
                                         TaskStatus.INVALIDATED,
                                         TaskStatus.MAPPED)

        # Validator marks task as valid
        StatsService._update_tasks_stats(test_project, test_validator,
                                         TaskStatus.MAPPED,
                                         TaskStatus.VALIDATED)

        # Assert
        self.assertEqual(test_project.tasks_mapped, 0)
        self.assertEqual(test_project.tasks_validated, 1)
        self.assertEqual(test_project.tasks_bad_imagery, 0)
        self.assertEqual(test_mapper.tasks_mapped, 2)
        self.assertEqual(test_mapper.tasks_validated, 0)
        self.assertEqual(test_mapper.tasks_invalidated, 0)
        self.assertEqual(test_validator.tasks_mapped, 0)
        self.assertEqual(test_validator.tasks_validated, 1)
        self.assertEqual(test_validator.tasks_invalidated, 1)
        self.assertEqual(test_admin.tasks_mapped, 0)
        self.assertEqual(test_admin.tasks_validated, 0)
        self.assertEqual(test_admin.tasks_invalidated, 0)