class IndicatorDelete(DeleteView): model = Indicator form_class = IndicatorForm @method_decorator(login_required) @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) @method_decorator(indicator_pk_adapter(has_indicator_write_access)) def dispatch(self, request, *args, **kwargs): return super(IndicatorDelete, self).dispatch(request, *args, **kwargs) def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, _('Success, Indicator Deleted!')) return self.render_to_response(self.get_context_data(form=form)) def delete(self, request, *args, **kwargs): if request.is_ajax(): indicator = self.get_object() if not request.POST.get('rationale'): # if an indicator has results and no rationale is provided, fail: if indicator.result_set.all().count() > 0: return JsonResponse( { "status": "failed", "msg": _("Reason for change is required.") }, status=400) # otherwise the rationale is this default: else: rationale = _( "Reason for change is not required when deleting an indicator with no linked results." ) else: rationale = request.POST.get('rationale') indicator_values = indicator.logged_fields program_page_url = indicator.program.program_page_url indicator.delete() ProgramAuditLog.log_indicator_deleted(self.request.user, indicator, indicator_values, rationale) success_message = _("The indicator was successfully deleted.") response_context = {'status': 'success'} if request.POST.get('redirect'): response_context['redirect_url'] = program_page_url # message tagged "pnotify" to display a success popup after redirect messages.success(request, success_message, extra_tags="pnotify pnotify-success") else: response_context['msg'] = success_message return JsonResponse(response_context) else: return super(IndicatorDelete, self).delete(request, *args, **kwargs) def get_success_url(self): return self.object.program.program_page_url
class DashboardThemeCreate(CreateView): model = DashboardTheme template_name = 'configurabledashboard/themes/admin/form.html' # add the request to the kwargs def get_form_kwargs(self): kwargs = super(DashboardThemeCreate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): try: self.guidance = FormGuidance.objects.get(form="DashboardTheme") except FormGuidance.DoesNotExist: self.guidance = None return super(DashboardThemeCreate, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super(DashboardThemeCreate, self).get_context_data(**kwargs) return context def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, 'Success, Theme Created!') return self.render_to_response(self.get_context_data(form=form)) form_class = DashboardThemeCreateForm
class ResultDelete(DeleteView): """TODO: This should handle GET differently - currently returns a nonexistent template""" model = Result @method_decorator(login_required) @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) @method_decorator(result_pk_adapter(has_result_write_access)) def dispatch(self, request, *args, **kwargs): return super(ResultDelete, self).dispatch(request, *args, **kwargs) def delete(self, request, *args, **kwargs): if request.is_ajax(): if not request.POST.get('rationale'): return JsonResponse( { "status": "failed", "msg": _("Reason for change is required") }, status=401) result = self.get_object() result_values = result.logged_fields result.delete() ProgramAuditLog.log_result_deleted(self.request.user, result.indicator, result_values, self.request.POST['rationale']) return JsonResponse({"status": "success", "msg": "Result Deleted"}) else: return super(ResultDelete, self).delete(request, *args, **kwargs) def get_success_url(self): return self.object.program.program_page_url
class TrainingUpdate(UpdateView): """ Training Form """ model = TrainingAttendance @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): try: self.guidance = FormGuidance.objects.get(form="Training") except FormGuidance.DoesNotExist: self.guidance = None return super(TrainingUpdate, self).dispatch(request, *args, **kwargs) # add the request to the kwargs def get_form_kwargs(self): kwargs = super(TrainingUpdate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, 'Success, Training Updated!') return self.render_to_response(self.get_context_data(form=form)) form_class = TrainingAttendanceForm
class BeneficiaryDelete(DeleteView): """ Beneficiary Delete """ model = Beneficiary success_url = reverse_lazy('beneficiary_list') @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): return super(BeneficiaryDelete, self).dispatch(request, *args, **kwargs) def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, 'Success, Beneficiary Deleted!') return self.render_to_response(self.get_context_data(form=form)) form_class = BeneficiaryForm
class CustomDashboardCreate(CreateView): # :param request: # :param id: # """ model = CustomDashboard template_name = 'configurabledashboard/dashboard/form.html' @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): try: self.guidance = FormGuidance.objects.get(form="CustomDashboard") except FormGuidance.DoesNotExist: self.guidance = None return super(CustomDashboardCreate, self).dispatch(request, *args, **kwargs) # add the request to the kwargs def get_form_kwargs(self): kwargs = super(CustomDashboardCreate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def get_initial(self): initial = {} return initial def get_context_data(self, **kwargs): context = super(CustomDashboardCreate, self).get_context_data(**kwargs) pk = self.kwargs['pk'] context.update({'pk': pk}) return context def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): if form.is_valid(): data = form.save(commit=False) form_theme = data.theme getSelectedTheme = DashboardTheme.objects.all().filter( id=form_theme.id) parsedLayout = json.loads(getSelectedTheme[0].layout_dictionary, object_pairs_hook=OrderedDict) new_map = {} for key in parsedLayout: new_map[key] = "NONE" data.component_map = json.dumps(new_map) data.save() #save formset from context context = self.get_context_data() messages.success(self.request, 'Success, Dashboard Created!') redirect_url = '/configurabledashboard/' + str(self.kwargs['pk']) return HttpResponseRedirect(redirect_url) form_class = CustomDashboardCreateForm
class SiteProfileUpdate(UpdateView): """ SiteProfile Form Update an existing site profile """ model = SiteProfile @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): self.guidance = None return super(SiteProfileUpdate, self).dispatch(request, *args, **kwargs) # add the request to the kwargs def get_form_kwargs(self): kwargs = super(SiteProfileUpdate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def get_context_data(self, **kwargs): context = super(SiteProfileUpdate, self).get_context_data(**kwargs) getProjects = [] context.update({'getProjects': getProjects}) return context def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, 'Success, SiteProfile Updated!') return self.render_to_response(self.get_context_data(form=form)) form_class = SiteProfileForm
class CustomDashboardCreate(CreateView): # :param request: # :param id: # """ model = CustomDashboard template_name = 'configurabledashboard/dashboard/form.html' try: guidance = FormGuidance.objects.get(form="CustomDashboard") except FormGuidance.DoesNotExist: guidance = None @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): return super(CustomDashboardCreate, self).dispatch(request, *args, **kwargs) # add the request to the kwargs def get_form_kwargs(self): kwargs = super(CustomDashboardCreate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def get_initial(self): initial = {} return initial def get_context_data(self, **kwargs): context = super(CustomDashboardCreate, self).get_context_data(**kwargs) pk = self.kwargs['pk'] context.update({'pk': pk}) return context def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): print form form.save() #save formset from context context = self.get_context_data() messages.success(self.request, 'Success, Dashboard Created!') latest = CustomDashboard.objects.latest('id') getCustomDashboard = CustomDashboard.objects.get(id=latest.id) dashboard_id = getCustomDashboard.id redirect_url = '/configurabledashboard/update/' + str(dashboard_id) return HttpResponseRedirect(redirect_url) form_class = CustomDashboardCreateForm
class DashboardComponentCreate(CreateView): model = DashboardComponent template_name = 'configurabledashboard/components/admin/form.html' # add the request to the kwargs def get_form_kwargs(self): kwargs = super(DashboardComponentCreate, self).get_form_kwargs() kwargs['request'] = self.request id = self.kwargs['id'] return kwargs @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): try: guidance = FormGuidance.objects.get(form="DashboardComponent") except FormGuidance.DoesNotExist: guidance = None return super(DashboardComponentCreate, self).dispatch(request, *args, **kwargs) def get_initial(self): initial = { 'getCustomDashboard': CustomDashboard.objects.get(id=self.kwargs['id']), 'getComponentDataSources': ComponentDataSource.objects.all(), } def get_context_data(self, **kwargs): context = super(DashboardComponentCreate, self).get_context_data(**kwargs) try: getCustomDashboard = CustomDashboard.objects.get( id=self.kwargs['id']) except CustomDashboard.DoesNotExist: getCustomDashboard = None context.update({'getCustomDashboard': getCustomDashboard}) return context def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() context = self.get_context_data() messages.success(self.request, 'Success, Component Created!') latestComponent = DashboardComponent.objects.latest('id') getCustomDashboard = CustomDashboard.objects.get(id=self.kwargs['id']) getCustomDashboard.components.add(latestComponent) return self.render_to_response(self.get_context_data(form=form)) form_class = DashboardComponentCreateForm
class SiteProfileCreate(CreateView): """ Using SiteProfile Form, create a new site profile """ model = SiteProfile @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): self.guidance = None return super(SiteProfileCreate, self).dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): # permission check that includes checking the user is associated with the sites Country verify_program_access_level_of_any_program( request, 'high', country_id=request.POST['country']) return super(SiteProfileCreate, self).post(request, *args, **kwargs) # add the request to the kwargs def get_form_kwargs(self): kwargs = super(SiteProfileCreate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def get_initial(self): countries = getCountry(self.request.user) default_country = None if countries: default_country = countries[0] initial = { 'approved_by': self.request.user, 'filled_by': self.request.user, 'country': default_country } return initial def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, 'Success, Site Profile Created!') latest = SiteProfile.objects.latest('id') redirect_url = '/workflow/siteprofile_update/' + str(latest.id) return HttpResponseRedirect(redirect_url) form_class = SiteProfileForm
class BeneficiaryCreate(CreateView): """ Beneficiary Form """ model = Beneficiary @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): try: self.guidance = FormGuidance.objects.get(form="Beneficiary") except FormGuidance.DoesNotExist: self.guidance = None return super(BeneficiaryCreate, self).dispatch(request, *args, **kwargs) def get_initial(self): initial = { 'training': self.kwargs['id'], } return initial # add the request to the kwargs def get_form_kwargs(self): kwargs = super(BeneficiaryCreate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def form_invalid(self, form): messages.error(self.request, 'Invalid Form', fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): form.save() messages.success(self.request, 'Success, Beneficiary Created!') latest = Beneficiary.objects.latest('id') redirect_url = '/formlibrary/beneficiary_update/' + str(latest.id) return HttpResponseRedirect(redirect_url) form_class = BeneficiaryForm
class CustomDashboardUpdate(UpdateView): model = CustomDashboard form_class = CustomDashboardForm @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) def dispatch(self, request, *args, **kwargs): try: guidance = FormGuidance.objects.get(form="CustomDashboard") except FormGuidance.DoesNotExist: guidance = None return super(CustomDashboardUpdate, self).dispatch(request, *args, **kwargs) def get_initial(self): getCustomDashboard = CustomDashboard.objects.get(id=self.kwargs['pk']) getDashboardComponents = DashboardComponent.objects.all().filter( componentset=getCustomDashboard) getAllComponentDataSources = ComponentDataSource.objects.all() initial = { 'getCustomDashboard': getCustomDashboard, 'getDashboardComponents': getDashboardComponents, 'getAllComponentDataSources': getAllComponentDataSources, } return initial def get_form(self, form_class): check_form_type = self.request.get_full_path() if check_form_type.startswith('/configurabledashboard/edit'): form = CustomDashboardModalForm else: form = CustomDashboardForm return form(**self.get_form_kwargs()) def get_context_data(self, **kwargs): context = super(CustomDashboardUpdate, self).get_context_data(**kwargs) pk = self.kwargs['pk'] context.update({'pk': pk}) check_path = self.request.get_full_path() if check_path.startswith('/confirgureabledashboard/map'): location = self.kwargs['location'] component_type = self.kwargs['type'] # TODO: If applicable, work through flow for remapping # else if check_form_type.startswith('/confirgureabledashboard/remap'): # location = self.kwargs['location'] # component_type = self.kwargs['type'] else: location = None component_type = None context.update({'location': location}) context.update({'component_type': component_type}) try: getCustomDashboard = CustomDashboard.objects.get( id=self.kwargs['pk']) except CustomDashboard.DoesNotExist: getCustomDashboard = None context.update({'getCustomDashboard': getCustomDashboard}) try: selected_theme = getCustomDashboard.theme getDashboardTheme = DashboardTheme.objects.all().filter( id=selected_theme.id) except DashboardTheme.DoesNotExist: getDashboardTheme = None context.update({'getDashboardTheme': getDashboardTheme}) # This theme layout helps to map the components to their position on the page # Dashboard layout should be pairs of the # position in the template (1,2,3...) and # the component type that needs to be slotted into that position if getDashboardTheme: getDashboardTheme[0].layout_dictionary layout = getDashboardTheme[0].layout_dictionary getDashboardLayoutList = json.loads(layout, object_pairs_hook=OrderedDict) else: getDashboardLayoutList = None context.update({'getDashboardLayoutList': getDashboardLayoutList}) try: getDashboardComponents = DashboardComponent.objects.all().filter( componentset=getCustomDashboard) except DashboardComponent.DoesNotExist: getDashboardComponents = None context.update({'getDashboardComponents': getDashboardComponents}) try: getComponentOrder = json.loads(getCustomDashboard.component_map, object_pairs_hook=OrderedDict) except: getComponentOrder = None context.update({'getComponentOrder': getComponentOrder}) try: getAllComponentMaps = [] for component in getDashboardComponents: if component.data_map: getAllComponentMaps[component] = json.loads( component.data_map) except not getDashboardComponents: getAllComponentMaps = None context.update({'getAllComponentMaps': getAllComponentMaps}) try: getAllComponentDataSources = ComponentDataSource.objects.all() except ComponentDataSource.DoesNotExist: getAllComponentDataSources = None context.update( {'getAllComponentDataSources': getAllComponentDataSources}) # try: # getAllDataFilters = {} # for data_source in getAllComponentDataSources: # data_source_filters = import_data_filter_options(data_source.data_source, data_source.data_filter_key, data_source.authorization_token) # getAllDataFilters[data_source.id] = data_source_filters # except not getAllComponentDataSources: # getAllDataFilters = None # context.update({'getAllDataFilters': getAllDataFilters}) mapped_location = self.request.GET.get('location') component_type = self.request.GET.get('type') if mapped_location and component_type: context.update({'mapped_location': mapped_location}) context.update({'component_type': component_type}) return context # add the request to the kwargs def get_form_kwargs(self): kwargs = super(CustomDashboardUpdate, self).get_form_kwargs() kwargs['request'] = self.request return kwargs def form_invalid(self, form): messages.error(self.request, self, fail_silently=False) return self.render_to_response(self.get_context_data(form=form)) def form_valid(self, form): check_form_type = self.request.get_full_path() #TODO: Once refactoring is done, revisit whether checking form type still needed # if check_form_type.startswith('/confirgureabledashboard/map'): # getCustomDashboard.component_map = form.cleaned_data['component_map'] # getCustomDashboard.save() # for position in getCustomDashboard.component_map: # mapped_position = form.component_map.0 # if position.0 == mapped_position: # position.1 == form.component_map.1 # mapped = true # if mapped != true: # getCustomDashboard.component_map.append(form.component_map) # else: form.save() messages.success(self.request, 'Success, CustomDashboard Output Updated!') return self.render_to_response(self.get_context_data(form=form))
class ResultUpdate(ResultFormMixin, UpdateView): """Update Result view called by result_update as modal""" model = Result form_class = ResultForm @method_decorator(login_required) @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) @method_decorator(result_pk_adapter(has_result_write_access)) def dispatch(self, request, *args, **kwargs): self.result = get_object_or_404(Result, pk=self.kwargs.get('pk')) self.indicator = self.result.indicator return super(ResultUpdate, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): custom_disaggregation_values = DisaggregationValue.objects.filter( result=self.result).exclude( disaggregation_label__disaggregation_type__standard=True) standard_disaggregation_values = DisaggregationValue.objects.filter( result=self.result).filter( disaggregation_label__disaggregation_type__standard=True) standard_disaggregation_labels = DisaggregationLabel.get_standard_labels( ) custom_disaggregation_labels = DisaggregationLabel.objects.filter( disaggregation_type__indicator=self.indicator.id) context = super(ResultUpdate, self).get_context_data(**kwargs) context['indicator'] = self.indicator context['readonly'] = not self.request.has_write_access context['custom_disaggregation_labels'] = custom_disaggregation_labels context['custom_disaggregation_values'] = custom_disaggregation_values context[ 'standard_disaggregation_labels'] = standard_disaggregation_labels context[ 'standard_disaggregation_values'] = standard_disaggregation_values context['title_str'] = u'{}: {}'.format( str(self.indicator.form_title_level), str(self.indicator.name)) return context def get_form_kwargs(self): kwargs = super(ResultUpdate, self).get_form_kwargs() kwargs['user'] = self.request.user kwargs['indicator'] = self.indicator kwargs['program'] = self.indicator.program kwargs['request'] = self.request return kwargs def form_valid(self, form): old_result = Result.objects.get(id=self.kwargs['pk']) getDisaggregationLabel = DisaggregationLabel.objects.filter( Q(disaggregation_type__indicator__id=old_result.indicator_id) | Q(disaggregation_type__standard=True)).distinct() # save the form then update manytomany relationships old_values = old_result.logged_fields new_result = form.save( ) # internally this clears disaggregation_value m2m relationships! # like the create view, create disaggregation values as all or nothing disaggregation_label_ids = set( [str(dl.id) for dl in getDisaggregationLabel]) process_disaggregation = False for k, v in self.request.POST.items(): if k in disaggregation_label_ids and v: process_disaggregation = True break if process_disaggregation: for label in getDisaggregationLabel: form_id_for_label = str(label.id) form_disagg_value = self.request.POST.get( form_id_for_label, '') new_result.disaggregation_value.create( disaggregation_label=label, value=form_disagg_value) # Result.achieved comes back different from the DB than from the ResultForm new_result.refresh_from_db() ProgramAuditLog.log_result_updated(self.request.user, new_result.indicator, old_values, new_result.logged_fields, form.cleaned_data.get('rationale')) if self.request.is_ajax(): data = serializers.serialize('json', [self.object]) return HttpResponse(data) messages.success(self.request, _('Success, Data Updated!')) redirect_url = new_result.program.program_page_url return HttpResponseRedirect(redirect_url)
class ResultCreate(ResultFormMixin, CreateView): """Create new Result called by result_add as modal""" model = Result form_class = ResultForm @method_decorator(login_required) @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) @method_decorator(indicator_adapter(has_result_write_access)) def dispatch(self, request, *args, **kwargs): if not request.has_write_access: raise PermissionDenied self.indicator = get_object_or_404(Indicator, pk=self.kwargs['indicator']) return super(ResultCreate, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): custom_disaggregation_labels = DisaggregationLabel.objects.filter( disaggregation_type__indicator=self.indicator.id) standard_disaggregation_labels = DisaggregationLabel.get_standard_labels( ) context = super(ResultCreate, self).get_context_data(**kwargs) context['indicator'] = self.indicator context['custom_disaggregation_labels'] = custom_disaggregation_labels context[ 'standard_disaggregation_labels'] = standard_disaggregation_labels context['title_str'] = u'{}: {}'.format( str(self.indicator.form_title_level), str(self.indicator.name)) return context def get_form_kwargs(self): kwargs = super(ResultCreate, self).get_form_kwargs() kwargs['user'] = self.request.user kwargs['indicator'] = self.indicator kwargs['program'] = self.indicator.program kwargs['request'] = self.request return kwargs def form_valid(self, form): indicator = self.request.POST['indicator'] disaggregation_labels = DisaggregationLabel.objects.filter( Q(disaggregation_type__indicator__id=indicator) | Q(disaggregation_type__standard=True)) new = form.save() # The following code appears to be accomplishing the following # The for submitted contains key/vals for all disaggregation_labels on creation # if 1 or more values are present, create values in the DB for all key/vals # otherwise leave the disaggregation_value associates completely empty # In other words, save key/vals as all or nothing disaggregation_label_ids = set( [str(dl.id) for dl in disaggregation_labels]) process_disaggregation = False for k, v in self.request.POST.items(): if k in disaggregation_label_ids and v: process_disaggregation = True break if process_disaggregation is True: for label in disaggregation_labels: form_id_for_label = str(label.id) form_disagg_value = self.request.POST.get( form_id_for_label, '') new.disaggregation_value.create(disaggregation_label=label, value=form_disagg_value) ProgramAuditLog.log_result_created(self.request.user, new.indicator, new) if self.request.is_ajax(): data = { 'pk': new.pk, 'url': reverse('result_update', kwargs={'pk': new.pk}) } return JsonResponse(data) messages.success(self.request, _('Success, Data Created!')) redirect_url = new.indicator.program.program_page_url return HttpResponseRedirect(redirect_url)
class IndicatorUpdate(IndicatorFormMixin, UpdateView): """ Update and Edit Indicators. url: indicator_update/<pk> """ @method_decorator(login_required) @method_decorator(group_excluded('ViewOnly', url='workflow/permission')) @method_decorator(indicator_pk_adapter(has_indicator_write_access)) @transaction.atomic def dispatch(self, request, *args, **kwargs): if request.method == 'GET': # If target_frequency is set but not targets are saved then # unset target_frequency too. indicator = self.get_object() reset_indicator_target_frequency(indicator) self.set_form_guidance() return super(IndicatorUpdate, self).dispatch(request, *args, **kwargs) @property def _form_title_display_str(self): """ The header of the form when updating - composed here instead of in the template such that it can also be used via AJAX """ if self.object.results_framework and self.object.auto_number_indicators: if self.object.level_display_ontology: return u'{} {} {}{}'.format( self.object.leveltier_name, _('indicator'), self.object.level_display_ontology, self.object.level_order_display, ) else: return _('Indicator setup') elif self.object.results_framework and self.object.number: return u'{} {}'.format(_('indicator'), self.object.number) elif self.object.results_framework: return _('Indicator setup') elif self.object.old_level: return u'{} {}'.format( str(ugettext(self.object.old_level)), str(_('indicator')), ) else: return _('Indicator setup') @property def _form_subtitle_display_str(self): return truncatechars(self.object.name, 300) def get_context_data(self, **kwargs): context = super(IndicatorUpdate, self).get_context_data(**kwargs) indicator = self.object program = indicator.program context['program'] = program pts = PeriodicTarget.objects.filter(indicator=indicator) \ .annotate(num_data=Count('result')).order_by('customsort', 'create_date', 'period') ptargets = [] for pt in pts: ptargets.append({ 'id': pt.pk, 'num_data': pt.num_data, 'start_date': pt.start_date, 'end_date': pt.end_date, 'period': pt. period, # period is deprecated, this should move to .period_name 'period_name': pt.period_name, 'target': pt.target }) # if the modal is loaded (not submitted) and the indicator frequency is a periodic if self.request.method == 'GET' and indicator.target_frequency in Indicator.REGULAR_TARGET_FREQUENCIES: latest_pt_end_date = indicator.periodictargets.aggregate( lastpt=Max('end_date'))['lastpt'] if latest_pt_end_date is None or latest_pt_end_date == 'None': latest_pt_end_date = program.reporting_period_start else: latest_pt_end_date += timedelta(days=1) target_frequency_num_periods = len([ p for p in PeriodicTarget.generate_for_frequency( indicator.target_frequency)(latest_pt_end_date, program.reporting_period_end) ]) # target_frequency_num_periods = IPTT_ReportView._get_num_periods( # latest_pt_end_date, program.reporting_period_end, indicator.target_frequency) num_existing_targets = pts.count() event_name = '' generated_targets = generate_periodic_targets( indicator.target_frequency, latest_pt_end_date, target_frequency_num_periods, event_name, num_existing_targets) # combine the list of existing periodic_targets with the newly generated placeholder for missing targets ptargets += generated_targets context['periodic_targets'] = ptargets # redirect user to certain tabs of the form given GET params if self.request.GET.get('targetsactive') == 'true': context['targetsactive'] = True context['readonly'] = not self.request.has_write_access context['title_str'] = self._form_title_display_str context['subtitle_str'] = self._form_subtitle_display_str return context def get_initial(self): target_frequency_num_periods = self.get_object( ).target_frequency_num_periods if not target_frequency_num_periods: target_frequency_num_periods = 1 initial = { 'target_frequency_num_periods': target_frequency_num_periods } return initial # add the request to the kwargs def get_form_kwargs(self): kwargs = super(IndicatorUpdate, self).get_form_kwargs() kwargs['request'] = self.request program = Program.rf_aware_objects.get(pk=self.object.program_id) kwargs['program'] = program return kwargs def form_valid(self, form, **kwargs): periodic_targets = self.request.POST.get('periodic_targets') old_indicator = Indicator.objects.get(pk=self.kwargs.get('pk')) existing_target_frequency = old_indicator.target_frequency new_target_frequency = form.cleaned_data.get('target_frequency') lop = form.cleaned_data.get('lop_target') rationale = form.cleaned_data.get('rationale') old_indicator_values = old_indicator.logged_fields prev_level = old_indicator.level # previous value of "new" level (not to be confused with Indicator.old_level) # if existing_target_frequency != new_target_frequency # then either existing_target_frequency is None or LoP # It shouldn't be anything else as the user should have to delete all targets first # Disassociate existing records and delete old PeriodicTargets if existing_target_frequency != new_target_frequency: PeriodicTarget.objects.filter(indicator=old_indicator).delete() # Save completed PeriodicTargets to the DB) if new_target_frequency == Indicator.LOP: # assume only 1 PT at this point lop_pt, created = PeriodicTarget.objects.update_or_create( indicator=old_indicator, defaults={ 'target': lop, 'period': PeriodicTarget.LOP_PERIOD, }) if created: lop_pt.create_date = timezone.now() lop_pt.save() # Redirect results to new LoP target Result.objects.filter(indicator=old_indicator).update( periodic_target=lop_pt) else: # now create/update periodic targets (will be empty u'[]' for LoP) pt_json = json.loads(periodic_targets) normalized_pt_json = self.normalize_periodic_target_client_json_dates( pt_json) self.validate_periodic_target_json_from_client( normalized_pt_json, old_indicator.program, new_target_frequency) generated_pt_ids = [] for i, pt in enumerate(normalized_pt_json): defaults = { 'period': pt.get('period', ''), 'target': pt.get('target', 0), 'customsort': i, 'start_date': pt['start_date'], 'end_date': pt['end_date'], 'edit_date': timezone.now() } periodic_target, created = PeriodicTarget.objects \ .update_or_create(indicator=old_indicator, id=pt['id'], defaults=defaults) if created: periodic_target.create_date = timezone.now() periodic_target.save() generated_pt_ids.append(periodic_target.id) # Reassign results to newly created PTs if generated_pt_ids: pts = PeriodicTarget.objects.filter(indicator=old_indicator, pk__in=generated_pt_ids) for pt in pts: Result.objects.filter(indicator=old_indicator, date_collected__range=[ pt.start_date, pt.end_date ]).update(periodic_target=pt) # save the indicator form form.save() self.object.refresh_from_db() # Write to audit log if results attached or special case of RF level reassignment results_count = Result.objects.filter(indicator=self.object).count() if (results_count and results_count > 0 ) or old_indicator.level_id != self.object.level_id: ProgramAuditLog.log_indicator_updated(self.request.user, self.object, old_indicator_values, self.object.logged_fields, rationale) # refresh the periodic targets form such that pkids of new PeriodicTargets are submitted in the future content = render_to_string( 'indicators/indicatortargets.html', { 'indicator': self.object, 'periodic_targets': PeriodicTarget.objects.filter(indicator=self.object).annotate( num_data=Count('result')) }) return JsonResponse({ 'content': content, 'title_str': self._form_title_display_str, 'subtitle_str': self._form_subtitle_display_str, 'save_success_msg': self._save_success_msg( self.object, created=False, level_changed=(self.object.level != prev_level)), })