class XFormPhoneMetadata(jsonobject.JsonObject): """ Metadata of an xform, from a meta block structured like: <Meta> <timeStart /> <timeEnd /> <instanceID /> <userID /> <deviceID /> <username /> <!-- CommCare extension --> <appVersion /> <location /> </Meta> See spec: https://bitbucket.org/javarosa/javarosa/wiki/OpenRosaMetaDataSchema username is not part of the spec but included for convenience """ timeStart = jsonobject.DateTimeProperty() timeEnd = jsonobject.DateTimeProperty() instanceID = jsonobject.StringProperty() userID = jsonobject.StringProperty() deviceID = jsonobject.StringProperty() username = jsonobject.StringProperty() appVersion = jsonobject.StringProperty() location = GeoPointProperty()
class TestFormMetadata(jsonobject.JsonObject): domain = jsonobject.StringProperty(required=False) xmlns = jsonobject.StringProperty(default='http://openrosa.org/formdesigner/form-processor') app_id = jsonobject.StringProperty(default='123') form_name = jsonobject.StringProperty(default='New Form') device_id = jsonobject.StringProperty(default='DEV IL') user_id = jsonobject.StringProperty(default='cruella_deville') username = jsonobject.StringProperty(default='eve') time_end = jsonobject.DateTimeProperty(default=datetime(2013, 4, 19, 16, 52, 2)) time_start = jsonobject.DateTimeProperty(default=datetime(2013, 4, 19, 16, 53, 2)) # Set this property to fake the submission time received_on = jsonobject.DateTimeProperty(default=datetime.utcnow)
class ChangeMeta(jsonobject.JsonObject): """ Metadata about a change. If available, this will be set on Change.metadata. This is only used in kafka-based pillows. """ _allow_dynamic_properties = False document_id = DefaultProperty(required=True) # Only relevant for Couch documents document_rev = jsonobject.StringProperty() # 'couch' or 'sql' data_source_type = jsonobject.StringProperty(required=True) # couch database name or one of data sources listed in corehq.apps.change_feed.data_sources data_source_name = jsonobject.StringProperty(required=True) # doc_type property of doc or else the topic name document_type = DefaultProperty() document_subtype = jsonobject.StringProperty() domain = jsonobject.StringProperty() is_deletion = jsonobject.BooleanProperty() publish_timestamp = jsonobject.DateTimeProperty(default=datetime.utcnow) # track of retry attempts attempts = jsonobject.IntegerProperty(default=0)
class StockLedgerValueWrapper(jsonobject.JsonObject): """ Wrapper class to abstract StockState and the equivalent model coming out of Elasticsearch """ domain = jsonobject.StringProperty() case_id = jsonobject.StringProperty() section_id = jsonobject.StringProperty() entry_id = jsonobject.StringProperty() balance = jsonobject.DecimalProperty() # todo: should this be an int? last_modified = jsonobject.DateTimeProperty() last_modified_form_id = jsonobject.StringProperty() daily_consumption = jsonobject.FloatProperty() location_id = jsonobject.StringProperty() _sql_product = None _sql_location = None def __init__(self, _obj=None, sql_product=None, sql_location=None, *args, **kwargs): self._sql_product = sql_product self._sql_location = sql_location super(StockLedgerValueWrapper, self).__init__(_obj, *args, **kwargs) @property def sql_product(self): if self.entry_id and not self._sql_product: try: self._sql_product = SQLProduct.objects.get(domain=self.domain, product_id=self.entry_id) except ObjectDoesNotExist: # todo: cache this result so multiple failing calls don't keep hitting the DB return None return self._sql_product @property def sql_location(self): if self.location_id and not self._sql_location: try: self._sql_location = SQLLocation.objects.get(domain=self.domain, location_id=self.location_id) except ObjectDoesNotExist: # todo: cache this result so multiple failing calls don't keep hitting the DB return None return self._sql_location @classmethod def from_stock_state(cls, stock_state): return cls( case_id=stock_state.case_id, section_id=stock_state.section_id, entry_id=stock_state.product_id, balance=stock_state.stock_on_hand, last_modified=stock_state.last_modified_date, last_modified_form_id=stock_state.last_modified_form_id, daily_consumption=stock_state.daily_consumption, location_id=stock_state.location_id, sql_location=stock_state.sql_location, sql_product=stock_state.sql_product, )
class StockReportHelper(jsonobject.JsonObject): """ Intermediate class for dealing with stock XML """ domain = jsonobject.StringProperty() form_id = jsonobject.StringProperty() timestamp = jsonobject.DateTimeProperty() tag = jsonobject.StringProperty() transactions = jsonobject.ListProperty(lambda: StockTransactionHelper) server_date = jsonobject.DateTimeProperty() deprecated = jsonobject.BooleanProperty() @property def report_type(self): # this is for callers to be able to use a less confusing name return self.tag @classmethod def make_from_form(cls, form, timestamp, tag, transactions): deprecated = form.is_deprecated return cls( domain=form.domain, form_id=form.form_id if not deprecated else form.orig_id, timestamp=timestamp, tag=tag, transactions=transactions, server_date=form.received_on, deprecated=deprecated, ) def validate(self): """ Validates this object as best we can and raises Exceptions if we find anything invalid . """ if any(transaction_helper.product_id in ('', None) for transaction_helper in self.transactions): raise MissingProductId( _('Product IDs must be set for all ledger updates!'))
class XFormPhoneMetadata(jsonobject.JsonObject): """ Metadata of an xform, from a meta block structured like: <Meta> <timeStart /> <timeEnd /> <instanceID /> <userID /> <deviceID /> <username /> <!-- CommCare extension --> <appVersion /> <location /> </Meta> See spec: https://bitbucket.org/javarosa/javarosa/wiki/OpenRosaMetaDataSchema username is not part of the spec but included for convenience """ timeStart = jsonobject.DateTimeProperty() timeEnd = jsonobject.DateTimeProperty() instanceID = jsonobject.StringProperty() userID = jsonobject.StringProperty() deviceID = jsonobject.StringProperty() username = jsonobject.StringProperty() appVersion = jsonobject.StringProperty() location = GeoPointProperty() @property def commcare_version(self): from corehq.apps.receiverwrapper.util import get_commcare_version_from_appversion_text from distutils.version import LooseVersion version_text = get_commcare_version_from_appversion_text(self.appVersion) if version_text: return LooseVersion(version_text)
class StockLedgerValueWrapper(jsonobject.JsonObject): """ Wrapper class to abstract StockState and the equivalent model coming out of Elasticsearch """ domain = jsonobject.StringProperty() case_id = jsonobject.StringProperty() section_id = jsonobject.StringProperty() entry_id = jsonobject.StringProperty() balance = jsonobject.DecimalProperty() # todo: should this be an int? last_modified = jsonobject.DateTimeProperty() last_modified_form_id = jsonobject.StringProperty() daily_consumption = jsonobject.FloatProperty() location_id = jsonobject.StringProperty() @property @quickcache(['self.domain', 'self.entry_id']) def sql_product(self): try: return SQLProduct.objects.get(domain=self.domain, product_id=self.entry_id) except ObjectDoesNotExist: return None @property @quickcache(['self.domain', 'self.location_id']) def sql_location(self): try: return ( SQLLocation.objects .prefetch_related('location_type') .get(domain=self.domain, location_id=self.location_id) ) except ObjectDoesNotExist: return None @classmethod def from_stock_state(cls, stock_state): return cls( case_id=stock_state.case_id, section_id=stock_state.section_id, entry_id=stock_state.product_id, balance=stock_state.stock_on_hand, last_modified=stock_state.last_modified_date, last_modified_form_id=stock_state.last_modified_form_id, daily_consumption=stock_state.daily_consumption, location_id=stock_state.location_id, sql_location=stock_state.sql_location, sql_product=stock_state.sql_product, )
class ChangeMeta(jsonobject.JsonObject): """ Metadata about a change. If available, this will be set on Change.metadata. This is only used in kafka-based pillows. """ document_id = DefaultProperty(required=True) document_rev = jsonobject.StringProperty() # Only relevant for Couch documents data_source_type = jsonobject.StringProperty(required=True) data_source_name = jsonobject.StringProperty(required=True) document_type = DefaultProperty() document_subtype = jsonobject.StringProperty() domain = jsonobject.StringProperty() is_deletion = jsonobject.BooleanProperty() publish_timestamp = jsonobject.DateTimeProperty(default=datetime.utcnow) _allow_dynamic_properties = False
class StockTransactionHelper(jsonobject.JsonObject): """ Helper class for transactions """ product_id = jsonobject.StringProperty() action = jsonobject.StringProperty() subaction = jsonobject.StringProperty() domain = jsonobject.StringProperty() quantity = jsonobject.DecimalProperty() # todo: this field is never populated during normal form submissions, only on SMS submissions location_id = jsonobject.StringProperty() timestamp = jsonobject.DateTimeProperty() case_id = jsonobject.StringProperty() section_id = jsonobject.StringProperty() @property def ledger_reference(self): return UniqueLedgerReference( case_id=self.case_id, section_id=self.section_id, entry_id=self.product_id ) @property def relative_quantity(self): """ Gets the quantity of this transaction as a positive or negative number depending on the action/context """ if self.action == const.StockActions.CONSUMPTION: return -self.quantity else: return self.quantity def action_config(self, commtrack_config): action = CommtrackActionConfig(action=self.action, subaction=self.subaction) for a in commtrack_config.all_actions: if a.name == action.name: return a return None @property def date(self): if self.timestamp: return dateparse.json_format_datetime(self.timestamp) def to_xml(self, E=None, **kwargs): if not E: E = XML() return E.entry( id=self.product_id, quantity=str(self.quantity if self.action != StockActions.STOCKOUT else 0), ) @property def category(self): return 'stock' def fragment(self): """ A short string representation of this to be used in sms correspondence """ if self.quantity is not None: quant = self.quantity else: quant = '' # FIXME product fetch here is inefficient return '%s%s' % (Product.get(self.product_id).code.lower(), quant) def __repr__(self): return '{action} ({subaction}): {quantity} (loc: {location_id}, product: {product_id})'.format( action=self.action, subaction=self.subaction, quantity=self.quantity, location_id=self.location_id, product_id=self.product_id, )