Esempio n. 1
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
Esempio n. 2
0
    def get_project_summary(self, preferred_locale) -> ProjectSummary:
        """ Create Project Summary model for postgis project object"""
        summary = ProjectSummary()
        summary.project_id = self.id
        summary.campaign_tag = self.campaign_tag
        summary.created = self.created
        summary.last_updated = self.last_updated
        summary.mapper_level = MappingLevel(self.mapper_level).name
        summary.organisation_tag = self.organisation_tag
        summary.status = ProjectStatus(self.status).name

        centroid_geojson = db.session.scalar(self.centroid.ST_AsGeoJSON())
        summary.aoi_centroid = geojson.loads(centroid_geojson)

        summary.percent_mapped = int(
            (self.tasks_mapped /
             (self.total_tasks - self.tasks_bad_imagery)) * 100)
        summary.percent_validated = int(
            ((self.tasks_validated + self.tasks_bad_imagery) /
             self.total_tasks) * 100)

        project_info = ProjectInfo.get_dto_for_locale(self.id,
                                                      preferred_locale,
                                                      self.default_locale)
        summary.name = project_info.name
        summary.short_description = project_info.short_description

        return summary
Esempio n. 3
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. 4
0
    def get_project_summary(self, preferred_locale) -> ProjectSummary:
        """ Create Project Summary model for postgis project object"""
        summary = ProjectSummary()
        summary.project_id = self.id
        priority = self.priority
        if priority == 0:
            summary.priority = 'URGENT'
        elif priority == 1:
            summary.priority = 'HIGH'
        elif priority == 2:
            summary.priority = 'MEDIUM'
        else:
            summary.priority = 'LOW'
        summary.author = User().get_by_id(self.author_id).username
        polygon = to_shape(self.geometry)
        polygon_aea = transform(
                            partial(
                            pyproj.transform,
                            pyproj.Proj(init='EPSG:4326'),
                            pyproj.Proj(
                                proj='aea',
                                lat1=polygon.bounds[1],
                                lat2=polygon.bounds[3])),
                            polygon)
        area = polygon_aea.area/1000000
        summary.area = area
        summary.campaign_tag = self.campaign_tag
        summary.changeset_comment = self.changeset_comment
        summary.created = self.created
        summary.last_updated = self.last_updated
        summary.due_date = self.due_date
        summary.mapper_level = MappingLevel(self.mapper_level).name
        summary.mapper_level_enforced = self.enforce_mapper_level
        summary.validator_level_enforced = self.enforce_validator_role
        summary.organisation_tag = self.organisation_tag
        summary.status = ProjectStatus(self.status).name
        summary.entities_to_map = self.entities_to_map

        centroid_geojson = db.session.scalar(self.centroid.ST_AsGeoJSON())
        summary.aoi_centroid = geojson.loads(centroid_geojson)

        summary.percent_mapped = Project.calculate_tasks_percent('mapped', self.total_tasks,
                                                                 self.tasks_mapped, self.tasks_validated,
                                                                 self.tasks_bad_imagery)
        summary.percent_validated = Project.calculate_tasks_percent('validated', self.total_tasks,
                                                                    self.tasks_mapped, self.tasks_validated,
                                                                    self.tasks_bad_imagery)
        summary.percent_bad_imagery = Project.calculate_tasks_percent('bad_imagery', self.total_tasks,
                                                                      self.tasks_mapped, self.tasks_validated,
                                                                      self.tasks_bad_imagery)

        project_info = ProjectInfo.get_dto_for_locale(self.id, preferred_locale, self.default_locale)
        summary.name = project_info.name
        summary.short_description = project_info.short_description

        return summary
Esempio n. 5
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. 7
0
    def _get_project_and_base_dto(self):
        """ Populates a project DTO with properties common to all roles """
        base_dto = ProjectDTO()
        base_dto.project_id = self.id
        base_dto.project_status = ProjectStatus(self.status).name
        base_dto.default_locale = self.default_locale
        base_dto.project_priority = ProjectPriority(self.priority).name
        base_dto.area_of_interest = self.get_aoi_geometry_as_geojson()
        base_dto.enforce_mapper_level = self.enforce_mapper_level
        base_dto.enforce_validator_role = self.enforce_validator_role
        base_dto.private = self.private
        base_dto.mapper_level = MappingLevel(self.mapper_level).name
        base_dto.entities_to_map = self.entities_to_map
        base_dto.changeset_comment = self.changeset_comment
        base_dto.due_date = self.due_date
        base_dto.imagery = self.imagery
        base_dto.josm_preset = self.josm_preset
        base_dto.campaign_tag = self.campaign_tag
        base_dto.organisation_tag = self.organisation_tag
        base_dto.license_id = self.license_id
        base_dto.created = self.created
        base_dto.last_updated = self.last_updated
        base_dto.id_custom_imagery = self.id_custom_imagery
        base_dto.id_custom_presets = self.id_custom_presets
        base_dto.id_min_editable_zoom = self.id_min_editable_zoom
        base_dto.author = User().get_by_id(self.author_id).username
        base_dto.active_mappers = Project.get_active_mappers(self.id)
        base_dto.task_creation_mode = TaskCreationMode(
            self.task_creation_mode).name

        if self.private:
            # If project is private it should have a list of allowed users
            allowed_usernames = []
            for user in self.allowed_users:
                allowed_usernames.append(user.username)
            base_dto.allowed_usernames = allowed_usernames

        if self.mapping_types:
            mapping_types = []
            for mapping_type in self.mapping_types:
                mapping_types.append(MappingTypes(mapping_type).name)

            base_dto.mapping_types = mapping_types

        if self.priority_areas:
            geojson_areas = []
            for priority_area in self.priority_areas:
                geojson_areas.append(priority_area.get_as_geojson())

            base_dto.priority_areas = geojson_areas

        return self, base_dto
Esempio n. 8
0
    def _get_project_and_base_dto(self, project_id):
        """ Populates a project DTO with properties common to all roles """
        project = Project.get(project_id)

        if project is None:
            return None, None

        aoi = project.area_of_interest

        base_dto = ProjectDTO()
        base_dto.project_id = project_id
        base_dto.project_status = ProjectStatus(project.status).name
        base_dto.default_locale = project.default_locale
        base_dto.project_priority = ProjectPriority(project.priority).name
        base_dto.area_of_interest = aoi.get_aoi_geometry_as_geojson()
        base_dto.enforce_mapper_level = project.enforce_mapper_level
        base_dto.enforce_validator_role = project.enforce_validator_role
        base_dto.private = project.private
        base_dto.mapper_level = MappingLevel(project.mapper_level).name
        base_dto.entities_to_map = project.entities_to_map
        base_dto.changeset_comment = project.changeset_comment
        base_dto.due_date = project.due_date
        base_dto.imagery = project.imagery
        base_dto.josm_preset = project.josm_preset
        base_dto.campaign_tag = project.campaign_tag
        base_dto.organisation_tag = project.organisation_tag
        base_dto.license_id = project.license_id
        base_dto.last_updated = project.last_updated
        base_dto.author = User().get_by_id(project.author_id).username

        if project.private:
            # If project is private it should have a list of allowed users
            allowed_usernames = []
            for user in project.allowed_users:
                allowed_usernames.append(user.username)
            base_dto.allowed_usernames = allowed_usernames

        if project.mapping_types:
            mapping_types = []
            for mapping_type in project.mapping_types:
                mapping_types.append(MappingTypes(mapping_type).name)

            base_dto.mapping_types = mapping_types

        if project.priority_areas:
            geojson_areas = []
            for priority_area in project.priority_areas:
                geojson_areas.append(priority_area.get_as_geojson())

            base_dto.priority_areas = geojson_areas

        return project, base_dto
Esempio n. 9
0
    def get_projects_for_admin(admin_id: int, preferred_locale: str) -> PMDashboardDTO:
        """ Get projects for admin """
        admins_projects = Project.query.filter_by(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(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
    def get_projects_geojson(
            search_bbox_dto: ProjectSearchBBoxDTO
    ) -> geojson.FeatureCollection:
        """  search for projects meeting criteria provided return as a geojson feature collection"""

        # make a polygon from provided bounding box
        polygon = ProjectSearchService._make_4326_polygon_from_bbox(
            search_bbox_dto.bbox, search_bbox_dto.input_srid)

        # validate the bbox area is less than or equal to the max area allowed to prevent
        # abuse of the api or performance issues from large requests
        if not ProjectSearchService.validate_bbox_area(polygon):
            raise BBoxTooBigError('Requested bounding box is too large')

        # get projects intersecting the polygon for created by the author_id
        intersecting_projects = ProjectSearchService._get_intersecting_projects(
            polygon, search_bbox_dto.project_author)

        # allow an empty feature collection to be returned if no intersecting features found, since this is primarily
        # for returning data to show on a map
        features = []
        for project in intersecting_projects:
            try:
                localDTO = ProjectInfo.get_dto_for_locale(
                    project.id, search_bbox_dto.preferred_locale,
                    project.default_locale)
            except Exception as e:
                pass

            properties = {
                "projectId": project.id,
                "projectStatus": ProjectStatus(project.status).name,
                "projectName": localDTO.name
            }
            feature = geojson.Feature(geometry=geojson.loads(project.geometry),
                                      properties=properties)
            features.append(feature)

        return geojson.FeatureCollection(features)
Esempio n. 11
0
    def get_project_summary(self, preferred_locale) -> ProjectSummary:
        """ Create Project Summary model for postgis project object"""
        summary = ProjectSummary()
        summary.project_id = self.id
        priority = self.priority
        if priority == 0:
            summary.priority = 'URGENT'
        elif priority == 1:
            summary.priority = 'HIGH'
        elif priority == 2:
            summary.priority = 'MEDIUM'
        else:
            summary.priority = 'LOW'
        summary.author = User().get_by_id(self.author_id).username
        polygon = to_shape(self.geometry)
        polygon_aea = transform(
                            partial(
                            pyproj.transform,
                            pyproj.Proj(init='EPSG:4326'),
                            pyproj.Proj(
                                proj='aea',
                                lat1=polygon.bounds[1],
                                lat2=polygon.bounds[3])),
                            polygon)
        area = polygon_aea.area/1000000
        summary.area = area
        summary.campaign_tag = self.campaign_tag
        summary.changeset_comment = self.changeset_comment
        summary.created = self.created
        summary.last_updated = self.last_updated
        summary.due_date = self.due_date
        summary.mapper_level = MappingLevel(self.mapper_level).name
        summary.mapper_level_enforced = self.enforce_mapper_level
        summary.validator_level_enforced = self.enforce_validator_role
        summary.organisation_tag = self.organisation_tag
        summary.status = ProjectStatus(self.status).name
        summary.total_mappers = db.session.query(User).filter(User.projects_mapped.any(self.id)).count()
        unique_mappers = TaskHistory.query.filter(
                TaskHistory.action == 'LOCKED_FOR_MAPPING',
                TaskHistory.project_id == self.id
            ).distinct(TaskHistory.user_id).count()
        unique_validators = TaskHistory.query.filter(
                TaskHistory.action == 'LOCKED_FOR_VALIDATION',
                TaskHistory.project_id == self.id
            ).distinct(TaskHistory.user_id).count()
        summary.total_tasks = self.total_tasks
        summary.total_comments = db.session.query(ProjectChat).filter(ProjectChat.project_id == self.id).count()

        summary.entities_to_map = self.entities_to_map

        centroid_geojson = db.session.scalar(self.centroid.ST_AsGeoJSON())
        summary.aoi_centroid = geojson.loads(centroid_geojson)

        summary.percent_mapped = int(((self.tasks_mapped + self.tasks_bad_imagery) / self.total_tasks) * 100)
        summary.percent_validated = int((self.tasks_validated / self.total_tasks) * 100)
        summary.percent_bad_imagery = int((self.tasks_bad_imagery / self.total_tasks) * 100)
        project_info = ProjectInfo.get_dto_for_locale(self.id, preferred_locale, self.default_locale)
        summary.name = project_info.name
        summary.short_description = project_info.short_description
        summary.total_time_spent = 0
        summary.total_mapping_time = 0
        summary.total_validation_time = 0
        summary.average_mapping_time = 0
        summary.average_validation_time = 0

        sql = '''SELECT SUM(TO_TIMESTAMP(action_text, 'HH24:MI:SS')::TIME) FROM task_history
                 WHERE action='LOCKED_FOR_MAPPING'and project_id = {0};'''.format(self.id)
        total_mapping_time = db.engine.execute(sql)
        for row in total_mapping_time:
            total_mapping_time = row[0]
            if total_mapping_time:
                total_mapping_seconds = total_mapping_time.total_seconds()
                summary.total_mapping_time = total_mapping_seconds
                summary.total_time_spent += summary.total_mapping_time
                if unique_mappers:
                    average_mapping_time = total_mapping_seconds/unique_mappers
                    summary.average_mapping_time = average_mapping_time

        sql = '''SELECT SUM(TO_TIMESTAMP(action_text, 'HH24:MI:SS')::TIME) FROM task_history
                WHERE action='LOCKED_FOR_VALIDATION' and project_id = {0};'''.format(self.id)
        total_validation_time = db.engine.execute(sql)
        for row in total_validation_time:
            total_validation_time = row[0]
            if total_validation_time:
                total_validation_seconds = total_validation_time.total_seconds()
                summary.total_validation_time = total_validation_seconds
                summary.total_time_spent += summary.total_validation_time
                if unique_validators:
                    average_validation_time = total_validation_seconds/unique_validators
                    summary.average_validation_time = average_validation_time

        return summary