class ClusterIndicatorAPIView(CreateAPIView, UpdateAPIView):
    """
    Add and Update Indicator on cluster reporting screen.
    """

    serializer_class = ClusterIndicatorSerializer
    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.cluster_member,
        )
    )
    queryset = Reportable.objects.all()

    def get_object(self):
        return get_object_or_404(self.get_queryset(), pk=self.request.data.get("id"))
Ejemplo n.º 2
0
class ClusterActivityPartnersAPIView(ListAPIView):

    serializer_class = ClusterActivityPartnersSerializer
    permission_classes = (IsAuthenticated,
                          HasAnyRole(PRP_ROLE_TYPES.cluster_system_admin,
                                     PRP_ROLE_TYPES.cluster_imo,
                                     PRP_ROLE_TYPES.cluster_member,
                                     PRP_ROLE_TYPES.cluster_coordinator,
                                     PRP_ROLE_TYPES.cluster_viewer))
    pagination_class = SmallPagination
    filter_backends = (django_filters.rest_framework.DjangoFilterBackend, )
    filter_class = ClusterActivityPartnersFilter
    lookup_field = lookup_url_kwarg = 'pk'

    def get_queryset(self, *args, **kwargs):
        cluster_activity_id = self.kwargs.get(self.lookup_field)
        return Partner.objects.filter(
            partner_activities__cluster_activity_id=cluster_activity_id)
class RPMWorkspaceResponsePlanDetailAPIView(APIView):

    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
        ),
    )

    def get(self, request, *args, **kwargs):
        source_url = HPC_V1_ROOT_URL + 'rpm/plan/id/{}?format=json&content=entities'.format(
            self.kwargs['id'])
        try:
            plan_data = get_json_from_url(source_url)['data']
        except Exception:
            raise serializers.ValidationError('OCHA service unavailable.')

        out_data = {
            k: v
            for k, v in plan_data.items() if type(v) not in {list, dict}
        }

        if 'governingEntities' in plan_data:
            cluster_names = [
                ge['governingEntityVersion']['name']
                for ge in plan_data['governingEntities']
                if ge['entityPrototype']['refCode'] == RefCode.CLUSTER
            ]
        else:
            cluster_names = []
        out_data['clusterNames'] = cluster_names
        if plan_data['categories'] and plan_data['categories'][0]['id'] == 5:
            out_data['planType'] = RESPONSE_PLAN_TYPE.fa
        else:
            out_data['planType'] = RESPONSE_PLAN_TYPE.hrp

        out_data['startDate'] = parse(
            plan_data['planVersion']['startDate']).strftime(
                settings.DATE_FORMAT)
        out_data['endDate'] = parse(
            plan_data['planVersion']['endDate']).strftime(settings.DATE_FORMAT)

        return Response(out_data)
class ReportableDetailAPIView(RetrieveAPIView):
    """
    Get details about a single Reportable, its blueprint, its disaggregations
    etc.
    """
    serializer_class = IndicatorListSerializer
    queryset = Reportable.objects.all()
    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.ip_authorized_officer,
            PRP_ROLE_TYPES.ip_admin,
            PRP_ROLE_TYPES.ip_editor,
            PRP_ROLE_TYPES.ip_viewer,
        ),
    )
    lookup_url_kwarg = 'reportable_id'

    def patch(self, request, reportable_id, *args, **kwargs):
        pass
class ClusterObjectiveIndicatorAdoptAPIView(APIView):
    """
    Create a PartnerProject Reportable from ClusterObjective Reportable.

    Only a IMO should be allowed to do this action.
    """
    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.cluster_member,
        )
    )

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        serializer = ClusterObjectiveIndicatorAdoptSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        co_reportable = Reportable.objects.get(id=serializer.validated_data['reportable_id'])
        pp = PartnerProject.objects.get(id=serializer.validated_data['partner_project_id'])
        pp_reportable = create_reportable_for_pp_from_co_reportable(pp, co_reportable)
        pp_reportable.target = serializer.validated_data['target']
        pp_reportable.baseline = serializer.validated_data['baseline']
        pp_reportable.save()

        for item in serializer.validated_data['locations']:
            ReportableLocationGoal.objects.create(
                reportable=pp_reportable,
                location=item['location'],
                target=item['target'],
                baseline=item['baseline'],
            )

        result_serializer = ClusterIndicatorSerializer(instance=pp_reportable)

        return Response(result_serializer.data, status=status.HTTP_200_OK)
class RPMProjectDetailAPIView(APIView):

    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.ip_authorized_officer,
        ),
    )

    def get(self, request, *args, **kwargs):
        details_url = HPC_V2_ROOT_URL + 'project/{}'.format(self.kwargs['id'])

        details = fetch_json_urls([
            details_url,
        ])

        # We should use project code whenever is possible. ID filtering might be not working in case of new OPS data
        if details:
            project_code = details[0]['data']['projectVersion']['code']
            budget_url = HPC_V1_ROOT_URL + 'fts/flow?projectCode={}'.format(
                project_code)
        else:
            budget_url = HPC_V1_ROOT_URL + 'fts/flow?projectId={}'.format(
                self.kwargs['id'])

        details, budget_info = fetch_json_urls([
            details_url,
            budget_url,
        ])

        out_data = {
            k: v
            for k, v in details['data'].items() if type(v) not in {list, dict}
        }

        # Grab project details from projectVersion array of dict
        current_project_data = None

        for project in details['data']['projectVersions']:
            if details['data']['currentPublishedVersionId'] == project['id']:
                current_project_data = project
                break

        # Fetch attachment data
        attachment_url = HPC_V2_ROOT_URL \
            + 'project/{}/attachments'.format(details['data']['id'])
        attachments = get_json_from_url(attachment_url)

        if 'data' in attachments:
            out_data['attachments'] = map(
                lambda item: item['attachment']['attachmentVersion']['value'][
                    'description'],
                filter(lambda x: x['attachment']['type'] == 'indicator',
                       attachments['data']))

        out_data['startDate'] = current_project_data['startDate']
        out_data['endDate'] = current_project_data['endDate']
        out_data['name'] = current_project_data['name']

        # out_data['totalBudgetUSD'] = sum([
        #     f['amountUSD'] for f in budget_info['data']['flows']
        # ]) if budget_info['data']['flows'] else None

        out_data['totalBudgetUSD'] = current_project_data[
            'currentRequestedFunds']

        funding_sources = []

        if 'data' in budget_info:
            for flow in budget_info['data']['flows']:
                funding_sources.extend([
                    fs['name'] for fs in flow.get('sourceObjects', [])
                    if fs['type'] == 'Organization'
                ])

        out_data['fundingSources'] = funding_sources
        out_data['objective'] = current_project_data['objective']
        additional_information = list()
        if 'contacts' in current_project_data:
            for contact in current_project_data['contacts']:
                if "website" in contact and contact['website']:
                    additional_information.append(contact['website'])
        out_data['additional_information'] = ", ".join(additional_information)

        start_datetime = parse(out_data['startDate'])
        end_datetime = parse(out_data['endDate'])

        out_data['startDate'] = start_datetime.strftime(settings.DATE_FORMAT)
        out_data['endDate'] = end_datetime.strftime(settings.DATE_FORMAT)

        clusters = []

        try:
            clusters += [
                global_cluster_data['name'] for global_cluster_data in
                current_project_data['globalClusters']
            ]
        except Exception:
            pass

        try:
            clusters += [
                c['name'] for c in current_project_data['governingEntities']
                if c['entityPrototypeId'] == 9
            ]
        except Exception:
            pass

        out_data['clusters'] = clusters

        today = timezone.now()
        if start_datetime > today:
            out_data['status'] = 'Planned'
        elif end_datetime < today:
            out_data['status'] = 'Completed'
        else:
            out_data['status'] = 'Ongoing'

        return Response(out_data)
class ReportRefreshAPIView(APIView):

    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.cluster_member,
            PRP_ROLE_TYPES.ip_authorized_officer,
            PRP_ROLE_TYPES.ip_editor,
        ),
    )

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        """
        Removes all IndicatorReport instances for given ProgressReport,
        including underlying IndicatorLocationData instances
        """
        serializer = ReportRefreshSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        if serializer.validated_data['report_type'] == 'PR':
            report = get_object_or_404(ProgressReport, id=serializer.validated_data['report_id'])
            target_prs = ProgressReport.objects.filter(
                programme_document=report.programme_document,
                report_type=report.report_type,
                report_number__gte=report.report_number
            )

            for pr in target_prs:
                reset_progress_report_data(pr)
        else:
            report = get_object_or_404(IndicatorReport, id=serializer.validated_data['report_id'])

            if report.progress_report:
                raise ValidationError(
                    "This indicator report is linked to a progress report. "
                    "Use the progress report ID instead.",
                )

            # if future report and indicator location data exists,
            # then do not perform reset
            future_reports_qs = IndicatorReport.objects.exclude(
                pk=report.pk,
            ).filter(
                progress_report=report.progress_report,
                due_date__gt=report.due_date,
            )
            if future_reports_qs.exists():
                # make sure the future reports have data
                data = [
                    d for d in IndicatorLocationData.objects.filter(
                        indicator_report__in=future_reports_qs.all()
                    )
                    if d.disaggregation.get("()") != {"c": 0, "d": 0, "v": 0}
                ]
                if data:
                    msg = {
                        "response": "Data has already been submitted for "
                        "reports that follow the current report you are "
                        "trying to refresh. To avoid data loss, please "
                        "contact the PRP help desk "
                        "[https://prphelp.zendesk.com/hc/en-us/requests/new] "
                        "to request assistance in refreshing your reporting "
                        "data.",
                    }
                    return Response(msg, status=status.HTTP_400_BAD_REQUEST)

            reset_indicator_report_data(report)

        return Response({"response": "OK"}, status=status.HTTP_200_OK)
class ClusterIndicatorSendIMOMessageAPIView(APIView):
    """
    ClusterIndicatorSendIMOMessageAPIView sends
    an message to belonging cluster's IMO
    via e-mail.

    Raises:
        Http404 -- Throws 404 HTTP response

    Returns:
        Response -- DRF Response object
    """

    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.ip_authorized_officer,
            PRP_ROLE_TYPES.ip_editor,
            PRP_ROLE_TYPES.cluster_member,
        ),
    )

    def post(self, request, *args, **kwargs):
        serializer = ClusterIndicatorIMOMessageSerializer(
            data=request.data,
            context={'request': request},
        )
        serializer.is_valid(raise_exception=True)

        reportable = serializer.validated_data['reportable']
        cluster = serializer.validated_data['cluster']
        imo_users = cluster.imo_users.all()

        imo_frontend_indicators_url = \
            '{}/app/{}/cluster-reporting/plan/{}/response-parameters/clusters/activity/{}/indicators'.format(
                settings.FRONTEND_HOST,
                cluster.response_plan.workspace.workspace_code,
                cluster.response_plan.id,
                reportable.content_object.activity.cluster_activity.id
            )

        try:
            project_name = reportable.content_object.project.title
        except Exception:
            project_name = ''

        context = {
            "indicator_name": reportable.blueprint.title,
            "partner_name": request.user.partner.title,
            "sender_user": request.user,
            "message": serializer.validated_data['message'],
            "target_url": imo_frontend_indicators_url,
            "project_name": project_name,
            "response_plan_name": cluster.response_plan.title,
            "locations": reportable.locations.all(),
        }

        for imo_user in imo_users:
            context["imo_user"] = imo_user
            send_email_from_template(
                'email/notify_imo_on_cluster_indicator_change_request_subject.txt',
                'email/notify_imo_on_cluster_indicator_change_request.html',
                context,
                to_email_list=[imo_user.email, ],
                fail_silently=False,
                reply_to=[request.user.email],
                content_subtype='html',
            )

        return Response('OK', status=status.HTTP_200_OK)
class IndicatorLocationDataUpdateAPIView(APIView):
    """
    REST API endpoint to update one IndicatorLocationData, including disaggregation data.
    """
    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.ip_authorized_officer,
            PRP_ROLE_TYPES.ip_editor,
            PRP_ROLE_TYPES.ip_admin,
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.cluster_member,
        )
    )

    def get_object(self, request, pk=None):
        return get_object_or_404(IndicatorLocationData, id=pk)

    def put(self, request, *args, **kwargs):
        if 'id' not in request.data:
            raise Http404('id is required in request body')

        indicator_location_data = self.get_object(
            request, pk=request.data['id'])

        if indicator_location_data.is_locked:
            raise ValidationError("This location data is locked to be updated.")

        serializer = IndicatorLocationDataUpdateSerializer(
            instance=indicator_location_data, data=request.data)

        serializer.is_valid(raise_exception=True)
        serializer.save()

        blueprint = indicator_location_data.indicator_report.reportable.blueprint

        if blueprint.unit == IndicatorBlueprint.NUMBER:
            QuantityIndicatorDisaggregator.post_process(indicator_location_data)

        if blueprint.unit == IndicatorBlueprint.PERCENTAGE:
            RatioIndicatorDisaggregator.post_process(indicator_location_data)

        # Re-calculating the child IR's ILD instance if exists
        if indicator_location_data.indicator_report.children.exists():
            try:
                # Grab LLO Reportable's indicator reports from parent-child
                ild = IndicatorLocationData.objects.get(
                    indicator_report=indicator_location_data.indicator_report.children.first(),
                    location=indicator_location_data.location,
                )

                if ild.indicator_report.reportable.blueprint.unit == IndicatorBlueprint.NUMBER:
                    QuantityIndicatorDisaggregator.post_process(ild)

                if ild.indicator_report.reportable.blueprint.unit == IndicatorBlueprint.PERCENTAGE:
                    RatioIndicatorDisaggregator.post_process(ild)

            # If IndicatorLocationData is not marked as dual reporting then skip
            except IndicatorLocationData.DoesNotExist:
                pass

        serializer.data['disaggregation'] = indicator_location_data.disaggregation

        return Response(serializer.data, status=status.HTTP_200_OK)
Ejemplo n.º 10
0
class IndicatorReportListAPIView(APIView):
    """
    REST API endpoint to get a list of IndicatorReport objects, including each
    set of disaggregation data per report.

    kwargs:
    - reportable_id: Reportable pk (if given, the API will only return
    IndicatorReport objects tied to this Reportable)

    GET parameter:
    - pks = A comma-separated string for IndicatorReport pks (If this GET
    parameter is given, Reportable pk kwargs will be ignored) - TODO: not
    ideal design, since frontend is sending to this endpoint with irrelevant
    reportable_id many times.
    """
    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.ip_authorized_officer,
            PRP_ROLE_TYPES.ip_admin,
            PRP_ROLE_TYPES.ip_editor,
            PRP_ROLE_TYPES.ip_viewer,
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.cluster_member,
            PRP_ROLE_TYPES.cluster_coordinator,
            PRP_ROLE_TYPES.cluster_viewer
        )
    )

    def get_queryset(self, *args, **kwargs):
        pks = self.request.query_params.get('pks', None)
        reportable_id = self.kwargs.get('reportable_id', None)

        if not pks and not reportable_id:
            raise Http404

        if pks:
            pk_list = map(lambda item: int(item), filter(
                lambda item: item != '' and item.isdigit(), pks.split(',')))
            indicator_reports = IndicatorReport.objects.filter(id__in=pk_list)
        else:
            reportable = get_object_or_404(Reportable, pk=reportable_id)
            indicator_reports = reportable.indicator_reports.filter(
                report_status__in=[INDICATOR_REPORT_STATUS.submitted, INDICATOR_REPORT_STATUS.accepted]
            ).order_by('-time_period_start')

        if 'limit' in self.request.query_params:
            limit = int(self.request.query_params.get('limit', '2'))
            indicator_reports = indicator_reports[:limit]

        return indicator_reports

    def get(self, request, *args, **kwargs):
        pd_id_for_locations = int(self.request.query_params.get('pd_id_for_locations', '-1'))
        hide_children = int(self.request.query_params.get('hide_children', '-1'))

        indicator_reports = self.get_queryset()
        serializer = IndicatorReportListSerializer(
            indicator_reports,
            many=True,
            context={
                'pd_id_for_locations': pd_id_for_locations,
                'hide_children': hide_children,
            }
        )
        return Response(serializer.data, status=status.HTTP_200_OK)
Ejemplo n.º 11
0
class IndicatorListAPIView(ListAPIView):
    """
    REST API endpoint to get a list of Indicator objects and to create a new
    Indicator object.

    List filtering keywords:
    - locations (A comma-separated location id list)
    - pds (A comma-separated programme document id list)
    - pd_statuses (A comma-separated PD_STATUS string list)
    - blueprint__title (string as Indicator title)

    Filtering list Example:
     - /api/indicator/<content_object>/?blueprint__title=indicator_blueprint_0
     - /api/indicator/<content_object>/?locations=20,21,24&blueprint__title=indicator_blueprint_17
     - /api/indicator/<content_object>/?pds=37,63,65
     - /api/indicator/<content_object>/?content_object=co,object_id=34    [for cluster objective indicators]
     - /api/indicator/<content_object>/                                   [will throw exception]
    """
    permission_classes = (
        IsAuthenticated,
        HasAnyRole(
            PRP_ROLE_TYPES.cluster_system_admin,
            PRP_ROLE_TYPES.cluster_imo,
            PRP_ROLE_TYPES.cluster_member,
            PRP_ROLE_TYPES.cluster_coordinator,
            PRP_ROLE_TYPES.cluster_viewer
        )
    )
    serializer_class = IndicatorListSerializer
    pagination_class = SmallPagination
    filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
    filter_class = IndicatorFilter
    lookup_url_kwarg = 'content_object'

    def get_queryset(self):
        content_object = self.kwargs.get(self.lookup_url_kwarg)
        if content_object == REPORTABLE_LLO_CONTENT_OBJECT:
            queryset = Reportable.objects.filter(
                lower_level_outputs__isnull=False)
        elif content_object == REPORTABLE_CO_CONTENT_OBJECT:
            queryset = Reportable.objects.filter(
                cluster_objectives__isnull=False)
        elif content_object == REPORTABLE_CA_CONTENT_OBJECT:
            queryset = Reportable.objects.filter(
                cluster_activities__isnull=False)
        elif content_object == REPORTABLE_PP_CONTENT_OBJECT:
            queryset = Reportable.objects.filter(
                partner_projects__isnull=False)
        elif content_object == REPORTABLE_PA_CONTENT_OBJECT:
            queryset = Reportable.objects.filter(
                partner_activity_project_contexts__isnull=False)
        else:
            raise Http404

        object_id = self.request.query_params.get('object_id', None)
        if content_object is not None and object_id is not None:
            if content_object == REPORTABLE_PA_CONTENT_OBJECT:
                queryset = queryset.filter(partner_activity_project_contexts__activity=object_id)
            else:
                queryset = queryset.filter(object_id=object_id)

        q_list = []

        locations = self.request.query_params.get('locations', None)
        pds = self.request.query_params.get('pds', None)

        clusters = self.request.query_params.get('clusters', None)
        pd_statuses = self.request.query_params.get('pd_statuses', None)

        if locations:
            location_list = map(
                lambda item: int(item),
                filter(
                    lambda item: item != '' and item.isdigit(),
                    locations.split(',')))
            q_list.append(Q(locations__id__in=location_list))

        if pds:
            pd_list = map(
                lambda item: int(item),
                filter(
                    lambda item: item != '' and item.isdigit(),
                    pds.split(',')))
            q_list.append(
                Q(lower_level_outputs__cp_output__programme_document__id__in=pd_list))

        if clusters:
            cluster_list = list(map(lambda item: int(item), filter(
                lambda item: item != '' and item.isdigit(), clusters.split(
                    ','))))
            q_list.append(Q(cluster_objectives__cluster__id__in=cluster_list))
            q_list.append(Q(
                cluster_activities__cluster_objective__cluster__id__in=cluster_list))
            q_list.append(Q(partner_projects__clusters__id__in=cluster_list))
            q_list.append(Q(
                partner_activity_project_contexts__project__clusters__id__in=cluster_list))

        if pd_statuses:
            pd_status_list = map(
                lambda item: item,
                filter(
                    lambda item: item != '' and item.isdigit(),
                    pd_statuses.split(',')))
            q_list.append(
                Q(lower_level_outputs__cp_output__programme_document__status__in=pd_status_list))

        if q_list:
            queryset = queryset.filter(reduce(operator.or_, q_list))

        queryset = queryset.distinct()

        return queryset