Example #1
0
 def delete(self, organisation_id, campaign_id):
     """
     Unassigns an organization from an campaign
     ---
     tags:
         - campaigns
     produces:
         - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           required: true
           type: string
           default: Token sessionTokenHere==
         - name: organisation_id
           in: path
           description: Unique organisation ID
           required: true
           type: integer
           default: 1
         - name: campaign_id
           in: path
           description: Unique campaign ID
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: Organisation and campaign unassociated successfully
         401:
             description: Unauthorized - Invalid credentials
         403:
             description: Forbidden - users have submitted mapping
         404:
             description: Project not found
         500:
             description: Internal Server Error
     """
     try:
         if OrganisationService.can_user_manage_organisation(
             organisation_id, token_auth.current_user()
         ):
             CampaignService.delete_organisation_campaign(
                 organisation_id, campaign_id
             )
             return (
                 {"Success": "Organisation and campaign unassociated successfully"},
                 200,
             )
         else:
             return {"Error": "User is not a manager of the organisation"}, 403
     except NotFound:
         return {"Error": "Organisation Campaign Not Found"}, 404
     except Exception as e:
         error_msg = f"Organisation Campaigns DELETE - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": error_msg}, 500
    def delete(self, campaign_id):
        """
        Deletes an existing campaign
        ---
        tags:
          - campaigns
        produces:
          - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              type: string
              required: true
              default: Token sessionTokenHere==
            - in: header
              name: Accept-Language
              description: Language user is requesting
              type: string
              required: true
              default: en
            - name: campaign_id
              in: path
              description: Campaign ID
              required: true
              type: integer
              default: 1
        responses:
            200:
                description: Campaign deleted successfully
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            500:
                description: Internal Server Error
        """
        try:
            orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto(
                token_auth.current_user()
            )
            if len(orgs_dto.organisations) < 1:
                raise ValueError("User not a Org Manager")
        except ValueError as e:
            error_msg = f"CampaignsRestAPI DELETE: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            campaign = CampaignService.get_campaign(campaign_id)
            CampaignService.delete_campaign(campaign.id)
            return {"Success": "Campaign deleted"}, 200
        except NotFound:
            return {"Error": "Campaign not found"}, 404
        except Exception as e:
            error_msg = f"Campaign DELETE - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Example #3
0
    def delete(self, project_id, campaign_id):
        """
        Delete a campaign for a project
        ---
        tags:
          - campaigns
        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: Unique project ID
              required: true
              type: integer
              default: 1
            - name: campaign_id
              in: path
              description: Unique campaign ID
              required: true
              type: integer
              default: 1
        responses:
            200:
                description: Campaign assigned successfully
            400:
                description: Client Error - Invalid Request
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            500:
                description: Internal Server Error
        """
        try:
            ProjectAdminService.is_user_action_permitted_on_project(
                tm.authenticated_user_id, project_id)
        except ValueError as e:
            error_msg = f"ProjectsCampaignsAPI DELETE: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            CampaignService.delete_project_campaign(project_id, campaign_id)
            return {"Success": "Campaigns Deleted"}, 200
        except NotFound:
            return {"Error": "Campaign Not Found"}, 404
        except Exception as e:
            error_msg = f"ProjectsCampaignsAPI DELETE - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
 def get(self, campaign_id):
     """
     Get an active campaign's information
     ---
     tags:
         - campaigns
     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
         - name: campaign_id
           in: path
           description: Campaign ID
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: Campaign found
         404:
             description: No Campaign found
         500:
             description: Internal Server Error
     """
     try:
         authenticated_user_id = token_auth.current_user()
         if authenticated_user_id:
             campaign = CampaignService.get_campaign_as_dto(
                 campaign_id, authenticated_user_id
             )
         else:
             campaign = CampaignService.get_campaign_as_dto(campaign_id, 0)
         return campaign.to_primitive(), 200
     except NotFound:
         return {"Error": "No campaign found"}, 404
     except Exception as e:
         error_msg = f"Campaign GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": error_msg}, 500
Example #5
0
 def get(self, project_id):
     """
     Gets all campaigns for a project
     ---
     tags:
       - campaigns
     produces:
       - application/json
     parameters:
         - name: project_id
           in: path
           description: Unique project ID
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: Campaign list returned successfully
         400:
             description: Client Error - Invalid Request
         401:
             description: Unauthorized - Invalid credentials
         500:
             description: Internal Server Error
     """
     try:
         campaigns = CampaignService.get_project_campaigns_as_dto(
             project_id)
         return campaigns.to_primitive(), 200
     except NotFound:
         return {"Error": "No campaign found"}, 404
     except Exception as e:
         error_msg = f"Messages GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": error_msg}, 500
Example #6
0
 def get(self):
     """
     Get all active campaigns
     ---
     tags:
       - campaigns
     produces:
       - application/json
     responses:
         200:
             description: All Campaigns returned successfully
         500:
             description: Internal Server Error
     """
     try:
         campaigns = CampaignService.get_all_campaigns()
         return campaigns.to_primitive(), 200
     except Exception as e:
         error_msg = f"User GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": error_msg}, 500
Example #7
0
 def get(self, organisation_id):
     """
     Returns all campaigns related to an organisation
     ---
     tags:
         - campaigns
     produces:
         - application/json
     parameters:
         - in: header
           name: Authorization
           description: Base64 encoded session token
           required: false
           type: string
           default: Token sessionTokenHere==
         - name: organisation_id
           in: path
           description: Unique project ID
           required: true
           type: integer
           default: 1
     responses:
         200:
             description: Success
         404:
             description: Organisation not found
         500:
             description: Internal Server Error
     """
     try:
         campaigns = CampaignService.get_organisation_campaigns_as_dto(
             organisation_id
         )
         return campaigns.to_primitive(), 200
     except NotFound:
         return {"Error": "No campaign found"}, 404
     except Exception as e:
         error_msg = f"Organisation Campaigns GET - unhandled error: {str(e)}"
         current_app.logger.critical(error_msg)
         return {"Error": error_msg}, 500
Example #8
0
    def post(self, project_id, campaign_id):
        """
        Assign a campaign for a project
        ---
        tags:
          - campaigns
        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: Unique project ID
              required: true
              type: integer
              default: 1
            - name: campaign_id
              in: path
              description: Unique campaign ID
              required: true
              type: integer
              default: 1
        responses:
            201:
                description: Campaign assigned successfully
            400:
                description: Client Error - Invalid Request
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            500:
                description: Internal Server Error
        """
        try:
            ProjectAdminService.is_user_action_permitted_on_project(
                tm.authenticated_user_id, project_id)
        except ValueError as e:
            error_msg = f"ProjectsCampaignsAPI POST: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            campaign_project_dto = CampaignProjectDTO()
            campaign_project_dto.campaign_id = campaign_id
            campaign_project_dto.project_id = project_id
            campaign_project_dto.validate()
        except DataError as e:
            current_app.logger.error(f"error validating request: {str(e)}")
            return str(e), 400

        try:
            CampaignService.create_campaign_project(campaign_project_dto)
            message = "campaign with id {} assigned successfully for project with id {}".format(
                campaign_id, project_id)
            return ({"Success": message}, 200)
        except Exception as e:
            error_msg = f"ProjectsCampaignsAPI POST - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Example #9
0
    def patch(self, campaign_id):
        """
        Updates an existing campaign
        ---
        tags:
          - campaigns
        produces:
          - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              type: string
              required: true
              default: Token sessionTokenHere==
            - in: header
              name: Accept-Language
              description: Language user is requesting
              type: string
              required: true
              default: en
            - name: campaign_id
              in: path
              description: Campaign ID
              required: true
              type: integer
              default: 1
            - in: body
              name: body
              required: true
              description: JSON object for updating a Campaign
              schema:
                properties:
                    name:
                        type: string
                        example: HOT Campaign
                    logo:
                        type: string
                        example: https://tasks.hotosm.org/assets/img/hot-tm-logo.svg
                    url:
                        type: string
                        example: https://hotosm.org
                    organisations:
                        type: array
                        items:
                            type: integer
                        default: [
                            1
                        ]
        responses:
            200:
                description: Campaign updated successfully
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            409:
                description: Resource duplication
            500:
                description: Internal Server Error
        """
        try:
            orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto(
                token_auth.current_user())
            if len(orgs_dto.organisations) < 1:
                raise ValueError("User not a Org Manager")
        except ValueError as e:
            error_msg = f"CampaignsRestAPI PATCH: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            campaign_dto = CampaignDTO(request.get_json())
            campaign_dto.validate()
        except DataError as e:
            current_app.logger.error(f"error validating request: {str(e)}")
            return str(e), 400

        try:
            campaign = CampaignService.update_campaign(campaign_dto,
                                                       campaign_id)
            return {"Success": "Campaign {} updated".format(campaign.id)}, 200
        except NotFound:
            return {"Error": "Campaign not found"}, 404
        except ValueError:
            error_msg = "Campaign PATCH - name already exists"
            return {"Error": error_msg}, 409
        except Exception as e:
            error_msg = f"Campaign PATCH - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Example #10
0
    def post(self, organisation_id, campaign_id):
        """
        Assigns a campaign to an organisation
        ---
        tags:
            - campaigns
        produces:
            - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              required: true
              type: string
              default: Token sessionTokenHere==
            - name: organisation_id
              in: path
              description: Unique organisation ID
              required: true
              type: integer
              default: 1
            - name: campaign_id
              in: path
              description: Unique campaign ID
              required: true
              type: integer
              default: 1
        responses:
            200:
                description: Organisation and campaign assigned successfully
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden - users have submitted mapping
            404:
                description: Project not found
            500:
                description: Internal Server Error
        """
        try:
            if OrganisationService.can_user_manage_organisation(
                organisation_id, token_auth.current_user()
            ):
                if CampaignService.campaign_organisation_exists(
                    campaign_id, organisation_id
                ):
                    message = (
                        "Campaign {} is already assigned to organisation {}.".format(
                            campaign_id, organisation_id
                        )
                    )
                    return {"Error": message}, 400

                CampaignService.create_campaign_organisation(
                    organisation_id, campaign_id
                )
                message = (
                    "campaign with id {} assigned for organisation with id {}".format(
                        campaign_id, organisation_id
                    )
                )
                return {"Success": message}, 200
            else:
                return {"Error": "User is not a manager of the organisation"}, 403
        except Exception as e:
            error_msg = f"Campaign Organisation POST - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Example #11
0
    def post(self):
        """
        Creates a new campaign
        ---
        tags:
          - campaigns
        produces:
          - application/json
        parameters:
            - in: header
              name: Authorization
              description: Base64 encoded session token
              type: string
              required: true
              default: Token sessionTokenHere==
            - in: header
              name: Accept-Language
              description: Language user is requesting
              type: string
              required: true
              default: en
            - in: body
              name: body
              required: true
              description: JSON object for creating a new Campaign
              schema:
                properties:
                    name:
                        type: string
                        example: HOT Campaign
                    logo:
                        type: string
                        example: https://tasks.hotosm.org/assets/img/hot-tm-logo.svg
                    url:
                        type: string
                        example: https://hotosm.org
                    organisations:
                        type: array
                        items:
                            type: integer
                        default: [
                            1
                        ]
        responses:
            200:
                description: New campaign created successfully
            401:
                description: Unauthorized - Invalid credentials
            403:
                description: Forbidden
            500:
                description: Internal Server Error
        """
        try:
            orgs_dto = OrganisationService.get_organisations_managed_by_user_as_dto(
                tm.authenticated_user_id
            )
            if len(orgs_dto.organisations) < 1:
                raise ValueError("User not a Org Manager")
        except ValueError as e:
            error_msg = f"CampaignsAllAPI POST: {str(e)}"
            return {"Error": error_msg}, 403

        try:
            campaign_dto = NewCampaignDTO(request.get_json())
            campaign_dto.validate()
        except DataError as e:
            current_app.logger.error(f"error validating request: {str(e)}")
            return str(e), 400

        try:
            campaign = CampaignService.create_campaign(campaign_dto)
            return {"campaignId": campaign.id}, 200
        except Exception as e:
            error_msg = f"Campaign POST - unhandled error: {str(e)}"
            current_app.logger.critical(error_msg)
            return {"Error": error_msg}, 500
Example #12
0
    def get_task_stats(
        start_date, end_date, org_id, org_name, campaign, project_id, country
    ):
        """ Creates tasks stats for a period using the TaskStatsDTO """

        query = (
            db.session.query(
                TaskHistory.task_id,
                TaskHistory.project_id,
                TaskHistory.action_text,
                func.DATE(TaskHistory.action_date).label("day"),
            )
            .distinct(
                tuple_(
                    TaskHistory.project_id, TaskHistory.task_id, TaskHistory.action_text
                )
            )
            .filter(
                TaskHistory.action == "STATE_CHANGE",
                or_(
                    TaskHistory.action_text == "MAPPED",
                    TaskHistory.action_text == "VALIDATED",
                    TaskHistory.action_text == "BADIMAGERY",
                ),
            )
            .order_by(
                TaskHistory.project_id,
                TaskHistory.task_id,
                TaskHistory.action_text,
                TaskHistory.action_date,
            )
        )

        if org_id:
            query = query.join(Project, Project.id == TaskHistory.project_id).filter(
                Project.organisation_id == org_id
            )
        if org_name:
            try:
                organisation_id = OrganisationService.get_organisation_by_name(
                    org_name
                ).id
            except NotFound:
                organisation_id = None
            query = query.join(Project, Project.id == TaskHistory.project_id).filter(
                Project.organisation_id == organisation_id
            )
        if campaign:
            try:
                campaign_id = CampaignService.get_campaign_by_name(campaign).id
            except NotFound:
                campaign_id = None
            query = query.join(
                campaign_projects,
                campaign_projects.c.project_id == TaskHistory.project_id,
            ).filter(campaign_projects.c.campaign_id == campaign_id)
        if project_id:
            query = query.filter(TaskHistory.project_id.in_(project_id))
        if 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(country))).filter(
                TaskHistory.project_id == sq.c.id
            )

        query = query.subquery()

        date_query = db.session.query(
            func.DATE(
                func.generate_series(start_date, end_date, timedelta(days=1))
            ).label("d_day")
        ).subquery()

        grouped_dates = (
            db.session.query(
                date_query.c.d_day,
                query.c.action_text,
                func.count(query.c.action_text).label("cnt"),
            )
            .join(date_query, date_query.c.d_day == query.c.day)
            .group_by(date_query.c.d_day, query.c.action_text)
            .order_by(date_query.c.d_day)
        ).subquery()

        mapped = (
            db.session.query(
                grouped_dates.c.d_day, grouped_dates.c.action_text, grouped_dates.c.cnt
            )
            .select_from(grouped_dates)
            .filter(grouped_dates.c.action_text == "MAPPED")
            .subquery()
        )
        validated = (
            db.session.query(
                grouped_dates.c.d_day, grouped_dates.c.action_text, grouped_dates.c.cnt
            )
            .select_from(grouped_dates)
            .filter(grouped_dates.c.action_text == "VALIDATED")
            .subquery()
        )
        badimagery = (
            db.session.query(
                grouped_dates.c.d_day, grouped_dates.c.action_text, grouped_dates.c.cnt
            )
            .select_from(grouped_dates)
            .filter(grouped_dates.c.action_text == "BADIMAGERY")
            .subquery()
        )

        result = (
            db.session.query(
                func.to_char(grouped_dates.c.d_day, "YYYY-MM-DD"),
                func.coalesce(mapped.c.cnt, 0).label("mapped"),
                func.coalesce(validated.c.cnt, 0).label("validated"),
                func.coalesce(badimagery.c.cnt, 0).label("badimagery"),
            )
            .select_from(grouped_dates)
            .distinct(grouped_dates.c.d_day)
            .filter(grouped_dates.c.d_day is not None)
            .outerjoin(mapped, mapped.c.d_day == grouped_dates.c.d_day)
            .outerjoin(validated, validated.c.d_day == grouped_dates.c.d_day)
            .outerjoin(badimagery, badimagery.c.d_day == grouped_dates.c.d_day)
        )

        day_stats_dto = list(map(StatsService.set_task_stats, result))

        results_dto = TaskStatsDTO()
        results_dto.stats = day_stats_dto

        return results_dto