Пример #1
0
    def test_post_process_reporting_period_max_calc(self):
        unit_type = IndicatorBlueprint.NUMBER
        calc_type = IndicatorBlueprint.MAX

        blueprint = QuantityTypeIndicatorBlueprintFactory(
            unit=unit_type,
            calculation_formula_across_locations=calc_type,
            calculation_formula_across_periods=calc_type,
        )
        partneractivity_reportable = QuantityReportableToPartnerActivityProjectContextFactory(
            content_object=self.project_context, blueprint=blueprint)

        partneractivity_reportable.disaggregations.clear()

        add_disaggregations_to_reportable(
            partneractivity_reportable,
            disaggregation_targets=["age", "gender", "height"])

        LocationWithReportableLocationGoalFactory(
            location=self.loc1,
            reportable=partneractivity_reportable,
        )

        LocationWithReportableLocationGoalFactory(
            location=self.loc2,
            reportable=partneractivity_reportable,
        )

        for _ in range(2):
            ClusterIndicatorReportFactory(
                reportable=partneractivity_reportable,
                report_status=INDICATOR_REPORT_STATUS.due,
            )

        # Creating Level-3 disaggregation location data for all locations
        generate_3_num_disagg_data(partneractivity_reportable,
                                   indicator_type="quantity")

        report_total = 0

        for loc_data in IndicatorLocationData.objects.filter(
                indicator_report__reportable=partneractivity_reportable):
            QuantityIndicatorDisaggregator.post_process(loc_data)

        # Indicator total only gets calculated if it's accepted or is sent back
        for ir in partneractivity_reportable.indicator_reports.all():
            ir.report_status = INDICATOR_REPORT_STATUS.accepted
            ir.save()

            if ir.total['c'] > report_total:
                report_total = ir.total['c']

        self.assertEquals(partneractivity_reportable.total['c'], report_total)
Пример #2
0
def trigger_indicator_report_recalculation(sender, instance, **kwargs):
    """
    Whenever an indicator blueprint is saved, IndicatorReport objects
    linked to this IndicatorBlueprint via its Reportable should all be
    recalculated for its total.
    """
    irs = IndicatorReport.objects.filter(
        reportable__in=instance.reportables.all())

    if instance.unit == IndicatorBlueprint.NUMBER:
        for ir in irs:
            QuantityIndicatorDisaggregator.calculate_indicator_report_total(ir)

    elif instance.unit == IndicatorBlueprint.PERCENTAGE:
        for ir in irs:
            RatioIndicatorDisaggregator.calculate_indicator_report_total(ir)
Пример #3
0
    def test_post_process_location_sum_calc(self):
        unit_type = IndicatorBlueprint.NUMBER
        calc_type = IndicatorBlueprint.SUM

        indicator = Reportable.objects.filter(
            blueprint__unit=unit_type,
            blueprint__calculation_formula_across_locations=calc_type,
        ).first()

        indicator_report = indicator.indicator_reports.first()
        loc_total = 0

        for loc_data in indicator_report.indicator_location_data.all():
            QuantityIndicatorDisaggregator.post_process(loc_data)
            loc_total += loc_data.disaggregation['()']['c']

        self.assertEquals(indicator_report.total['c'], loc_total)
Пример #4
0
    def test_post_process_location_avg_calc(self):
        unit_type = IndicatorBlueprint.NUMBER
        calc_type = IndicatorBlueprint.AVG

        blueprint = QuantityTypeIndicatorBlueprintFactory(
            unit=unit_type,
            calculation_formula_across_locations=calc_type,
        )
        partneractivity_reportable = QuantityReportableToPartnerActivityProjectContextFactory(
            content_object=self.project_context, blueprint=blueprint)

        partneractivity_reportable.disaggregations.clear()

        add_disaggregations_to_reportable(
            partneractivity_reportable,
            disaggregation_targets=["age", "gender", "height"])

        LocationWithReportableLocationGoalFactory(
            location=self.loc1,
            reportable=partneractivity_reportable,
        )

        LocationWithReportableLocationGoalFactory(
            location=self.loc2,
            reportable=partneractivity_reportable,
        )

        ir = ClusterIndicatorReportFactory(
            reportable=partneractivity_reportable,
            report_status=INDICATOR_REPORT_STATUS.due,
        )

        # Creating Level-3 disaggregation location data for all locations
        generate_3_num_disagg_data(partneractivity_reportable,
                                   indicator_type="quantity")

        avg_value = 0

        for loc_data in ir.indicator_location_data.all():
            QuantityIndicatorDisaggregator.post_process(loc_data)
            avg_value += loc_data.disaggregation['()']['c']

        avg_value /= (ir.indicator_location_data.count() * 1.0)

        self.assertEquals(ir.total['c'], avg_value)
Пример #5
0
    def test_post_process_location_avg_calc(self):
        unit_type = IndicatorBlueprint.NUMBER
        calc_type = IndicatorBlueprint.AVG

        indicator = Reportable.objects.filter(
            blueprint__unit=unit_type,
            blueprint__calculation_formula_across_locations=calc_type,
        ).first()

        indicator_report = indicator.indicator_reports.first()
        avg_value = 0

        for loc_data in indicator_report.indicator_location_data.all():
            QuantityIndicatorDisaggregator.post_process(loc_data)
            avg_value += loc_data.disaggregation['()']['c']

        avg_value /= (indicator_report.indicator_location_data.count() * 1.0)

        self.assertEquals(indicator_report.total['c'], avg_value)
Пример #6
0
    def test_post_process_reporting_period_max_calc(self):
        unit_type = IndicatorBlueprint.NUMBER
        calc_type = IndicatorBlueprint.MAX

        indicator = Reportable.objects.filter(
            blueprint__unit=unit_type,
            blueprint__calculation_formula_across_periods=calc_type,
        ).first()

        indicator_report = indicator.indicator_reports.first()
        report_total = 0

        for loc_data in indicator_report.indicator_location_data.all():
            QuantityIndicatorDisaggregator.post_process(loc_data)

        for report in indicator.indicator_reports.all():
            if report.total['c'] > report_total:
                report_total = report.total['c']

        self.assertEquals(indicator.total['c'], report_total)
Пример #7
0
    def import_data(self):
        for self.sheet in self.wb.worksheets:
            # Find "Location ID" column
            location_column_id = None
            for column in range(1, MAX_COLUMNS):
                if self.sheet.cell(row=COLUMN_HASH_ID, column=column).value == "#loc+id":
                    location_column_id = column
                    break
            if not location_column_id:
                return "Cannot find Location ID column"

            # Find "Total" column
            total_column_id = None
            for column in range(1, MAX_COLUMNS):
                if self.sheet.cell(row=1, column=column).value == "Total":
                    total_column_id = column
                    break
            if not total_column_id:
                return "Cannot find Total column"

            # Find first Disaggregation Value column
            dis_data_column_start_id = None
            for column in range(1, MAX_COLUMNS):
                if not self.sheet.cell(row=COLUMN_HASH_ID, column=column).value:
                    break
                if "#indicator+value" in self.sheet.cell(row=COLUMN_HASH_ID, column=column).value:
                    dis_data_column_start_id = column
                    break

            # Iterate over rows and save disaggregation values
            for row in range(COLUMN_HASH_ID + 1, self.sheet.max_row):
                # If row is empty, end of sheet
                if not self.sheet.cell(row=row, column=1).value:
                    break

                # Get IndicatorLocationData ID
                try:
                    ild_id = str(int(self.sheet.cell(row=row, column=location_column_id).value))

                    ind = IndicatorLocationData.objects.filter(pk=ild_id)

                    # Check if indicator has parent (UNICEF)
                    # If does, use parent to check partner
                    if self.partner and ind.filter(indicator_report__parent__isnull=False):
                        if not ind.filter(
                                indicator_report__parent__reportable__partner_activity_project_contexts__project__partner=self.partner
                        ).exists():
                            return "Parent of Indicator ID " \
                                + ild_id \
                                + " does not belong to partner " \
                                + str(self.partner)
                    # Check if Partner is allowed to modify data
                    elif self.partner and ind.filter(
                            Q(**{
                                'indicator_report__reportable__cluster_objectives'
                                '__cluster__partner_projects__partner': self.partner
                            }) |
                            Q(**{
                                'indicator_report__reportable'
                                '__cluster_objectives__cluster_activities__partner_activities'
                                '__partner': self.partner
                            }) |
                            Q(**{
                                'indicator_report__reportable'
                                '__cluster_activities__cluster_objective__cluster__partner_projects'
                                '__partner': self.partner
                            }) |
                            Q(**{
                                'indicator_report__reportable'
                                '__cluster_activities__partner_activities'
                                '__partner': self.partner
                            }) |
                            Q(**{
                                'indicator_report__reportable'
                                '__partner_activity_project_contexts__project'
                                '__partner': self.partner
                            }) |
                            Q(**{
                                'indicator_report__reportable'
                                '__partner_projects'
                                '__partner': self.partner
                            })).count() == 0:
                        return "Indicator ID " + ild_id + " does not belong to partner " + str(self.partner)
                    indicator = IndicatorLocationData.objects.get(
                        pk=int(self.sheet.cell(row=row, column=location_column_id).value)
                    )

                    # Check if Indicator Report is not able to submit anymore
                    if not indicator.indicator_report.can_import:
                        transaction.rollback()
                        return "Indicator in row {} is already submitted. Please remove row and try again.".format(
                            row,)

                except IndicatorLocationData.DoesNotExist:
                    return "Cannot find Indicator Location Data data for ID " \
                        + str(self.sheet.cell(row=row, column=location_column_id).value)

                blueprint = indicator.indicator_report.reportable.blueprint
                data = indicator.disaggregation
                # Prepare
                already_updated_row_value = False
                for column in range(dis_data_column_start_id if dis_data_column_start_id else total_column_id,
                                    total_column_id + 1):
                    try:
                        value = self.sheet.cell(row=row, column=column).value
                        # Check if value is present in cell
                        if value is not None:
                            # Evaluate ID of Disaggregation Type
                            dis_type_id = "()"
                            dis_type_value = self.sheet.cell(row=2, column=column).value
                            if dis_type_value:
                                dis_type_value = sorted(list(map(int, str(dis_type_value).split(","))), key=int)
                                dis_type_id = str(tuple(dis_type_value))

                            if dis_type_id not in data:
                                # Check if data is proper disaggregation value
                                for dt in dis_type_value:
                                    if not DisaggregationValue.objects.filter(pk=dt).exists():
                                        transaction.rollback()
                                        return "Disaggregation {} does not exists".format(
                                            self.sheet.cell(row=4, column=column).value)
                                    # Check if filled disaggregation values belongs to their type
                                    dv = DisaggregationValue.objects.get(pk=dt)
                                    if dv.disaggregation.id not in indicator.disaggregation_reported_on:
                                        transaction.rollback()
                                        return "Disaggregation {} does not belong to this Indicator".format(
                                            self.sheet.cell(row=4, column=column).value)
                                # Create value
                                data[dis_type_id] = dict()

                            already_updated_row_value = True

                            # Update values
                            if blueprint.unit == IndicatorBlueprint.NUMBER:
                                data[dis_type_id]["v"] = value
                            else:
                                if isinstance(value, datetime):
                                    transaction.rollback()
                                    return "Value in column {}, row {} is Date Time. Please format row to Plain Text."\
                                        .format(self.sheet.cell(row=4, column=column).value, row)
                                values = value.split("/")
                                data[dis_type_id]["v"] = int(values[0])
                                data[dis_type_id]["d"] = int(values[1])
                        else:
                            # if value is not present, check if it should be
                            # all rows need to updated

                            # Evaluate ID of Disaggregation Type
                            dis_type_value = self.sheet.cell(row=2, column=column).value
                            if dis_type_value:
                                dis_type_value = sorted(list(map(int, str(dis_type_value).split(","))), key=int)
                            if dis_type_value:
                                if len(dis_type_value) == indicator.level_reported:
                                    # Check if data is proper disaggregation value
                                    for dt in dis_type_value:
                                        dv = DisaggregationValue.objects.get(pk=dt)
                                        if dv.disaggregation.id in indicator.disaggregation_reported_on and \
                                                already_updated_row_value:
                                            transaction.rollback()
                                            return "Please fulfill required value to column {}, row {}"\
                                                .format(self.sheet.cell(row=4, column=column).value, row)

                    except Exception:
                        traceback.print_exc()
                        transaction.rollback()
                        return "Cannot assign disaggregation value to column {}, row {}"\
                            .format(self.sheet.cell(row=4, column=column).value, row)

                indicator.disaggregation = data
                indicator.save()

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

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

        return
Пример #8
0
def generate_1_num_disagg_data(reportable, indicator_type="quantity"):
    # IndicatorReport from QuantityReportable object -
    # IndicatorLocationData
    locations = Location.objects.all()

    for idx, indicator_report_from_reportable in enumerate(
            reportable.indicator_reports.all()):
        disagg_idx = 0

        # 1 num_disaggregation & 0 level_reported
        location = locations[disagg_idx]

        if indicator_type == "quantity":
            disaggregation = {
                '()': {
                    'v': random.randint(50, 1000),
                    'd': 0,
                    'c': 0
                }
            }

        elif indicator_type == "ratio":
            disaggregation = {
                '()': {
                    'v': random.randint(50, 1000),
                    'd': random.randint(2000, 4000),
                    'c': 0
                }
            }

        location_data = IndicatorLocationDataFactory(
            indicator_report=indicator_report_from_reportable,
            location=location,
            num_disaggregation=1,
            level_reported=0,
            disaggregation_reported_on=list(),
            disaggregation=disaggregation,
        )

        if indicator_type == "quantity":
            QuantityIndicatorDisaggregator.post_process(location_data)

        elif indicator_type == "ratio":
            RatioIndicatorDisaggregator.post_process(location_data)

        disagg_idx += 1

        # 1 num_disaggregation & 1 level_reported
        disaggregation_comb_1_pairs = list(
            combinations(
                list(
                    indicator_report_from_reportable.disaggregations.
                    values_list('id', flat=True)), 1))

        for pair in disaggregation_comb_1_pairs:
            location = locations[disagg_idx]

            location_data = IndicatorLocationDataFactory(
                indicator_report=indicator_report_from_reportable,
                location=location,
                num_disaggregation=1,
                level_reported=1,
                disaggregation_reported_on=pair,
                disaggregation=generate_data_combination_entries(
                    indicator_report_from_reportable.disaggregation_values(
                        id_only=True, filter_by_id__in=pair),
                    indicator_type=indicator_type,
                    r=1))

            if indicator_type == "quantity":
                QuantityIndicatorDisaggregator.post_process(location_data)

            elif indicator_type == "ratio":
                RatioIndicatorDisaggregator.post_process(location_data)

            disagg_idx += 1
Пример #9
0
    def setUp(self):
        self.country = CountryFactory()
        self.workspace = WorkspaceFactory(countries=[
            self.country,
        ])
        self.response_plan = ResponsePlanFactory(workspace=self.workspace)
        self.cluster = ClusterFactory(type='cccm',
                                      response_plan=self.response_plan)
        self.loc_type = GatewayTypeFactory(country=self.country)
        self.carto_table = CartoDBTableFactory(location_type=self.loc_type,
                                               country=self.country)
        self.loc1 = LocationFactory(gateway=self.loc_type,
                                    carto_db_table=self.carto_table)
        self.loc2 = LocationFactory(gateway=self.loc_type,
                                    carto_db_table=self.carto_table)
        self.unicef_officer = PersonFactory()
        self.unicef_focal_point = PersonFactory()
        self.partner_focal_point = PersonFactory()
        self.objective = ClusterObjectiveFactory(cluster=self.cluster,
                                                 locations=[
                                                     self.loc1,
                                                     self.loc2,
                                                 ])
        self.activity = ClusterActivityFactory(
            cluster_objective=self.objective, locations=[self.loc1, self.loc2])
        self.partner = PartnerFactory(
            country_code=self.country.country_short_code)
        self.user = NonPartnerUserFactory()
        self.partner_user = PartnerUserFactory(partner=self.partner)
        ClusterPRPRoleFactory(user=self.user,
                              workspace=self.workspace,
                              cluster=self.cluster,
                              role=PRP_ROLE_TYPES.cluster_imo)
        IPPRPRoleFactory(user=self.partner_user,
                         workspace=self.workspace,
                         role=PRP_ROLE_TYPES.ip_authorized_officer)
        IPPRPRoleFactory(user=self.partner_user,
                         workspace=self.workspace,
                         cluster=None,
                         role=PRP_ROLE_TYPES.cluster_member)
        self.project = PartnerProjectFactory(
            partner=self.partner,
            clusters=[self.cluster],
            locations=[self.loc1, self.loc2],
        )
        self.p_activity = ClusterActivityPartnerActivityFactory(
            partner=self.partner,
            cluster_activity=self.activity,
        )
        self.project_context = PartnerActivityProjectContextFactory(
            project=self.project,
            activity=self.p_activity,
        )
        self.sample_disaggregation_value_map = {
            "height": ["tall", "medium", "short", "extrashort"],
            "age": ["1-2m", "3-4m", "5-6m", '7-10m', '11-13m', '14-16m'],
            "gender": ["male", "female", "other"],
        }

        blueprint = QuantityTypeIndicatorBlueprintFactory(
            unit=IndicatorBlueprint.NUMBER,
            calculation_formula_across_locations=IndicatorBlueprint.SUM,
            calculation_formula_across_periods=IndicatorBlueprint.SUM,
        )
        self.partneractivity_reportable = QuantityReportableToPartnerActivityProjectContextFactory(
            content_object=self.project_context, blueprint=blueprint)

        LocationWithReportableLocationGoalFactory(
            location=self.loc1,
            reportable=self.partneractivity_reportable,
        )

        LocationWithReportableLocationGoalFactory(
            location=self.loc2,
            reportable=self.partneractivity_reportable,
        )

        self.pd = ProgrammeDocumentFactory(workspace=self.workspace,
                                           partner=self.partner,
                                           sections=[
                                               SectionFactory(),
                                           ],
                                           unicef_officers=[
                                               self.unicef_officer,
                                           ],
                                           unicef_focal_point=[
                                               self.unicef_focal_point,
                                           ],
                                           partner_focal_point=[
                                               self.partner_focal_point,
                                           ])

        for idx in range(2):
            qpr_period = QPRReportingPeriodDatesFactory(
                programme_document=self.pd)
            ProgressReportFactory(
                start_date=qpr_period.start_date,
                end_date=qpr_period.end_date,
                due_date=qpr_period.due_date,
                report_number=idx + 1,
                report_type=qpr_period.report_type,
                is_final=False,
                programme_document=self.pd,
                submitted_by=self.user,
                submitting_user=self.user,
            )

        for idx in range(6):
            hr_period = HRReportingPeriodDatesFactory(
                programme_document=self.pd)
            ProgressReportFactory(
                start_date=hr_period.start_date,
                end_date=hr_period.end_date,
                due_date=hr_period.due_date,
                report_number=idx + 1,
                report_type=hr_period.report_type,
                is_final=False,
                programme_document=self.pd,
                submitted_by=self.user,
                submitting_user=self.user,
            )

        self.cp_output = PDResultLinkFactory(programme_document=self.pd, )
        self.llo = LowerLevelOutputFactory(cp_output=self.cp_output, )
        self.llo_reportable = QuantityReportableToLowerLevelOutputFactory(
            content_object=self.llo,
            blueprint=QuantityTypeIndicatorBlueprintFactory(
                unit=IndicatorBlueprint.NUMBER,
                calculation_formula_across_locations=IndicatorBlueprint.SUM,
            ))

        self.llo_reportable.disaggregations.clear()
        self.partneractivity_reportable.disaggregations.clear()

        # Create the disaggregations and values in the db for all response plans
        # including one for no response plan as well
        for disagg_name, values in self.sample_disaggregation_value_map.items(
        ):
            disagg = IPDisaggregationFactory(name=disagg_name)
            cluster_disagg = DisaggregationFactory(
                name=disagg_name, response_plan=self.response_plan)

            self.llo_reportable.disaggregations.add(disagg)
            self.partneractivity_reportable.disaggregations.add(cluster_disagg)

            for value in values:
                DisaggregationValueFactory(disaggregation=cluster_disagg,
                                           value=value)
                DisaggregationValueFactory(disaggregation=disagg, value=value)

        LocationWithReportableLocationGoalFactory(
            location=self.loc1,
            reportable=self.llo_reportable,
        )

        LocationWithReportableLocationGoalFactory(
            location=self.loc2,
            reportable=self.llo_reportable,
        )

        for _ in range(2):
            with patch("django.db.models.signals.ModelSignal.send", Mock()):
                ClusterIndicatorReportFactory(
                    reportable=self.partneractivity_reportable,
                    report_status=INDICATOR_REPORT_STATUS.submitted,
                )

        # Creating Level-3 disaggregation location data for all locations
        generate_3_num_disagg_data(self.partneractivity_reportable,
                                   indicator_type="quantity")

        for loc_data in IndicatorLocationData.objects.filter(
                indicator_report__reportable=self.partneractivity_reportable):
            QuantityIndicatorDisaggregator.post_process(loc_data)

        for pr in self.pd.progress_reports.all():
            ProgressReportIndicatorReportFactory(
                progress_report=pr,
                reportable=self.llo_reportable,
                report_status=INDICATOR_REPORT_STATUS.submitted,
                overall_status=OVERALL_STATUS.met,
            )

        # Creating Level-3 disaggregation location data for all locations
        generate_3_num_disagg_data(self.llo_reportable,
                                   indicator_type="quantity")

        for loc_data in IndicatorLocationData.objects.filter(
                indicator_report__reportable=self.llo_reportable):
            QuantityIndicatorDisaggregator.post_process(loc_data)

        super().setUp()
Пример #10
0
    def import_data(self):
        partner_contribution = None
        challenges = None
        proposed_way_forward = None
        pd_output_narratives = dict()

        for idx, self.sheet in enumerate(self.wb.worksheets):
            if self.sheet.title.lower() == 'readme':
                continue

            # Find "Location ID" column
            location_column_id = None
            for column in range(1, MAX_COLUMNS):
                if self.sheet.cell(row=COLUMN_HASH_ID,
                                   column=column).value == "#loc+id":
                    location_column_id = column
                    break
            if not location_column_id:
                return "Cannot find Location ID column"

            # Find "Total" column
            total_column_id = None
            for column in range(1, MAX_COLUMNS):
                if self.sheet.cell(row=1, column=column).value == "Total":
                    total_column_id = column
                    break
            if not total_column_id:
                return "Cannot find Total column"

            # Find first Disaggregation Value column
            dis_data_column_start_id = None
            for column in range(1, MAX_COLUMNS):
                if not self.sheet.cell(row=COLUMN_HASH_ID,
                                       column=column).value:
                    break
                if "#indicator+value" in self.sheet.cell(row=COLUMN_HASH_ID,
                                                         column=column).value:
                    dis_data_column_start_id = column
                    break

            # Find "Progress" column
            progress_column_id = None
            for column in range(1, MAX_COLUMNS):
                if self.sheet.cell(row=COLUMN_HASH_ID,
                                   column=column).value == "#pr+id":
                    progress_column_id = column
                    break
            if not progress_column_id:
                return "Cannot find Progress ID column"

            # Other Info columns data retrieve
            # Partner contribution to date
            if not partner_contribution:
                partner_contribution = self.sheet.cell(row=COLUMN_HASH_ID + 1,
                                                       column=9).value

            # Challenges/bottlenecks in the reporting period
            if not challenges:
                challenges = self.sheet.cell(row=COLUMN_HASH_ID + 1,
                                             column=11).value

            # Proposed way forward
            if not proposed_way_forward:
                proposed_way_forward = self.sheet.cell(row=COLUMN_HASH_ID + 1,
                                                       column=12).value

            # ... and assign if is QPR
            try:
                progress_id = self.sheet.cell(row=COLUMN_HASH_ID + 1,
                                              column=progress_column_id).value
                pr = ProgressReport.objects.get(pk=progress_id)
            except ProgressReport.DoesNotExist:
                return "Cannot find Progress Report"

            # Iterate over rows and save disaggregation values
            for row in range(COLUMN_HASH_ID + 1, self.sheet.max_row):
                # If row is empty, end of sheet
                if not self.sheet.cell(row=row, column=1).value:
                    # Update Other Info sheet
                    if pr.report_type == common.QPR_TYPE:
                        pr.partner_contribution_to_date = partner_contribution
                        pr.challenges_in_the_reporting_period = challenges
                        pr.proposed_way_forward = proposed_way_forward
                        pr.save()
                    break

                # Get IndicatorLocationData ID
                try:
                    ild_id = str(
                        int(
                            self.sheet.cell(row=row,
                                            column=location_column_id).value))

                    ind = IndicatorLocationData.objects.filter(pk=ild_id)

                    # Check if indicator has parent (UNICEF)
                    # If does, use parent to check partner
                    if self.partner and ind.filter(
                            indicator_report__parent__isnull=False):
                        if not ind.filter(
                                indicator_report__parent__reportable__partner_activity_project_contexts__project__partner
                                =self.partner).exists():
                            return "Parent of Indicator ID " \
                                + ild_id \
                                + " does not belong to partner " \
                                + str(self.partner)
                    # Check if Partner is allowed to modify data
                    elif self.partner and ind.filter(
                            indicator_report__progress_report__programme_document__partner
                            =self.partner).count() == 0:
                        return "Indicator ID " + ild_id + " does not belong to partner " + str(
                            self.partner)
                    indicator = IndicatorLocationData.objects.get(pk=int(
                        self.sheet.cell(row=row,
                                        column=location_column_id).value))

                    # Check if Indicator Report is not able to submit anymore
                    if not indicator.indicator_report.can_import:
                        transaction.rollback()
                        return "Indicator in row {} is already submitted. Please remove row and try again.".format(
                            row, )

                except IndicatorLocationData.DoesNotExist:
                    return "Cannot find Indicator Location Data data for ID " \
                        + str(self.sheet.cell(row=row,
                                              column=location_column_id).value)

                blueprint = indicator.indicator_report.reportable.blueprint
                data = indicator.disaggregation

                if pr.report_type == common.QPR_TYPE:
                    narrative_assessment = self.sheet.cell(row=row,
                                                           column=19).value
                    llo = indicator.indicator_report.reportable.content_object

                    if llo.id not in pd_output_narratives \
                            and (narrative_assessment is not None and narrative_assessment != ''):
                        pd_output_narratives[llo.id] = narrative_assessment

                        indicator.indicator_report.narrative_assessment = narrative_assessment
                        indicator.indicator_report.save()

                        pr.indicator_reports.filter(
                            reportable__lower_level_outputs=llo).update(
                                narrative_assessment=narrative_assessment)

                # Prepare
                already_updated_row_value = False
                for column in range(
                        dis_data_column_start_id if dis_data_column_start_id
                        else total_column_id, total_column_id + 1):
                    try:
                        value = self.sheet.cell(row=row, column=column).value
                        # Check if value is present in cell
                        if value is not None:
                            # Evaluate ID of Disaggregation Type
                            dis_type_id = "()"
                            dis_type_value = self.sheet.cell(
                                row=2, column=column).value
                            if dis_type_value:
                                dis_type_value = sorted(list(
                                    map(int,
                                        str(dis_type_value).split(","))),
                                                        key=int)
                                dis_type_id = str(tuple(dis_type_value))

                            if dis_type_id not in data:
                                # Check if data is proper disaggregation value
                                for dt in dis_type_value:
                                    if not DisaggregationValue.objects.filter(
                                            pk=dt).exists():
                                        transaction.rollback()
                                        return "Disaggregation {} does not exists".format(
                                            self.sheet.cell(
                                                row=4, column=column).value)
                                    # Check if filled disaggregation values
                                    # belongs to their type
                                    dv = DisaggregationValue.objects.get(pk=dt)
                                    if dv.disaggregation.id not in indicator.disaggregation_reported_on:
                                        transaction.rollback()
                                        return "Disaggregation {} does not belong to this Indicator".format(
                                            self.sheet.cell(
                                                row=4, column=column).value)
                                # Create value
                                data[dis_type_id] = dict()

                            already_updated_row_value = True

                            # Update values
                            if blueprint.unit == IndicatorBlueprint.NUMBER:
                                data[dis_type_id]["v"] = int(value)
                            else:
                                if isinstance(value, datetime):
                                    transaction.rollback()
                                    return "Value in column {}, row {} is Date Time. Please format row to Plain Text."\
                                        .format(self.sheet.cell(row=4, column=column).value, row)
                                values = value.split("/")
                                data[dis_type_id]["v"] = int(values[0])
                                data[dis_type_id]["d"] = int(values[1])
                        else:
                            # if value is not present, check if it should be
                            # all rows need to updated

                            # Evaluate ID of Disaggregation Type
                            dis_type_value = self.sheet.cell(
                                row=2, column=column).value
                            if dis_type_value:
                                dis_type_value = sorted(list(
                                    map(int,
                                        str(dis_type_value).split(","))),
                                                        key=int)
                            if dis_type_value:
                                if len(dis_type_value
                                       ) == indicator.level_reported:
                                    # Check if data is proper disaggregation
                                    # value
                                    for dt in dis_type_value:
                                        dv = DisaggregationValue.objects.get(
                                            pk=dt)
                                        if dv.disaggregation.id in indicator.disaggregation_reported_on and \
                                                already_updated_row_value:
                                            transaction.rollback()
                                            return "Please fulfill required value to column {}, row {}"\
                                                .format(self.sheet.cell(row=4, column=column).value, row)

                    except Exception:
                        traceback.print_exc()
                        transaction.rollback()
                        return "Cannot assign disaggregation value to column {}, row {}"\
                            .format(self.sheet.cell(row=4, column=column).value, row)

                indicator.disaggregation = data
                indicator.save()

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

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

        return