def action(self): """ Send every reporter with an action left to do a reminder """ today = date.today() level = self.args.level if not level: return False logger.info(u"Level: %s" % level) if level == 'cscom': message = u"[PNLP] Votre rapport mensuel paludisme est " \ u"attendu au plus tard le %(date)s" \ % {'date': date(today.year, \ today.month, 5).strftime('%x')} for cscom in mobile_entity_gen(cscom_without_report(self.args.period)): contact = contact_for(cscom, recursive=False) if not contact or not contact.phone_number: continue # CSCOM: only cellphone transmiting ones. if not cscom.parent.slug in ('nion', 'maci'): continue logger.info(u"Sending cscom text to %s" % contact.phone_number) send_sms(to=contact.phone_number, text=message) # end of CSCom section return for stat in level_statistics(self.args.period, level).values(): contact = contact_for(stat['entity'], recursive=False) if not contact or not contact.phone_number: continue # nothing waiting validation. if not stat['unval']: continue logger.info(u"Sending %s text to %s" \ % (level, contact.phone_number)) dom = 15 if level == 'district' else 25 message = u"[PNLP] Vous avez %(unval)d rapports a valider " \ u"au plus tard le %(date)s." \ % {'unval': stat['unval'], \ 'date': date(today.year, \ today.month, dom).strftime('%x')} email_title = u"[PNLP] Rapports a valider" # send_sms(to=contact.phone_number, text=message) if not contact.email: continue sent, sent_message = send_email(recipients=contact.email, message=message, title=email_title)
def action(self): """ send SMS to non-sender and to districts Non-sending CSCom will receive a message saying they missed. District will be reminded they have reports to validate and non-sending cscom if applicable """ # send an SMS to non-reporting CSCOM. message = u"[PNLP] Vous n'avez pas envoye votre rapport mensuel." \ " Il est desormais trop tard pour l'envoyer. " \ "Vos donnees ne seront donc pas integrees." for contact in [contact_for(entity, recursive=False) \ for entity in mobile_entity_gen(self.get_bad_cscom())]: if not contact or not contact.phone_number: continue send_sms(to=contact.phone_number, text=message) # send an SMS to districts: # 1. those with non-validated reports. # 2. those with non-reporting cscom. for dis, e in self.get_all_district().items(): contact = contact_for(e['entity'], recursive=False) if not contact or not contact.phone_number: continue # already received everything and validated. # congrats, we won't bug you buddy. if e['unval'] == 0 and e['unsent'] == 0: continue if e['unval']: msg_unval = u"%(nb)s de vos CSCom ont envoye leur " \ u"rapport. Vous devez les valider avant le 15." \ % {'nb': e['unval']} else: msg_unval = u"" if e['unsent']: msg_unsent = u"%(nb)s n'ont pas envoye leur rapport " \ u"mensuel dans les temps!" % {'nb': e['unsent']} else: msg_unsent = u"" # add space before second sentence if both if e['unval'] and e['unsent']: msg_unsent = u" %s" % msg_unsent message = u"[PNLP] %(unval)s%(unsent)s" \ % {'unval': msg_unval, 'unsent': msg_unsent} send_sms(to=contact.phone_number, text=message)
def validation_list(request): context = {'category': 'validation'} web_provider = request.user.get_profile() entity = provider_entity(web_provider) period = current_reporting_period() # check permission or raise 403 # should never raise as already checked by decorator provider_can_or_403('can_validate_report', web_provider, entity) not_sent = [(ent, contact_for(ent)) \ for ent in get_not_received_reports(entity, period)] context.update({'not_validated': get_reports_to_validate(entity, period), 'validated': get_validated_reports(entity, period), 'not_sent': not_sent}) context.update({'is_complete': context['validated'].__len__() == \ entity.get_children().__len__(), 'is_idle': context['not_validated'].__len__() == 0 \ and context['not_sent'].__len__() > 0}) context.update({'time_cscom_over': time_cscom_over(), \ 'time_district_over': time_district_over(), \ 'time_region_over': time_region_over()}) context.update({'validation_over': not time_can_validate(entity)}) context.update({'current_period': current_period(), \ 'current_reporting_period': period}) return render(request, 'validation_list.html', context)
def validation_list(request): context = {'category': 'validation'} web_provider = request.user.get_profile() entity = provider_entity(web_provider) period = current_reporting_period() # check permission or raise 403 # should never raise as already checked by decorator provider_can_or_403('can_validate_report', web_provider, entity) not_sent = [(ent, contact_for(ent)) \ for ent in get_not_received_reports(entity, period)] context.update({ 'not_validated': get_reports_to_validate(entity, period), 'validated': get_validated_reports(entity, period), 'not_sent': not_sent }) context.update({'is_complete': context['validated'].__len__() == \ entity.get_children().__len__(), 'is_idle': context['not_validated'].__len__() == 0 \ and context['not_sent'].__len__() > 0}) context.update({'time_cscom_over': time_cscom_over(), \ 'time_district_over': time_district_over(), \ 'time_region_over': time_region_over()}) context.update({'validation_over': not time_can_validate(entity)}) context.update({'current_period': current_period(), \ 'current_reporting_period': period}) return render(request, 'validation_list.html', context)
def nb_reports_for(entity, period): nb_rec = MalariaReport.objects.filter(entity__parent=entity, period=period).count() next_period = period.next() if entity.type.slug == 'district': nb_ent = entity.get_children().count() incoming_sms = None all_sms = None else: nb_ent = 1 number = contact_for(entity, True).phone_number if not number is None and number.startswith('+223'): number = '+223' + number incoming_sms = Inbox.objects.filter(receivingdatetime__gte=next_period.start_on, receivingdatetime__lte=next_period.end_on, sendernumber=number) sent_sms = SentItems.objects.filter(sendingdatetime__gte=next_period.start_on, sendingdatetime__lte=next_period.end_on, destinationnumber=number) if incoming_sms.count() != sent_sms.count(): all_sms = list(incoming_sms) + list(sent_sms) else: all_sms = sent_sms percent = float(nb_rec) / nb_ent return {'entity': entity, 'nb_received': nb_rec, 'nb_expected': nb_ent, 'received_rate': percent, 'incoming_sms': incoming_sms, 'all_sms': all_sms}
def nb_reports_for(entity, period): nb_rec = MalariaReport.objects.filter(entity__parent=entity, period=period).count() next_period = period.next() if entity.type.slug == 'district': nb_ent = entity.get_children().count() incoming_sms = None all_sms = None else: nb_ent = 1 number = contact_for(entity, True).phone_number if not number is None and number.startswith('+223'): number = '+223' + number incoming_sms = Inbox.objects.filter( receivingdatetime__gte=next_period.start_on, receivingdatetime__lte=next_period.end_on, sendernumber=number) sent_sms = SentItems.objects.filter( sendingdatetime__gte=next_period.start_on, sendingdatetime__lte=next_period.end_on, destinationnumber=number) if incoming_sms.count() != sent_sms.count(): all_sms = list(incoming_sms) + list(sent_sms) else: all_sms = sent_sms percent = float(nb_rec) / nb_ent return { 'entity': entity, 'nb_received': nb_rec, 'nb_expected': nb_ent, 'received_rate': percent, 'incoming_sms': incoming_sms, 'all_sms': all_sms }
def malitel_list(request): context = {'category': 'malitel'} entities = [] for entity in Entity.objects.filter(Q(parent__slug='maci') | Q(parent__slug='nion')): pn = contact_for(entity).phone_number if '624175' in pn: entities.append({'name': entity, 'contact': pn}) entities.sort(key=lambda x: x['contact']) context.update({'entities': entities}) return render(request, 'malitel.html', context)
def malitel_list(request): context = {'category': 'malitel'} entities = [] for entity in Entity.objects.filter( Q(parent__slug='maci') | Q(parent__slug='nion')): pn = contact_for(entity).phone_number if '624175' in pn: entities.append({'name': entity, 'contact': pn}) entities.sort(key=lambda x: x['contact']) context.update({'entities': entities}) return render(request, 'malitel.html', context)
def entities_autoreports(level): districts_missed_report = {} auto_validated_cscom_reports = \ MalariaReport.validated\ .filter(entity__type__slug=level, \ modified_by__user__username='******') for report in auto_validated_cscom_reports: if not report.entity.parent.slug in districts_missed_report: districts_missed_report[report.entity.parent.slug] = \ {'entity': report.entity.parent, \ 'nbauto': 0, \ 'contact': contact_for(report.entity.parent, False)} districts_missed_report[report.entity.parent.slug]['nbauto'] += 1 return districts_missed_report
def entities_autoreports(level): districts_missed_report = {} auto_validated_cscom_reports = \ MalariaReport.validated\ .filter(entity__type__slug=level, \ modified_by__user__username='******') for report in auto_validated_cscom_reports: if not report.entity.parent.slug in districts_missed_report: districts_missed_report[report.entity.parent.slug] = \ {'entity': report.entity.parent, \ 'nbauto': 0, \ 'contact': contact_for(report.entity.parent, False)} districts_missed_report[report.entity.parent.slug]['nbauto'] += 1 return districts_missed_report
def action(self): """ send SMS to district/region for each new report """ report = self.args.report # we have a district/cscom report logger.info(u"Report %s found." % report) contact = contact_for(report.entity.parent) message = u"[PNLP] Le rapport %(receipt)s de %(entity)s " \ u"a ete recu. Vous devez le valider." \ % {'receipt': report.receipt, 'entity': report.entity} # if contact.phone_number: # send_sms(contact.phone_number, message) if contact.email: send_email(recipients=contact.email, message=message, \ title=u"[PNLP] Nouveau rapport recu!") else: send_email(settings.HOTLINE_EMAIL, message, \ u"[PNLP] Unable to send report notification " \ u"to %(contact)s" \ % {'contact': contact.name_access()})
def palu(message): # common start of error message error_start = u"Impossible d'enregistrer le rapport. " # create variables from text messages. try: args_names = ['kw1', 'username', 'password', 'month', 'year', \ 'u5_total_consultation_all_causes', \ 'u5_total_suspected_malaria_cases', \ 'u5_total_simple_malaria_cases', \ 'u5_total_severe_malaria_cases', \ 'u5_total_tested_malaria_cases', \ 'u5_total_confirmed_malaria_cases', \ 'u5_total_treated_malaria_cases', \ 'u5_total_inpatient_all_causes', \ 'u5_total_malaria_inpatient', \ 'u5_total_death_all_causes', \ 'u5_total_malaria_death', \ 'u5_total_distributed_bednets', \ 'o5_total_consultation_all_causes', \ 'o5_total_suspected_malaria_cases', \ 'o5_total_simple_malaria_cases', \ 'o5_total_severe_malaria_cases', \ 'o5_total_tested_malaria_cases', \ 'o5_total_confirmed_malaria_cases', \ 'o5_total_treated_malaria_cases', \ 'o5_total_inpatient_all_causes', \ 'o5_total_malaria_inpatient', \ 'o5_total_death_all_causes', \ 'o5_total_malaria_death', \ 'pw_total_consultation_all_causes', \ 'pw_total_suspected_malaria_cases', \ 'pw_total_severe_malaria_cases', \ 'pw_total_tested_malaria_cases', \ 'pw_total_confirmed_malaria_cases', \ 'pw_total_treated_malaria_cases', \ 'pw_total_inpatient_all_causes', \ 'pw_total_malaria_inpatient', \ 'pw_total_death_all_causes', \ 'pw_total_malaria_death', \ 'pw_total_distributed_bednets', \ 'pw_total_anc1', \ 'pw_total_sp1', \ 'pw_total_sp2', \ 'stockout_act_children', 'stockout_act_youth', 'stockout_act_adult', \ 'stockout_artemether', 'stockout_quinine', 'stockout_serum', \ 'stockout_bednet', 'stockout_rdt', 'stockout_sp'] args_values = message.content.strip().lower().split() arguments = dict(zip(args_names, args_values)) except ValueError: # failure to split means we proabably lack a data or more # we can't process it. message.respond(error_start + u" Le format du SMS est incorrect.") return True # convert form-data to int or bool respectively try: for key, value in arguments.items(): if key.split('_')[0] in ('u5', 'o5', 'pw', 'month', 'year'): arguments[key] = int(value) if key.split('_')[0] == 'stockout': arguments[key] = MalariaReport.YES if bool(int(value)) \ else MalariaReport.NO except: raise # failure to convert means non-numeric value which we can't process. message.respond(error_start + u" Les données sont malformées.") return True # check credentials try: provider = Provider.active.get(user__username=arguments['username']) except Provider.DoesNotExist: message.respond(error_start + u"Ce nom d'utilisateur " + u"(%s) n'existe pas." % \ arguments['username']) return True if not provider.check_password(arguments['password']): message.respond(error_start + u"Votre mot de passe est incorrect.") return True # now we have well formed and authenticated data. # let's check for business-logic errors. # create a data holder for validator data_browser = MalariaDataHolder() # feed data holder with sms provided data for key, value in arguments.items(): if key.split('_')[0] in ('u5', 'o5', 'pw', \ 'stockout', 'year', 'month'): data_browser.set(key, value) # feed data holder with guessable data try: hc = entity_for(provider).slug except: hc = None data_browser.set('hc', hc) today = datetime.date.today() data_browser.set('fillin_day', today.day) data_browser.set('fillin_month', today.month) data_browser.set('fillin_year', today.year) data_browser.set('author', provider.name()) # create validator and fire validator = MalariaReportValidator(data_browser) validator.errors.reset() try: validator.validate() except AttributeError as e: message.respond(error_start + e.__str__()) return True errors = validator.errors # return first error to user if errors.count() > 0: message.respond(error_start + errors.all()[0]) return True # create the report try: period = MonthPeriod.find_create_from(year=data_browser.get('year'), \ month=data_browser.get('month')) entity = Entity.objects.get(slug=data_browser.get('hc'), \ type__slug='cscom') report = MalariaReport.start(period, entity, provider, \ type=MalariaReport.TYPE_SOURCE) report.add_underfive_data(*data_browser.data_for_cat('u5')) report.add_overfive_data(*data_browser.data_for_cat('o5')) report.add_pregnantwomen_data(*data_browser.data_for_cat('pw')) report.add_stockout_data(*data_browser.data_for_cat('so')) #report.save() with reversion.create_revision(): report.save() reversion.set_user(provider.user) except Exception as e: message.respond(error_start + u"Une erreur technique s'est " \ u"produite. Reessayez plus tard et " \ u"contactez ANTIM si le probleme persiste.") logger.error(u"Unable to save report to DB. Message: %s | Exp: %r" \ % (message.content, e)) return True message.respond(u"[SUCCES] Le rapport de %(cscom)s pour %(period)s " u"a ete enregistre. " \ u"Le No de recu est #%(receipt)s." \ % {'cscom': report.entity.display_full_name(), \ 'period': report.period, \ 'receipt': report.receipt}) try: to = contact_for(report.entity.parent).phone_number except: to = None if not to: return True send_sms(to, u"[ALERTE] Le CSCom %(cscom)s vient d'envoyer le " \ u"rapport #%(receipt)s pour %(period)s." \ % {'cscom': report.entity.display_full_name(), \ 'period': report.period, \ 'receipt': report.receipt}) return True
def indicator_browser(request, entity_code=None, period_str=None, \ section_index='1', sub_section=None): context = {'category': 'indicator_data'} web_provider = request.user.get_profile() root = web_provider.first_target() periods = [] speriod = eperiod = None entity = None #section_index = int(section_index) - 1 # find entity or default to provider target # raise 404 on wrong provided entity code if entity_code: entity = get_object_or_404(Entity, slug=entity_code) if not entity: entity = web_provider.first_target() context.update({'entity': entity}) # define a list of all possible periods. # this is the list of all existing MonthPeriod anterior to current def all_anterior_periods(period): return MonthPeriod.objects\ .filter(start_on__lte=period.start_on)\ .order_by('start_on') all_periods = list(all_anterior_periods(current_reporting_period()).all()) if not MalariaReport.validated.filter(entity=entity, period=current_reporting_period()) \ .count(): all_periods.remove(current_reporting_period()) # retrieve Periods from string # if period_string include innexistant periods -> 404. if period_str: speriod_str, eperiod_str = period_str.split('-') try: speriod = MonthPeriod.find_create_from(\ year=int(speriod_str[-4:]), \ month=int(speriod_str[:2]), \ dont_create=True) eperiod = MonthPeriod.find_create_from(\ year=int(eperiod_str[-4:]), \ month=int(eperiod_str[:2]), \ dont_create=True) # loop on Period.next() from start one to end one. period = speriod while period.middle() <= eperiod.middle(): periods.append(period) period = period.next() except: raise Http404(_(u"Requested period interval (%(period_str)s) " \ u"includes inexistant periods.") \ % {'period': period_str}) # in case user did not request a specific interval # default to current_reporting_period if not speriod or not eperiod: speriod = eperiod = current_reporting_period() periods = [speriod] # if end period is before start period, redirect to opposite if eperiod.middle() < speriod.middle(): return redirect('indicator_data', \ entity_code=entity.slug, \ period_str='%s-%s' % (eperiod.pid, speriod.pid)) # periods variables context.update({'period_str': '%s-%s' % (speriod.pid, eperiod.pid), \ 'speriod': speriod, 'eperiod': eperiod}) context.update({'periods': [(p.pid, p.middle()) for p in periods], \ 'all_periods': [(p.pid, p.middle()) for p in all_periods]}) # check permissions on this entity and raise 403 provider_can_or_403('can_view_indicator_data', web_provider, entity) # build entities browser context.update({'root': root, \ 'paths': entities_path(root, entity)}) from pnlp_core.indicators import INDICATOR_SECTIONS context.update({'sections': \ sorted(INDICATOR_SECTIONS.values(), \ cmp=lambda a, b: int(a['id'].strip('a').strip('b')) \ - int(b['id'].strip('a').strip('b')))}) try: section = INDICATOR_SECTIONS[section_index] if not sub_section: if len(section['sections']): sub_section = section['sections'].keys()[0] sname = 'pnlp_core.indicators.section%s' % section_index.__str__() if sub_section: sname = '%s_%s' % (sname, sub_section.__str__()) sm = import_path(sname) except: raise raise Http404(_(u"This section does not exist.")) # section 1 specifics if section_index == '1': context.update({'contact': contact_for(entity)}) context.update({'section': section, 'sub_section': sub_section}) context.update({'widgets': [widget(entity=entity, periods=periods) \ for widget in sm.WIDGETS]}) return render(request, 'indicator_data.html', context)
def dashboard(request): category = 'dashboard' context = {'category': category} from bolibana.models import Entity from pnlp_core.data import (current_period, current_stage, \ time_cscom_over, time_district_over, \ time_region_over) def sms_received_sent_by_period(period): received = Inbox.objects.filter(receivingdatetime__gte=period.start_on, receivingdatetime__lte=period.end_on) \ .count() sent = SentItems.objects.filter(sendingdatetime__gte=period.start_on, sendingdatetime__lte=period.end_on) \ .count() return (received, sent) def received_reports(period, type_): return MalariaReport.objects.filter(period=period, \ entity__type__slug=type_) def reports_validated(period, type_): return MalariaReport.validated.filter(period=period, \ entity__type__slug=type_) def reporting_rate(period, entity): return float(MalariaReport.validated.filter(period=period, \ entity__parent=entity).count()) \ / Entity.objects.filter(parent__slug=entity.slug).count() current_period = current_period() period = current_reporting_period() context.update({'current_period': current_period, 'current_reporting_period': period, 'current_stage': current_stage(), 'current_sms': sms_received_sent_by_period(current_period), 'current_reporting_sms': \ sms_received_sent_by_period(period), 'total_cscom': Entity.objects\ .filter(type__slug='cscom').count(), 'time_cscom_over': time_cscom_over(period), 'time_district_over': time_district_over(period), 'time_region_over': time_region_over(period)}) received_cscom_reports = received_reports(period, 'cscom') cscom_reports_validated = reports_validated(period, 'cscom') district_reports_validated = reports_validated(period, 'district') reporting_rate = \ float(MalariaReport.validated.filter(period=period).count()) \ / Entity.objects.count() cscom_missed_report = \ Entity.objects.filter(type__slug='cscom')\ .exclude(id__in=[r.entity.id \ for r \ in received_cscom_reports])\ .order_by('name') def entities_autoreports(level): districts_missed_report = {} auto_validated_cscom_reports = \ MalariaReport.validated\ .filter(entity__type__slug=level, \ modified_by__user__username='******') for report in auto_validated_cscom_reports: if not report.entity.parent.slug in districts_missed_report: districts_missed_report[report.entity.parent.slug] = \ {'entity': report.entity.parent, \ 'nbauto': 0, \ 'contact': contact_for(report.entity.parent, False)} districts_missed_report[report.entity.parent.slug]['nbauto'] += 1 return districts_missed_report districts_missed_report = entities_autoreports('cscom') regions_missed_report = entities_autoreports('district') context.update({'received_cscom_reports': received_cscom_reports.count(), 'cscom_reports_validated': cscom_reports_validated.count(), 'district_reports_validated': district_reports_validated.count(), 'reporting_rate': reporting_rate, 'cscom_missed_report_count': cscom_missed_report.count(), 'cscom_missed_report': [(e, contact_for(e, True)) \ for e in cscom_missed_report[:20]], 'districts_missed_report': districts_missed_report, 'regions_missed_report': regions_missed_report}) return render(request, 'dashboard.html', context)
def palu(message): # common start of error message error_start = u"Impossible d'enregistrer le rapport. " # create variables from text messages. try: args_names = ['kw1', 'username', 'password', 'month', 'year', \ 'u5_total_consultation_all_causes', \ 'u5_total_suspected_malaria_cases', \ 'u5_total_simple_malaria_cases', \ 'u5_total_severe_malaria_cases', \ 'u5_total_tested_malaria_cases', \ 'u5_total_confirmed_malaria_cases', \ 'u5_total_treated_malaria_cases', \ 'u5_total_inpatient_all_causes', \ 'u5_total_malaria_inpatient', \ 'u5_total_death_all_causes', \ 'u5_total_malaria_death', \ 'u5_total_distributed_bednets', \ 'o5_total_consultation_all_causes', \ 'o5_total_suspected_malaria_cases', \ 'o5_total_simple_malaria_cases', \ 'o5_total_severe_malaria_cases', \ 'o5_total_tested_malaria_cases', \ 'o5_total_confirmed_malaria_cases', \ 'o5_total_treated_malaria_cases', \ 'o5_total_inpatient_all_causes', \ 'o5_total_malaria_inpatient', \ 'o5_total_death_all_causes', \ 'o5_total_malaria_death', \ 'pw_total_consultation_all_causes', \ 'pw_total_suspected_malaria_cases', \ 'pw_total_severe_malaria_cases', \ 'pw_total_tested_malaria_cases', \ 'pw_total_confirmed_malaria_cases', \ 'pw_total_treated_malaria_cases', \ 'pw_total_inpatient_all_causes', \ 'pw_total_malaria_inpatient', \ 'pw_total_death_all_causes', \ 'pw_total_malaria_death', \ 'pw_total_distributed_bednets', \ 'pw_total_anc1', \ 'pw_total_sp1', \ 'pw_total_sp2', \ 'stockout_act_children', 'stockout_act_youth', 'stockout_act_adult', \ 'stockout_artemether', 'stockout_quinine', 'stockout_serum', \ 'stockout_bednet', 'stockout_rdt', 'stockout_sp'] args_values = message.content.strip().lower().split() arguments = dict(zip(args_names, args_values)) except ValueError: # failure to split means we proabably lack a data or more # we can't process it. message.respond(error_start + u" Le format du SMS est incorrect.") return True # convert form-data to int or bool respectively try: for key, value in arguments.items(): if key.split('_')[0] in ('u5', 'o5', 'pw', 'month', 'year'): arguments[key] = int(value) if key.split('_')[0] == 'stockout': arguments[key] = MalariaReport.YES if bool(int(value)) \ else MalariaReport.NO except: raise # failure to convert means non-numeric value which we can't process. message.respond(error_start + u" Les données sont malformées.") return True # check credentials try: provider = Provider.active.get(user__username=arguments['username']) except Provider.DoesNotExist: message.respond(error_start + u"Ce nom d'utilisateur " + u"(%s) n'existe pas." % \ arguments['username']) return True if not provider.check_password(arguments['password']): message.respond(error_start + u"Votre mot de passe est incorrect.") return True # now we have well formed and authenticated data. # let's check for business-logic errors. # create a data holder for validator data_browser = MalariaDataHolder() # feed data holder with sms provided data for key, value in arguments.items(): if key.split('_')[0] in ('u5', 'o5', 'pw', \ 'stockout', 'year', 'month'): data_browser.set(key, value) # feed data holder with guessable data try: hc = entity_for(provider).slug except: hc = None data_browser.set('hc', hc) today = datetime.date.today() data_browser.set('fillin_day', today.day) data_browser.set('fillin_month', today.month) data_browser.set('fillin_year', today.year) data_browser.set('author', provider.name()) # create validator and fire validator = MalariaReportValidator(data_browser) validator.errors.reset() try: validator.validate() except AttributeError as e: message.respond(error_start + e.__str__()) return True errors = validator.errors # return first error to user if errors.count() > 0: message.respond(error_start + errors.all()[0]) return True # create the report try: period = MonthPeriod.find_create_from(year=data_browser.get('year'), \ month=data_browser.get('month')) entity = Entity.objects.get(slug=data_browser.get('hc'), \ type__slug='cscom') report = MalariaReport.start(period, entity, provider, \ type=MalariaReport.TYPE_SOURCE) report.add_underfive_data(*data_browser.data_for_cat('u5')) report.add_overfive_data(*data_browser.data_for_cat('o5')) report.add_pregnantwomen_data(*data_browser.data_for_cat('pw')) report.add_stockout_data(*data_browser.data_for_cat('so')) #report.save() with reversion.create_revision(): report.save() reversion.set_user(provider.user) except Exception as e: message.respond(error_start + u"Une erreur technique s'est " \ u"produite. Reessayez plus tard et " \ u"contactez ANTIM si le probleme persiste.") logger.error(u"Unable to save report to DB. Message: %s | Exp: %r" \ % (message.content, e)) return True message.respond(u"[SUCCES] Le rapport de %(cscom)s pour %(period)s " u"a ete enregistre. " \ u"Le No de recu est #%(receipt)s." \ % {'cscom': report.entity.display_full_name(), \ 'period': report.period, \ 'receipt': report.receipt}) try: to = contact_for(report.entity.parent).phone_number except: to = None if not to: return True send_sms(to, u"[ALERTE] Le CSCom %(cscom)s vient d'envoyer le " \ u"rapport #%(receipt)s pour %(period)s." \ % {'cscom': report.entity.display_full_name(), \ 'period': report.period, \ 'receipt': report.receipt}) return True
def action(self): """ Validate remaining reports and inform region """ # retrieve autobot provider if possible try: author = Provider.active.get(user__username='******') except: author = None if self.args.is_district: validate_level = 'cscom' aggregate_level = 'district' else: validate_level = 'district' aggregate_level = 'region' # validate non-validated reports for report in MalariaReport.unvalidated\ .filter(period=self.args.period, \ entity__type__slug=validate_level): report._status = MalariaReport.STATUS_VALIDATED if author: report.modified_by = author report.modified_on = datetime.now() with reversion.create_revision(): report.save() if author: reversion.set_user(author.user) else: reversion.set_user(get_autobot().user) #report.save() # create aggregated reports for entity in Entity.objects.filter(type__slug=aggregate_level): if entity.pnlp_core_malariareport_reports\ .filter(period=self.args.period).count() > 0: continue rauthor = contact_for(entity) if not author else author logger.info(u"Creating Aggregated report for %s" % entity) report = MalariaReport.create_aggregated(self.args.period, \ entity, rauthor) # region auto-validates their reports if not self.args.is_district: report._status = MalariaReport.STATUS_VALIDATED #report.save() with reversion.create_revision(): report.save() reversion.set_user(rauthor.user) # region-only section # create national report mali = Entity.objects.get(slug='mali') if not self.args.is_district \ and mali.pnlp_core_malariareport_reports\ .filter(period=self.args.period).count() == 0: rauthor = author if author else get_autobot() logger.info(u"Creating National report") report = MalariaReport.create_aggregated(self.args.period, \ mali, rauthor) # following only applies to districts (warn regions). return # district-only section # let region know there are reports to validate for region in Entity.objects.filter(type__slug='region'): contact = contact_for(region, recursive=False) # skip if not recipient if not contact or not contact.phone_number: continue nb_reports = MalariaReport.unvalidated\ .filter(period=self.args.period, \ entity__type__slug='district', \ entity__parent=region).count() message = u"[PNLP] La periode de validation CSRef est " \ u"terminee. Vous avez %(nb)d rapports de " \ u"CSRef a valider." % {'nb': nb_reports} email_title = u"[PNLP] Fin de la periode de validation CSRef" if not contact.email: continue send_email(contact.email, message=message, title=email_title)
def dashboard(request): category = 'dashboard' context = {'category': category} from bolibana.models import Entity from pnlp_core.data import (current_period, current_stage, \ time_cscom_over, time_district_over, \ time_region_over) def sms_received_sent_by_period(period): received = Inbox.objects.filter(receivingdatetime__gte=period.start_on, receivingdatetime__lte=period.end_on) \ .count() sent = SentItems.objects.filter(sendingdatetime__gte=period.start_on, sendingdatetime__lte=period.end_on) \ .count() return (received, sent) def received_reports(period, type_): return MalariaReport.objects.filter(period=period, \ entity__type__slug=type_) def reports_validated(period, type_): return MalariaReport.validated.filter(period=period, \ entity__type__slug=type_) def reporting_rate(period, entity): return float(MalariaReport.validated.filter(period=period, \ entity__parent=entity).count()) \ / Entity.objects.filter(parent__slug=entity.slug).count() current_period = current_period() period = current_reporting_period() context.update({'current_period': current_period, 'current_reporting_period': period, 'current_stage': current_stage(), 'current_sms': sms_received_sent_by_period(current_period), 'current_reporting_sms': \ sms_received_sent_by_period(period), 'total_cscom': Entity.objects\ .filter(type__slug='cscom').count(), 'time_cscom_over': time_cscom_over(period), 'time_district_over': time_district_over(period), 'time_region_over': time_region_over(period)}) received_cscom_reports = received_reports(period, 'cscom') cscom_reports_validated = reports_validated(period, 'cscom') district_reports_validated = reports_validated(period, 'district') reporting_rate = \ float(MalariaReport.validated.filter(period=period).count()) \ / Entity.objects.count() cscom_missed_report = \ Entity.objects.filter(type__slug='cscom')\ .exclude(id__in=[r.entity.id \ for r \ in received_cscom_reports])\ .order_by('name') def entities_autoreports(level): districts_missed_report = {} auto_validated_cscom_reports = \ MalariaReport.validated\ .filter(entity__type__slug=level, \ modified_by__user__username='******') for report in auto_validated_cscom_reports: if not report.entity.parent.slug in districts_missed_report: districts_missed_report[report.entity.parent.slug] = \ {'entity': report.entity.parent, \ 'nbauto': 0, \ 'contact': contact_for(report.entity.parent, False)} districts_missed_report[report.entity.parent.slug]['nbauto'] += 1 return districts_missed_report districts_missed_report = entities_autoreports('cscom') regions_missed_report = entities_autoreports('district') context.update({'received_cscom_reports': received_cscom_reports.count(), 'cscom_reports_validated': cscom_reports_validated.count(), 'district_reports_validated': district_reports_validated.count(), 'reporting_rate': reporting_rate, 'cscom_missed_report_count': cscom_missed_report.count(), 'cscom_missed_report': [(e, contact_for(e, True)) \ for e in cscom_missed_report[:20]], 'districts_missed_report': districts_missed_report, 'regions_missed_report': regions_missed_report}) return render(request, 'dashboard.html', context)