コード例 #1
0
    def handle(self):
        location = self.user.location
        domain = self.domain_object

        location_id = self.location_id

        if not location_id:
            return False

        if location.location_type_name == 'FACILITY':
            try:
                data = self.data
                if not data:
                    return True

                if not data.get('transactions'):
                    self.on_error(data)
                    return True

                process(domain.name, data)
                if not data['errors']:
                    self.on_success()
                else:
                    self.on_error(data)
                    return True
                self.respond(self.get_message(data))
            except NotAUserClassError:
                return True
            except Exception, e:  # todo: should we only trap SMSErrors?
                if settings.UNIT_TESTING or settings.DEBUG:
                    raise
                send_sms_to_verified_number(
                    self.verified_contact,
                    'problem with stock report: %s' % str(e))
コード例 #2
0
    def handle(self):
        domain = Domain.get_by_name(self.domain)
        split_text = self.msg.text.split(' ', 1)
        if split_text[0].lower() == 'soh':
            text = split_text[1]
        elif split_text[0].startswith('soh'):
            text = split_text[0][3:]
        else:
            text = self.msg.text

        if not domain.commtrack_enabled:
            return False

        if not self.sql_location:
            self.respond(NO_SUPPLY_POINT_MESSAGE)
            return True

        try:
            parser = self.parser
            formatted_text = EWSFormatter().format(text)
            data = parser.parse(formatted_text)
            if not data:
                return False
            if EWS_INVALID_REPORT_RESPONSE.enabled(self.domain):
                filtered_transactions = self.get_valid_reports(data)

                if not filtered_transactions:
                    return True

                data['transactions'] = filtered_transactions

        except NotAUserClassError:
            return False
        except (SMSError, NoDefaultLocationException):
            self.respond(six.text_type(INVALID_MESSAGE))
            return True
        except ProductCodeException as e:
            self.respond(six.text_type(e))
            return True
        except Exception as e:
            if settings.UNIT_TESTING or settings.DEBUG:
                raise
            self.respond('problem with stock report: %s' % str(e))
            return True

        stockouts = set()
        if self.sql_location.location_type.name in ['Regional Medical Store', 'Central Medical Store']:
            stockouts = set(StockState.objects.filter(
                case_id=self.sql_location.supply_point_id,
                stock_on_hand=0
            ).values_list('sql_product__name', flat=True))

        process(domain.name, data)
        transactions = data['transactions']

        if not self.async_response:
            self.send_messages(parser, stockouts, transactions)
        else:
            send_soh_messages_task.delay(self, parser, stockouts, transactions)
        return True
コード例 #3
0
    def handle(self):
        location = self.user.location
        domain = self.domain_object

        location_id = self.location_id

        if not location_id:
            return False

        if location.location_type_name == 'FACILITY':
            try:
                data = self.data
                if not data:
                    return True

                if not data.get('transactions'):
                    self.on_error(data)
                    return True

                process(domain.name, data)
                if not data['errors']:
                    self.on_success()
                else:
                    self.on_error(data)
                    return True
                self.respond(self.get_message(data))
            except NotAUserClassError:
                return True
            except Exception, e:  # todo: should we only trap SMSErrors?
                if settings.UNIT_TESTING or settings.DEBUG:
                    raise
                send_sms_to_verified_number(self.verified_contact, 'problem with stock report: %s' % str(e))
コード例 #4
0
ファイル: receipts.py プロジェクト: ye-man/commcare-hq
    def handle(self):
        verified_contact = self.verified_contact
        domain = Domain.get_by_name(verified_contact.domain)
        text = self.msg.text

        try:
            data = StockReportParser(domain, verified_contact).parse(text)
            if not data:
                return False
        except NotAUserClassError:
            return False
        except Exception as e:
            if settings.UNIT_TESTING or settings.DEBUG:
                raise
            send_sms_to_verified_number(
                verified_contact, 'problem with stock report: %s' % str(e))
            return True
        transactions = data['transactions']
        products = [
            SQLProduct.objects.get(product_id=transaction.product_id).code
            for transaction in transactions
        ]
        process(domain.name, data)
        send_sms_to_verified_number(
            verified_contact,
            RECEIPT_CONFIRM % {'products': ' '.join(products)})
        return True
コード例 #5
0
ファイル: soh.py プロジェクト: tstalka/commcare-hq
    def handle(self):
        domain = Domain.get_by_name(self.domain)
        split_text = self.msg.text.split(' ', 1)
        if split_text[0].lower() == 'soh':
            text = split_text[1]
        elif split_text[0].startswith('soh'):
            text = split_text[0][3:]
        else:
            text = self.msg.text

        if not domain.commtrack_enabled:
            return False

        if not self.sql_location:
            self.respond(NO_SUPPLY_POINT_MESSAGE)
            return True

        try:
            parser = self.parser
            formatted_text = EWSFormatter().format(text)
            data = parser.parse(formatted_text)
            if not data:
                return False

        except NotAUserClassError:
            return False
        except (SMSError, NoDefaultLocationException):
            self.respond(str(INVALID_MESSAGE))
            return True
        except ProductCodeException as e:
            self.respond(str(e))
            return True
        except Exception as e:
            if settings.UNIT_TESTING or settings.DEBUG:
                raise
            self.respond('problem with stock report: %s' % str(e))
            return True

        stockouts = set()
        if self.sql_location.location_type.name in [
                'Regional Medical Store', 'Central Medical Store'
        ]:
            stockouts = set(
                StockState.objects.filter(
                    case_id=self.sql_location.supply_point_id,
                    stock_on_hand=0).values_list('sql_product__name',
                                                 flat=True))

        process(domain.name, data)
        transactions = data['transactions']

        if not self.async_response:
            self.send_messages(parser, stockouts, transactions)
        else:
            send_soh_messages_task.delay(self, parser, stockouts, transactions)
        return True
コード例 #6
0
ファイル: views.py プロジェクト: aristide/commcare-hq
 def post(self, request, *args, **kwargs):
     InputStockFormSet = formset_factory(InputStockForm)
     formset = InputStockFormSet(request.POST)
     if formset.is_valid():
         try:
             location = SQLLocation.objects.get(site_code=kwargs['site_code'], domain=self.domain)
         except SQLLocation.DoesNotExist:
             raise Http404()
         data = []
         for form in formset:
             product = Product.get(docid=form.cleaned_data['product_id'])
             if form.cleaned_data['receipts']:
                 data.append(
                     StockTransaction(
                         domain=self.domain,
                         location_id=location.location_id,
                         case_id=location.supply_point_id,
                         product=product,
                         action=const.StockActions.RECEIPTS,
                         quantity=form.cleaned_data['receipts']
                     ),
                 )
             if form.cleaned_data['stock_on_hand'] is not None:
                 data.append(
                     StockTransaction(
                         domain=self.domain,
                         location_id=location.location_id,
                         case_id=location.supply_point_id,
                         product=product,
                         action=const.StockActions.STOCKONHAND,
                         quantity=form.cleaned_data['stock_on_hand']
                     ),
                 )
         if data:
             unpacked_data = {
                 'timestamp': datetime.utcnow(),
                 'user': self.request.couch_user,
                 'phone': 'ewsghana-input-stock',
                 'location': location.couch_location,
                 'transactions': data,
             }
             process(self.domain, unpacked_data)
         url = make_url(
             StockLevelsReport,
             self.domain,
             '?location_id=%s&filter_by_program=%s&startdate='
             '&enddate=&report_type=&filter_by_product=%s',
             (location.location_id, ALL_OPTION, ALL_OPTION)
         )
         return HttpResponseRedirect(url)
     context = self.get_context_data(**kwargs)
     context['formset'] = formset
     return self.render_to_response(context)
コード例 #7
0
ファイル: util.py プロジェクト: elbowink/commcare-hq
def fake_sms(user, text):
    """
    Fake a commtrack SMS submission for a user.
    `text` might be "soh myproduct 100"
    Don't use this with a real user
    """
    if not user.phone_number:
        raise ValueError("User does not have a phone number")
    if not user.get_verified_number():
        user.save_verified_number(user.domain, user.phone_number, True, None)
    domain_obj = Domain.get_by_name(user.domain)
    parser = StockReportParser(domain_obj, user.get_verified_number())
    process(user.domain, parser.parse(text.lower()))
コード例 #8
0
 def post(self, request, *args, **kwargs):
     InputStockFormSet = formset_factory(InputStockForm)
     formset = InputStockFormSet(request.POST)
     if formset.is_valid():
         try:
             location = SQLLocation.objects.get(
                 site_code=kwargs['site_code'], domain=self.domain)
         except SQLLocation.DoesNotExist:
             raise Http404()
         data = []
         for form in formset:
             product = Product.get(docid=form.cleaned_data['product_id'])
             if form.cleaned_data['receipts']:
                 data.append(
                     StockTransactionHelper(
                         domain=self.domain,
                         location_id=location.location_id,
                         case_id=location.supply_point_id,
                         product_id=product.get_id,
                         action=const.StockActions.RECEIPTS,
                         quantity=form.cleaned_data['receipts']), )
             if form.cleaned_data['stock_on_hand'] is not None:
                 data.append(
                     StockTransactionHelper(
                         domain=self.domain,
                         location_id=location.location_id,
                         case_id=location.supply_point_id,
                         product_id=product.get_id,
                         action=const.StockActions.STOCKONHAND,
                         quantity=form.cleaned_data['stock_on_hand']), )
         if data:
             unpacked_data = {
                 'timestamp': datetime.utcnow(),
                 'user': self.request.couch_user,
                 'phone': 'ewsghana-input-stock',
                 'location': location.couch_location,
                 'transactions': data,
             }
             process(self.domain, unpacked_data)
         url = make_url(
             StockLevelsReport, self.domain,
             '?location_id=%s&filter_by_program=%s&startdate='
             '&enddate=&report_type=&filter_by_product=%s',
             (location.location_id, ALL_OPTION, ALL_OPTION))
         return HttpResponseRedirect(url)
     context = self.get_context_data(**kwargs)
     context['formset'] = formset
     return self.render_to_response(context)
コード例 #9
0
ファイル: bulk.py プロジェクト: pawelreise/commcare-hq
def import_row(row, data_cols, domain, make_tx):
    """process (import) a single stock report row"""
    def get_data():
        for header, meta in data_cols.iteritems():
            val = row[header]
            if val is not None and val != '':
                yield make_tx(domain=domain, action_name=meta['action'], product=meta['product'], value=int(val))

    report = {
        'location': row['loc'],
        'timestamp': row['timestamp'],
        'user': row['user'],
        'phone': row.get('phone'),
        'transactions': list(get_data()),
    }
    sms.process(domain, report)
コード例 #10
0
ファイル: bulk.py プロジェクト: tsinkala/commcare-hq
def import_row(row, data_cols, domain, make_tx):
    """process (import) a single stock report row"""
    def get_data():
        for header, meta in data_cols.iteritems():
            val = row[header]
            if val is not None and val != '':
                yield make_tx(domain=domain, action_name=meta['action'], product=meta['product'], value=int(val))

    report = {
        'location': row['loc'],
        'timestamp': row['timestamp'],
        'user': row['user'],
        'phone': row.get('phone'),
        'transactions': list(get_data()),
    }
    sms.process(domain, report)
コード例 #11
0
ファイル: receipts.py プロジェクト: kkrampa/commcare-hq
    def handle(self):
        verified_contact = self.verified_contact
        domain = Domain.get_by_name(verified_contact.domain)
        text = self.msg.text

        try:
            data = StockReportParser(domain, verified_contact).parse(text)
            if not data:
                return False
        except NotAUserClassError:
            return False
        except Exception as e:
            if settings.UNIT_TESTING or settings.DEBUG:
                raise
            send_sms_to_verified_number(verified_contact, 'problem with stock report: %s' % str(e))
            return True
        transactions = data['transactions']
        products = [SQLProduct.objects.get(product_id=transaction.product_id).code for transaction in transactions]
        process(domain.name, data)
        send_sms_to_verified_number(verified_contact, RECEIPT_CONFIRM % {'products': ' '.join(products)})
        return True
コード例 #12
0
class SOHHandler(KeywordHandler):

    async_response = False

    def get_valid_reports(self, data):
        filtered_transactions = []
        excluded_products = []
        for product_id, transactions in get_transactions_by_product(
                data['transactions']).iteritems():
            begin_soh = None
            end_soh = None
            receipt = 0
            for transaction in transactions:
                if begin_soh is None:
                    sql_location = SQLLocation.objects.get(
                        location_id=transaction.location_id)
                    latest = StockTransaction.latest(
                        sql_location.supply_point_id, SECTION_TYPE_STOCK,
                        transaction.product_id)
                    begin_soh = 0
                    if latest:
                        begin_soh = float(latest.stock_on_hand)

                if transaction.action == 'receipts':
                    receipt += float(transaction.quantity)
                elif not end_soh:
                    end_soh = float(transaction.quantity)
            if end_soh > begin_soh + receipt:
                excluded_products.append(transaction.product_id)
            else:
                filtered_transactions.append(transaction)
        if excluded_products:
            message = ERROR_MESSAGE.format(products_list=', '.join([
                SQLProduct.objects.get(product_id=product_id).code
                for product_id in set(excluded_products)
            ]))
            self.respond(message)
        return filtered_transactions

    def send_errors(self, transactions, bad_codes):
        report_helper = ProductsReportHelper(self.user.sql_location,
                                             transactions)

        kwargs = {}

        if report_helper.reported_products():
            kwargs['stocks'] = ", ".join([
                product.code for product in
                report_helper.reported_products().order_by('code')
            ])
            error_message = 'You reported: {stocks}, but there were errors: {err}'
        else:
            error_message = '{err}'

        missing = report_helper.missing_products()
        if missing:
            kwargs['missing'] = ", ".join(
                [product.code for product in missing])
            error_message += " Please report {missing}"

        bad_codes = ', '.join(bad_codes)
        if bad_codes:
            kwargs[
                'err'] = 'Unrecognized commodity codes: {bad_codes}.'.format(
                    bad_codes=bad_codes)

        self.respond('{} {}'.format(error_message.format(**kwargs),
                                    unicode(ASSISTANCE_MESSAGE)))

    def send_ms_alert(self, previous_stockouts, transactions, ms_type):
        stockouts = {
            SQLProduct.objects.get(product_id=transaction.product_id).name
            for transaction in transactions if transaction.quantity == 0
            and transaction.action == 'stockonhand'
        }

        with_stock = {
            SQLProduct.objects.get(product_id=transaction.product_id).name
            for transaction in transactions if transaction.quantity != 0
            and transaction.action == 'stockonhand'
        }

        resolved_stockouts = previous_stockouts.intersection(with_stock)

        locations = self.sql_location.parent.get_descendants(include_self=True)\
            .filter(location_type__administrative=True)
        for sql_location in locations:
            for user in get_all_users_by_location(self.domain,
                                                  sql_location.location_id):
                phone_number = get_preferred_phone_number_for_recipient(user)
                if not phone_number:
                    continue

                stockouts_and_resolved = [(MS_RESOLVED_STOCKOUTS,
                                           resolved_stockouts),
                                          (MS_STOCKOUT, stockouts)]

                for message, data in stockouts_and_resolved:
                    if data:
                        message = message % {
                            'products_names': ', '.join(data),
                            'ms_type': ms_type
                        }
                        send_sms(self.domain, user, phone_number, message)

    def send_message_to_admins(self, message):
        in_charge_users = map(
            CommCareUser.wrap,
            iter_docs(CommCareUser.get_db(), [
                in_charge.user_id
                for in_charge in self.sql_location.facilityincharge_set.all()
            ]))
        for in_charge_user in in_charge_users:
            phone_number = get_preferred_phone_number_for_recipient(
                in_charge_user)
            if not phone_number:
                continue
            send_sms(
                self.sql_location.domain, in_charge_user, phone_number,
                message % {
                    'name': in_charge_user.full_name,
                    'location': self.sql_location.name
                })

    @property
    def parser(self):
        parser = EWSStockAndReceiptParser(self.domain_object,
                                          self.verified_contact)
        return parser

    def send_messages(self, parser, stockouts, transactions):
        if not parser.bad_codes:
            if self.sql_location.location_type.name == 'Regional Medical Store':
                self.send_ms_alert(stockouts, transactions, 'RMS')
            elif self.sql_location.location_type.name == 'Central Medical Store':
                self.send_ms_alert(stockouts, transactions, 'CMS')
            message, super_message = SOHAlerts(
                self.user, self.sql_location).get_alerts(transactions)
            if super_message:
                self.send_message_to_admins(super_message)
            self.respond(message)
        else:
            self.send_errors(transactions, parser.bad_codes)

    def handle(self):
        domain = Domain.get_by_name(self.domain)
        split_text = self.msg.text.split(' ', 1)
        if split_text[0].lower() == 'soh':
            text = split_text[1]
        elif split_text[0].startswith('soh'):
            text = split_text[0][3:]
        else:
            text = self.msg.text

        if not domain.commtrack_enabled:
            return False

        if not self.sql_location:
            self.respond(NO_SUPPLY_POINT_MESSAGE)
            return True

        try:
            parser = self.parser
            formatted_text = EWSFormatter().format(text)
            data = parser.parse(formatted_text)
            if not data:
                return False
            if EWS_INVALID_REPORT_RESPONSE.enabled(self.domain):
                filtered_transactions = self.get_valid_reports(data)

                if not filtered_transactions:
                    return True

                data['transactions'] = filtered_transactions

        except NotAUserClassError:
            return False
        except (SMSError, NoDefaultLocationException):
            self.respond(unicode(INVALID_MESSAGE))
            return True
        except ProductCodeException as e:
            self.respond(e.message)
            return True
        except Exception, e:  # todo: should we only trap SMSErrors?
            if settings.UNIT_TESTING or settings.DEBUG:
                raise
            self.respond('problem with stock report: %s' % str(e))
            return True

        stockouts = set()
        if self.sql_location.location_type.name in [
                'Regional Medical Store', 'Central Medical Store'
        ]:
            stockouts = set(
                StockState.objects.filter(
                    case_id=self.sql_location.supply_point_id,
                    stock_on_hand=0).values_list('sql_product__name',
                                                 flat=True))

        process(domain.name, data)
        transactions = data['transactions']

        if not self.async_response:
            self.send_messages(parser, stockouts, transactions)
        else:
            send_soh_messages_task.delay(self, parser, stockouts, transactions)
        return True