def get_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)
        if self.program_id:
            product_cases = filter(lambda c: Product.get(c.product).program_id == self.program_id, product_cases)
        def latest_case(cases):
            # getting last report date should probably be moved to a util function in a case wrapper class
            return max(cases, key=lambda c: getattr(c, 'last_reported', datetime(2000, 1, 1)).date())
        cases_by_site = map_reduce(lambda c: [(tuple(c.location_),)],
                                   lambda v: reporting_status(latest_case(v), self.start_date, self.end_date),
                                   data=product_cases, include_docs=True)

        # TODO if aggregating, won't want to fetch all these locs (will only want to fetch aggregation sites)
        locs = dict((loc._id, loc) for loc in Location.view(
                '_all_docs',
                keys=[path[-1] for path in cases_by_site.keys()],
                include_docs=True))

        for path, status in cases_by_site.iteritems():
            loc = locs[path[-1]]

            yield {
                'loc_id': loc._id,
                'loc_path': loc.path,
                'name': loc.name,
                'type': loc.location_type,
                'reporting_status': status,
                'geo': loc._geopoint,
            }
Beispiel #2
0
def make_supply_point_product(supply_point_case, product_uuid, owner_id=None):
    domain = supply_point_case.domain
    id = uuid.uuid4().hex
    user_id = const.get_commtrack_user_id(domain)
    owner_id = owner_id or get_owner_id(supply_point_case) or user_id
    username = const.COMMTRACK_USERNAME
    product_name = Product.get(product_uuid).name
    caseblock = CaseBlock(
        case_id=id,
        create=True,
        version=V2,
        case_name=product_name,
        user_id=user_id,
        owner_id=owner_id,
        case_type=const.SUPPLY_POINT_PRODUCT_CASE_TYPE,
        update={
            "product": product_uuid
        },
        index={
            const.PARENT_CASE_REF: (const.SUPPLY_POINT_CASE_TYPE,
                                    supply_point_case._id),
        }
    )
    casexml = ElementTree.tostring(caseblock.as_xml())
    submit_case_blocks(casexml, domain, username, user_id,
                       xmlns=const.COMMTRACK_SUPPLY_POINT_PRODUCT_XMLNS)
    sppc = SupplyPointProductCase.get(id)
    sppc.bind_to_location(supply_point_case.location)
    sppc.save()
    return sppc
Beispiel #3
0
def make_supply_point_product(supply_point_case, product_uuid, owner_id=None):
    domain = supply_point_case.domain
    id = uuid.uuid4().hex
    user_id = get_commtrack_user_id(domain)
    owner_id = owner_id or get_owner_id(supply_point_case) or user_id
    username = const.COMMTRACK_USERNAME
    product_name = Product.get(product_uuid).name
    caseblock = CaseBlock(case_id=id,
                          create=True,
                          version=V2,
                          case_name=product_name,
                          user_id=user_id,
                          owner_id=owner_id,
                          case_type=const.SUPPLY_POINT_PRODUCT_CASE_TYPE,
                          update={"product": product_uuid},
                          index={
                              const.PARENT_CASE_REF:
                              (const.SUPPLY_POINT_CASE_TYPE,
                               supply_point_case._id),
                          })
    casexml = ElementTree.tostring(caseblock.as_xml())
    submit_case_blocks(casexml,
                       domain,
                       username,
                       user_id,
                       xmlns=const.COMMTRACK_SUPPLY_POINT_PRODUCT_XMLNS)
    return SupplyPointProductCase.get(id)
Beispiel #4
0
    def get_data(self, slugs=None):
        startkey = [self.domain, self.active_location._id if self.active_location else None]
        if self.active_product:
            startkey.append(self.active_product['_id'])

        product_cases = SPPCase.view('commtrack/product_cases', startkey=startkey, endkey=startkey + [{}], include_docs=True)

        if self.config.get('aggregate'):
            return self.aggregate_cases(product_cases, slugs)
        else:
            return self.raw_cases(product_cases, slugs)
    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
Beispiel #6
0
    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 _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'])

            yield [
                p.name,
                stats['total_stock'],
                stats['total_consumption'],
                months_left,
                category,
            ]
Beispiel #7
0
def attach_locations(xform, cases):
    """
    Given a received form and cases, update the location of that form to the location
    of its cases (if they have one).
    """

    # todo: this won't change locations if you are trying to do that via XML.
    # this is mainly just a performance thing so you don't have to do extra lookups
    # every time you touch a case

    if cases:
        found_loc = None
        for case in cases:
            loc = None
            if not case.location_:
                if case.type == const.SUPPLY_POINT_CASE_TYPE:
                    loc_id = getattr(case, 'location_id', None)
                    if loc_id:
                        loc = Location.get(loc_id)
                        case.bind_to_location(loc)

                elif case.type == const.SUPPLY_POINT_PRODUCT_CASE_TYPE:
                    wrapped_case = SupplyPointProductCase.wrap(case._doc)
                    sp = wrapped_case.get_supply_point_case()
                    if sp and sp.location_:
                        loc = sp.location_
                        case.location_ = loc

                elif case.type == const.REQUISITION_CASE_TYPE:
                    req = RequisitionCase.wrap(case._doc)
                    prod = req.get_product_case()
                    if prod and prod.location_ and prod.location_ != case.location_:
                        case.location_ = prod.location_
                        case.save()

            if loc and found_loc and loc != found_loc:
                raise Exception(
                    'Submitted a commtrack case with multiple locations in a single form. '
                    'This is currently not allowed.'
                )
            found_loc = loc

        case = cases[0]
        if case.location_ is not None:
            # should probably store this in computed_
            xform.location_ = list(case.location_)
Beispiel #8
0
    def _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)

        def latest_case(cases):
            # getting last report date should probably be moved to a util function in a case wrapper class
            return max(cases, key=lambda c: getattr(c, 'last_reported', datetime(2000, 1, 1)).date())
        cases_by_site = map_reduce(lambda c: [(tuple(c.location_),)],
                                   lambda v: reporting_status(latest_case(v)),
                                   data=product_cases, include_docs=True)

        def child_loc(path):
            root = self.active_location
            ix = path.index(root._id) if root else -1
            try:
                return path[ix + 1]
            except IndexError:
                return None
        def case_iter():
            for k, v in cases_by_site.iteritems():
                if child_loc(k) is not None:
                    yield (k, v)
        status_by_agg_site = map_reduce(lambda (path, status): [(child_loc(path), status)],
                                        data=case_iter())
        sites_by_agg_site = map_reduce(lambda (path, status): [(child_loc(path), path[-1])],
                                       data=case_iter())

        def status_tally(statuses):
            total = len(statuses)
            return map_reduce(lambda s: [(s,)], lambda v: {'count': len(v), 'pct': len(v) / float(total)}, data=statuses)
        status_counts = dict((loc_id, status_tally(statuses)) for loc_id, statuses in status_by_agg_site.iteritems())

        master_tally = status_tally(cases_by_site.values())

        locs = sorted(Location.view('_all_docs', keys=status_counts.keys(), include_docs=True), key=lambda loc: loc.name)
        def fmt(pct):
            return '%.1f%%' % (100. * pct)
        def fmt_col(loc, col_type):
            return fmt(status_counts[loc._id].get(col_type, {'pct': 0.})['pct'])
        def _rows():
            for loc in locs:
                num_sites = len(sites_by_agg_site[loc._id])
                yield [loc.name, len(sites_by_agg_site[loc._id])] + [fmt_col(loc, k) for k in ('ontime', 'late', 'nonreporting')]

        return master_tally, _rows()
Beispiel #9
0
    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]
Beispiel #10
0
    def requisition_case_id(self):
        # for somewhat obscure reasons, the case_id is the id of the
        # supply_point_product case, so we add a new field for this.
        # though for newly created requisitions it's just empty
        if self.base_action_type == RequisitionActions.REQUEST:
            return None
        if self.base_action_type == RequisitionActions.RECEIPTS:
            # for receipts the id should point to the most recent open requisition
            # (or none)
            try:
                product_stock_case = SupplyPointProductCase.get(self.case_id)
                return RequisitionCase.open_for_product_case(
                    self.domain, product_stock_case.location_[-1], self.case_id
                )[0]
            except IndexError:
                # there was no open requisition. this is ok
                return None

        assert False, "%s is an unexpected action type!" % self.base_action_type
Beispiel #11
0
    def requisition_case_id(self):
        # for somewhat obscure reasons, the case_id is the id of the
        # supply_point_product case, so we add a new field for this.
        # though for newly created requisitions it's just empty
        if self.base_action_type == RequisitionActions.REQUEST:
            return None
        if self.base_action_type == RequisitionActions.RECEIPTS:
            # for receipts the id should point to the most recent open requisition
            # (or none)
            try:
                product_stock_case = SupplyPointProductCase.get(self.case_id)
                return RequisitionCase.open_for_product_case(
                    self.domain, product_stock_case.location_[-1],
                    self.case_id)[0]
            except IndexError:
                # there was no open requisition. this is ok
                return None

        assert False, "%s is an unexpected action type!" % self.base_action_type