def prepare_commtrack_config(self): """ Bootstraps the domain-level metadata according to the static config. - Sets the proper location types hierarchy on the domain object. - Sets a keyword handler for reporting receipts """ for location_type in LocationType.objects.by_domain(self.domain): location_type.delete() previous = None for loc_type in LOCATION_TYPES: previous, _ = LocationType.objects.get_or_create( domain=self.domain, name=loc_type, parent_type=previous, administrative=(loc_type != 'FACILITY'), ) config = CommtrackConfig.for_domain(self.domain) config.consumption_config.exclude_invalid_periods = True actions = [action.keyword for action in config.actions] if 'delivered' not in actions: config.actions.append( CommtrackActionConfig( action='receipts', keyword='delivered', caption='Delivered') ) config.save()
def testOTASettings(self): domain = bootstrap_domain() ct_settings = CommtrackConfig.for_domain(domain.name) ct_settings.consumption_config = ConsumptionConfig( min_transactions=10, min_window=20, optimal_window=60, ) ct_settings.ota_restore_config = StockRestoreConfig( section_to_consumption_types={'stock': 'consumption'}, ) set_default_monthly_consumption_for_domain(domain.name, 5 * DAYS_IN_MONTH) restore_settings = ct_settings.get_ota_restore_settings() self.assertEqual(1, len(restore_settings.section_to_consumption_types)) self.assertEqual('consumption', restore_settings.section_to_consumption_types['stock']) self.assertEqual(10, restore_settings.consumption_config.min_periods) self.assertEqual(20, restore_settings.consumption_config.min_window) self.assertEqual(60, restore_settings.consumption_config.max_window) self.assertEqual(150, restore_settings.consumption_config.default_monthly_consumption_function('foo', 'bar')) self.assertFalse(restore_settings.force_consumption_case_filter(CommCareCase(type='force-type'))) self.assertEqual(0, len(restore_settings.default_product_list)) ct_settings.ota_restore_config.force_consumption_case_types=['force-type'] ct_settings.ota_restore_config.use_dynamic_product_list=True restore_settings = ct_settings.get_ota_restore_settings() self.assertTrue(restore_settings.force_consumption_case_filter(CommCareCase(type='force-type'))) self.assertEqual(3, len(restore_settings.default_product_list))
def process(domain, instance): """process an incoming commtrack stock report instance""" config = CommtrackConfig.for_domain(domain) root = etree.fromstring(instance) transactions = unpack_transactions(root, config) case_ids = [tx["case_id"] for tx in transactions] cases = dict((c._id, c) for c in CommCareCase.view("_all_docs", keys=case_ids, include_docs=True)) # ensure transaction types are processed in the correct order def transaction_order(tx): return [action.action_name for action in config.actions].index(tx["action"]) transactions.sort(key=transaction_order) # apply all transactions to each product case in bulk transactions_by_product = map_reduce(lambda tx: [(tx["case_id"],)], data=transactions, include_docs=True) for product_id, txs in transactions_by_product.iteritems(): product_case = cases[product_id] case_block, reconciliations = process_product_transactions(product_case, txs) for recon in reconciliations: root.append(recon) root.append(case_block) submission = etree.tostring(root) logger.debug("submitting: %s" % submission) submit_time = root.find(".//%s" % _("timeStart", META_XMLNS)).text spoof_submission(get_submit_url(domain), submission, headers={"HTTP_X_SUBMIT_TIME": submit_time})
def prepare_commtrack_config(domain): def _make_loc_type(name, administrative=False, parent_type=None): return LocationType.objects.get_or_create( domain=domain, name=name, administrative=administrative, parent_type=parent_type, )[0] for location_type in LocationType.objects.by_domain(domain): location_type.delete() country = _make_loc_type(name="country", administrative=True) _make_loc_type(name="Central Medical Store", parent_type=country) region = _make_loc_type(name="region", administrative=True, parent_type=country) _make_loc_type(name="Teaching Hospital", parent_type=region) _make_loc_type(name="Regional Medical Store", parent_type=region) _make_loc_type(name="Regional Hospital", parent_type=region) district = _make_loc_type(name="district", administrative=True, parent_type=region) _make_loc_type(name="Clinic", parent_type=district) _make_loc_type(name="District Hospital", parent_type=district) _make_loc_type(name="Health Centre", parent_type=district) _make_loc_type(name="CHPS Facility", parent_type=district) _make_loc_type(name="Hospital", parent_type=district) _make_loc_type(name="Psychiatric Hospital", parent_type=district) _make_loc_type(name="Polyclinic", parent_type=district) _make_loc_type(name="facility", parent_type=district) config = CommtrackConfig.for_domain(domain) config.consumption_config.exclude_invalid_periods = True config.save()
def commtrack_settings_sync(project, locations_types): if MigrationCheckpoint.objects.filter(domain=project).count() != 0: return config = CommtrackConfig.for_domain(project) domain = Domain.get_by_name(project) domain.location_types = [] for i, value in enumerate(locations_types): if not any(lt.name == value for lt in domain.location_types): allowed_parents = [locations_types[i - 1]] if i > 0 else [""] domain.location_types.append( LocationType( name=value, allowed_parents=allowed_parents, administrative=(value.lower() != 'facility') ) ) actions = [action.keyword for action in config.actions] if 'delivered' not in actions: config.actions.append( CommtrackActionConfig( action='receipts', keyword='delivered', caption='Delivered') ) config.save()
def setUpClass(cls): super(TestStockOut, cls).setUpClass() cls.facility2 = make_loc(code="loc2", name="Test Facility 2", type="FACILITY", domain=TEST_DOMAIN, parent=cls.district) cls.user2 = bootstrap_user( cls.facility2, username='******', domain=TEST_DOMAIN, home_loc='loc2', phone_number='5551235', first_name='test', last_name='Test' ) SLABConfig.objects.create( is_pilot=True, sql_location=cls.facility.sql_location ) slab_config = SLABConfig.objects.create( is_pilot=True, sql_location=cls.facility2.sql_location ) slab_config.closest_supply_points.add(cls.facility.sql_location) slab_config.save() config = CommtrackConfig.for_domain(TEST_DOMAIN) config.use_auto_consumption = False config.individual_consumption_defaults = True config.consumption_config = ConsumptionConfig( use_supply_point_type_default_consumption=True, exclude_invalid_periods=True ) config.save() set_default_consumption_for_supply_point(TEST_DOMAIN, cls.id.get_id, cls.facility_sp_id, 100) set_default_consumption_for_supply_point(TEST_DOMAIN, cls.dp.get_id, cls.facility_sp_id, 100) set_default_consumption_for_supply_point(TEST_DOMAIN, cls.ip.get_id, cls.facility_sp_id, 100)
def process_stock(xform, case_db=None): """ process the commtrack xml constructs in an incoming submission """ case_db = case_db or CaseDbCache() assert isinstance(case_db, CaseDbCache) if is_device_report(xform): return [] domain = xform.domain config = CommtrackConfig.for_domain(domain) # these are the raw stock report objects from the xml stock_reports = list(unpack_commtrack(xform, config)) # flattened transaction list spanning all stock reports in the form transactions = [t for r in stock_reports for t in r.transactions] # omitted: normalize_transactions (used for bulk requisitions?) if not transactions: return [] # transactions grouped by case/product id grouped_tx = map_reduce(lambda tx: [((tx.case_id, tx.product_id),)], lambda v: sorted(v, key=lambda tx: tx.timestamp), data=transactions, include_docs=True) case_ids = list(set(k[0] for k in grouped_tx)) # list of cases that had stock reports in the form # there is no need to wrap them by case type relevant_cases = [case_db.get(case_id) for case_id in case_ids] user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] # touch every case for proper ota restore logic syncing to be preserved for case in relevant_cases: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) # hack: clear the sync log id so this modification always counts # since consumption data could change server-side case_action.sync_log_id = '' case.actions.append(case_action) case_db.mark_changed(case) # also purge the sync token cache for the same reason if relevant_cases and xform.get_sync_token(): xform.get_sync_token().invalidate_cached_payloads() # create the django models for report in stock_reports: report.create_models(domain) # TODO make this a signal from corehq.apps.commtrack.signals import send_notifications, raise_events send_notifications(xform, relevant_cases) raise_events(xform, relevant_cases) return relevant_cases
def _create_commtrack_config_if_needed(domain): if CommtrackConfig.for_domain(domain): return CommtrackConfig( domain=domain, actions=[ CommtrackActionConfig( action='receipts', keyword='r', caption='Received', ), CommtrackActionConfig( action='consumption', keyword='c', caption='Consumed', ), CommtrackActionConfig( action='consumption', subaction='loss', keyword='l', caption='Losses', ), CommtrackActionConfig( action='stockonhand', keyword='soh', caption='Stock on hand', ), CommtrackActionConfig( action='stockout', keyword='so', caption='Stock-out', ), ], ).save()
def setUp(self): # might as well clean house before doing anything delete_all_xforms() delete_all_cases() StockReport.objects.all().delete() StockTransaction.objects.all().delete() self.backend = test.bootstrap(TEST_BACKEND, to_console=True) self.domain = bootstrap_domain() self.ct_settings = CommtrackConfig.for_domain(self.domain.name) if self.requisitions_enabled: self.ct_settings.requisition_config = get_default_requisition_config() self.ct_settings.save() self.loc = make_loc('loc1') self.sp = make_supply_point(self.domain.name, self.loc) self.users = [bootstrap_user(self, **user_def) for user_def in self.user_definitions] if False: # bootstrap additional users for requisitions # needs to get reinserted for requisition stuff later self.approver = bootstrap_user(self, **APPROVER_USER) self.packer = bootstrap_user(self, **PACKER_USER) self.users += [self.approver, self.packer] # everyone should be in a group. self.group = Group(domain=TEST_DOMAIN, name='commtrack-folks', users=[u._id for u in self.users], case_sharing=True) self.group.save() self.sp.owner_id = self.group._id self.sp.save() self.products = sorted(Product.by_domain(self.domain.name), key=lambda p: p._id) self.assertEqual(3, len(self.products))
def bootstrap_commtrack_settings_if_necessary(domain, requisitions_enabled=False): if not (domain and domain.commtrack_enabled and not CommtrackConfig.for_domain(domain.name)): return c = CommtrackConfig( domain=domain.name, multiaction_enabled=True, multiaction_keyword="report", actions=[ CommtrackActionConfig(action="receipts", keyword="r", caption="Received"), CommtrackActionConfig(action="consumption", keyword="c", caption="Consumed"), CommtrackActionConfig(action="consumption", subaction="loss", keyword="l", caption="Losses"), CommtrackActionConfig(action="stockonhand", keyword="soh", caption="Stock on hand"), CommtrackActionConfig(action="stockout", keyword="so", caption="Stock-out"), ], location_types=[ LocationType(name="state", allowed_parents=[""], administrative=True), LocationType(name="district", allowed_parents=["state"], administrative=True), LocationType(name="block", allowed_parents=["district"], administrative=True), LocationType(name="village", allowed_parents=["block"], administrative=True), LocationType(name="outlet", allowed_parents=["village"]), ], supply_point_types=[], ) if requisitions_enabled: c.requisition_config = get_default_requisition_config() c.save() program = make_program(domain.name, "Default", "def") make_product(domain.name, "Sample Product 1", "pp", program.get_id) make_product(domain.name, "Sample Product 2", "pq", program.get_id) make_product(domain.name, "Sample Product 3", "pr", program.get_id) return c
def prepare_commtrack_config(self): for location_type in LocationType.objects.by_domain(self.domain): location_type.delete() country = self._make_loc_type(name="country", administrative=True) self._make_loc_type(name="Central Medical Store", parent_type=country) region = self._make_loc_type(name="region", administrative=True, parent_type=country) self._make_loc_type(name="Teaching Hospital", parent_type=region) self._make_loc_type(name="Regional Medical Store", parent_type=region) self._make_loc_type(name="Regional Hospital", parent_type=region) district = self._make_loc_type(name="district", administrative=True, parent_type=region) self._make_loc_type(name="Clinic", parent_type=district) self._make_loc_type(name="District Hospital", parent_type=district) self._make_loc_type(name="Health Centre", parent_type=district) self._make_loc_type(name="CHPS Facility", parent_type=district) self._make_loc_type(name="Hospital", parent_type=district) self._make_loc_type(name="Psychiatric Hospital", parent_type=district) self._make_loc_type(name="Polyclinic", parent_type=district) self._make_loc_type(name="facility", parent_type=district) config = CommtrackConfig.for_domain(self.domain) config.consumption_config.exclude_invalid_periods = True config.save()
def commtrack_settings(self): # this import causes some dependency issues so lives in here from corehq.apps.commtrack.models import CommtrackConfig if self.commtrack_enabled: return CommtrackConfig.for_domain(self.name) else: return None
def get_data(self): if self.active_product: sql_product = SQLProduct.objects.get(product_id=self.active_product.get_id) filtered_locations = [ location for location in self.locations if sql_product in location.products ] else: filtered_locations = [] for location in filtered_locations: if location.supply_point_id: stock_states = StockState.objects.filter( case_id=location.supply_point_id, section_id=STOCK_SECTION_TYPE, product_id=self.active_product.get_id ).order_by('-last_modified_date') else: stock_states = None stock_levels = CommtrackConfig.for_domain(self.domain).stock_levels_config category = "no-data" if not stock_states: quantity = "No data" months_until_stockout = None else: monthly_consumption = stock_states[0].get_monthly_consumption() quantity = stock_states[0].stock_on_hand if not monthly_consumption: months_until_stockout = None else: months_until_stockout = (float(stock_states[0].stock_on_hand) / float(monthly_consumption)) if quantity == 0: category = 'stockout' months_until_stockout = 0 elif months_until_stockout is None: category = "no-data" elif months_until_stockout < location.location_type.understock_threshold: category = 'understock' elif stock_levels.understock_threshold < months_until_stockout < \ location.location_type.overstock_threshold: category = 'adequate' elif months_until_stockout > location.location_type.overstock_threshold: category = 'overstock' icon, color = self._get_icon_and_color(category) geo_point = None if location.latitude is not None and location.latitude is not None: geo_point = '%s %s' % (location.latitude, location.longitude) yield { 'name': location.name, 'type': location.location_type.name, 'geo': geo_point, 'quantity': quantity, 'category': category, 'icon': icon, 'color': color, 'months_until_stockout': "%.2f" % months_until_stockout if months_until_stockout is not None else "No data", 'last_reported': stock_states[0].last_modified_date if stock_states else None }
def all_sms_codes(domain): config = CommtrackConfig.for_domain(domain) actions = dict((action.keyword, action) for action in config.actions) products = dict((p.code, p) for p in Product.by_domain(domain)) sms_codes = zip(('action', 'product'), (actions, products)) return dict(itertools.chain(*([(k.lower(), (type, v)) for k, v in codes.iteritems()] for type, codes in sms_codes)))
def stock_level_config_for_domain(domain, commtrack_enabled): from corehq.apps.commtrack.models import CommtrackConfig ct_config = CommtrackConfig.for_domain(domain) if ((ct_config is None) or (not commtrack_enabled) or LOCATION_TYPE_STOCK_RATES.enabled(domain)): return None else: return ct_config.stock_levels_config
def all_sms_codes(domain): config = CommtrackConfig.for_domain(domain) actions = dict((action.keyword, action) for action in config.actions) products = dict((p.code, p) for p in Product.by_domain(domain)) sms_codes = zip(('action', 'product'), (actions, products)) return dict(itertools.chain(*([(k.lower(), (type, v)) for k, v in six.iteritems(codes)] for type, codes in sms_codes)))
def _prepare_ledger_for_es(ledger): from corehq.apps.commtrack.models import CommtrackConfig commtrack_config = CommtrackConfig.for_domain(ledger['domain']) if commtrack_config and commtrack_config.use_auto_consumption: daily_consumption = _get_daily_consumption_for_ledger(ledger) ledger['daily_consumption'] = daily_consumption return ledger
def stock_level_config_for_domain(domain, commtrack_enabled): if not commtrack_enabled: return None from corehq.apps.commtrack.models import CommtrackConfig ct_config = CommtrackConfig.for_domain(domain) if ct_config is None or not hasattr(ct_config, 'stocklevelsconfig'): return None else: return ct_config.stocklevelsconfig
def all_sms_codes(domain): config = CommtrackConfig.for_domain(domain) actions = dict((action.keyword, action) for action in config.actions) products = dict((p.code, p) for p in Product.by_domain(domain)) commands = {config.multiaction_keyword: {"type": "stock_report_generic", "caption": "Stock Report"}} sms_codes = zip(("action", "product", "command"), (actions, products, commands)) return dict(itertools.chain(*([(k.lower(), (type, v)) for k, v in codes.iteritems()] for type, codes in sms_codes)))
def process_stock(xform): """ process the commtrack xml constructs in an incoming submission """ if is_device_report(xform): return domain = xform.domain config = CommtrackConfig.for_domain(domain) # these are the raw stock report objects from the xml stock_reports = list(unpack_commtrack(xform, config)) # flattened transaction list spanning all stock reports in the form transactions = [t for r in stock_reports for t in r.transactions] # omitted: normalize_transactions (used for bulk requisitions?) if not transactions: return # transactions grouped by case/product id grouped_tx = map_reduce(lambda tx: [((tx.case_id, tx.product_id),)], lambda v: sorted(v, key=lambda tx: tx.timestamp), data=transactions, include_docs=True) # list of cases that had stock reports in the form, properly wrapped by case type try: relevant_cases = [wrap_commtrack_case(result['doc']) for result in CommCareCase.get_db().view('_all_docs', keys=list(set(k[0] for k in grouped_tx)), include_docs=True)] except KeyError: raise Exception("Cannot find case matching supplied entity id") user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] # touch every case for proper ota restore logic syncing to be preserved for case in relevant_cases: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) # hack: clear the sync log id so this modification always counts # since consumption data could change server-side case_action.sync_log_id = '' case.actions.append(case_action) case.save() # create the django models for report in stock_reports: report.create_models() # TODO make this a signal from corehq.apps.commtrack.signals import send_notifications, raise_events send_notifications(xform, relevant_cases) raise_events(xform, relevant_cases)
def bootstrap_commtrack_settings_if_necessary(domain, requisitions_enabled=False): if not(domain and domain.commtrack_enabled and not CommtrackConfig.for_domain(domain.name)): return c = CommtrackConfig( domain=domain.name, multiaction_enabled=True, multiaction_keyword='report', actions=[ CommtrackActionConfig( action='receipts', keyword='r', caption='Received', ), CommtrackActionConfig( action='consumption', keyword='c', caption='Consumed', ), CommtrackActionConfig( action='consumption', subaction='loss', keyword='l', caption='Losses', ), CommtrackActionConfig( action='stockonhand', keyword='soh', caption='Stock on hand', ), CommtrackActionConfig( action='stockout', keyword='so', caption='Stock-out', ), ], location_types=[ LocationType(name='state', allowed_parents=[''], administrative=True), LocationType(name='district', allowed_parents=['state'], administrative=True), LocationType(name='block', allowed_parents=['district'], administrative=True), LocationType(name='village', allowed_parents=['block'], administrative=True), LocationType(name='outlet', allowed_parents=['block', 'village']), ], supply_point_types=[], ) if requisitions_enabled: c.requisition_config = get_default_requisition_config() c.save() program = make_program(domain.name, 'Default', 'def') make_product(domain.name, 'Sample Product 1', 'pp', program.get_id) make_product(domain.name, 'Sample Product 2', 'pq', program.get_id) make_product(domain.name, 'Sample Product 3', 'pr', program.get_id) return c
def get_months_until_stockout_icon(value): stock_levels = CommtrackConfig.for_domain(self.config['domain']).stock_levels_config if float(value) == 0.0: return '%s <span class="icon-remove" style="color:red"/>' % value elif float(value) < stock_levels.understock_threshold: return '%s <span class="icon-warning-sign" style="color:orange"/>' % value elif stock_levels.understock_threshold < float(value) < stock_levels.overstock_threshold: return '%s <span class="icon-ok" style="color:green"/>' % value elif float(value) >= stock_levels.overstock_threshold: return '%s <span class="icon-arrow-up" style="color:purple"/>' % value
def setUp(self): super(OpenLMISTestBase, self).setUp() self.api = MockOpenLMISEndpoint("uri://mock/lmis/endpoint", username="******", password="******") openlmis_config = OpenLMISConfig() openlmis_config.enabled = True commtrack_config = CommtrackConfig.for_domain(self.domain.name) commtrack_config.openlmis_config = openlmis_config commtrack_config.save()
def _populate_stock_levels(self): from corehq.apps.commtrack.models import CommtrackConfig ct_config = CommtrackConfig.for_domain(self.domain) if ((ct_config is None) or (not self.commtrack_enabled) or LOCATION_TYPE_STOCK_RATES.enabled(self.domain)): return config = ct_config.stock_levels_config self.emergency_level = config.emergency_level self.understock_threshold = config.understock_threshold self.overstock_threshold = config.overstock_threshold
def should_exclude_invalid_periods(domain): """ Whether the domain's consumption calculation should exclude invalid periods """ from corehq.apps.commtrack.models import CommtrackConfig if domain: config = CommtrackConfig.for_domain(domain) if config: return config.consumption_config.exclude_invalid_periods return False
def all_sms_codes(domain): config = CommtrackConfig.for_domain(domain) actions = dict((action.keyword, action) for action in config.actions) products = dict((p.code, p) for p in Product.by_domain(domain)) commands = { config.multiaction_keyword: {'type': 'stock_report_generic', 'caption': 'Stock Report'}, } sms_codes = zip(('action', 'product', 'command'), (actions, products, commands)) return dict(itertools.chain(*([(k.lower(), (type, v)) for k, v in codes.iteritems()] for type, codes in sms_codes)))
def stock_level_config_for_domain(domain, commtrack_enabled): from corehq.apps.commtrack.models import CommtrackConfig ct_config = CommtrackConfig.for_domain(domain) if ( (ct_config is None) or (not commtrack_enabled) or LOCATION_TYPE_STOCK_RATES.enabled(domain) ): return None else: return ct_config.stock_levels_config
def should_exclude_invalid_periods(domain): """ Whether the domain's consumption calculation should exclude invalid periods i.e. periods where the stock went up without a receipt being reported """ from corehq.apps.commtrack.models import CommtrackConfig if domain: config = CommtrackConfig.for_domain(domain) if config: return config.consumption_config.exclude_invalid_periods return False
def _prepare_ledger_for_es(ledger): from corehq.apps.commtrack.models import CommtrackConfig commtrack_config = CommtrackConfig.for_domain(ledger['domain']) if commtrack_config and commtrack_config.use_auto_consumption: daily_consumption = _get_daily_consumption_for_ledger(ledger) ledger['daily_consumption'] = daily_consumption if not ledger.get('location_id') and ledger.get('case_id'): ledger['location_id'] = _location_id_for_case(ledger['case_id']) return ledger
def setUpClass(cls): domain = prepare_domain(TEST_DOMAIN) p = Product(domain=domain.name, name='Jadelle', code='jd', unit='each') p.save() p2 = Product(domain=domain.name, name='Male Condom', code='mc', unit='each') p2.save() p3 = Product(domain=domain.name, name='Lofem', code='lf', unit='each') p3.save() p4 = Product(domain=domain.name, name='Ng', code='ng', unit='each') p4.save() p5 = Product(domain=domain.name, name='Micro-G', code='mg', unit='each') p5.save() loc = make_loc(code="garms", name="Test RMS", type="Regional Medical Store", domain=domain.name) test.bootstrap(TEST_BACKEND, to_console=True) cls.user1 = bootstrap_user(username='******', first_name='test1', last_name='test1', domain=domain.name, home_loc=loc) cls.user2 = bootstrap_user(username='******', domain=domain.name, home_loc=loc, first_name='test2', last_name='test2', phone_number='222222', user_data={'role': 'In Charge'}) try: XFormInstance.get(docid='test-xform') except ResourceNotFound: xform = XFormInstance(_id='test-xform') xform.save() sql_location = loc.sql_location sql_location.products = SQLProduct.objects.filter(product_id=p5.get_id) sql_location.save() config = CommtrackConfig.for_domain(domain.name) config.actions.append( CommtrackActionConfig(action='receipts', keyword='rec', caption='receipts')) config.consumption_config = ConsumptionConfig(min_transactions=0, min_window=0, optimal_window=60) config.save()
def process(domain, instance): """process an incoming commtrack stock report instance""" config = CommtrackConfig.for_domain(domain) root = etree.fromstring(instance) user_id, transactions = unpack_transactions(root, config) transactions = list(normalize_transactions(transactions)) def get_transactions(all_tx, type_filter): """get all the transactions of the relevant type (filtered by type_filter), grouped by product (returns a dict of 'product subcase id' => list of transactions), with each set of transactions sorted in the correct order for processing """ return map_reduce( lambda tx: [(tx.case_id,)], lambda v: sorted(v, key=lambda tx: tx.priority_order), # important! data=filter(type_filter, all_tx), include_docs=True, ) # split transactions by type and product stock_transactions = get_transactions(transactions, lambda tx: tx.category == "stock") requisition_transactions = get_transactions(transactions, lambda tx: tx.category == "requisition") case_ids = list(set(itertools.chain(*[tx.get_case_ids() for tx in transactions]))) cases = dict((c._id, c) for c in CommCareCase.view("_all_docs", keys=case_ids, include_docs=True)) # TODO: code to auto generate / update requisitions from transactions if # project is configured for that. # TODO: when we start receiving commcare-submitted reports, we should be using a server time rather # than relying on timeStart (however timeStart is set to server time for reports received via sms) submit_time = root.find(".//%s" % _("timeStart", META_XMLNS)).text post_processed_transactions = list(transactions) for product_id, product_case in cases.iteritems(): stock_txs = stock_transactions.get(product_id, []) if stock_txs: case_block, reconciliations = process_product_transactions(user_id, submit_time, product_case, stock_txs) root.append(case_block) post_processed_transactions.extend(reconciliations) req_txs = requisition_transactions.get(product_id, []) if req_txs and config.requisitions_enabled: req = RequisitionState.from_transactions(user_id, product_case, req_txs) case_block = etree.fromstring(req.to_xml()) root.append(case_block) replace_transactions(root, post_processed_transactions) submission = etree.tostring(root) logger.debug("submitting: %s" % submission) spoof_submission( get_submit_url(domain), submission, headers={"HTTP_X_SUBMIT_TIME": submit_time}, hqsubmission=False )
def setUp(self): super(OpenLMISTestBase, self).setUp() self.api = MockOpenLMISEndpoint("uri://mock/lmis/endpoint", username='******', password='******') openlmis_config = OpenLMISConfig() openlmis_config.enabled = True commtrack_config = CommtrackConfig.for_domain(self.domain.name) commtrack_config.openlmis_config = openlmis_config commtrack_config.save()
def _populate_stock_levels(self): from corehq.apps.commtrack.models import CommtrackConfig ct_config = CommtrackConfig.for_domain(self.domain) if ( (ct_config is None) or (not Domain.get_by_name(self.domain).commtrack_enabled) or LOCATION_TYPE_STOCK_RATES.enabled(self.domain) ): return config = ct_config.stock_levels_config self.emergency_level = config.emergency_level self.understock_threshold = config.understock_threshold self.overstock_threshold = config.overstock_threshold
def process_change(self, change): ledger = change.get_document() from corehq.apps.commtrack.models import CommtrackConfig commtrack_config = CommtrackConfig.for_domain(ledger['domain']) if commtrack_config and commtrack_config.use_auto_consumption: daily_consumption = _get_daily_consumption_for_ledger(ledger) ledger['daily_consumption'] = daily_consumption if not ledger.get('location_id') and ledger.get('case_id'): ledger['location_id'] = _location_id_for_case(ledger['case_id']) _update_ledger_section_entry_combinations(ledger)
def setUp(self): # might as well clean house before doing anything delete_all_xforms() delete_all_cases() StockReport.objects.all().delete() StockTransaction.objects.all().delete() self.backend = test.bootstrap(TEST_BACKEND, to_console=True) self.domain = bootstrap_domain() self.ct_settings = CommtrackConfig.for_domain(self.domain.name) self.ct_settings.consumption_config = ConsumptionConfig( min_transactions=0, min_window=0, optimal_window=60, min_periods=0, ) if self.requisitions_enabled: self.ct_settings.requisition_config = get_default_requisition_config( ) self.ct_settings.save() self.domain = Domain.get(self.domain._id) self.loc = make_loc('loc1') self.sp = make_supply_point(self.domain.name, self.loc) self.users = [ bootstrap_user(self, **user_def) for user_def in self.user_definitions ] if False: # bootstrap additional users for requisitions # needs to get reinserted for requisition stuff later self.approver = bootstrap_user(self, **APPROVER_USER) self.packer = bootstrap_user(self, **PACKER_USER) self.users += [self.approver, self.packer] # everyone should be in a group. self.group = Group(domain=TEST_DOMAIN, name='commtrack-folks', users=[u._id for u in self.users], case_sharing=True) self.group.save() self.sp.owner_id = self.group._id self.sp.save() self.products = sorted(Product.by_domain(self.domain.name), key=lambda p: p._id) self.assertEqual(3, len(self.products))
def fixmetestApproveRequisition(self): from corehq.apps.commtrack.stockreport import Requisition requisition_cases = [] config = CommtrackConfig.for_domain(self.domain) for spp in self.spps.values(): transaction = Requisition( config=config, product_id=spp.product, case_id=spp._id, action_name=config.get_action_by_type(RequisitionActions.REQUEST).action_name, value=20, ) req = create_requisition(self.user._id, spp, transaction) requisition_cases.append(req) self.assertTrue(requisition_approved.send(sender=None, requisitions=requisition_cases))
def page_context(self): try: runner = ReportRun.objects.filter(domain=self.domain, complete=False).latest('start_run') except ReportRun.DoesNotExist: runner = None return { 'runner': runner, 'settings': self.settings_context, 'source': self.source, 'sync_url': self.sync_urlname, 'sync_stock_url': self.sync_stock_url, 'clear_stock_url': self.clear_stock_url, 'is_developer': toggles.IS_DEVELOPER.enabled(self.request.couch_user.username), 'is_commtrack_enabled': CommtrackConfig.for_domain(self.domain) }
def all_sms_codes(domain): config = CommtrackConfig.for_domain(domain) actions = {action.keyword: action for action in config.actions} products = { p.code: p for p in SQLProduct.active_objects.filter(domain=domain) } ret = {} for action_key, action in actions.items(): ret[action_key] = ('action', action) for product_key, product in products.items(): ret[product_key] = ('product', product) return ret
def page_context(self): try: runner = ReportRun.objects.filter(domain=self.domain, complete=False).latest('start_run') except ReportRun.DoesNotExist: runner = None return { 'runner': runner, 'settings': self.settings_context, 'source': self.source, 'sync_url': self.sync_urlname, 'sync_stock_url': self.sync_stock_url, 'clear_stock_url': self.clear_stock_url, 'is_contractor': toggles.IS_CONTRACTOR.enabled(self.request.couch_user.username), 'is_commtrack_enabled': CommtrackConfig.for_domain(self.domain) }
def setUpClass(cls): super(SOHSLABTest, cls).setUpClass() SLABConfig.objects.create(is_pilot=True, sql_location=cls.facility.sql_location) config = CommtrackConfig.for_domain(TEST_DOMAIN) config.use_auto_consumption = False config.individual_consumption_defaults = True config.consumption_config = ConsumptionConfig( use_supply_point_type_default_consumption=True, exclude_invalid_periods=True) config.save() set_default_consumption_for_supply_point(TEST_DOMAIN, cls.id.get_id, cls.facility_sp_id, 100) set_default_consumption_for_supply_point(TEST_DOMAIN, cls.dp.get_id, cls.facility_sp_id, 100) set_default_consumption_for_supply_point(TEST_DOMAIN, cls.ip.get_id, cls.facility_sp_id, 100)
def send_confirmation(v, data): C = CommtrackConfig.for_domain(v.domain) static_loc = data['location'] location_name = static_loc.name metadata = MessageMetadata(location_id=static_loc.get_id) tx_by_action = map_reduce(lambda tx: [(tx.action_config(C).name,)], data=data['transactions'], include_docs=True) def summarize_action(action, txs): return '%s %s' % (txs[0].action_config(C).keyword.upper(), ' '.join(sorted(tx.fragment() for tx in txs))) msg = 'received stock report for %s(%s) %s' % ( static_loc.site_code, truncate(location_name, 20), ' '.join(sorted(summarize_action(a, txs) for a, txs in tx_by_action.iteritems())) ) send_sms_to_verified_number(v, msg, metadata=metadata)
class BaseConfigView(BaseCommTrackManageView): @cls_require_superuser_or_developer def dispatch(self, request, *args, **kwargs): return super(BaseConfigView, self).dispatch(request, *args, **kwargs) @property def page_context(self): try: checkpoint = MigrationCheckpoint.objects.get(domain=self.domain) except MigrationCheckpoint.DoesNotExist: checkpoint = None try: runner = ReportRun.objects.get(domain=self.domain, complete=False) except ReportRun.DoesNotExist: runner = None try: stock_data_checkpoint = StockDataCheckpoint.objects.get( domain=self.domain) except StockDataCheckpoint.DoesNotExist, StockDataCheckpoint.MultipleObjectsReturned: stock_data_checkpoint = None return { 'stock_data_checkpoint': stock_data_checkpoint, 'runner': runner, 'checkpoint': checkpoint, 'settings': self.settings_context, 'source': self.source, 'sync_url': self.sync_urlname, 'sync_stock_url': self.sync_stock_url, 'clear_stock_url': self.clear_stock_url, 'is_developer': toggles.IS_DEVELOPER.enabled(self.request.couch_user.username), 'is_commtrack_enabled': CommtrackConfig.for_domain(self.domain) }
def setUpClass(cls): super(StockStateTest, cls).setUpClass() cls.domain_obj = util.bootstrap_domain(cls.domain) util.bootstrap_location_types(cls.domain) util.bootstrap_products(cls.domain) cls.ct_settings = CommtrackConfig.for_domain(cls.domain) cls.ct_settings.use_auto_consumption = True cls.ct_settings.consumption_config = ConsumptionConfig( min_transactions=0, min_window=0, optimal_window=60, min_periods=0, ) cls.ct_settings.save() cls.loc = util.make_loc('loc1', domain=cls.domain) cls.sp = cls.loc.linked_supply_point() cls.products = sorted(Product.by_domain(cls.domain), key=lambda p: p._id)
def fixmetestApproveRequisition(self): from corehq.apps.commtrack.stockreport import Requisition requisition_cases = [] config = CommtrackConfig.for_domain(self.domain) for spp in self.spps.values(): transaction = Requisition( config=config, product_id=spp.product, case_id=spp._id, action_name=config.get_action_by_type( RequisitionActions.REQUEST).action_name, value=20, ) req = create_requisition(self.user._id, spp, transaction) requisition_cases.append(req) self.assertTrue( requisition_approved.send(sender=None, requisitions=requisition_cases))
def send_confirmation(v, data): C = CommtrackConfig.for_domain(v.domain) static_loc = data['location'] location_name = static_loc.name metadata = MessageMetadata(location_id=static_loc.get_id) tx_by_action = map_reduce(lambda tx: [(tx.action_config(C).name,)], data=data['transactions'], include_docs=True) def summarize_action(action, txs): return '%s %s' % (txs[0].action_config(C).keyword.upper(), ' '.join(sorted(tx.fragment() for tx in txs))) msg = 'received stock report for %s(%s) %s' % ( static_loc.site_code, truncate(location_name, 20), ' '.join(sorted(summarize_action(a, txs) for a, txs in six.iteritems(tx_by_action))) ) send_sms_to_verified_number(v, msg, metadata=metadata)
def clone_domain_and_settings(self): from corehq.apps.domain.models import Domain new_domain_obj = Domain.get_by_name(self.new_domain) if new_domain_obj: if raw_input( '{} domain already exists. Do you still want to continue? [y/n]' .format(self.new_domain)).lower() == 'y': return else: raise CommandError('abort') domain = Domain.get_by_name(self.existing_domain) domain.name = self.new_domain self.save_couch_copy(domain) from corehq.apps.commtrack.models import CommtrackConfig commtrack_config = CommtrackConfig.for_domain(self.existing_domain) if commtrack_config: self.save_couch_copy(commtrack_config, self.new_domain)
def commtrack_settings_sync(project): locations_types = ["MOHSW", "REGION", "DISTRICT", "FACILITY"] config = CommtrackConfig.for_domain(project) config.location_types = [] for i, value in enumerate(locations_types): if not any(lt.name == value for lt in config.location_types): allowed_parents = [locations_types[i - 1]] if i > 0 else [""] config.location_types.append( LocationType(name=value, allowed_parents=allowed_parents, administrative=(value != 'FACILITY'))) actions = [action.keyword for action in config.actions] if 'delivered' not in actions: config.actions.append( CommtrackActionConfig( action='receipts', keyword='delivered', caption='Delivered') ) config.save()
def setUp(self): super(XMLTest, self).setUp() self.domain = util.bootstrap_domain(util.TEST_DOMAIN) util.bootstrap_location_types(self.domain.name) util.bootstrap_products(self.domain.name) self.products = sorted(Product.by_domain(self.domain.name), key=lambda p: p._id) self.ct_settings = CommtrackConfig.for_domain(self.domain.name) self.ct_settings.consumption_config = ConsumptionConfig( min_transactions=0, min_window=0, optimal_window=60, min_periods=0, ) self.ct_settings.save() self.domain = Domain.get(self.domain._id) self.loc = make_loc('loc1') self.sp = self.loc.linked_supply_point() self.users = [util.bootstrap_user(self, **user_def) for user_def in self.user_definitions] self.user = self.users[0]
def _create_commtrack_config_if_needed(domain): if CommtrackConfig.for_domain(domain): return config = CommtrackConfig(domain=domain) config.save() # must be saved before submodels can be saved AlertConfig(commtrack_config=config).save() ConsumptionConfig(commtrack_config=config).save() StockLevelsConfig(commtrack_config=config).save() StockRestoreConfig(commtrack_config=config).save() config.set_actions([ ActionConfig( action='receipts', keyword='r', caption='Received', ), ActionConfig( action='consumption', keyword='c', caption='Consumed', ), ActionConfig( action='consumption', subaction='loss', keyword='l', caption='Losses', ), ActionConfig( action='stockonhand', keyword='soh', caption='Stock on hand', ), ActionConfig( action='stockout', keyword='so', caption='Stock-out', ), ]) config.save() # save actions, and sync couch
def send_confirmation(v, data): C = CommtrackConfig.for_domain(v.domain) static_loc = Location.get(data['location'].location_[-1]) location_name = static_loc.name action_to_code = dict((v, k) for k, v in C.all_keywords().iteritems()) tx_by_action = map_reduce(lambda tx: [(tx.action_name, )], data=data['transactions'], include_docs=True) def summarize_action(action, txs): return '%s %s' % (action_to_code[action].upper(), ' '.join( sorted(tx.fragment() for tx in txs))) msg = 'received stock report for %s(%s) %s' % ( static_loc.site_code, truncate(location_name, 20), ' '.join( sorted( summarize_action(a, txs) for a, txs in tx_by_action.iteritems()))) send_sms_to_verified_number(v, msg)
def testOTASettings(self): self.domain = bootstrap_domain() bootstrap_products(self.domain.name) ct_settings = CommtrackConfig.for_domain(self.domain.name) ct_settings.consumption_config = ConsumptionConfig( min_transactions=10, min_window=20, optimal_window=60, ) ct_settings.ota_restore_config = StockRestoreConfig( section_to_consumption_types={'stock': 'consumption'}, ) set_default_monthly_consumption_for_domain(self.domain.name, 5 * DAYS_IN_MONTH) restore_settings = ct_settings.get_ota_restore_settings() self.assertEqual(1, len(restore_settings.section_to_consumption_types)) self.assertEqual( 'consumption', restore_settings.section_to_consumption_types['stock']) self.assertEqual(10, restore_settings.consumption_config.min_periods) self.assertEqual(20, restore_settings.consumption_config.min_window) self.assertEqual(60, restore_settings.consumption_config.max_window) self.assertEqual( 150, restore_settings.consumption_config. default_monthly_consumption_function('foo', 'bar')) self.assertFalse( restore_settings.force_consumption_case_filter( CommCareCase(type='force-type'))) self.assertEqual(0, len(restore_settings.default_product_list)) ct_settings.ota_restore_config.force_consumption_case_types = [ 'force-type' ] ct_settings.ota_restore_config.use_dynamic_product_list = True restore_settings = ct_settings.get_ota_restore_settings() self.assertTrue( restore_settings.force_consumption_case_filter( CommCareCase(type='force-type'))) self.assertEqual(3, len(restore_settings.default_product_list))
def _create_commtrack_config_if_needed(domain): if CommtrackConfig.for_domain(domain): return CommtrackConfig( domain=domain, multiaction_enabled=True, multiaction_keyword='report', actions=[ CommtrackActionConfig( action='receipts', keyword='r', caption='Received', ), CommtrackActionConfig( action='consumption', keyword='c', caption='Consumed', ), CommtrackActionConfig( action='consumption', subaction='loss', keyword='l', caption='Losses', ), CommtrackActionConfig( action='stockonhand', keyword='soh', caption='Stock on hand', ), CommtrackActionConfig( action='stockout', keyword='so', caption='Stock-out', ), ], ).save()
def setUpClass(cls): super(TestStockOut, cls).setUpClass() cls.facility2 = make_loc(code="loc2", name="Test Facility 2", type="FACILITY", domain=TEST_DOMAIN, parent=cls.district) cls.user2 = bootstrap_user(cls.facility2, username='******', domain=TEST_DOMAIN, home_loc='loc2', phone_number='5551236', first_name='test', last_name='Test') SLABConfig.objects.create(is_pilot=True, sql_location=cls.facility.sql_location) slab_config = SLABConfig.objects.create( is_pilot=True, sql_location=cls.facility2.sql_location) slab_config.closest_supply_points.add(cls.facility.sql_location) slab_config.save() config = CommtrackConfig.for_domain(TEST_DOMAIN) config.use_auto_consumption = False config.individual_consumption_defaults = True config.consumption_config = ConsumptionConfig( use_supply_point_type_default_consumption=True, exclude_invalid_periods=True) config.save() set_default_consumption_for_supply_point(TEST_DOMAIN, cls.id.get_id, cls.facility_sp_id, 100) set_default_consumption_for_supply_point(TEST_DOMAIN, cls.dp.get_id, cls.facility_sp_id, 100) set_default_consumption_for_supply_point(TEST_DOMAIN, cls.ip.get_id, cls.facility_sp_id, 100)