def delete(self, request, *args, **kwargs): result_count = self.get_object().result_set.count() rationale = request.POST.get('rationale') indicator = self.get_object().indicator old_indicator_values = indicator.logged_fields if result_count and result_count > 0: if not rationale: return JsonResponse( { "status": "failed", "msg": _("Reason for change is required") }, status=400) else: self.get_object().result_set.all().update(periodic_target=None) if not rationale and result_count == 0: rationale = _('No reason for change required.') self.get_object().delete() if indicator.periodictargets.count() == 0: indicator.target_frequency = None indicator.target_frequency_num_periods = 1 indicator.target_frequency_start = None indicator.target_frequency_custom = None indicator.lop_target = indicator.calculated_lop_target indicator.save() ProgramAuditLog.log_indicator_updated(request.user, indicator, old_indicator_values, indicator.logged_fields, rationale) return JsonResponse({"status": "success"})
def form_valid(self, form, **kwargs): indicator = form.save() periodic_targets = self.request.POST.get('periodic_targets') # Save completed PeriodicTargets to the DB (will be empty u'[]' for LoP) if indicator.target_frequency == Indicator.LOP: PeriodicTarget.objects.create( indicator=indicator, period=PeriodicTarget.LOP_PERIOD, target=indicator.lop_target, create_date=timezone.now(), ) else: # now create/update periodic targets 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, indicator.program, indicator.target_frequency) for i, pt in enumerate(normalized_pt_json): values = dict( period=pt.get('period', ''), target=pt.get('target', 0), start_date=pt['start_date'], end_date=pt['end_date'], ) PeriodicTarget.objects.create(indicator=indicator, customsort=i, create_date=timezone.now(), **values) ProgramAuditLog.log_indicator_created(self.request.user, indicator, 'N/A') return JsonResponse({ 'success': True, 'id': indicator.id, 'save_success_msg': self._save_success_msg(indicator, created=True) })
def update(self, request, pk=None): instance = self.get_object() program = instance.program role = request.user.tola_user.program_role(program.id) if request.user.is_anonymous or role != 'high': return HttpResponseRedirect('/') # Pull rationale string outside of model serializer, since not part of model rationale_str = request.data.get('rationale', '') instance = self.get_object() old_level_fields = self.get_object().logged_fields with transaction.atomic(): # update Level serializer = self.get_serializer(instance, data=request.data, partial=False) serializer.is_valid(raise_exception=True) serializer.save() # log changes new_level_fields = instance.logged_fields # only log changes if indicators attached and not just adding assumption text has_indicators = instance.indicator_set.exists() diff_fields = set(old_level_fields.items()) - set( new_level_fields.items()) only_added_assumptions = len( diff_fields) == 1 and diff_fields.pop() == ('assumptions', '') if has_indicators and not only_added_assumptions: ProgramAuditLog.log_result_level_updated( self.request.user, instance, old_level_fields, new_level_fields, rationale_str, ) # DRF stuff if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data)
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)
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)
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 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 post(self, request, *args, **kwargs): indicator = Indicator.objects.get( pk=self.kwargs.get('indicator', None)) rationale = request.POST.get('rationale') if not rationale: if indicator.result_set.all().exists(): return JsonResponse( { "status": "failed", "msg": _("Reason for change is required") }, status=400) else: rationale = _('No reason for change required.') periodic_targets = PeriodicTarget.objects.filter(indicator=indicator) old = indicator.logged_fields for pt in periodic_targets: pt.result_set.all().update(periodic_target=None) pt.delete() indicator.target_frequency = None indicator.target_frequency_num_periods = 1 indicator.target_frequency_start = None indicator.target_frequency_custom = None indicator.lop_target = None # since lop target is auto-calculated, unset it when PTs are destroyed indicator.save() ProgramAuditLog.log_indicator_updated(self.request.user, indicator, old, indicator.logged_fields, rationale) return JsonResponse({"status": "success"})
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)), })
def reportingperiod_update(request, pk): program = Program.objects.get(pk=pk) old_dates = program.dates_for_logging # In some cases the start date input will be disabled and won't come through POST reporting_period_start = False reporting_period_end = False try: reporting_period_start = parser.parse( request.POST['reporting_period_start']) except MultiValueDictKeyError as e: pass reporting_period_end = parser.parse(request.POST['reporting_period_end']) success = True failmsg = [] failfields = [] if not request.POST.get('rationale') and program.indicator_set.all( ).exists(): success = False # Translators: Text of an error message that appears when a user hasn't provided a justification for the change they are making to some data failmsg.append(_('Reason for change is required')) if reporting_period_start: if reporting_period_start.day != 1: success = False failmsg.append( _('Reporting period must start on the first of the month')) failfields.append('reporting_period_start') elif reporting_period_start.date() == program.reporting_period_start: pass elif program.has_time_aware_targets: success = False failmsg.append( _('Reporting period start date cannot be changed while time-aware periodic targets are in place' )) else: program.reporting_period_start = reporting_period_start if reporting_period_end: next_day = reporting_period_end + timedelta(days=1) if next_day.day != 1: success = False failmsg.append( _('Reporting period must end on the last day of the month')) failfields.append('reporting_period_end') elif reporting_period_end.date() == program.reporting_period_end: pass elif (program.last_time_aware_indicator_start_date and reporting_period_end.date() < program.last_time_aware_indicator_start_date): success = False failmsg.append( _('Reporting period must end after the start of the last target period' )) failfields.append('reporting_period_end') else: program.reporting_period_end = reporting_period_end if reporting_period_start and reporting_period_start >= reporting_period_end: success = False failmsg.append( _('Reporting period must start before reporting period ends')) failfields.append('reporting_period_start') failfields.append('reporting_period_end') else: success = False failmsg.append(_('You must select a reporting period end date')) failfields.append('reporting_period_end') if success: program.save() ProgramAuditLog.log_program_dates_updated( request.user, program, old_dates, program.dates_for_logging, request.POST.get('rationale')) return JsonResponse( { 'msg': 'success' if success else 'fail', 'failmsg': failmsg, 'failfields': failfields, 'program_id': pk, 'rptstart': program.reporting_period_start, 'rptend': program.reporting_period_end, }, status=200 if success else 422)