Пример #1
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)
Пример #2
0
    def send_message_to_all_contributors(project_id: int, message_dto: MessageDTO):
        """  Sends supplied message to all contributors on specified project.  Message all contributors 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():
            contributors = Message.get_all_contributors(project_id)

            project_link = MessageService.get_project_link(project_id)

            message_dto.message = (
                f"{project_link}<br/><br/>" + message_dto.message
            )  # Append project link to end of message

            messages = []
            for contributor in contributors:
                message = Message.from_dto(contributor[0], message_dto)
                message.message_type = MessageType.BROADCAST.value
                message.project_id = project_id
                message.save()
                user = UserService.get_user_by_id(contributor[0])
                messages.append(dict(message=message, user=user))

            MessageService._push_messages(messages)
    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
Пример #4
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)
Пример #5
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")
Пример #6
0
    def send_message_to_all_contributors(project_id: int,
                                         message_dto: MessageDTO):
        """Sends supplied message to all contributors on specified project.  Message all contributors 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():
            contributors = Message.get_all_contributors(project_id)
            message_dto.message = "A message from {} managers:<br/><br/>{}".format(
                MessageService.get_project_link(project_id),
                markdown(message_dto.message, output_format="html"),
            )

            messages = []
            for contributor in contributors:
                message = Message.from_dto(contributor[0], message_dto)
                message.message_type = MessageType.BROADCAST.value
                message.project_id = project_id
                user = UserService.get_user_by_id(contributor[0])
                messages.append(dict(message=message, user=user))

            MessageService._push_messages(messages)
Пример #7
0
    def send_favorite_project_activities(user_id: int):
        current_app.logger.debug("Sending Favorite Project Activities")
        favorited_projects = UserService.get_projects_favorited(user_id)
        contributed_projects = UserService.get_projects_mapped(user_id)
        if contributed_projects is None:
            contributed_projects = []

        for favorited_project in favorited_projects.favorited_projects:
            contributed_projects.append(favorited_project.project_id)

        recently_updated_projects = (Project.query.with_entities(
            Project.id,
            func.DATE(Project.last_updated).label("last_updated")).filter(
                Project.id.in_(contributed_projects)).filter(
                    func.DATE(Project.last_updated) > datetime.date.today() -
                    datetime.timedelta(days=300)))
        user = UserService.get_user_by_id(user_id)
        messages = []
        for project in recently_updated_projects:
            activity_message = []
            query_last_active_users = """ select distinct(user_id) from
                                        (select user_id from task_history where project_id = :project_id
                                        order by action_date desc limit 15 ) t """
            last_active_users = db.engine.execute(
                text(query_last_active_users), project_id=project.id)

            for recent_user_id in last_active_users:
                recent_user_details = UserService.get_user_by_id(
                    recent_user_id)
                user_profile_link = MessageService.get_user_profile_link(
                    recent_user_details.username)
                activity_message.append(user_profile_link)

            activity_message = str(activity_message)[1:-1]
            project_link = MessageService.get_project_link(project.id)
            message = Message()
            message.message_type = MessageType.PROJECT_ACTIVITY_NOTIFICATION.value
            message.project_id = project.id
            message.to_user_id = user.id
            message.subject = (
                "Recent activities from your contributed/favorited Projects")
            message.message = (
                f"{activity_message} contributed to {project_link} recently")
            messages.append(dict(message=message, user=user))

        MessageService._push_messages(messages)
Пример #8
0
    def get_team_as_dto(team_id: int, user_id: int) -> TeamDTO:
        team = TeamService.get_team_by_id(team_id)

        if team is None:
            raise NotFound()

        team_dto = TeamDetailsDTO()
        team_dto.team_id = team.id
        team_dto.name = team.name
        team_dto.invite_only = team.invite_only
        team_dto.visibility = TeamVisibility(team.visibility).name
        team_dto.description = team.description
        team_dto.logo = team.organisation.logo
        team_dto.organisation = team.organisation.name
        team_dto.organisation_id = team.organisation.id

        if user_id != 0:
            if UserService.is_user_an_admin(user_id):
                team_dto.is_general_admin = True

            if OrganisationService.is_user_an_org_manager(
                    team.organisation.id, user_id):
                team_dto.is_org_admin = True
        else:
            team_dto.is_general_admin = False
            team_dto.is_org_admin = False

        team_members = TeamService._get_team_members(team_id)
        for member in team_members:
            user = UserService.get_user_by_id(member.user_id)
            member_dto = TeamMembersDTO()
            member_dto.username = user.username
            member_dto.pictureUrl = user.picture_url
            member_dto.function = TeamMemberFunctions(member.function).name
            member_dto.picture_url = user.picture_url
            member_dto.active = member.active

            team_dto.members.append(member_dto)

        team_projects = TeamService.get_projects_by_team_id(team.id)
        for team_project in team_projects:
            project_team_dto = TeamProjectDTO()
            project_team_dto.project_name = team_project.name
            project_team_dto.project_id = team_project.project_id
            project_team_dto.role = TeamRoles(team_project.role).name

            team_dto.team_projects.append(project_team_dto)

        org_projects = OrganisationService.get_projects_by_organisation_id(
            team.organisation.id)
        for org_project in org_projects:
            org_project_dto = OrganisationProjectsDTO()
            org_project_dto.project_id = org_project.id
            org_project_dto.project_name = org_project.name
            team_dto.organisation_projects.append(org_project_dto)

        return team_dto
Пример #9
0
    def create_or_update_user_interests(user_id, interests):
        user = UserService.get_user_by_id(user_id)
        user.create_or_update_interests(interests)

        # Return DTO.
        dto = InterestsListDTO()
        dto.interests = [i.as_dto() for i in user.interests]

        return dto
Пример #10
0
    def _push_messages(messages):
        if len(messages) == 0:
            return

        messages_objs = []
        for i, message in enumerate(messages):
            user = message.get("user")
            obj = message.get("message")
            # Store message in the database only if mentions option are disabled.
            if (user.mentions_notifications is False and obj.message_type
                    == MessageType.MENTION_NOTIFICATION.value):
                messages_objs.append(obj)
                continue
            if (user.projects_notifications is False and obj.message_type
                    == MessageType.PROJECT_ACTIVITY_NOTIFICATION.value):
                continue
            if (user.projects_notifications is False
                    and obj.message_type == MessageType.BROADCAST.value):
                continue
            if (user.teams_notifications is False
                    and obj.message_type == MessageType.TEAM_BROADCAST.value):
                messages_objs.append(obj)
                continue
            if user.comments_notifications is False and obj.message_type in (
                    MessageType.TASK_COMMENT_NOTIFICATION.value,
                    MessageType.PROJECT_CHAT_NOTIFICATION.value,
            ):
                continue
            if user.tasks_notifications is False and obj.message_type in (
                    MessageType.VALIDATION_NOTIFICATION.value,
                    MessageType.INVALIDATION_NOTIFICATION.value,
            ):
                messages_objs.append(obj)
                continue
            messages_objs.append(obj)

            SMTPService.send_email_alert(
                user.email_address,
                user.username,
                message["message"].id,
                UserService.get_user_by_id(
                    message["message"].from_user_id).username,
                message["message"].project_id,
                message["message"].task_id,
                clean_html(message["message"].subject),
                message["message"].message,
                obj.message_type,
            )

            if i + 1 % 10 == 0:
                time.sleep(0.5)

        # Flush messages to the database.
        if len(messages_objs) > 0:
            db.session.add_all(messages_objs)
            db.session.flush()
            db.session.commit()
    def is_user_an_org_manager(organisation_id: int, user_id: int):
        """ Check that the user is an manager for the org """

        org = Organisation.get(organisation_id)

        if org is None:
            raise NotFound()
        user = UserService.get_user_by_id(user_id)

        return user in org.managers
Пример #12
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,
        }
    def create_draft_project(draft_project_dto: DraftProjectDTO) -> int:
        """
        Validates and then persists draft projects in the DB
        :param draft_project_dto: Draft Project DTO with data from API
        :raises InvalidGeoJson
        :returns ID of new draft project
        """
        user_id = draft_project_dto.user_id
        is_admin = UserService.is_user_an_admin(user_id)
        user_orgs = OrganisationService.get_organisations_managed_by_user_as_dto(
            user_id)
        is_org_manager = len(user_orgs.organisations) > 0

        # First things first, we need to validate that the author_id is a PM. issue #1715
        if not (is_admin or is_org_manager):
            user = UserService.get_user_by_id(user_id)
            raise (ProjectAdminServiceError(
                f"User {user.username} is not permitted to create project"))

        # If we're cloning we'll copy all the project details from the clone, otherwise create brand new project
        if draft_project_dto.cloneFromProjectId:
            draft_project = Project.clone(draft_project_dto.cloneFromProjectId,
                                          user_id)
        else:
            draft_project = Project()
            org = OrganisationService.get_organisation_by_id(
                draft_project_dto.organisation)
            if org is None:
                raise NotFound("Organisation does not exist")
            draft_project_dto.organisation = org
            draft_project.create_draft_project(draft_project_dto)

        draft_project.set_project_aoi(draft_project_dto)

        # if arbitrary_tasks requested, create tasks from aoi otherwise use tasks in DTO
        if draft_project_dto.has_arbitrary_tasks:
            tasks = GridService.tasks_from_aoi_features(
                draft_project_dto.area_of_interest)
            draft_project.task_creation_mode = TaskCreationMode.ARBITRARY.value
        else:
            tasks = draft_project_dto.tasks
        ProjectAdminService._attach_tasks_to_project(draft_project, tasks)

        if draft_project_dto.cloneFromProjectId:
            draft_project.save()  # Update the clone
        else:
            draft_project.create()  # Create the new project

        draft_project.set_default_changeset_comment()
        draft_project.set_country_info()
        return draft_project.id
Пример #14
0
    def send_message_after_validation(
        status: int, validated_by: int, mapped_by: int, task_id: int, project_id: int
    ):
        """ Sends mapper a notification after their task has been marked valid or invalid """
        if validated_by == mapped_by:
            return  # No need to send a notification if you've verified your own task

        user = UserService.get_user_by_id(mapped_by)
        text_template = get_txt_template(
            "invalidation_message_en.txt"
            if status == TaskStatus.INVALIDATED
            else "validation_message_en.txt"
        )
        status_text = (
            "marked invalid" if status == TaskStatus.INVALIDATED else "validated"
        )
        task_link = MessageService.get_task_link(project_id, task_id)
        replace_list = [
            ["[USERNAME]", user.username],
            ["[TASK_LINK]", task_link],
            ["[ORG_NAME]", current_app.config["ORG_NAME"]],
        ]
        text_template = template_var_replacing(text_template, replace_list)

        messages = []
        validation_message = Message()
        validation_message.message_type = (
            MessageType.INVALIDATION_NOTIFICATION.value
            if status == TaskStatus.INVALIDATED
            else MessageType.VALIDATION_NOTIFICATION.value
        )
        validation_message.project_id = project_id
        validation_message.task_id = task_id
        validation_message.from_user_id = validated_by
        validation_message.to_user_id = mapped_by
        validation_message.subject = (
            f"{task_link} mapped by you in Project {project_id} has been {status_text}"
        )
        validation_message.message = text_template
        messages.append(dict(message=validation_message, user=user))

        # For email alerts
        MessageService._push_messages(messages)
Пример #15
0
    def send_message_after_validation(
        status: int, validated_by: int, mapped_by: int, task_id: int, project_id: int
    ):
        """ Sends mapper a notification after their task has been marked valid or invalid """
        if validated_by == mapped_by:
            return  # No need to send a message to yourself

        user = UserService.get_user_by_id(mapped_by)
        if user.validation_message is False:
            return  # No need to send validation message
        if user.projects_notifications is False:
            return

        text_template = get_template(
            "invalidation_message_en.txt"
            if status == TaskStatus.INVALIDATED
            else "validation_message_en.txt"
        )
        status_text = (
            "marked invalid" if status == TaskStatus.INVALIDATED else "validated"
        )
        task_link = MessageService.get_task_link(project_id, task_id)
        text_template = text_template.replace("[USERNAME]", user.username)
        text_template = text_template.replace("[TASK_LINK]", task_link)

        validation_message = Message()
        validation_message.message_type = (
            MessageType.INVALIDATION_NOTIFICATION.value
            if status == TaskStatus.INVALIDATED
            else MessageType.VALIDATION_NOTIFICATION.value
        )
        validation_message.project_id = project_id
        validation_message.task_id = task_id
        validation_message.from_user_id = validated_by
        validation_message.to_user_id = mapped_by
        validation_message.subject = f"Your mapping in Project {project_id} on {task_link} has just been {status_text}"
        validation_message.message = text_template
        validation_message.add_message()

        SMTPService.send_email_alert(
            user.email_address, user.username, validation_message.id
        )
Пример #16
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)
Пример #17
0
    def send_message_after_comment(comment_from: int, comment: str,
                                   task_id: int, project_id: int):
        """ Will send a canned message to anyone @'d in a comment """
        usernames = MessageService._parse_message_for_username(
            comment, project_id)
        if len(usernames) != 0:
            task_link = MessageService.get_task_link(project_id, task_id)

            messages = []
            for username in usernames:
                try:
                    user = UserService.get_user_by_username(username)
                except NotFound:
                    continue  # If we can't find the user, keep going no need to fail

                message = Message()
                message.message_type = MessageType.MENTION_NOTIFICATION.value
                message.project_id = project_id
                message.task_id = task_id
                message.from_user_id = comment_from
                message.to_user_id = user.id
                message.subject = f"You were mentioned in a comment in {task_link} of Project {project_id}"
                message.message = comment
                messages.append(dict(message=message, user=user))

            MessageService._push_messages(messages)

        # Notify all contributors except the user that created the comment.
        results = (TaskHistory.query.with_entities(
            TaskHistory.user_id.distinct()
        ).filter(TaskHistory.project_id == project_id).filter(
            TaskHistory.task_id == task_id).filter(
                TaskHistory.user_id != comment_from).filter(
                    TaskHistory.action == TaskAction.STATE_CHANGE.name).all())
        contributed_users = [r[0] for r in results]

        if len(contributed_users) != 0:
            user_from = User.query.get(comment_from)
            if user_from is None:
                raise ValueError("Username not found")
            user_link = MessageService.get_user_link(user_from.username)

            task_link = MessageService.get_task_link(project_id, task_id)
            messages = []
            for user_id in contributed_users:
                try:
                    user = UserService.get_user_by_id(user_id)
                    # if user was mentioned, a message has already been sent to them,
                    # so we can skip
                    if user.username in usernames:
                        break
                except NotFound:
                    continue  # If we can't find the user, keep going no need to fail

                message = Message()
                message.message_type = MessageType.TASK_COMMENT_NOTIFICATION.value
                message.project_id = project_id
                message.from_user_id = comment_from
                message.task_id = task_id
                message.to_user_id = user.id
                message.subject = (
                    f"{user_link} left a comment in {task_link} of Project {project_id}"
                )
                message.message = comment
                messages.append(dict(message=message, user=user))

            MessageService._push_messages(messages)
    def _filter_projects(search_dto: ProjectSearchDTO, user):
        """ Filters all projects based on criteria provided by user"""
        query = ProjectSearchService.create_search_query(user)
        query = query.join(ProjectInfo).filter(
            ProjectInfo.locale.in_([search_dto.preferred_locale, "en"]))
        project_status_array = []
        if search_dto.project_statuses:
            for project_status in search_dto.project_statuses:
                project_status_array.append(
                    ProjectStatus[project_status].value)
            query = query.filter(Project.status.in_(project_status_array))
        else:
            if not search_dto.created_by:
                project_status_array = [ProjectStatus.PUBLISHED.value]
                query = query.filter(Project.status.in_(project_status_array))
        if search_dto.interests:
            query = query.join(
                project_interests,
                project_interests.c.project_id == Project.id).filter(
                    project_interests.c.interest_id.in_(search_dto.interests))
        if search_dto.created_by:
            query = query.filter(Project.author_id == search_dto.created_by)
        if search_dto.mapped_by:
            projects_mapped = UserService.get_projects_mapped(
                search_dto.mapped_by)
            query = query.filter(Project.id.in_(projects_mapped))
        if search_dto.favorited_by:
            user = UserService.get_user_by_id(search_dto.favorited_by)
            projects_favorited = user.favorites
            query = query.filter(
                Project.id.in_([project.id for project in projects_favorited]))
        if search_dto.mapper_level and search_dto.mapper_level.upper(
        ) != "ALL":
            query = query.filter(Project.mapper_level == MappingLevel[
                search_dto.mapper_level].value)

        if search_dto.organisation_name:
            query = query.filter(
                Organisation.name == search_dto.organisation_name)

        if search_dto.organisation_id:
            query = query.filter(Organisation.id == search_dto.organisation_id)

        if search_dto.team_id:
            query = query.join(ProjectTeams,
                               ProjectTeams.project_id == Project.id).filter(
                                   ProjectTeams.team_id == search_dto.team_id)

        if search_dto.campaign:
            query = query.join(Campaign, Project.campaign).group_by(
                Project.id, Campaign.name)
            query = query.filter(Campaign.name == search_dto.campaign)

        if search_dto.mapping_types:
            # Construct array of mapping types for query
            mapping_type_array = []
            for mapping_type in search_dto.mapping_types:
                mapping_type_array.append(MappingTypes[mapping_type].value)

            query = query.filter(
                Project.mapping_types.contains(mapping_type_array))

        if search_dto.text_search:
            # We construct an OR search, so any projects that contain or more of the search terms should be returned
            or_search = " | ".join(
                [x for x in search_dto.text_search.split(" ") if x != ""])
            opts = [
                ProjectInfo.text_searchable.match(
                    or_search, postgresql_regconfig="english"),
                ProjectInfo.name.like(f"%{or_search}%"),
            ]
            try:
                opts.append(Project.id == int(search_dto.text_search))
            except ValueError:
                pass

            query = query.filter(or_(*opts))

        if search_dto.country:
            # Unnest country column array.
            sq = Project.query.with_entities(
                Project.id,
                func.unnest(Project.country).label("country")).subquery()
            query = query.filter(
                sq.c.country.ilike("%{}%".format(
                    search_dto.country))).filter(Project.id == sq.c.id)

        order_by = search_dto.order_by
        if search_dto.order_by_type == "DESC":
            order_by = desc(search_dto.order_by)

        query = query.order_by(order_by).group_by(Project.id)

        if search_dto.managed_by and user.role != UserRole.ADMIN.value:
            team_projects = query.join(ProjectTeams).filter(
                ProjectTeams.role == TeamRoles.PROJECT_MANAGER.value,
                ProjectTeams.project_id == Project.id,
            )
            user_orgs_list = OrganisationService.get_organisations_managed_by_user(
                search_dto.managed_by)
            orgs_managed = [org.id for org in user_orgs_list]
            org_projects = query.filter(
                Project.organisation_id.in_(orgs_managed))
            query = org_projects.union(team_projects)

        all_results = []
        if not search_dto.omit_map_results:
            all_results = query.all()
        paginated_results = query.paginate(search_dto.page, 14, True)

        return all_results, paginated_results
Пример #19
0
    def get_all_teams(
        user_id: int = None,
        team_name_filter: str = None,
        team_role_filter: str = None,
        member_filter: int = None,
        member_request_filter: int = None,
        manager_filter: int = None,
        organisation_filter: int = None,
    ) -> TeamsListDTO:

        query = db.session.query(Team).outerjoin(TeamMembers).outerjoin(
            ProjectTeams)

        orgs_query = None
        is_admin = UserService.is_user_an_admin(user_id)

        if organisation_filter:
            orgs_query = query.filter(
                Team.organisation_id.in_(organisation_filter))

        if manager_filter and not (manager_filter == user_id and is_admin):
            query = query.filter(
                TeamMembers.user_id == manager_filter,
                TeamMembers.active == True,  # noqa
                TeamMembers.function == TeamMemberFunctions.MANAGER.value,
            )

        if team_name_filter:
            query = query.filter(Team.name.contains(team_name_filter))

        if team_role_filter:
            try:
                role = TeamRoles[team_role_filter.upper()].value
                query = query.filter(ProjectTeams.role == role)
            except KeyError:
                pass

        if member_filter:
            query = query.filter(
                TeamMembers.user_id == member_filter,
                TeamMembers.active == True  # noqa
            )

        if member_request_filter:
            query = query.filter(
                TeamMembers.user_id == member_request_filter,
                TeamMembers.active == False,  # noqa
            )

        if orgs_query:
            query = query.union(orgs_query)

        teams_list_dto = TeamsListDTO()

        for team in query.all():
            team_dto = TeamDTO()
            team_dto.team_id = team.id
            team_dto.name = team.name
            team_dto.invite_only = team.invite_only
            team_dto.visibility = TeamVisibility(team.visibility).name
            team_dto.description = team.description
            team_dto.logo = team.organisation.logo
            team_dto.organisation = team.organisation.name
            team_dto.organisation_id = team.organisation.id
            team_dto.members = []
            team_members = TeamService._get_team_members(team.id)
            is_team_manager = False
            is_team_member = False
            for member in team_members:
                user = UserService.get_user_by_id(member.user_id)
                member_dto = TeamMembersDTO()
                member_dto.username = user.username
                member_dto.function = TeamMemberFunctions(member.function).name
                if member.user_id == user_id:
                    is_team_member = True
                    if member_dto.function == "MANAGER":
                        is_team_manager = True
                member_dto.picture_url = user.picture_url
                member_dto.active = member.active

                team_dto.members.append(member_dto)
            if team_dto.visibility == "PRIVATE" and not is_admin:
                if is_team_manager or is_team_member:
                    teams_list_dto.teams.append(team_dto)
            else:
                teams_list_dto.teams.append(team_dto)

        return teams_list_dto
Пример #20
0
 def get(self):
     """
     List and search for projects
     ---
     tags:
         - projects
     produces:
         - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           type: string
           default: Token sessionTokenHere==
         - in: header
           name: Accept-Language
           description: Language user is requesting
           type: string
           required: true
           default: en
         - in: query
           name: mapperLevel
           type: string
         - in: query
           name: orderBy
           type: string
           default: priority
           enum: [id,mapper_level,priority,status,last_updated,due_date]
         - in: query
           name: orderByType
           type: string
           default: ASC
           enum: [ASC, DESC]
         - in: query
           name: mappingTypes
           type: string
         - in: query
           name: mappingTypesExact
           type: boolean
           default: false
           description: if true, limits projects to match the exact mapping types requested
         - in: query
           name: organisationName
           description: Organisation name to search for
           type: string
         - in: query
           name: organisationId
           description: Organisation ID to search for
           type: integer
         - in: query
           name: campaign
           description: Campaign name to search for
           type: string
         - in: query
           name: page
           description: Page of results user requested
           type: integer
           default: 1
         - in: query
           name: textSearch
           description: Text to search
           type: string
         - in: query
           name: country
           description: Project country
           type: string
         - in: query
           name: action
           description: Filter projects by possible actions
           enum: [map, validate, any]
           type: string
         - in: query
           name: projectStatuses
           description: Authenticated PMs can search for archived or draft statuses
           type: string
         - in: query
           name: interests
           type: string
           description: Filter by interest on project
           default: null
         - in: query
           name: createdByMe
           description: Limit to projects created by the authenticated user
           type: boolean
           default: false
         - in: query
           name: mappedByMe
           description: Limit to projects mapped/validated by the authenticated user
           type: boolean
           default: false
         - in: query
           name: favoritedByMe
           description: Limit to projects favorited by the authenticated user
           type: boolean
           default: false
         - in: query
           name: managedByMe
           description:
             Limit to projects that can be managed by the authenticated user,
             excluding the ones created by them
           type: boolean
           default: false
         - in: query
           name: teamId
           type: string
           description: Filter by team on project
           default: null
           name: omitMapResults
           type: boolean
           description: If true, it will not return the project centroid's geometries.
           default: false
     responses:
         200:
             description: Projects found
         404:
             description: No projects found
         500:
             description: Internal Server Error
     """
     try:
         user = None
         user_id = token_auth.current_user()
         if user_id:
             user = UserService.get_user_by_id(user_id)
         search_dto = self.setup_search_dto()
         results_dto = ProjectSearchService.search_projects(search_dto, user)
         return results_dto.to_primitive(), 200
     except NotFound:
         return {"mapResults": {}, "results": []}, 200
     except Exception as e:
         error_msg = f"Project GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": "Unable to fetch projects"}, 500
Пример #21
0
    def get(self, user_id):
        """
        Get a list of tasks a user has interacted with
        ---
        tags:
          - users
        produces:
          - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              required: true
              type: string
              default: Token sessionTokenHere==
            - name: user_id
              in: path
              description: Mapper's OpenStreetMap ID
              required: true
              type: integer
            - in: query
              name: status
              description: Task Status filter
              required: false
              type: string
              default: null
            - in: query
              name: project_status
              description: Project Status filter
              required: false
              type: string
              default: null
            - in: query
              name: project_id
              description: Project id
              required: false
              type: integer
              default: null
            - in: query
              name: start_date
              description: Date to filter as minimum
              required: false
              type: string
              default: null
            - in: query
              name: end_date
              description: Date to filter as maximum
              required: false
              type: string
              default: null
            - in: query
              name: sort_by
              description:
                    criteria to sort by. The supported options are action_date, -action_date.
                    The default value is -action_date.
              required: false
              type: string
            - in: query
              name: page
              description: Page of results user requested
              type: integer
            - in: query
              name: page_size
              description: Size of page, defaults to 10
              type: integer
        responses:
            200:
                description: Mapped projects found
            404:
                description: No mapped projects found
            500:
                description: Internal Server Error
        """
        try:
            user = UserService.get_user_by_id(user_id)
            status = request.args.get("status")
            project_status = request.args.get("project_status")
            project_id = int(request.args.get("project_id", 0))
            start_date = (
                date_parse(request.args.get("start_date"))
                if request.args.get("start_date")
                else None
            )
            end_date = (
                date_parse(request.args.get("end_date"))
                if request.args.get("end_date")
                else None
            )
            sort_by = request.args.get("sort_by", "-action_date")

            tasks = UserService.get_tasks_dto(
                user.id,
                project_id=project_id,
                project_status=project_status,
                task_status=status,
                start_date=start_date,
                end_date=end_date,
                page=request.args.get("page", None, type=int),
                page_size=request.args.get("page_size", 10, type=int),
                sort_by=sort_by,
            )
            return tasks.to_primitive(), 200
        except ValueError:
            return {"tasks": [], "pagination": {"total": 0}}, 200
        except NotFound:
            return {"Error": "User or tasks not found"}, 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
Пример #22
0
    def delete(self, project_id):
        """
        Delete a list of tasks from a project
        ---
        tags:
            - tasks
        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: Project ID the task is associated with
              required: true
              type: integer
              default: 1
            - in: body
              name: body
              required: true
              description: JSON object with a list of tasks to delete
              schema:
                  properties:
                      tasks:
                          type: array
                          items:
                              type: integer
                          default: [ 1, 2 ]
        responses:
            200:
                description: Task(s) deleted
            400:
                description: Bad request
            403:
                description: Forbidden
            404:
                description: Project or Task Not Found
            500:
                description: Internal Server Error
        """
        user_id = token_auth.current_user()
        user = UserService.get_user_by_id(user_id)
        if user.role != UserRole.ADMIN.value:
            return {"Error": "This endpoint action is restricted to ADMIN users."}, 403

        tasks_ids = request.get_json().get("tasks")
        if tasks_ids is None:
            return {"Error": "Tasks ids not provided"}, 400
        if type(tasks_ids) != list:
            return {"Error": "Tasks were not provided as a list"}, 400

        try:
            ProjectService.delete_tasks(project_id, tasks_ids)
            return {"Success": "Task(s) deleted"}, 200
        except NotFound as e:
            return {"Error": f"Project or Task Not Found: {e}"}, 404
        except ProjectServiceError as e:
            return {"Error": str(e)}, 403
        except Exception as e:
            current_app.logger.critical(e)
            return {"Error": "Unable to delete tasks"}, 500
Пример #23
0
 def resend_email_validation(user_id: int):
     """ Resends the email validation email to the logged in user """
     user = UserService.get_user_by_id(user_id)
     SMTPService.send_verification_email(user.email_address, user.username)
Пример #24
0
    def send_message_after_chat(chat_from: int, chat: str, project_id: int):
        """ Send alert to user if they were @'d in a chat message """
        # Because message-all run on background thread it needs it's own app context
        app = create_app()
        with app.app_context():
            usernames = MessageService._parse_message_for_username(
                chat, project_id)
            if len(usernames) != 0:
                link = MessageService.get_project_link(
                    project_id, include_chat_section=True)
                messages = []
                for username in usernames:
                    current_app.logger.debug(f"Searching for {username}")
                    try:
                        user = UserService.get_user_by_username(username)
                    except NotFound:
                        current_app.logger.error(
                            f"Username {username} not found")
                        continue  # If we can't find the user, keep going no need to fail

                    message = Message()
                    message.message_type = MessageType.MENTION_NOTIFICATION.value
                    message.project_id = project_id
                    message.from_user_id = chat_from
                    message.to_user_id = user.id
                    message.subject = f"You were mentioned in {link} chat"
                    message.message = chat
                    messages.append(dict(message=message, user=user))

                MessageService._push_messages(messages)

            query = """ select user_id from project_favorites where project_id = :project_id"""
            favorited_users_results = db.engine.execute(text(query),
                                                        project_id=project_id)
            favorited_users = [r[0] for r in favorited_users_results]

            # Notify all contributors except the user that created the comment.
            contributed_users_results = (TaskHistory.query.with_entities(
                TaskHistory.user_id.distinct()
            ).filter(TaskHistory.project_id == project_id).filter(
                TaskHistory.user_id != chat_from).filter(
                    TaskHistory.action == TaskAction.STATE_CHANGE.name).all())
            contributed_users = [r[0] for r in contributed_users_results]

            users_to_notify = list(set(contributed_users + favorited_users))

            if len(users_to_notify) != 0:
                from_user = User.query.get(chat_from)
                from_user_link = MessageService.get_user_link(
                    from_user.username)
                project_link = MessageService.get_project_link(
                    project_id, include_chat_section=True)
                messages = []
                for user_id in users_to_notify:
                    try:
                        user = UserService.get_user_by_id(user_id)
                    except NotFound:
                        continue  # If we can't find the user, keep going no need to fail
                    message = Message()
                    message.message_type = MessageType.PROJECT_CHAT_NOTIFICATION.value
                    message.project_id = project_id
                    message.from_user_id = chat_from
                    message.to_user_id = user.id
                    message.subject = (
                        f"{from_user_link} left a comment in {project_link}")
                    message.message = chat
                    messages.append(dict(message=message, user=user))

                # it's important to keep that line inside the if to avoid duplicated emails
                MessageService._push_messages(messages)