def get_prod_data(self): startkey = [ self.domain, self.active_location._id if self.active_location else None ] product_cases = CommCareCase.view('commtrack/product_cases', startkey=startkey, endkey=startkey + [{}], include_docs=True) cases_by_product = map_reduce(lambda c: [(c.product, )], data=product_cases, include_docs=True) products = Product.view('_all_docs', keys=cases_by_product.keys(), include_docs=True) def _sum(vals): return sum(vals) if vals else None def aggregate_product(cases): data = [(current_stock(c), monthly_consumption(c)) for c in cases if is_timely(c, 1000)] total_stock = _sum([d[0] for d in data if d[0] is not None]) total_consumption = _sum([d[1] for d in data if d[1] is not None]) # exclude stock values w/o corresponding consumption figure from total months left calculation consumable_stock = _sum( [d[0] for d in data if d[0] is not None and d[1] is not None]) try: months_left = consumable_stock / total_consumption except (TypeError, ZeroDivisionError): months_left = None return { 'total_stock': total_stock, 'total_consumption': total_consumption, 'months_left': months_left, } status_by_product = dict((p, aggregate_product(cases)) for p, cases in cases_by_product.iteritems()) for p in sorted(products, key=lambda p: p.name): stats = status_by_product[p._id] yield [ p.name, stats['total_stock'], stats['total_consumption'], stats['months_left'], stock_category(stats['total_stock'], stats['total_consumption'], stats['months_left']), ]
def get_prod_data(self): startkey = [self.domain, self.active_location._id if self.active_location else None] product_cases = SPPCase.view('commtrack/product_cases', startkey=startkey, endkey=startkey + [{}], include_docs=True) cases_by_product = map_reduce(lambda c: [(c.product,)], data=product_cases, include_docs=True) products = Product.view('_all_docs', keys=cases_by_product.keys(), include_docs=True) def status(case): return case.current_stock_category if is_timely(case, 1000) else 'nonreporting' status_by_product = dict((p, map_reduce(lambda c: [(status(c),)], len, data=cases)) for p, cases in cases_by_product.iteritems()) cols = ['stockout', 'understock', 'adequate', 'overstock', 'nodata'] #'nonreporting', 'nodata'] for p in sorted(products, key=lambda p: p.name): cases = cases_by_product.get(p._id, []) results = status_by_product.get(p._id, {}) def val(key): return results.get(key, 0) / float(len(cases)) yield [p.name, len(cases)] + [100. * val(key) for key in cols]
def aggregate_cases(self, product_cases, slugs): cases_by_product = map_reduce(lambda c: [(c.product,)], data=product_cases, include_docs=True) products = Product.view('_all_docs', keys=cases_by_product.keys(), include_docs=True) def _sum(vals): return sum(vals) if vals else None def aggregate_product(cases): data = [(c.current_stock_level, c.monthly_consumption) for c in cases if is_timely(c, 1000)] total_stock = _sum([d[0] for d in data if d[0] is not None]) total_consumption = _sum([d[1] for d in data if d[1] is not None]) # exclude stock values w/o corresponding consumption figure from total months left calculation consumable_stock = _sum([d[0] for d in data if d[0] is not None and d[1] is not None]) return { 'total_stock': total_stock, 'total_consumption': total_consumption, 'consumable_stock': consumable_stock, } status_by_product = dict((p, aggregate_product(cases)) for p, cases in cases_by_product.iteritems()) for p in sorted(products, key=lambda p: p.name): stats = status_by_product[p._id] months_left = SPPCase.months_of_stock_remaining(stats['consumable_stock'], stats['total_consumption']) category = SPPCase.stock_category(stats['total_stock'], stats['total_consumption'], stats['consumable_stock']) full_output = { self.SLUG_PRODUCT_NAME: p.name, self.SLUG_PRODUCT_ID: p._id, self.SLUG_LOCATION_ID: self.active_location._id if self.active_location else None, self.SLUG_LOCATION_LINEAGE: self.active_location.lineage if self.active_location else None, self.SLUG_CURRENT_STOCK: stats['total_stock'], self.SLUG_CONSUMPTION: stats['total_consumption'], self.SLUG_MONTHS_REMAINING: months_left, self.SLUG_CATEGORY: category, } yield dict((slug, full_output['slug']) for slug in slugs) if slugs else full_output
def get_prod_data(self): startkey = [ self.domain, self.active_location._id if self.active_location else None ] product_cases = CommCareCase.view('commtrack/product_cases', startkey=startkey, endkey=startkey + [{}], include_docs=True) cases_by_product = map_reduce(lambda c: [(c.product, )], data=product_cases, include_docs=True) products = Product.view('_all_docs', keys=cases_by_product.keys(), include_docs=True) def case_stock_category(case): return stock_category(current_stock(case), monthly_consumption(case)) def status(case): return case_stock_category(case) if is_timely( case, 1000) else 'nonreporting' status_by_product = dict( (p, map_reduce(lambda c: [(status(c), )], len, data=cases)) for p, cases in cases_by_product.iteritems()) cols = ['stockout', 'understock', 'adequate', 'overstock', 'nodata'] #'nonreporting', 'nodata'] for p in sorted(products, key=lambda p: p.name): cases = cases_by_product.get(p._id, []) results = status_by_product.get(p._id, {}) def val(key): return results.get(key, 0) / float(len(cases)) yield [p.name, len(cases)] + [100. * val(key) for key in cols]
def get_prod_data(self): startkey = [self.domain, self.active_location._id if self.active_location else None] product_cases = CommCareCase.view('commtrack/product_cases', startkey=startkey, endkey=startkey + [{}], include_docs=True) cases_by_product = map_reduce(lambda c: [(c.product,)], data=product_cases, include_docs=True) products = Product.view('_all_docs', keys=cases_by_product.keys(), include_docs=True) def _sum(vals): return sum(vals) if vals else None def aggregate_product(cases): data = [(current_stock(c), monthly_consumption(c)) for c in cases if is_timely(c, 1000)] total_stock = _sum([d[0] for d in data if d[0] is not None]) total_consumption = _sum([d[1] for d in data if d[1] is not None]) # exclude stock values w/o corresponding consumption figure from total months left calculation consumable_stock = _sum([d[0] for d in data if d[0] is not None and d[1] is not None]) try: months_left = consumable_stock / total_consumption except (TypeError, ZeroDivisionError): months_left = None return { 'total_stock': total_stock, 'total_consumption': total_consumption, 'months_left': months_left, } status_by_product = dict((p, aggregate_product(cases)) for p, cases in cases_by_product.iteritems()) for p in sorted(products, key=lambda p: p.name): stats = status_by_product[p._id] yield [ p.name, stats['total_stock'], stats['total_consumption'], stats['months_left'], stock_category(stats['total_stock'], stats['total_consumption'], stats['months_left']), ]
def display_config(self): conf = { 'name_column': 'name', 'detail_columns': ['type'], 'table_columns': ['type'], 'column_titles': { 'type': 'Supply Point Type', }, 'enum_captions': {}, 'numeric_format': {}, 'metrics': [ { 'color': { 'column': 'type', }, }, ], } titles = { 'current_stock': 'Stock on Hand', 'consumption': 'Monthly Consumption', 'months_remaining': 'Months of Stock Remaining', 'category': 'Current Stock Status', } products = sorted(Product.view('commtrack/products', startkey=[self.domain], endkey=[self.domain, {}], include_docs=True), key=lambda p: p.name) if self.program_id: products = filter(lambda c: c.program_id == self.program_id, products) for p in products: col_id = lambda c: '%s-%s' % (p._id, c) product_cols = [] for c in ('category', 'current_stock', 'months_remaining', 'consumption'): conf['column_titles'][col_id(c)] = titles[c] product_cols.append(col_id(c)) conf['detail_columns'].extend(product_cols) product_metrics = [{ 'icon': { 'column': col_id('category'), 'categories': { 'stockout': '/static/commtrack/img/stockout.png', 'understock': '/static/commtrack/img/warning.png', 'adequate': '/static/commtrack/img/goodstock.png', 'overstock': '/static/commtrack/img/overstock.png', #'nodata': '/static/commtrack/img/no_data.png', '_null': '/static/commtrack/img/no_data.png', }, } }] conf['enum_captions'][col_id('category')] = { 'stockout': 'Stocked out', 'understock': 'Under-stock', 'adequate': 'Adequate Stock', 'overstock': 'Over-stock', '_null': 'No Data', } for c in ('current_stock', 'months_remaining', 'consumption'): metric = { 'title': conf['column_titles'][col_id(c)], 'size': { 'column': col_id(c), }, } if c not in ('consumption', ): metric['color'] = { 'column': col_id('category'), 'categories': { 'stockout': 'rgba(255, 0, 0, .8)', 'understock': 'rgba(255, 120, 0, .8)', 'adequate': 'rgba(50, 200, 50, .8)', 'overstock': 'rgba(120, 0, 255, .8)', '_null': 'rgba(128, 128, 128, .8)', }, } else: metric['color'] = 'rgba(120, 120, 255, .8)' product_metrics.append(metric) conf['numeric_format'][col_id(c)] = { 'current_stock': "return x + ' %s'" % (p.unit or 'unit'), 'months_remaining': "return (Math.round(10 * x) / 10) + (x == 1 ? ' month' : ' months')", 'consumption': "return (Math.round(10 * x) / 10) + ' %s / month'" % (p.unit or 'unit'), }[c] conf['metrics'].append({ 'title': p.name, 'group': True, 'children': product_metrics, }) conf['table_columns'].append({ 'title': p.name, 'subcolumns': product_cols, }) conf['detail_template'] = render_to_string( 'reports/partials/commtrack/stockstatus_mapdetail.html', { 'products': products, 'columns': [{ 'id': c, 'title': titles[c] } for c in ('category', 'current_stock', 'consumption', 'months_remaining')], }) #print conf['detail_template'] return conf
def display_config(self): conf = { 'name_column': 'name', 'detail_columns': ['type'], 'table_columns': ['type'], 'column_titles': { 'type': 'Supply Point Type', }, 'enum_captions': {}, 'numeric_format': {}, 'metrics': [ { 'color': { 'column': 'type', }, }, ], } titles = { 'current_stock': 'Stock on Hand', 'consumption': 'Monthly Consumption', 'months_remaining': 'Months of Stock Remaining', 'category': 'Current Stock Status', } products = sorted(Product.view('commtrack/products', startkey=[self.domain], endkey=[self.domain, {}], include_docs=True), key=lambda p: p.name) if self.program_id: products = filter(lambda c: c.program_id == self.program_id, products) for p in products: col_id = lambda c: '%s-%s' % (p._id, c) product_cols = [] for c in ('category', 'current_stock', 'months_remaining', 'consumption'): conf['column_titles'][col_id(c)] = titles[c] product_cols.append(col_id(c)) conf['detail_columns'].extend(product_cols) product_metrics = [ { 'icon': { 'column': col_id('category'), 'categories': { 'stockout': '/static/commtrack/img/stockout.png', 'understock': '/static/commtrack/img/warning.png', 'adequate': '/static/commtrack/img/goodstock.png', 'overstock': '/static/commtrack/img/overstock.png', #'nodata': '/static/commtrack/img/no_data.png', '_null': '/static/commtrack/img/no_data.png', }, } } ] conf['enum_captions'][col_id('category')] = { 'stockout': 'Stocked out', 'understock': 'Under-stock', 'adequate': 'Adequate Stock', 'overstock': 'Over-stock', '_null': 'No Data', } for c in ('current_stock', 'months_remaining', 'consumption'): metric = { 'title': conf['column_titles'][col_id(c)], 'size': { 'column': col_id(c), }, } if c not in ('consumption',): metric['color'] = { 'column': col_id('category'), 'categories': { 'stockout': 'rgba(255, 0, 0, .8)', 'understock': 'rgba(255, 120, 0, .8)', 'adequate': 'rgba(50, 200, 50, .8)', 'overstock': 'rgba(120, 0, 255, .8)', '_null': 'rgba(128, 128, 128, .8)', }, } else: metric['color'] = 'rgba(120, 120, 255, .8)' product_metrics.append(metric) conf['numeric_format'][col_id(c)] = { 'current_stock': "return x + ' %s'" % (p.unit or 'unit'), 'months_remaining': "return (Math.round(10 * x) / 10) + (x == 1 ? ' month' : ' months')", 'consumption': "return (Math.round(10 * x) / 10) + ' %s / month'" % (p.unit or 'unit'), }[c] conf['metrics'].append({ 'title': p.name, 'group': True, 'children': product_metrics, }) conf['table_columns'].append({ 'title': p.name, 'subcolumns': product_cols, }) conf['detail_template'] = render_to_string('reports/partials/commtrack/stockstatus_mapdetail.html', { 'products': products, 'columns': [{'id': c, 'title': titles[c]} for c in ('category', 'current_stock', 'consumption', 'months_remaining')], }) #print conf['detail_template'] return conf