Ejemplo n.º 1
0
    def accept_reject_join_request(team_id, from_user_id, username, function,
                                   action):
        from_user = UserService.get_user_by_id(from_user_id)
        to_user_id = UserService.get_user_by_username(username).id
        team = TeamService.get_team_by_id(team_id)
        MessageService.accept_reject_request_to_join_team(
            from_user_id, from_user.username, to_user_id, team.name, team_id,
            action)

        is_member = TeamService.is_user_team_member(team_id, to_user_id)
        if action == "accept":
            if is_member:
                TeamService.activate_team_member(team_id, to_user_id)
            else:
                TeamService.add_team_member(
                    team_id,
                    to_user_id,
                    TeamMemberFunctions[function.upper()].value,
                    True,
                )
        elif action == "reject":
            if is_member:
                TeamService.delete_invite(team_id, to_user_id)
        else:
            raise TeamServiceError("Invalid action type")
Ejemplo n.º 2
0
 def send_invite(team_id, from_user_id, username):
     to_user = UserService.get_user_by_username(username)
     from_user = UserService.get_user_by_id(from_user_id)
     team = TeamService.get_team_by_id(team_id)
     MessageService.send_invite_to_join_team(from_user_id,
                                             from_user.username, to_user.id,
                                             team.name, team_id)
Ejemplo n.º 3
0
 def patch(self):
     """
     Resends the verification email token to the logged in user
     ---
     tags:
       - users
     produces:
       - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           required: true
           type: string
           default: Token sessionTokenHere==
     responses:
         200:
             description: Resends the user their email verification email
         500:
             description: Internal Server Error
     """
     try:
         MessageService.resend_email_validation(tm.authenticated_user_id)
         return {"Success": "Verification email resent"}, 200
     except Exception as e:
         error_msg = f"User GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": "Unable to send verification email"}, 500
    def stop_validating_tasks(
            stop_validating_dto: StopValidationDTO) -> TaskDTOs:
        """
        Unlocks supplied tasks after validation
        :raises ValidatorServiceError
        """
        reset_tasks = stop_validating_dto.reset_tasks
        project_id = stop_validating_dto.project_id
        user_id = stop_validating_dto.user_id
        tasks_to_unlock = ValidatorService.get_tasks_locked_by_user(
            project_id, reset_tasks, user_id)

        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(
                    user_id, task_to_unlock["comment"], task.id, project_id)

            task.reset_lock(user_id, task_to_unlock["comment"])
            dtos.append(
                task.as_dto_with_instructions(
                    stop_validating_dto.preferred_locale))

        task_dtos = TaskDTOs()
        task_dtos.tasks = dtos

        return task_dtos
    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)
Ejemplo n.º 6
0
    def send_message_to_all_team_members(team_id: int, team_name: str,
                                         message_dto: MessageDTO):
        """Sends supplied message to all contributors in a team.  Message all team members can take
        over a minute to run, so this method is expected to be called on its own thread"""
        app = (
            create_app()
        )  # Because message-all run on background thread it needs it's own app context

        with app.app_context():
            team_members = TeamService._get_active_team_members(team_id)
            sender = UserService.get_user_by_id(
                message_dto.from_user_id).username

            message_dto.message = (
                "A message from {}, manager of {} team:<br/><br/>{}".format(
                    MessageService.get_user_profile_link(sender),
                    MessageService.get_team_link(team_name, team_id, False),
                    markdown(message_dto.message, output_format="html"),
                ))

            messages = []
            for team_member in team_members:
                if team_member.user_id != message_dto.from_user_id:
                    message = Message.from_dto(team_member.user_id,
                                               message_dto)
                    message.message_type = MessageType.TEAM_BROADCAST.value
                    message.save()
                    user = UserService.get_user_by_id(team_member.user_id)
                    messages.append(dict(message=message, user=user))

            MessageService._push_messages(messages)
Ejemplo n.º 7
0
    def test_welcome_message_sent(self):
        self.test_user = create_canned_user()
        # Act
        message_id = MessageService.send_welcome_message(self.test_user)
        self.assertIsNotNone(message_id)
        message = MessageService.get_message(message_id, self.test_user.id)

        # Assert
        self.assertTrue(message, "Message should be saved to DB")

        # Tidyup
        MessageService.delete_message(message_id, self.test_user.id)
Ejemplo n.º 8
0
    def test_welcome_message_sent(self):
        if self.skip_tests:
            return

        # Act
        message_id = MessageService.send_welcome_message(self.test_user)
        message = MessageService.get_message(message_id, self.test_user.id)

        # Assert
        self.assertTrue(message, "Message should be saved to DB")

        # Tidyup
        MessageService.delete_message(message_id, self.test_user.id)
Ejemplo n.º 9
0
    def stop_mapping_task(stop_task: StopMappingTaskDTO) -> TaskDTO:
        """ Unlocks the task and sets the task history appropriately """
        task = MappingService.get_task_locked_by_user(stop_task.project_id,
                                                      stop_task.task_id,
                                                      stop_task.user_id)

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

        task.reset_lock(stop_task.user_id, stop_task.comment)
        return task.as_dto_with_instructions(stop_task.preferred_locale)
Ejemplo n.º 10
0
    def add_task_comment(task_comment: TaskCommentDTO) -> TaskDTO:
        """ Adds the comment to the task history """
        task = Task.get(task_comment.task_id, task_comment.project_id)
        if task is None:
            raise MappingServiceError(f"Task {task_comment.task_id} not found")

        task.set_task_history(TaskAction.COMMENT, task_comment.user_id,
                              task_comment.comment)
        # Parse comment to see if any users have been @'d
        MessageService.send_message_after_comment(task_comment.user_id,
                                                  task_comment.comment,
                                                  task.id,
                                                  task_comment.project_id)
        task.update()
        return task.as_dto_with_instructions(task_comment.preferred_locale)
Ejemplo n.º 11
0
 def get(self):
     """
     Gets count of unread messages
     ---
     tags:
       - notifications
     produces:
       - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           required: true
           type: string
           default: Token sessionTokenHere==
     responses:
         200:
             description: Message info
         500:
             description: Internal Server Error
     """
     try:
         unread_count = MessageService.has_user_new_messages(
             token_auth.current_user()
         )
         return unread_count, 200
     except Exception as e:
         error_msg = f"User GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": "Unable to fetch messages count"}, 500
    def test_message_service_generates_correct_chat_link(self):
        # Act
        link = MessageService.get_project_link(1, "http://test.com")

        self.assertEqual(
            link,
            '<a href="http://test.com/projects/1#questionsAndComments">Project 1</a>',
        )
Ejemplo n.º 13
0
    def test_message_service_generates_correct_task_link(self):
        # Act
        link = MessageService.get_task_link(1, 1, "http://test.com")

        # Assert
        self.assertEqual(
            link,
            '<a href="http://test.com/projects/1/tasks/?search=1">Task 1</a>')
Ejemplo n.º 14
0
    def test_message_service_identifies_all_users(self):
        # Act
        usernames = MessageService._parse_message_for_username(
            'Hello @[Iain Hunter] and "[LindaA1]')

        # Assert
        self.assertEqual(usernames[0], "Iain Hunter")
        self.assertEqual(usernames[1], "LindaA1")
Ejemplo n.º 15
0
    def post_message(chat_dto: ChatMessageDTO, project_id: int,
                     authenticated_user_id: int) -> ProjectChatDTO:
        """ Save message to DB and return latest chat"""
        current_app.logger.debug("Posting Chat Message")

        if UserService.is_user_blocked(authenticated_user_id):
            raise ValueError("User is on read only mode")

        project = ProjectService.get_project_by_id(project_id)
        is_allowed_user = True
        is_manager_permission = ProjectAdminService.is_user_action_permitted_on_project(
            authenticated_user_id, project_id)
        is_team_member = False

        # Draft (public/private) accessible only for is_manager_permission
        if (ProjectStatus(project.status) == ProjectStatus.DRAFT
                and not is_manager_permission):
            raise ValueError("User not permitted to post Comment")

        if project.private:
            is_allowed_user = False
            if not is_manager_permission:
                allowed_roles = [
                    TeamRoles.PROJECT_MANAGER.value,
                    TeamRoles.VALIDATOR.value,
                    TeamRoles.MAPPER.value,
                ]
                is_team_member = TeamService.check_team_membership(
                    project_id, allowed_roles, authenticated_user_id)
                if not is_team_member:
                    is_allowed_user = (len([
                        user for user in project.allowed_users
                        if user.id == authenticated_user_id
                    ]) > 0)

        if is_manager_permission or is_team_member or is_allowed_user:
            chat_message = ProjectChat.create_from_dto(chat_dto)
            MessageService.send_message_after_chat(chat_dto.user_id,
                                                   chat_message.message,
                                                   chat_dto.project_id)
            db.session.commit()
            # Ensure we return latest messages after post
            return ProjectChat.get_messages(chat_dto.project_id, 1)
        else:
            raise ValueError("User not permitted to post Comment")
Ejemplo n.º 16
0
    def join_team(team_id: int,
                  requesting_user: int,
                  username: str,
                  role: str = None):
        is_manager = TeamService.is_user_team_manager(team_id, requesting_user)
        team = TeamService.get_team_by_id(team_id)
        user = UserService.get_user_by_username(username)

        if TeamService.is_user_team_member(team.id, user.id):
            raise TeamJoinNotAllowed(
                "User is already a member of this team or has already requested to join"
            )

        if is_manager:
            if role:
                try:
                    role = TeamMemberFunctions[role.upper()].value
                except KeyError:
                    raise Exception("Invalid TeamMemberFunction")
            else:
                role = TeamMemberFunctions.MEMBER.value

            TeamService.add_team_member(team_id, user.id, role, True)
        else:
            if user.id != requesting_user:
                raise TeamJoinNotAllowed("User not allowed to join team")

            role = TeamMemberFunctions.MEMBER.value

            # active if the team is open
            if team.invite_only:
                active = False
            else:
                active = True

            TeamService.add_team_member(team_id, user.id, role, active)

            if team.invite_only:
                team_managers = team.get_team_managers()
                for member in team_managers:
                    MessageService.send_request_to_join_team(
                        user.id, user.username, member.user_id, team.name,
                        team_id)
Ejemplo n.º 17
0
 def delete(self, message_id):
     """
     Deletes the specified message
     ---
     tags:
       - notifications
     produces:
       - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           required: true
           type: string
           default: Token sessionTokenHere==
         - name: message_id
           in: path
           description: The unique message
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: Messages found
         403:
             description: Forbidden, if user attempting to ready other messages
         404:
             description: Not found
         500:
             description: Internal Server Error
     """
     try:
         MessageService.delete_message(message_id,
                                       token_auth.current_user())
         return {"Success": "Message deleted"}, 200
     except MessageServiceError:
         return {"Error": "Unable to delete message"}, 403
     except NotFound:
         return {"Error": "No messages found"}, 404
     except Exception as e:
         error_msg = f"Messages GET all - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": "Unable to delete message"}, 500
Ejemplo n.º 18
0
    def delete(self):
        """
        Delete specified messages for logged in user
        ---
        tags:
          - notifications
        produces:
          - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              required: true
              type: string
              default: Token sessionTokenHere==
            - in: body
              name: body
              required: true
              description: JSON object containing message ids to delete
              schema:
                  properties:
                      messageIds:
                          type: array
                          items: integer
                          required: true
        responses:
            200:
                description: Messages deleted
            500:
                description: Internal Server Error
        """
        try:
            message_ids = request.get_json()["messageIds"]
            if message_ids:
                MessageService.delete_multiple_messages(
                    message_ids, tm.authenticated_user_id)

            return {"Success": "Messages deleted"}, 200
        except Exception as e:
            error_msg = f"DeleteMultipleMessages - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": "Unable to delete messages"}, 500
Ejemplo n.º 19
0
    def accept_reject_invitation_request(team_id, from_user_id, username,
                                         function, action):
        from_user = UserService.get_user_by_id(from_user_id)
        to_user = UserService.get_user_by_username(username)
        team = TeamService.get_team_by_id(team_id)
        team_members = TeamService._get_team_managers(team_id)

        for member in team_members:
            MessageService.accept_reject_invitation_request_for_team(
                from_user_id,
                from_user.username,
                member.user_id,
                to_user.username,
                team.name,
                action,
            )
        if action == "accept":
            TeamService.add_team_member(
                team_id, from_user_id,
                TeamMemberFunctions[function.upper()].value)
Ejemplo n.º 20
0
    def login_user(osm_user_details, email, user_element="user") -> str:
        """
        Generates authentication details for user, creating in DB if user is unknown to us
        :param osm_user_details: XML response from OSM
        :param redirect_to: Route to redirect user to, from callback url
        :param user_element: Exists for unit testing
        :raises AuthServiceError
        :returns Authorized URL with authentication details in query string
        """
        osm_user = osm_user_details.find(user_element)

        if osm_user is None:
            raise AuthServiceError("User element not found in OSM response")

        osm_id = int(osm_user.attrib["id"])
        username = osm_user.attrib["display_name"]
        try:
            # get gravatar profile picture file name
            user_picture = osm_user.find("img").attrib["href"]
        except (AttributeError, IndexError):
            user_picture = None

        try:
            UserService.get_user_by_id(osm_id)
            UserService.update_user(osm_id, username, user_picture)
        except NotFound:
            # User not found, so must be new user
            changesets = osm_user.find("changesets")
            changeset_count = int(changesets.attrib["count"])
            new_user = UserService.register_user(osm_id, username,
                                                 changeset_count, user_picture,
                                                 email)
            MessageService.send_welcome_message(new_user)

        session_token = AuthenticationService.generate_session_token_for_user(
            osm_id)
        return {
            "username": username,
            "session_token": session_token,
            "picture": user_picture,
        }
Ejemplo n.º 21
0
    def post_message(
        chat_dto: ChatMessageDTO, project_id: int, authenticated_user_id: int
    ) -> ProjectChatDTO:
        """ Save message to DB and return latest chat"""
        current_app.logger.debug("Posting Chat Message")

        if UserService.is_user_blocked(authenticated_user_id):
            return ValueError("User is on read only mode")

        project = ProjectService.get_project_by_id(project_id)
        if project.private:
            author_id = project.author_id
            allowed_roles = [
                TeamRoles.PROJECT_MANAGER.value,
                TeamRoles.VALIDATOR.value,
                TeamRoles.MAPPER.value,
            ]

            is_admin = UserService.is_user_an_admin(authenticated_user_id)
            is_author = UserService.is_user_the_project_author(
                authenticated_user_id, author_id
            )
            is_org_manager = False
            if hasattr(project, "organisation_id") and project.organisation_id:
                org_id = project.organisation_id
                org = OrganisationService.get_organisation_by_id_as_dto(
                    org_id, authenticated_user_id
                )
                if org.is_manager:
                    is_org_manager = True

            is_team_member = None
            if hasattr(project, "project_teams") and project.project_teams:
                teams_dto = TeamService.get_project_teams_as_dto(project_id)
                if teams_dto.teams:
                    teams_allowed = [
                        team_dto
                        for team_dto in teams_dto.teams
                        if team_dto.role in allowed_roles
                    ]
                    user_membership = [
                        team_dto.team_id
                        for team_dto in teams_allowed
                        if TeamService.is_user_member_of_team(
                            team_dto.team_id, authenticated_user_id
                        )
                    ]
                    if user_membership:
                        is_team_member = True

            for user in project.allowed_users:
                if user.id == authenticated_user_id:
                    is_allowed_user = True
                    break

            if (
                is_admin
                or is_author
                or is_org_manager
                or is_team_member
                or is_allowed_user
            ):
                chat_message = ProjectChat.create_from_dto(chat_dto)
                MessageService.send_message_after_chat(
                    chat_dto.user_id, chat_message.message, chat_dto.project_id
                )
                db.session.commit()
                # Ensure we return latest messages after post
                return ProjectChat.get_messages(chat_dto.project_id, 1)
            else:
                raise ValueError("User not permitted to post Comment")
        else:
            chat_message = ProjectChat.create_from_dto(chat_dto)
            MessageService.send_message_after_chat(
                chat_dto.user_id, chat_message.message, chat_dto.project_id
            )
            db.session.commit()
            # Ensure we return latest messages after post
            return ProjectChat.get_messages(chat_dto.project_id, 1)
Ejemplo n.º 22
0
 def get(self):
     """
     Get all messages for logged in user
     ---
     tags:
       - notifications
     produces:
       - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           required: true
           type: string
           default: Token sessionTokenHere==
         - in: query
           name: messageType
           type: string
           description: Optional message-type filter; leave blank to retrieve all
           default: 1,2
         - in: query
           name: from
           description: Optional from username filter
           type: string
         - in: query
           name: project
           description: Optional project filter
           type: string
         - in: query
           name: taskId
           description: Optional task filter
           type: integer
         - in: query
           name: sortBy
           description: field to sort by, defaults to date
           type: string
         - in: query
           name: sortDirection
           description: direction of sort, defaults to desc
           type: string
         - in: query
           name: page
           description: Page of results user requested
           type: integer
         - in: query
           name: pageSize
           description: Size of page, defaults to 10
           type: integer
     responses:
         200:
             description: Messages found
         404:
             description: User has no messages
         500:
             description: Internal Server Error
     """
     try:
         preferred_locale = request.environ.get("HTTP_ACCEPT_LANGUAGE")
         page = request.args.get("page", 1, int)
         page_size = request.args.get("pageSize", 10, int)
         sort_by = request.args.get("sortBy", "date")
         sort_direction = request.args.get("sortDirection", "desc")
         message_type = request.args.get("messageType", None)
         from_username = request.args.get("from")
         project = request.args.get("project", None, int)
         task_id = request.args.get("taskId", None, int)
         user_messages = MessageService.get_all_messages(
             token_auth.current_user(),
             preferred_locale,
             page,
             page_size,
             sort_by,
             sort_direction,
             message_type,
             from_username,
             project,
             task_id,
         )
         return user_messages.to_primitive(), 200
     except Exception as e:
         error_msg = f"Messages GET all - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": "Unable to fetch messages"}, 500
    def unlock_tasks_after_validation(
        validated_dto: UnlockAfterValidationDTO, ) -> TaskDTOs:
        """
        Unlocks supplied tasks after validation
        :raises ValidatorServiceError
        """
        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