Beispiel #1
0
def nut_report_update(message, args, sub_cmd, **kwargs):
    """ Client sent an update to an existing report

    Only the modified fields are sent.
    Each section is coded accoding to report codes.
    All fields are coded according to nutrsc.

    > nut report-update rgaudin -1355030878 0112  #P &SAM 1d:2 "
      1h:2 1l6:2 #C &MAM a0:100 #T v:1 w:0 u:0-EOM-"""

    def resp_error(code, msg):
        # make sure we cancel whatever addition
        message.respond(u"nut report-update error %(code)s|%(msg)s" \
                        % {'code': code, 'msg': msg})
        return True

    def provider_entity(provider):
        """ Entity a Provider is attached to """
        try:
            return NUTEntity.objects.get(id=provider.first_access().target.id)
        except:
            return None

    # check that all parts made it together
    if not args.strip().endswith('-eom-'):
        return resp_error('BAD_FORM', REPORT_ERRORS['BAD_FORM'])
    else:
        args = args.strip()[:-5].strip()

    # split up sections
    sections = {}
    try:
        parts = args.strip().lower().split('#')
        for index in range(0, len(parts)):
            if index == 0:
                infos = parts[index]
            else:
                sections[parts[index][0].upper()] = parts[index][1:]
        pec_sec = sections.get('P', '').strip()
        cons_sec = sections.get('C', '').strip()
        order_sec = sections.get('O', '').strip()
        other_sec = sections.get('T', '').strip()
    except:
        return resp_error('BAD_FORM', REPORT_ERRORS['BAD_FORM'])

    # split up infos
    try:
        username, pwhash, date_str = infos.strip().split()
    except:
        return resp_error('BAD_FORM_INFO', REPORT_ERRORS['BAD_FORM_INFO'])

    # get Provider based on username
    try:
        provider = Provider.objects.get(user__username=username)
    except Provider.DoesNotExist:
        return resp_error('NO_ACC', REPORT_ERRORS['NO_ACC'])

    # check that provider pwhash is good
    if not provider.check_hash(pwhash):
        return resp_error('BAD_PASS', REPORT_ERRORS['BAD_PASS'])

    # check that user is not disabled
    if not provider.is_active:
        return resp_error('ACC_DIS', REPORT_ERRORS['ACC_DIS'])

    # check that user has permission to submit report on entity
    entity = provider_entity(provider)

    if not entity:
        return resp_error('NOT_ENT', REPORT_ERRORS['NOT_ENT'])

    eentity = Entity.objects.get(id=entity.id)
    if not provider_can('can_submit_report', provider, eentity):
        return resp_error('NO_PERM', REPORT_ERRORS['NO_PERM'])

    # parse date and check if period is valid
    try:
        month = int(date_str[0:2])
        year = int('%s%s' % ('20', date_str[2:4]))
        period = MonthPeriod.find_create_from(year=year, month=month)
    except:
        return resp_error('BAD_FORM_PERIOD', REPORT_ERRORS['BAD_FORM_PERIOD'])

    # check period is the one we want
    if not period == current_reporting_period():
        return resp_error('BAD_PERIOD', REPORT_ERRORS['BAD_PERIOD'])

    # global infos
    infos = {'entity': entity,
             'eentity': eentity,
             'provider': provider,
             'month': month,
             'year': year,
             'period': period,
             'username': username,
             'pwhash': pwhash}

    # Retrieve report
    try:
        nut_report = NutritionReport.objects.get(period=infos['period'],
                                                 entity=infos['entity'],
                                                 type=Report.TYPE_SOURCE)
    except:
        return resp_error('MISS', REPORT_ERRORS['MISS'])

    reports = []
    # common start of error message
    error_start = u"Impossible d'enregistrer le rapport. "

    logger.info("Processing PEC")

    if pec_sec:
        subs = pec_sec.split('&')
        subs = subs[1:]
        for sub in subs:
            fields = sub.split()
            cap = fields[0].lower()
            sub_report = getattr(nut_report, 'pec_%s_report' % cap)
            for field in fields[1:]:
                cfield, value = field.split(':')
                rfield = uncompress_pec_field(cfield)
                setattr(sub_report, rfield, int(value))
            validator = PECReportValidator(sub_report)
            validator.errors.reset()
            try:
                validator.validate()
            except AttributeError as e:
                return resp_error('PEC_%s' % cap.upper(),
                                  error_start + e.__str__())
            except:
                pass
            errors = validator.errors
            # return first error to user
            if errors.count() > 0:
                return resp_error('PEC_%s' % cap.upper(),
                                  error_start + errors.all()[0])
            else:
                reports.append(sub_report)

    logger.info("Processing CONS")

    if cons_sec:
        subs = cons_sec.split('&')
        subs = subs[1:]
        for sub in subs:
            fields = sub.split()
            cap = fields[0].lower()
            logger.info(cap.upper())
            for field in fields[1:]:
                cfield, value = field.split(':')
                rinpc, rfield = uncompress_cons_field(cfield)
                sub_report = getattr(getattr(nut_report, 
                                             'cons_%s_report' % cap),
                                     'icr')(rinpc)
                setattr(sub_report, rfield, int(value))
                if sub_report.valid and not sub_report in reports:
                    reports.append(sub_report)

    logger.info("Processing ORDER")

    if order_sec:
        subs = order_sec.split('&')
        subs = subs[1:]
        for sub in subs:
            logger.info("\t%s" % sub)
            fields = sub.split()
            cap = fields[0].lower()
            for field in fields[1:]:
                cfield, value = field.split(':')
                rinpc, rfield = uncompress_cons_field(cfield)
                sub_report = getattr(getattr(nut_report,
                                             'order_%s_report' % cap),
                                     'icr')(rinpc)
                setattr(sub_report, rfield, int(value))
                if not sub_report in reports:
                    reports.append(sub_report)

    logger.info("Processing OTHER")

    if other_sec:
        fields = other_sec.split()
        for field in fields[1:]:
            cfield, value = field.split(':')
            rfield = uncompress_pec_field(cfield)
            sub_report = nut_report.pec_other_report
            setattr(sub_report, rfield, int(value))
        # check validity relative to PEC
        if not sub_report.total == sub_report.nut_report.sum_all_other:
            return resp_error('OTHER_INT', REPORT_ERRORS['OTHER_INT'])
        else:
            reports.append(sub_report)


    # check validity of changes
    # save to DB
    @reversion.create_revision()
    @transaction.commit_manually
    def save_reports(reports, nut_report, provider=None):
        reversion.set_user(provider.user)
        reversion.set_comment("SMS report update")
        for report in reports:
            print("saving %s" % report)
            try:
                sub_report.save()
            except:
                transaction.rollback()
                return False
        try:
            nut_report._status = nut_report.STATUS_MODIFIED_AUTHOR
            nut_report.modified_by = provider
            nut_report.save()
        except:
            transaction.rollback()
            return False

        transaction.commit()
        return True

    logger.info("Saving reports")
    if not save_reports(reports, nut_report, provider):
        logger.warning("Unable to save reports")
        return resp_error('SRV', REPORT_ERRORS['SRV'])
    logger.info("Reports saved")

    ## CONFIRM RESPONSE
    
    confirm = "nut report-update ok %s" % nut_report.receipt

    message.respond(confirm)
    return True
Beispiel #2
0
def nut_report(message, args, sub_cmd, **kwargs):

    """ Client sent report for PEC CONS & ORDER

    Once the PEC MAM/SAM/SAM+ & CONS & ORDER is filled, all the data
    is sent via one single multipart SMS.
    SMS is divided in sections and sub sections
    #P #C #O respectively PEC, CONS and ORDER
    We don't combine sections in case we'll have to split message
    in different SMS.
    SMS can be 1000chars+
    Sub sections for capabilities: &MAM, &SAM &SAMP
    Sub sub sections for age break downs |u6 |u59 |o59 |pw |fu1 |fu12

    > nut report rgaudin 89080392890 1111
    #P&MAM|u59 6817 7162 2164 1033 6527 5715 6749 2174 4201
    3675 8412 2331 4868 4765 1896 2107 3457 6308 6238 6589 2432 5983 3871|pw
    6817 7162 2164 1033 6527 5715 6749 2174 4201 3675 8412 2331 4868 4765
    1896 2107 3457 6308 6238 6589 2432 5983 3871|fu12 6817 7162 2164 1033
    6527 5715 6749 2174 4201 3675 8412 2331 4868 4765 1896 2107 3457 6308
    6238 6589 2432 5983 3871&SAM|u59 6817 7162 2164 1033 6527 5715 6749 2174
    4201 3675 8412 2331 4868 4765 1896 2107 3457 6308 6238 6589 2432 5983
    3871|o59 6817 7162 2164 1033 6527 5715 6749 2174 4201 3675 8412 2331 4868
    4765 1896 2107 3457 6308 6238 6589 2432 5983 3871|fu1 6817 7162 2164 1033
    6527 5715 6749 2174 4201 3675 8412 2331 4868 4765 1896 2107 3457 6308
    6238 6589 2432 5983 3871#C&MAM|csb 1199 1199 1199 1199|unimix 1199 1199
    1199 1199|oil 1199 1199 1199 1199|sugar 1199 1199 1199 1199|mil 1199 1199
    1199 1199|niebe 1199 1199 1199 1199&SAM|plumpy 1199 1199 1199
    1199#O&MAM|csb 1199|unimix 1199|oil 1199|sugar 1199|mil 1199|niebe
    1199&SAM|plumpy 1199#T 1 2 3-EOM-
    
    T lwb tb hiv
    < nut report error Error-code | Error Message
    < nut report ok #P$MAM GA1/gao-343-VO5$SAM GA1/gao-343-VO5#C$MAM
    GA1/gao-343-VO5$SAM GA1/gao-343-VO5#O$MAM GA1/gao-343-VO5$SAM
    GA1/gao-343-VO5 """

    def resp_error(code, msg):
        # make sure we cancel whatever addition
        message.respond(u"nut report error %(code)s|%(msg)s" \
                        % {'code': code, 'msg': msg})
        return True

    def provider_entity(provider):
        """ Entity a Provider is attached to """
        try:
            return NUTEntity.objects.get(id=provider.first_access().target.id)
        except:
            return None


    def sub_sections_from_section(section):
        """ Returns an organised hash from raw string

        {'mam': {'u6': 'xx xx xx', 'o59': 'xx xx xx'}, 'sam': {}} """

        subs = section.split('&')
        subs = subs[1:]
        subsh = {}
        for sub in subs:
            sub_data = sub.split('|')
            subh = {}
            for age_line in sub_data[1:]:
                age_ls = age_line.split()
                subh[age_ls[0]] = ' '.join(age_ls[1:])
            subsh[sub_data[0]] = subh
        return subsh

    def check_capabilities(section, entity):
        """ return True if section's subs matches entity cap """
        for cap in ('mam', 'sam', 'samp'):
            if getattr(entity, 'is_%s' % cap):
                if not cap in section.keys():
                    return False
            else:
                if cap in section.keys():
                    return False
        return True

    @reversion.create_revision()
    @transaction.commit_manually
    def save_reports(reports, user=None):
        reversion.set_user(provider.user)
        reversion.set_comment("SMS transmitted report")   
        # save main first
        reports['main'].save()

        for secid, section in reports.items():
            if secid == 'main':
                continue
                      
            for catid, reports in section.items():
                    
                for report in reports:
                    logger.info("%s > %s > %s" \
                                % (secid, catid, report.__class__))
                    try:
                        # HACK: write foreign key id if needed
                        if hasattr(report, 'nut_report_id'):
                            report.nut_report_id = report.nut_report.id

                        if hasattr(report, 'cons_report_id'):
                            report.cons_report_id = report.cons_report.id

                        if hasattr(report, 'order_report_id'):
                            report.order_report_id = report.order_report.id

                        report.save()
                    except Exception as e:
                        logger.error(u"Unable to save report to DB. " \
                                     u"Message: %s | Exp: %r" \
                                     % (message.content, e))
                        transaction.rollback()
                        return False
        transaction.commit()
        return True

    # check that all parts made it together
    if not args.strip().endswith('-eom-'):
        return resp_error('BAD_FORM', REPORT_ERRORS['BAD_FORM'])
    else:
        args = args.strip()[:-5].strip()

    # split up sections
    try:
        infos, pec_sec, cons_sec, order_sec, other_sec = \
                                                 args.strip().lower().split('#')
        pec_sec = pec_sec[1:]
        cons_sec = cons_sec[1:]
        order_sec = order_sec[1:]
        other_sec = other_sec[1:]
    except:
        return resp_error('BAD_FORM', REPORT_ERRORS['BAD_FORM'])

    # split up infos
    try:
        username, pwhash, date_str = infos.strip().split()
    except:
        return resp_error('BAD_FORM_INFO', REPORT_ERRORS['BAD_FORM_INFO'])

    # get Provider based on username
    try:
        provider = Provider.objects.get(user__username=username)
    except Provider.DoesNotExist:
        return resp_error('NO_ACC', REPORT_ERRORS['NO_ACC'])

    # check that provider pwhash is good
    if not provider.check_hash(pwhash):
        return resp_error('BAD_PASS', REPORT_ERRORS['BAD_PASS'])

    # check that user is not disabled
    if not provider.is_active:
        return resp_error('ACC_DIS', REPORT_ERRORS['ACC_DIS'])

    # check that user has permission to submit report on entity
    entity = provider_entity(provider)

    if not entity:
        return resp_error('NOT_ENT', REPORT_ERRORS['NOT_ENT'])

    eentity = Entity.objects.get(id=entity.id)
    if not provider_can('can_submit_report', provider, eentity):
        return resp_error('NO_PERM', REPORT_ERRORS['NO_PERM'])

    # parse date and check if period is valid
    try:
        month = int(date_str[0:2])
        year = int('%s%s' % ('20', date_str[2:4]))
        period = MonthPeriod.find_create_from(year=year, month=month)
    except:
        return resp_error('BAD_FORM_PERIOD', REPORT_ERRORS['BAD_FORM_PERIOD'])

    # check period is the one we want
    if not period == current_reporting_period():
        return resp_error('BAD_PERIOD', REPORT_ERRORS['BAD_PERIOD'])

    # report receipts holder for confirm message
    #report_receipts = {}

    # reports holder for delayed database commit
    reports = {}

    # global infos
    infos = {'entity': entity,
             'eentity': eentity,
             'provider': provider,
             'month': month,
             'year': year,
             'period': period,
             'username': username,
             'pwhash': pwhash}

    # UNIQUENESS
    if NutritionReport.objects.filter(period=infos['period'],
                                      entity=infos['entity'],
                                      type=Report.TYPE_SOURCE).count() > 0:
        return resp_error('UNIQ', REPORT_ERRORS['UNIQ'])

    # create main report
    try:
        period = MonthPeriod.find_create_from(year=year, month=month)
        nut_report = NutritionReport.start(period, entity, provider,
                                           type=Report.TYPE_SOURCE)
        reports['main'] = nut_report
    except Exception as e:
        #raise
        logger.error(u"Unable to save report to DB. Message: %s | Exp: %r" \
                     % (message.content, e))
        return resp_error('SRV', REPORT_ERRORS['SRV'])

    # SECTIONS
    for sid, section in {'P': 'pec', 'C': 'cons',
                         'O': 'order', 'T': 'other'}.items():

        logger.info("Processing %s" % section)

        # extract/split sub sections info from string
        if sid == 'T':
            sec = eval('%s_sec' % section)
        else:
            sec = sub_sections_from_section(eval('%s_sec' % section))

        # check that capabilities correspond to entity
        if sid != 'T' and not check_capabilities(sec, entity):
            return resp_error('BAD_CAP', REPORT_ERRORS['BAD_CAP'])

        # call sub-report section handler
        sec_succ, sec_data = SUB_REPORTS.get(section)(message,
                                                      sec, infos, reports,
                                                      nut_report=nut_report)
        # cancel if sub report failed.
        if not sec_succ:
            logger.warning(u"   FAILED.")
            return resp_error(sec_data[0], sec_data[1])

        # add sub-report to list of reports
        reports[sid] = sec_data
        logger.info("---- Ended %s" % section)
    
    ## DB COMMIT
    
    # create the reports in DB
    # save receipts number
    logger.info("Saving reports")
    if not save_reports(reports, user=provider.user):
        logger.warning("Unable to save reports")
        return resp_error('SRV', REPORT_ERRORS['SRV'])
    logger.info("Reports saved")

    def flatten(iterable):
        values = []

        def add(value):
            if not isinstance(value, (list, dict)):
                values.append(value)
                return True
            return False

        if not add(iterable):
            miterable = iterable.values() if isinstance(iterable, dict) \
                                          else iterable
            for item in miterable:
                values += flatten(item)
        return values

    # for report in flatten(reports):
    #     with reversion.create_revision():
    #         reversion.set_user(provider.user)
    #         reversion.set_comment("SMS transmitted report")
    #         report.save()

    ## CONFIRM RESPONSE
    
    confirm = "nut report ok %s" % nut_report.receipt

    message.respond(confirm)
    return True

    ## TRIGGER ALERT
    
    """