def generate_gpx(project_id: int, task_ids_str: str, timestamp=None):
        """
        Creates a GPX file for supplied tasks.  Timestamp is for unit testing only.  You can use the following URL to test locally:
        http://www.openstreetmap.org/edit?editor=id&#map=11/31.50362930069913/34.628906243797054&comment=CHANGSET_COMMENT&gpx=http://localhost:5000/api/v1/project/111/tasks_as_gpx%3Ftasks=2
        """
        if timestamp is None:
            timestamp = datetime.datetime.utcnow()

        root = ET.Element('gpx',
                          attrib=dict(
                              xmlns='http://www.topografix.com/GPX/1/1',
                              version='1.1',
                              creator='HOT Tasking Manager'))

        # Create GPX Metadata element
        metadata = ET.Element('metadata')
        link = ET.SubElement(
            metadata,
            'link',
            attrib=dict(href='https://github.com/hotosm/tasking-manager'))
        ET.SubElement(link, 'text').text = 'HOT Tasking Manager'
        ET.SubElement(metadata, 'time').text = timestamp.isoformat()
        root.append(metadata)

        # Create trk element
        trk = ET.Element('trk')
        root.append(trk)
        ET.SubElement(
            trk, 'name'
        ).text = f'Task for project {project_id}. Do not edit outside of this area!'

        # Construct trkseg elements
        if task_ids_str is not None:
            task_ids = map(int, task_ids_str.split(','))
            tasks = Task.get_tasks(project_id, task_ids)
            if not tasks or len(tasks) == 0:
                raise NotFound()
        else:
            tasks = Task.get_all_tasks(project_id)
            if not tasks or len(tasks) == 0:
                raise NotFound()

        for task in tasks:
            task_geom = shape.to_shape(task.geometry)
            for poly in task_geom:
                trkseg = ET.SubElement(trk, 'trkseg')
                for point in poly.exterior.coords:
                    ET.SubElement(trkseg,
                                  'trkpt',
                                  attrib=dict(lon=str(point[0]),
                                              lat=str(point[1])))

                    # Append wpt elements to end of doc
                    wpt = ET.Element('wpt',
                                     attrib=dict(lon=str(point[0]),
                                                 lat=str(point[1])))
                    root.append(wpt)

        xml_gpx = ET.tostring(root, encoding='utf8')
        return xml_gpx
    def generate_osm_xml(project_id: int, task_ids_str: str) -> str:
        """ Generate xml response suitable for loading into JOSM.  A sample output file is in
            /server/helpers/testfiles/osm-sample.xml """
        # Note XML created with upload No to ensure it will be rejected by OSM if uploaded by mistake
        root = ET.Element('osm', attrib=dict(version='0.6', upload='never', creator='HOT Tasking Manager'))

        if task_ids_str:
            task_ids = map(int, task_ids_str.split(','))
            tasks = Task.get_tasks(project_id, task_ids)
            if not tasks or tasks.count() == 0:
                raise NotFound()
        else:
            tasks = Task.get_all_tasks(project_id)
            if not tasks or len(tasks) == 0:
                raise NotFound()

        fake_id = -1  # We use fake-ids to ensure XML will not be validated by OSM
        for task in tasks:
            task_geom = shape.to_shape(task.geometry)
            way = ET.SubElement(root, 'way', attrib=dict(id=str((task.id * -1)), action='modify', visible='true'))
            for poly in task_geom:
                for point in poly.exterior.coords:
                    ET.SubElement(root, 'node', attrib=dict(action='modify', visible='true', id=str(fake_id),
                                                            lon=str(point[0]), lat=str(point[1])))
                    ET.SubElement(way, 'nd', attrib=dict(ref=str(fake_id)))
                    fake_id -= 1

        xml_gpx = ET.tostring(root, encoding='utf8')
        return xml_gpx
Esempio n. 3
0
    def get_user_contributions(project_id: int) -> ProjectContributionsDTO:
        """ Get all user contributions on a project"""
        contrib_query = '''select m.mapped_by, m.username, m.mapped, v.validated_by, v.username, v.validated
                             from (select t.mapped_by, u.username, count(t.mapped_by) mapped
                                     from tasks t,
                                          users u
                                    where t.mapped_by = u.id
                                      and t.project_id = {0}
                                      and t.mapped_by is not null
                                    group by t.mapped_by, u.username) m FULL OUTER JOIN
                                  (select t.validated_by, u.username, count(t.validated_by) validated
                                     from tasks t,
                                          users u
                                    where t.validated_by = u.id
                                      and t.project_id = {0}
                                      and t.validated_by is not null
                                    group by t.validated_by, u.username) v
                                       ON m.mapped_by = v.validated_by
        '''.format(project_id)

        results = db.engine.execute(contrib_query)
        if results.rowcount == 0:
            raise NotFound()

        contrib_dto = ProjectContributionsDTO()
        for row in results:
            user_contrib = UserContribution()
            user_contrib.username = row[1] if row[1] else row[4]
            user_contrib.mapped = row[2] if row[2] else 0
            user_contrib.validated = row[5] if row[5] else 0

            contrib_dto.user_contributions.append(user_contrib)

        return contrib_dto
    def get_project_by_id(project_id: int) -> Project:
        project = Project.get(project_id)

        if project is None:
            raise NotFound()

        return project
Esempio n. 5
0
    def get_mapped_tasks_by_user(project_id: int):
        """ Gets all mapped tasks for supplied project grouped by user"""

        # Raw SQL is easier to understand that SQL alchemy here :)
        sql = """select u.username, u.mapping_level, count(distinct(t.id)), json_agg(distinct(t.id)),
                            max(th.action_date) last_seen, u.date_registered, u.last_validation_date
                      from tasks t,
                           task_history th,
                           users u
                     where t.project_id = th.project_id
                       and t.id = th.task_id
                       and t.mapped_by = u.id
                       and t.project_id = {0}
                       and t.task_status = 2
                       and th.action_text = 'MAPPED'
                     group by u.username, u.mapping_level, u.date_registered, u.last_validation_date""".format(project_id)

        results = db.engine.execute(sql)
        if results.rowcount == 0:
            raise NotFound()

        mapped_tasks_dto = MappedTasks()
        for row in results:
            user_mapped = MappedTasksByUser()
            user_mapped.username = row[0]
            user_mapped.mapping_level = MappingLevel(row[1]).name
            user_mapped.mapped_task_count = row[2]
            user_mapped.tasks_mapped = row[3]
            user_mapped.last_seen = row[4]
            user_mapped.date_registered = row[5]
            user_mapped.last_validation_date = row[6]

            mapped_tasks_dto.mapped_tasks.append(user_mapped)

        return mapped_tasks_dto
Esempio n. 6
0
    def filter_users(user_filter: str, project_id: int,
                     page: int) -> UserFilterDTO:
        """ Finds users that matches first characters, for auto-complete.

        Users who have participated (mapped or validated) in the project, if given, will be
        returned ahead of those who have not.
        """
        # Note that the projects_mapped column includes both mapped and validated projects.
        results = db.session.query(User.username, User.projects_mapped.any(project_id).label("participant")) \
            .filter(User.username.ilike(user_filter.lower() + '%')) \
            .order_by(desc("participant").nullslast(), User.username).paginate(page, 20, True)
        if results.total == 0:
            raise NotFound()

        dto = UserFilterDTO()
        for result in results.items:
            dto.usernames.append(result.username)
            if project_id is not None:
                participant = ProjectParticipantUser()
                participant.username = result.username
                participant.project_id = project_id
                participant.is_participant = bool(result.participant)
                dto.users.append(participant)

        dto.pagination = Pagination(results)
        return dto
Esempio n. 7
0
    def get_mapped_projects(user_id: int, preferred_locale: str) -> UserMappedProjectsDTO:
        """ Get all projects a user has mapped on """
        sql = '''select p.id, p.status, p.default_locale, count(t.mapped_by), count(t.validated_by), st_asgeojson(p.centroid),
                        st_asgeojson(p.geometry)
                   from projects p,
                        tasks t
                  where p.id in (select unnest(projects_mapped) from users where id = {0})
                    and p.id = t.project_id
                    and (t.mapped_by = {0} or t.mapped_by is null)
                    and (t.validated_by = {0} or t.validated_by is null)
               GROUP BY p.id, p.status, p.centroid, p.geometry'''.format(user_id)

        results = db.engine.execute(sql)

        if results.rowcount == 0:
            raise NotFound()

        mapped_projects_dto = UserMappedProjectsDTO()
        for row in results:
            mapped_project = MappedProject()
            mapped_project.project_id = row[0]
            mapped_project.status = ProjectStatus(row[1]).name
            mapped_project.tasks_mapped = row[3]
            mapped_project.tasks_validated = row[4]
            mapped_project.centroid = geojson.loads(row[5])
            mapped_project.aoi = geojson.loads(row[6])

            project_info = ProjectInfo.get_dto_for_locale(row[0], preferred_locale, row[2])
            mapped_project.name = project_info.name

            mapped_projects_dto.mapped_projects.append(mapped_project)

        return mapped_projects_dto
    def get_user_by_id(user_id: int) -> User:
        user = User().get_by_id(user_id)

        if user is None:
            raise NotFound()

        return user
Esempio n. 9
0
    def get_token(token: str):
        application = Application.get_token(token)

        if application is None:
            raise NotFound()

        return application
Esempio n. 10
0
    def get_projects_for_admin(admin_id: int, preferred_locale: str) -> PMDashboardDTO:
        """ Get projects for admin """
        admins_projects = db.session.query(Project.id,
                                           Project.status,
                                           Project.campaign_tag,
                                           Project.total_tasks,
                                           Project.tasks_mapped,
                                           Project.tasks_validated,
                                           Project.tasks_bad_imagery,
                                           Project.created,
                                           Project.last_updated,
                                           Project.default_locale,
                                           AreaOfInterest.centroid.ST_AsGeoJSON().label('geojson'))\
            .join(AreaOfInterest).filter(Project.author_id == admin_id).all()

        if admins_projects is None:
            raise NotFound('No projects found for admin')

        admin_projects_dto = PMDashboardDTO()
        for project in admins_projects:
            pm_project = Project.get_project_summary(project, preferred_locale)
            project_status = ProjectStatus(project.status)

            if project_status == ProjectStatus.DRAFT:
                admin_projects_dto.draft_projects.append(pm_project)
            elif project_status == ProjectStatus.PUBLISHED:
                admin_projects_dto.active_projects.append(pm_project)
            elif project_status == ProjectStatus.ARCHIVED:
                admin_projects_dto.archived_projects.append(pm_project)
            else:
                current_app.logger.error(f'Unexpected state project {project.id}')

        return admin_projects_dto
Esempio n. 11
0
    def get_latest_activity(project_id: int, page: int) -> ProjectActivityDTO:
        """ Gets all the activity on a project """

        results = db.session.query(
                TaskHistory.id, TaskHistory.task_id, TaskHistory.action, TaskHistory.action_date,
                TaskHistory.action_text, User.username
            ).join(User).filter(
                TaskHistory.project_id == project_id,
                TaskHistory.action != 'COMMENT'
            ).order_by(
                TaskHistory.action_date.desc()
            ).paginate(page, 10, True)

        if results.total == 0:
            raise NotFound()

        activity_dto = ProjectActivityDTO()
        for item in results.items:
            history = TaskHistoryDTO()
            history.history_id = item.id
            history.task_id = item.task_id
            history.action = item.action
            history.action_text = item.action_text
            history.action_date = item.action_date
            history.action_by = item.username
            activity_dto.activity.append(history)

        activity_dto.pagination = Pagination(results)
        return activity_dto
    def get_user_by_username(username: str) -> User:
        user = User().get_by_username(username)

        if user is None:
            raise NotFound()

        return user
Esempio n. 13
0
    def get_layer_by_id(layer_id: int) -> Layer:
        """ Returns a layer by ID """
        layer = Layer.get_by_id(layer_id)

        if layer is None:
            raise NotFound()

        return layer
Esempio n. 14
0
    def get_all_layers(locale: str = 'en') -> DMISLayersDTO:
        """ Returns a list of layers """
        layers = Layer.get_all_layers(locale)

        if layers is None:
            raise NotFound()

        return layers
Esempio n. 15
0
    def get_layer_dto_by_id(layer_id: int) -> LayerDetailsDTO:
        """ Returns a layer by ID """
        layer = Layer.get_by_id(layer_id)

        if layer is None:
            raise NotFound()

        return layer.as_dto()
Esempio n. 16
0
 def get_max_task_id_for_project(project_id: int):
     """Gets the nights task id currently in use on a project"""
     sql = """select max(id) from tasks where project_id = {0} GROUP BY project_id""".format(project_id)
     result = db.engine.execute(sql)
     if result.rowcount == 0:
         raise NotFound()
     for row in result:
         return row[0]
    def get_all_comments(project_id: int) -> ProjectCommentsDTO:
        """ Gets all comments mappers, validators have added to tasks associated with project """
        comments = TaskHistory.get_all_comments(project_id)

        if len(comments.comments) == 0:
            raise NotFound('No comments found on project')

        return comments
Esempio n. 18
0
    def get_user_contributions(project_id: int) -> ProjectContributionsDTO:
        """ Get all user contributions on a project"""
        contrib_query = '''select m.mapped_by, m.username, m.mapped, v.validated_by, v.username, v.validated
                             from (select t.mapped_by, u.username, count(t.mapped_by) mapped
                                     from tasks t,
                                          users u
                                    where t.mapped_by = u.id
                                      and t.project_id = {0}
                                      and t.mapped_by is not null
                                    group by t.mapped_by, u.username) m FULL OUTER JOIN
                                  (select t.validated_by, u.username, count(t.validated_by) validated
                                     from tasks t,
                                          users u
                                    where t.validated_by = u.id
                                      and t.project_id = {0}
                                      and t.validated_by is not null
                                    group by t.validated_by, u.username) v
                                       ON m.mapped_by = v.validated_by
        '''.format(project_id)

        results = db.engine.execute(contrib_query)
        if results.rowcount == 0:
            raise NotFound()

        contrib_dto = ProjectContributionsDTO()
        for row in results:
            if row[0]:
                user_contrib = UserContribution()
                user_contrib.username = row[1] if row[1] else row[4]
                user_contrib.mapped = row[2] if row[2] else 0
                user_contrib.validated = row[5] if row[5] else 0
                user_contrib.total_time_spent = 0
                user_contrib.time_spent_mapping = 0
                user_contrib.time_spent_validating = 0

                sql = """SELECT SUM(TO_TIMESTAMP(action_text, 'HH24:MI:SS')::TIME) FROM task_history
                        WHERE action='LOCKED_FOR_MAPPING'
                        and user_id = {0} and project_id = {1};""".format(row[0], project_id)
                total_mapping_time = db.engine.execute(sql)
                for time in total_mapping_time:
                    total_mapping_time = time[0]
                    if total_mapping_time:
                        user_contrib.time_spent_mapping = total_mapping_time.total_seconds()
                        user_contrib.total_time_spent += user_contrib.time_spent_mapping

                sql = """SELECT SUM(TO_TIMESTAMP(action_text, 'HH24:MI:SS')::TIME) FROM task_history
                        WHERE action='LOCKED_FOR_VALIDATION'
                        and user_id = {0} and project_id = {1};""".format(row[0], project_id)
                total_validation_time = db.engine.execute(sql)
                for time in total_validation_time:
                    total_validation_time = time[0]
                    if total_validation_time:
                        user_contrib.time_spent_validating = total_validation_time.total_seconds()
                        user_contrib.total_time_spent += user_contrib.time_spent_validating

                contrib_dto.user_contributions.append(user_contrib)

        return contrib_dto
Esempio n. 19
0
    def get_user_by_id(user_id: int) -> User:
        """ Returns user that matches ID """
        # TODO cache this
        user = User.get_by_id(user_id)

        if user is None:
            raise NotFound()

        return user
Esempio n. 20
0
    def get_mapped_projects(user_id: int,
                            preferred_locale: str) -> UserMappedProjectsDTO:
        """ Get all projects a user has mapped on """

        # This query looks scary, but we're really just creating an outer join between the query that gets the
        # counts of all mapped tasks and the query that gets counts of all validated tasks.  This is necessary to
        # handle cases where users have only validated tasks on a project, or only mapped on a project.
        sql = '''SELECT p.id,
                        p.status,
                        p.default_locale,
                        c.mapped,
                        c.validated,
                        st_asgeojson(p.centroid)
                   FROM projects p,
                        (SELECT coalesce(v.project_id, m.project_id) project_id,
                                coalesce(v.validated, 0) validated,
                                coalesce(m.mapped, 0) mapped
                          FROM (SELECT t.project_id,
                                       count (t.validated_by) validated
                                  FROM tasks t
                                 WHERE t.project_id IN (SELECT unnest(projects_mapped) FROM users WHERE id = {0})
                                   AND t.validated_by = {0}
                                 GROUP BY t.project_id, t.validated_by) v
                         FULL OUTER JOIN
                        (SELECT t.project_id,
                                count(t.mapped_by) mapped
                           FROM tasks t
                          WHERE t.project_id IN (SELECT unnest(projects_mapped) FROM users WHERE id = {0})
                            AND t.mapped_by = {0}
                          GROUP BY t.project_id, t.mapped_by) m
                         ON v.project_id = m.project_id) c
                   WHERE p.id = c.project_id ORDER BY p.id DESC'''.format(
            user_id)

        results = db.engine.execute(sql)

        if results.rowcount == 0:
            raise NotFound()

        mapped_projects_dto = UserMappedProjectsDTO()
        for row in results:
            mapped_project = MappedProject()
            mapped_project.project_id = row[0]
            mapped_project.status = ProjectStatus(row[1]).name
            mapped_project.tasks_mapped = row[3]
            mapped_project.tasks_validated = row[4]
            mapped_project.centroid = geojson.loads(row[5])

            project_info = ProjectInfo.get_dto_for_locale(
                row[0], preferred_locale, row[2])
            mapped_project.name = project_info.name

            mapped_projects_dto.mapped_projects.append(mapped_project)

        return mapped_projects_dto
    def search_projects(
            search_dto: ProjectSearchDTO) -> ProjectSearchResultsDTO:
        """ Searches all projects for matches to the criteria provided by the user """

        all_results, paginated_results = ProjectSearchService._filter_projects(
            search_dto)

        if paginated_results.total == 0:
            raise NotFound()

        features = []
        for project in all_results:
            # This loop creates a geojson feature collection so you can see all active projects on the map
            properties = {
                "projectId": project.id,
                "priority": ProjectPriority(project.priority).name
            }
            centroid = project.centroid
            feature = geojson.Feature(geometry=geojson.loads(project.centroid),
                                      properties=properties)
            features.append(feature)
        feature_collection = geojson.FeatureCollection(features)
        dto = ProjectSearchResultsDTO()
        dto.map_results = feature_collection

        for project in paginated_results.items:
            # This loop loads the paginated text results
            # TODO would be nice to get this for an array rather than individually would be more efficient
            project_info_dto = ProjectInfo.get_dto_for_locale(
                project.id, search_dto.preferred_locale,
                project.default_locale)

            list_dto = ListSearchResultDTO()
            list_dto.project_id = project.id
            list_dto.locale = project_info_dto.locale
            list_dto.name = project_info_dto.name
            list_dto.priority = ProjectPriority(project.priority).name
            list_dto.mapper_level = MappingLevel(project.mapper_level).name
            list_dto.short_description = project_info_dto.short_description
            list_dto.organisation_tag = project.organisation_tag
            list_dto.campaign_tag = project.campaign_tag
            list_dto.percent_mapped = Project.calculate_tasks_percent(
                'mapped', project.total_tasks, project.tasks_mapped,
                project.tasks_validated, project.tasks_bad_imagery)
            list_dto.percent_validated = Project.calculate_tasks_percent(
                'validated', project.total_tasks, project.tasks_mapped,
                project.tasks_validated, project.tasks_bad_imagery)
            list_dto.status = ProjectStatus(project.status).name
            list_dto.active_mappers = Project.get_active_mappers(project.id)

            dto.results.append(list_dto)

        dto.pagination = Pagination(paginated_results)
        return dto
Esempio n. 22
0
    def get_license(license_id: int) -> License:
        """
        Get task from DB
        :raises: NotFound
        """
        map_license = License.get_by_id(license_id)

        if map_license is None:
            raise NotFound()

        return map_license
    def get_task(task_id: int, project_id: int) -> Task:
        """
        Get task from DB
        :raises: NotFound
        """
        task = Task.get(task_id, project_id)

        if task is None:
            raise NotFound()

        return task
Esempio n. 24
0
    def get_task_for_logged_in_user(project_id: int, user_id: int):
        """ if the user is working on a task in the project return it """
        project = ProjectService.get_project_by_id(project_id)

        tasks = project.get_locked_tasks_for_user(user_id)

        if len(tasks) == 0:
            raise NotFound()

        tasks_dto = LockedTasksForUser()
        tasks_dto.locked_tasks = tasks
        return tasks_dto
Esempio n. 25
0
    def get_all_users():
        """ Return a list of all users """
        user_list = User.query.all()

        if len(user_list) == 0:
            raise NotFound()

        user_list_dto = UserListDTO()
        for user in user_list:
            user_list_dto.user_list.append(user.as_dto())

        return user_list_dto
Esempio n. 26
0
    def get_all_messages(user_id: int) -> MessagesDTO:
        """ Gets all messages to the user """
        user_messages = Message.query.filter(Message.to_user_id == user_id).all()

        if len(user_messages) == 0:
            raise NotFound()

        messages_dto = MessagesDTO()
        for message in user_messages:
            messages_dto.user_messages.append(message.as_dto())

        return messages_dto
Esempio n. 27
0
    def lock_tasks_for_validation(
            validation_dto: LockForValidationDTO) -> TaskDTOs:
        """
        Lock supplied tasks for validation
        :raises ValidatatorServiceError
        """
        # Loop supplied tasks to check they can all be locked for validation
        tasks_to_lock = []
        for task_id in validation_dto.task_ids:
            task = Task.get(task_id, validation_dto.project_id)

            if task is None:
                raise NotFound(f'Task {task_id} not found')

            if TaskStatus(task.task_status) not in [
                    TaskStatus.MAPPED, TaskStatus.VALIDATED,
                    TaskStatus.BADIMAGERY
            ]:
                raise ValidatatorServiceError(
                    f'Task {task_id} is not MAPPED, BADIMAGERY or VALIDATED')

            if not ValidatorService._user_can_validate_task(
                    validation_dto.user_id, task.mapped_by):
                raise ValidatatorServiceError(
                    f'Tasks cannot be validated by the same user who marked task as mapped or badimagery'
                )

            tasks_to_lock.append(task)

        user_can_validate, error_reason = ProjectService.is_user_permitted_to_validate(
            validation_dto.project_id, validation_dto.user_id)

        if not user_can_validate:
            if error_reason == ValidatingNotAllowed.USER_NOT_ACCEPTED_LICENSE:
                raise UserLicenseError(
                    'User must accept license to map this task')
            else:
                raise ValidatatorServiceError(
                    f'Mapping not allowed because: {error_reason.name}')

        # Lock all tasks for validation
        dtos = []
        for task in tasks_to_lock:
            task.lock_task_for_validating(validation_dto.user_id)
            dtos.append(
                task.as_dto_with_instructions(validation_dto.preferred_locale))

        task_dtos = TaskDTOs()
        task_dtos.tasks = dtos

        return task_dtos
Esempio n. 28
0
    def filter_users(user_filter: str, page: int) -> UserFilterDTO:
        """ Finds users that matches first characters, for auto-complete """
        results = db.session.query(User.username).filter(User.username.ilike(user_filter.lower() + '%')) \
            .order_by(User.username).paginate(page, 20, True)

        if results.total == 0:
            raise NotFound()

        dto = UserFilterDTO()
        for result in results.items:
            dto.usernames.append(result.username)

        dto.pagination = Pagination(results)
        return dto
    def get_all() -> LicenseListDTO:
        """ Gets all licenses currently stored """
        results = License.query.all()

        if len(results) == 0:
            raise NotFound()

        dto = LicenseListDTO()
        for result in results:
            imagery_license = LicenseDTO()
            imagery_license.license_id = result.id
            imagery_license.name = result.name
            imagery_license.description = result.description
            imagery_license.plain_text = result.plain_text
            dto.licenses.append(imagery_license)

        return dto
    def get_task_details_for_logged_in_user(project_id: int, user_id: int, preferred_locale: str):
        """ if the user is working on a task in the project return it """
        project = ProjectService.get_project_by_id(project_id)

        tasks = project.get_locked_tasks_details_for_user(user_id)

        if len(tasks) == 0:
            raise NotFound()

        # TODO put the task details in to a DTO
        dtos = []
        for task in tasks:
            dtos.append(task.as_dto_with_instructions(preferred_locale))

        task_dtos = TaskDTOs()
        task_dtos.tasks = dtos

        return task_dtos