def _set_balance(self, cur, imsi, pncounter): """ Sets a subscriber balance to the given PN counter. Subscriber must exist in the database. Important: This doesn't do anything about transactions, you need to make sure the caller appropriately implements that logic. Arguments: imsi: Subscriber IMSI cursor: DB cursor pncounter: A PNCounter representing the subscriber's balance Returns: None Raises: ValueError: Invalid PNCounter SubscriberNotFound: No subscriber exists in the DB """ bal = pncounter.serialize() # validate state; raises ValueError if there's a problem crdt.PNCounter.from_state(pncounter.state) try: self._update(cur, imsi, bal) except KeyError: # No subscriber affected raise SubscriberNotFound(imsi)
def _set_credit(self, imsi, new_balance): """ Set a subscriber's balance. Note: behavior of this is somewhat counter-intuitive. Internally, we'll either increment or decrement the counter such that balance.value() + X = new_balance. This kinda breaks the underlying model of the PNCounter representation, which only supports increment() and decrement() operations. So, if you run set_credit(imsi, 5) in two places such that those two set_credit's don't have a causal dependency, the subscriber balance will converge to a value that is something other than 5 probably, depending on what operations were necessary to get that value on each device. Raises: SubscriberNotFound if the subscriber doesn't exist """ try: self[imsi] = new_balance except TypeError: raise IOError('corrupt billing table: multiple records for %s' % (imsi, )) except IndexError: raise SubscriberNotFound(imsi)
def _get_balance(self, cur, imsi): """ Get a PN counter representing the subscribers' balance. Important: This doesn't do anything about transactions, you need to make sure the caller appropriately implements that logic. Arguments: imsi: Subscriber IMSI cursor: DB cursor Returns: PNCounter representing subscriber's balance. Raises: IOError: Invalid DB state (more than one record for an IMSI) SubscriberNotFound: No subscriber exists in the DB """ try: res = self._get_option(cur, imsi) except TypeError: raise IOError() else: if res is None: raise SubscriberNotFound(imsi) return crdt.PNCounter.from_json(res)
def get_account_balance(self, imsi): """ Gets a subscriber's balance as an integer. Raises: SubscriberNotFound if the subscriber doesn't exist """ try: return int(crdt.PNCounter.from_json(self[imsi]).value()) except KeyError: raise SubscriberNotFound(imsi)