def save_indicators(self, indicators_json, program): for indicator_data in indicators_json: # Pop off the ones that will need extensive processing unassigned_results_json = indicator_data.pop('result_set') periodic_targets_json = indicator_data.pop('periodictargets') # Pop off the many-to-many fields that will be added after the indicator is saved indicator_types = indicator_data.pop('indicator_type') objectives = indicator_data.pop('objectives') strategic_objectives = indicator_data.pop('strategic_objectives') # TODO: disaggregation types should be handled disaggregations = indicator_data.pop('disaggregation') # First do the foreign keys and save the indicator indicator_data['level'] = self.replace_names_with_values( indicator_data['level'], Level, 'name', ['name']) indicator_data[ 'data_collection_frequency'] = self.replace_names_with_values( indicator_data['data_collection_frequency'], DataCollectionFrequency, 'frequency', ['frequency']) indicator_data[ 'reporting_frequency'] = self.replace_names_with_values( indicator_data['reporting_frequency'], ReportingFrequency, 'frequency', ['frequency']) indicator_data['sector'] = self.replace_names_with_values( indicator_data['sector'], Sector, 'sector', ['sector']) indicator_data[ 'external_service_record'] = self.replace_names_with_values( indicator_data['external_service_record'], ExternalService, 'name', ['name']) indicator_data['approved_by'] = self.replace_names_with_values( indicator_data['approved_by'], TolaUser, 'name', ['name']) indicator_data[ 'approval_submitted_by'] = self.replace_names_with_values( indicator_data['approval_submitted_by'], TolaUser, 'name', ['name']) indicator = Indicator(**indicator_data) indicator.program = program indicator.save() indicator_types = self.replace_names_with_values( indicator_types, IndicatorType, 'indicator_type', ['indicator_type']) objectives = self.replace_names_with_values( objectives, Objective, 'name', ['name', 'program_id']) strategic_objectives = self.replace_names_with_values( strategic_objectives, StrategicObjective, 'name', ['name']) indicator.indicator_type.add(*indicator_types) indicator.objectives.add(*objectives) indicator.strategic_objectives.add(*strategic_objectives) indicator.save() self.save_periodic_targets(periodic_targets_json, indicator) self.save_results(unassigned_results_json, indicator)
def get_or_generate_indicator(indicator_description): try: indicator = Indicator.objects.get(slug=indicator_description['slug']) except ObjectDoesNotExist: # add basic fields to indicator indicator = Indicator(**{k: v for k, v in indicator_description.items() if k != 'subdomain'}) indicator.save() print(indicator_description['subdomain']) # add indicator to its subdomain subdomain = get_or_generate_subdomain(indicator_description['subdomain']) subdomain.indicators.add(indicator) subdomain.save() return indicator
def periodic_targets_form(request, program): """ Returns a form for the periodic targets sub-section, used by the Indicator Form For historical reasons, the input is a POST of the whole indicator form sent via ajax from which a subset of fields are used to generate the returned template """ if not request.has_write_access: raise PermissionDenied program = get_object_or_404(Program, pk=program) form = PTFormInputsForm(data=request.POST) if not form.is_valid(): return JsonResponse(form.errors) event_name = '' start_date = '' target_frequency_num_periods = 1 target_frequency_type = form.cleaned_data.get('target_frequency') if target_frequency_type in Indicator.REGULAR_TARGET_FREQUENCIES: start_date = program.reporting_period_start # target_frequency_num_periods = IPTT_ReportView._get_num_periods( # start_date, program.reporting_period_end, target_frequency_type) target_frequency_num_periods = len([ p for p in PeriodicTarget.generate_for_frequency( target_frequency_type)(start_date, program.reporting_period_end) ]) generated_targets = generate_periodic_targets( target_frequency_type, start_date, target_frequency_num_periods, event_name) dummy_indicator = Indicator( target_frequency=target_frequency_type, unit_of_measure_type=form.cleaned_data.get('unit_of_measure_type'), is_cumulative=False, ) content = render_to_string('indicators/indicatortargets.html', { 'indicator': dummy_indicator, 'periodic_targets': generated_targets }) return JsonResponse({ 'content': content, })
def create_indicators( self, program_id, param_sets, indicator_suffix='', apply_skips=True, apply_rf_skips=False, personal_indicator=False): indicator_ids = [] program = Program.objects.get(id=program_id) frequency_labels = { Indicator.LOP: 'LoP only', Indicator.MID_END: 'Midline and endline', Indicator.EVENT: 'Event', Indicator.ANNUAL: 'Annual', Indicator.SEMI_ANNUAL: 'Semi-annual', Indicator.TRI_ANNUAL: 'Tri-annual', Indicator.QUARTERLY: 'Quarterly', Indicator.MONTHLY: 'Monthly', } uom_labels = { Indicator.NUMBER: 'Number (#)', Indicator.PERCENTAGE: "Percentage (%)", } direction_labels = { Indicator.DIRECTION_OF_CHANGE_NONE: "Direction of change NA", Indicator.DIRECTION_OF_CHANGE_POSITIVE: "Increase (+)", Indicator.DIRECTION_OF_CHANGE_NEGATIVE: "Decrease (-)", } # Keep track of results and evidence created across the whole programs so we can skip them periodically result_count = 0 result_skip_mod = 7 evidence_count = 0 evidence_skip_mod = 7 old_levels = list(Indicator.objects.filter(old_level__isnull=False).order_by('old_level') .distinct().values_list('old_level', flat=True)) old_levels.append(None) old_level_cycle = cycle(old_levels) rf_levels = list(Level.objects.filter(program__id=program.id)) if apply_rf_skips: rf_levels.append(None) rf_level_cycle = cycle(rf_levels) indicator_types = list(IndicatorType.objects.all()) if apply_skips: indicator_types.append(None) type_cycle = cycle(indicator_types) sectors = list(Sector.objects.all()[:5]) if apply_skips: sectors.append(None) sector_cycle = cycle(sectors) sites = list(SiteProfile.objects.filter(country__country="Tolaland")) if apply_skips: sites.append(None) site_cycle = cycle(sites) for n, params in enumerate(param_sets): if params['is_cumulative']: cumulative_text = 'Cumulative' else: cumulative_text = 'Non-cumulative' null_text = '| No {}'.format(params['null_level']) if params['null_level'] else '' indicator_name = '{} | {} | {} | {} {}'.format( frequency_labels[params['freq']], uom_labels[params['uom_type']], cumulative_text, direction_labels[params['direction']], null_text, ) frequency = params['freq'] if params['null_level'] == 'targets': frequency = None # Finally, create the indicator indicator = Indicator( name=indicator_name + ' | ' + indicator_suffix, is_cumulative=params['is_cumulative'], target_frequency=frequency, unit_of_measure='This is a UOM', baseline=0, unit_of_measure_type=params['uom_type'], direction_of_change=params['direction'], program=program, old_level=None if program.results_framework else next(old_level_cycle), level=next(rf_level_cycle), sector=None if not personal_indicator else next(sector_cycle), ) indicator.save() i_type = next(type_cycle) if personal_indicator and i_type: indicator.indicator_type.add(i_type) indicator.save() indicator_ids.append(indicator.id) if params['null_level'] == 'targets': indicator.lop_target = 100 indicator.save() continue self.make_targets(program, indicator) periodic_targets = PeriodicTarget.objects.filter(indicator__id=indicator.id) # Different combinations of UOM type, direction of change and cummulativeness require # different inputs. if params['uom_type'] == Indicator.NUMBER: if params['direction'] == Indicator.DIRECTION_OF_CHANGE_POSITIVE: if params['is_cumulative']: target_start = 100 target_increment = target_start achieved_start = 90 achieved_increment = int(achieved_start * 1.1) else: target_start = 100 target_increment = target_start achieved_start = 90 achieved_increment = int(achieved_start * 1.1) else: if params['is_cumulative']: target_start = 500 target_increment = -int(math.floor((target_start/len(periodic_targets))/10)*10) achieved_start = 400 achieved_increment = target_increment+2 else: target_start = 500 target_increment = -int(math.floor((target_start/len(periodic_targets))/10)*10) achieved_start = 400 achieved_increment = target_increment * .8 else: if params['direction'] == Indicator.DIRECTION_OF_CHANGE_POSITIVE: # Don't need to check non-cumulative because we don't really handle it target_start = 10 target_increment = 3 achieved_start = 7 achieved_increment = 4 else: # Don't need to check non-cumulative because we don't really handle it target_start = 90 target_increment = max(-math.floor(target_start/len(periodic_targets)), -2) achieved_start = 95 achieved_increment = target_increment - 1 lop_target = 0 day_offset = timedelta(days=2) for i, pt in enumerate(periodic_targets): # Create the target amount (the PeriodicTarget object has already been created) pt.target = target_start + target_increment * i pt.save() if params['is_cumulative']: lop_target = pt.target else: lop_target += pt.target # Users shouldn't put in results with a date in the future, so neither should we. if pt.start_date and date.today() < pt.start_date + day_offset: continue # Skip creating a result if the null_level is result or if # the number of results has reached the arbitrary skip point. result_count += 1 if (apply_skips and result_count % result_skip_mod == result_skip_mod - 2) or \ params['null_level'] == 'results': continue # if params['direction'] == Indicator.DIRECTION_OF_CHANGE_NEGATIVE: # achieved_value = achieved_start - (achieved_increment * i) # else: achieved_value = achieved_start + (achieved_increment * i) results_to_create = 1 if apply_skips and result_count % result_skip_mod in (1, result_skip_mod - 3): results_to_create = 2 if params['uom_type'] == Indicator.NUMBER: achieved_value = int(achieved_value * .4) else: achieved_value = int(achieved_value * .9) # Now create the Results and their related Records if pt.start_date: date_collected = pt.start_date + day_offset else: date_collected = date.today() for c in range(results_to_create): rs = Result( periodic_target=pt, indicator=indicator, program=program, achieved=achieved_value, date_collected=date_collected) rs.save() date_collected = date_collected + day_offset if params['uom_type'] == Indicator.NUMBER: achieved_value = int(achieved_value * 1.5) else: achieved_value = int(achieved_value * 1.15) evidence_count += 1 if params['null_level'] == 'evidence': continue if apply_skips and evidence_count % evidence_skip_mod == int(evidence_skip_mod / 2): evidence_count += 1 continue rs.record_name = 'Evidence {} for result id {}'.format(evidence_count, rs.id) rs.evidence_url = 'http://my/evidence/url' r_site = next(site_cycle) if personal_indicator and r_site: rs.site.add(r_site) rs.save() indicator.lop_target = lop_target indicator.save() return indicator_ids
def create_partial_indicator_set(self, program_id, indicator_suffix=''): indicator_ids = [] program = Program.objects.get(id=program_id) frequency_labels = { Indicator.LOP: 'Life of Program (LoP) only', Indicator.MID_END: 'Midline and endline', Indicator.ANNUAL: 'Annual', Indicator.QUARTERLY: 'Quarterly', } uom_labels = { Indicator.DIRECTION_OF_CHANGE_NONE: "Direction of change (not applicable)", Indicator.NUMBER: 'Number (#)', Indicator.PERCENTAGE: "Percentage (%)", } direction_labels = { Indicator.DIRECTION_OF_CHANGE_NONE: "Direction of change (not applicable)", Indicator.DIRECTION_OF_CHANGE_POSITIVE: "Increase (+)", Indicator.DIRECTION_OF_CHANGE_NEGATIVE: "Decrease (-)", } indicator_types = [ { 'freq': Indicator.ANNUAL, 'uom_type': Indicator.NUMBER, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_POSITIVE }, { 'freq': Indicator.ANNUAL, 'uom_type': Indicator.PERCENTAGE, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_NONE }, { 'freq': Indicator.QUARTERLY, 'uom_type': Indicator.NUMBER, 'is_cumulative': False, 'direction': Indicator.DIRECTION_OF_CHANGE_NONE }, { 'freq': Indicator.QUARTERLY, 'uom_type': Indicator.PERCENTAGE, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_NEGATIVE }, { 'freq': Indicator.LOP, 'uom_type': Indicator.NUMBER, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_NONE }, { 'freq': Indicator.LOP, 'uom_type': Indicator.PERCENTAGE, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_NONE }, { 'freq': Indicator.MID_END, 'uom_type': Indicator.NUMBER, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_NONE }, { 'freq': Indicator.MID_END, 'uom_type': Indicator.PERCENTAGE, 'is_cumulative': True, 'direction': Indicator.DIRECTION_OF_CHANGE_NONE }, ] for q, combo in enumerate(indicator_types): if combo['is_cumulative']: cumulative_text = 'cumulative' else: cumulative_text = 'non-cumulative' indicator_name = '{} | {} | {} | {}'.format( frequency_labels[combo['freq']], uom_labels[combo['uom_type']], direction_labels[combo['direction']], cumulative_text) indicator = Indicator(name=indicator_name + indicator_suffix, is_cumulative=combo['is_cumulative'], target_frequency=combo['freq'], unit_of_measure='This is a UOM', baseline=0, unit_of_measure_type=combo['uom_type'], direction_of_change=combo['direction'], program=program, level=Level.objects.get(name='Goal')) indicator.save() indicator_ids.append(indicator.id) if q in [2, 3, 7]: continue self.make_targets(program, indicator) periodic_targets = PeriodicTarget.objects.filter( indicator__id=indicator.id) # Different combinations of UOM type, direction of change and cummulativeness require # different inputs. if q == 4: continue if combo['uom_type'] == Indicator.NUMBER: if combo[ 'direction'] == Indicator.DIRECTION_OF_CHANGE_POSITIVE: if combo['is_cumulative']: target_start = 100 target_increment = target_start achieved_start = 90 achieved_increment = achieved_start else: target_start = 100 target_increment = target_start achieved_start = 90 achieved_increment = achieved_start else: if combo['is_cumulative']: target_start = 500 target_increment = -int( math.floor( (target_start / len(periodic_targets)) / 10) * 10) achieved_start = 400 achieved_increment = -(target_increment - 2) else: target_start = 500 target_increment = -int( math.floor( (target_start / len(periodic_targets)) / 10) * 10) achieved_start = 400 achieved_increment = -(target_increment - 2) else: if combo[ 'direction'] == Indicator.DIRECTION_OF_CHANGE_POSITIVE: # Don't need to check cumulative because we don't really handle it target_start = 5 target_increment = 5 achieved_start = 4 achieved_increment = 5 else: # Don't need to check cumulative because we don't really handle it target_start = 90 target_increment = max( -math.floor(target_start / len(periodic_targets)), -2) achieved_start = 95 achieved_increment = target_increment + 1 lop_target = 0 day_offset = timedelta(days=2) for i, pt in enumerate(periodic_targets): # Create the target amount (the PeriodicTarget object has already been created) pt.target = target_start + target_increment * i pt.save() if combo['is_cumulative']: lop_target = pt.target else: lop_target += pt.target # Users shouldn't put in results with a date in the future, so neither should we. if pt.start_date and date.today() < pt.start_date + day_offset: continue # Now create the Results and their related Records if pt.start_date: date_collected = pt.start_date + day_offset else: date_collected = date.today() cd = CollectedData(periodic_target=pt, indicator=indicator, program=program, achieved=achieved_start + achieved_increment * i, date_collected=date_collected) cd.save() if q in [5, 6]: continue document = Documentation.objects.create( program=program, name='Doc for CDid {}'.format(cd.id), url='http://my/doc/here/') cd.evidence = document cd.save() indicator.lop_target = lop_target indicator.save() return indicator_ids
def create_full_indicator_set(self, program_id, indicator_suffix='', null_level=0): if null_level == self.NULL_LEVELS['INDICATORS']: return indicator_ids = [] program = Program.objects.get(id=program_id) seq = 0 for direction in (Indicator.DIRECTION_OF_CHANGE_POSITIVE, Indicator.DIRECTION_OF_CHANGE_NEGATIVE): for uom_type in Indicator.UNIT_OF_MEASURE_TYPES: for freq in Indicator.TARGET_FREQUENCIES: for is_cumulative in (True, False): if is_cumulative: cumulative_text = 'cumulative' else: cumulative_text = 'non-cumulative' indicator_name = '{} | {} | {} | {}'.format( freq[1], uom_type[1], Indicator.DIRECTION_OF_CHANGE[direction - 1][1], cumulative_text) levels = Level.objects.values_list('id', flat=True) indicator = Indicator( name=indicator_name + indicator_suffix, is_cumulative=is_cumulative, target_frequency=freq[0], unit_of_measure='This is a UOM', baseline=0, unit_of_measure_type=uom_type[0], direction_of_change=direction, program=program, level=Level.objects.get(id=levels[seq % len(levels)])) indicator.save() indicator_ids.append(indicator.id) seq += 1 if null_level == self.NULL_LEVELS['TARGETS']: continue self.make_targets(program, indicator) if null_level == self.NULL_LEVELS['RESULTS']: continue periodic_targets = PeriodicTarget.objects.filter( indicator__id=indicator.id) # Different combinations of UOM type, direction of change and cummulativeness require # different inputs. if uom_type[0] == Indicator.NUMBER: if direction == Indicator.DIRECTION_OF_CHANGE_POSITIVE: if is_cumulative: target_start = 100 target_increment = target_start achieved_start = 90 achieved_increment = achieved_start else: target_start = 100 target_increment = target_start achieved_start = 90 achieved_increment = achieved_start else: if is_cumulative: target_start = 500 target_increment = -int( math.floor( (target_start / len(periodic_targets)) / 10) * 10) achieved_start = 400 achieved_increment = -(target_increment - 2) else: target_start = 500 target_increment = -int( math.floor( (target_start / len(periodic_targets)) / 10) * 10) achieved_start = 400 achieved_increment = -(target_increment - 2) else: if direction == Indicator.DIRECTION_OF_CHANGE_POSITIVE: # Don't need to check cumulative because we don't really handle it target_start = 5 target_increment = 5 achieved_start = 4 achieved_increment = 5 else: # Don't need to check cumulative because we don't really handle it target_start = 90 target_increment = max( -math.floor( target_start / len(periodic_targets)), -2) achieved_start = 95 achieved_increment = target_increment + 1 lop_target = 0 day_offset = timedelta(days=2) for i, pt in enumerate(periodic_targets): # Create the target amount (the PeriodicTarget object has already been created) pt.target = target_start + target_increment * i pt.save() if is_cumulative: lop_target = pt.target else: lop_target += pt.target # Users shouldn't put in results with a date in the future, so neither should we. if pt.start_date and date.today( ) < pt.start_date + day_offset: continue # Now create the Results and their related Records if pt.start_date: date_collected = pt.start_date + day_offset else: date_collected = date.today() cd = CollectedData(periodic_target=pt, indicator=indicator, program=program, achieved=achieved_start + achieved_increment * i, date_collected=date_collected) cd.save() if null_level == self.NULL_LEVELS['EVIDENCE']: continue document = Documentation.objects.create( program=program, name='Doc for CDid {}'.format(cd.id), url='http://my/doc/here/') cd.evidence = document cd.save() indicator.lop_target = lop_target indicator.save() return indicator_ids