class Contact(BaseModel, IndexedModelMixin): """Contact model.""" _index_class = IndexedContact user_id = columns.UUID(primary_key=True) contact_id = columns.UUID(primary_key=True) # clustering key additional_name = columns.Text() addresses = columns.List(columns.UserDefinedType(PostalAddress)) avatar = columns.Text() date_insert = columns.DateTime() date_update = columns.DateTime() deleted = columns.Boolean(default=False) emails = columns.List(columns.UserDefinedType(Email)) family_name = columns.Text() given_name = columns.Text() groups = columns.List(columns.Text) identities = columns.List(columns.UserDefinedType(SocialIdentity)) ims = columns.List(columns.UserDefinedType(IM)) infos = columns.Map(columns.Text, columns.Text) name_prefix = columns.Text() name_suffix = columns.Text() organizations = columns.List(columns.UserDefinedType(Organization)) phones = columns.List(columns.UserDefinedType(Phone)) pi = columns.UserDefinedType(PIModel) privacy_features = columns.Map(columns.Text(), columns.Text()) tags = columns.List(columns.UserDefinedType(ResourceTag)) title = columns.Text() # computed value, read only
class TimeSeriesModel(Model): __table_name__ = "time_series2" name = columns.Text(primary_key=True, required=True) data_record = columns.Map( key_type=columns.Text(), value_type=columns.UserDefinedType(DataRecordModel), default={})
class UserOrderModel(Model): __table_name__ = "user_order5" account_name = columns.Text(required=True, primary_key=True) place_time = columns.DateTime(required=True, primary_key=True, clustering_order='ASC') type = columns.Text(required=True) code = columns.Text(required=True) direction = columns.Text(required=True) quantity = columns.Float(required=True) status = columns.Text(required=True) reason = columns.Text(required=True) remark = columns.Text() real_order_id = columns.Text() failed_reason = columns.Text() cancel_reason = columns.Text() ideal_price = columns.Float() filled_start_time = columns.DateTime() filled_end_time = columns.DateTime() filled_quantity = columns.Float() filled_avg_price = columns.Float() fee = columns.Float() limit_price = columns.Float() stop_price = columns.Float() execution_map = columns.Map( key_type=columns.Text, value_type=UserDefinedType(UserOrderExecutionModel), default={}) # price_change_history = columns.List(value_type=UserDefinedType(PriceChangeModel), default=[]) bargainer = columns.UserDefinedType(BargainerModel)
class Tag(Base): __table_name__ = 'tags' bucket = columns.Text(primary_key=True, partition_key=True, required=True) tag = columns.Text(primary_key=True, required=True) transactions = columns.List(columns.UserDefinedType(TransactionObject)) @classmethod def get_transaction_hashes(cls, tag): tag_meta = Tag.get(tag) if not tag_meta: return list() return [ transaction['hash'] for transaction in tag_meta['transactions'] ] @classmethod def get(cls, tag): try: tag = Tag.objects.get(bucket=tag[:5], tag=tag) return tag.as_json() except DoesNotExist: return None def as_json(self): return {'tag': self.tag, 'transactions': self.transactions}
class Transaction(UserType, JsonMixin): """ Transaction """ value = columns.UserDefinedType(Amount, required=True) type = columns.Text(required=True) event_id = columns.UUID(required=True)
class User(AbstractUser): """ Users within the Flask authentication system are represented by this model. """ __table_name__ = 'user' id = columns.UUID(primary_key=True, default=uuid.uuid4) profile = columns.UserDefinedType(Profile) address = columns.UserDefinedType(Address) is_verified = columns.Boolean(default=False) @classmethod def create(cls, **kwargs): password = kwargs.get('password') kwargs['password'] = cls.hash_password(password) return super(User, cls).create(**kwargs)
class Approvee(Base): __table_name__ = 'approvees' bucket = columns.Text(primary_key=True, partition_key=True, required=True) hash = columns.Text(primary_key=True, required=True) approvees = columns.List(columns.UserDefinedType(TransactionObject)) @classmethod def get(cls, hash): try: approvee = Approvee.objects.get(bucket=hash[:5], hash=hash) return approvee.as_json() except DoesNotExist: return None def as_json(self): return {'hash': self.hash, 'transactions': self.transactions} @classmethod def get_approvees_hashes(cls, hash): approvee_meta = Approvee.get(hash) if not approvee_meta: return list() return [ transaction['hash'] for transaction in approvee_meta['approvees'] ]
class LogHttpRequest(Log): """ Log requests """ __discriminator_value__ = 'http_request' request = columns.UserDefinedType(Request) response = columns.UserDefinedType(Response) def __init__(self, **values): super(LogHttpRequest, self).__init__(**values) self.type = self.__discriminator_value__ @classmethod def create(cls, **kwargs): from flask import request if request: kwargs['request'] = Request.validate_request(request) return super(LogHttpRequest, cls).create(**kwargs)
class Cancellation(UserType, JsonMixin): """ Cancellation """ __type_name__ = 'cancellation' reason = columns.Text() user = columns.UserDefinedType(CancellationByUser) task = columns.UserDefinedType(CancellationByTask) cancelled_on = columns.DateTime() def __init__(self, *args, **kwargs): super(Cancellation, self).__init__(*args, **kwargs) if self.cancelled_on is None: self.cancelled_on = datetime.utcnow() @property def type(self): cancelled_by = 'user' if isinstance(self.user, CancellationByUser) else 'task' return '{}_by_{}'.format(self.__type_name__, cancelled_by)
class Device(BaseModel): """User device.""" user_id = columns.UUID(primary_key=True) device_id = columns.UUID(primary_key=True, default=uuid.uuid4) name = columns.Text() date_insert = columns.DateTime(required=True, default=datetime.utcnow) date_revoked = columns.DateTime() type = columns.Text(required=True) # laptop, desktop, smartphone, etc status = columns.Text(default='unverified') user_agent = columns.Text() ip_creation = columns.Text() privacy_features = columns.Map(columns.Text, columns.Text) pi = columns.UserDefinedType(PIModel)
class Device(BaseModel): """User device.""" user_id = columns.UUID(primary_key=True) device_id = columns.UUID(primary_key=True, default=uuid.uuid4) name = columns.Text() signature_key = columns.Text() # secret key for device validation date_insert = columns.DateTime(required=True, default=datetime.datetime.now(tz=pytz.utc)) type = columns.Text(required=True) # laptop, desktop, smartphone, etc status = columns.Text(default='unknown') fingerprint = columns.Text() last_seen = columns.DateTime(default=datetime.datetime.now(tz=pytz.utc)) privacy_features = columns.Map(columns.Text, columns.Text) locations = columns.List(columns.UserDefinedType(DeviceLocation))
class User(BaseModel): """User main model.""" user_id = columns.UUID(primary_key=True, default=uuid.uuid4) name = columns.Text(required=True) password = columns.Text(required=True) date_insert = columns.DateTime() given_name = columns.Text() family_name = columns.Text() params = columns.Map(columns.Text, columns.Text) contact_id = columns.UUID() main_user_id = columns.UUID() recovery_email = columns.Text(required=True) local_identities = columns.List(columns.Text()) privacy_features = columns.Map(columns.Text(), columns.Text()) pi = columns.UserDefinedType(PIModel)
class Address(Base): __table_name__ = 'addresses' bucket = columns.Text(primary_key=True, partition_key=True, required=True) address = columns.Text(primary_key=True, required=True) transactions = columns.List(columns.UserDefinedType(TransactionObject)) @classmethod def get(cls, address): try: address = Address.objects.get(bucket=address[:5], address=address) return address.as_json() except DoesNotExist: return None def as_json(self): return {'address': self.address, 'transactions': self.transactions}
class Bundle(Base): __table_name__ = 'bundles' bucket = columns.Text(primary_key=True, partition_key=True, required=True) bundle = columns.Text(primary_key=True, required=True) transactions = columns.List(columns.UserDefinedType(TransactionObject)) @classmethod def get(cls, bundle): try: bundle = Bundle.objects.get(bucket=bundle[:5], bundle=bundle) return bundle.as_json() except DoesNotExist: return None def as_json(self): return {'bundle': self.bundle, 'transactions': self.transactions}
class UserModelText(Model): id = columns.Text(primary_key=True) info = columns.UserDefinedType(User)
class CurrentAccount(Account): """ CurrentAccount """ __discriminator_value__ = 'current' available = columns.UserDefinedType(Amount) pending = columns.Map(columns.UUID, columns.UserDefinedType(Operation)) # -------------- # Super Methods # -------------- def save(self): """ Note: convert key UUID to <type 'str'> in self.pending """ for o in self.pending: if isinstance(o, uuid.UUID): self.pending[o.__str__()] = self.pending.pop(o) return super(CurrentAccount, self).save() @classmethod def create(cls, **kwargs): kwargs['available'] = Amount(amount=0, currency=DEFAULT_CURRENCY[0]) return super(CurrentAccount, cls).create(**kwargs) # -------------- # Super Methods END # -------------- # -------------- # Properties # -------------- @property def pending_debits(self): return sum([ operation.value.amount for operation in self.pending.values() if operation.type == 'debit' ]) @property def pending_credits(self): return sum([ operation.value.amount for operation in self.pending.values() if operation.type == 'credit' ]) @property def net(self): total = self.available.amount - self.pending_debits return Amount(amount=total, currency=self.available.currency) @property def balance(self): total = self.available.amount - self.pending_debits + self.pending_credits return Amount(amount=total, currency=self.available.currency) @property def has_funds(self): return self.net.amount >= 0 # -------------- # Properties END # -------------- # -------------- # Methods # -------------- def _pop_pending(self, id=None): try: return self.pending.pop(id) except KeyError as e: logger.error('No pending operation found with key {}'.format(e)) return Response(error=FAILURE_TRANSACTION_OPERATION_ERROR) def execute_pending(self, id=None): o = self._pop_pending(id) if isinstance(o, Response): return o # make transaction amount available self.available.update(o) return Response() def cancel_pending(self, id=None): o = self._pop_pending(id) if isinstance(o, Response): return o return Response()
class Container(Model): id = columns.UUID(primary_key=True, default=uuid4) names = columns.List(columns.UserDefinedType(Name))
class Transfer(AbstractBaseModel, TaskQueueMixin, MachineMixin): """ Transfer """ __table_name__ = 'transfer' id = columns.UUID(primary_key=True, default=uuid.uuid4) description = columns.Text() value = columns.UserDefinedType(Amount, required=True) account_id = columns.UUID(required=True) destination_id = columns.UUID(required=True) # signatures signatures = columns.Map(columns.Text, columns.Text) # type = columns.Text(discriminator_column=True) status = columns.TinyInt(default=TRANSFER_CREATED[0]) # reverse reversed = columns.Boolean(default=False) value_reversed = columns.UserDefinedType(Amount) # failure failure_code = columns.SmallInt() # cancellation data cancellation_data = columns.UserDefinedType(Cancellation) # tasks tasks = columns.Map(columns.Text, columns.Text) def __init__(self, *args, **kwargs): super(Transfer, self).__init__(*args, **kwargs) self._init_machine() # --------------- # Machine Methods # --------------- def _init_machine(self): """ Method to hook a state machine to the instance """ states = list(TRANSFER_STATUS_STRING_CHOICES) transitions = TRANSFER_STATE_TRANSITIONS self.machine = Machine(model=self, states=states, transitions=transitions, auto_transitions=False, send_event=True, initial=TRANSFER_STATUS_MAP[self.status], after_state_change='_state_changed') def _state_changed(self, event): """ callback from state machine to change status on instance and persist :param event: EventData :return: """ self.status = TRANSFER_STATUS_STRING_MAP[event.state.name] persist = event.kwargs.get('persist', False) if persist: self.save() def _is_txn_valid(self, txn=None): return txn is not None and self.id == txn.source_id def _get_act_txn(self, event): act_txn = event.kwargs.get('act_txn') if self._is_txn_valid(act_txn): return act_txn act_txn = DebitAccountTransaction.objects.filter(account_id=self.account_id).get() if self._is_txn_valid(act_txn): return act_txn if act_txn is None: raise AccountTransactionNotAvailable def _get_dst_txn(self, event): dst_txn = event.kwargs.get('dst_txn') if self._is_txn_valid(dst_txn): return dst_txn dst_txn = CreditAccountTransaction.objects.filter(account_id=self.destination_id).get() if self._is_txn_valid(dst_txn): return dst_txn if dst_txn is None: raise DestinationTransactionNotAvailable def set_account_signature(self, event, **kwargs): """ :param args: :param kwargs: :return: """ act_signature = event.kwargs.get('act_signature') # TODO is signature valid if act_signature: self.signatures['act_signature'] = act_signature def set_destination_signature(self, event, **kwargs): """ :param args: :param kwargs: :return: """ dst_signature = event.kwargs.get('dst_signature') # TODO is signature valid if dst_signature: self.signatures['dst_signature'] = dst_signature def has_valid_account_signature(self, event, **kwargs): """ :param args: :param kwargs: :return: """ signature = self.signatures.get('act_signature') # TODO is signature valid if signature is not 'signature': self.failure_code = FAILURE_INVALID_ACCOUNT_SIGNATURE[0] return False return True def has_valid_destination_signature(self, event, **kwargs): """ :param args: :param kwargs: :return: """ signature = self.signatures.get('dst_signature') # TODO is signature valid if signature is not 'signature': self.failure_code = FAILURE_INVALID_DESTINATION_SIGNATURE[0] return False return True def has_transaction_account_succeed(self, event, **kwargs): """ :param args: :param kwargs: :return: """ act_txn = self._get_act_txn(event) if act_txn.is_succeed(): return True if act_txn.is_failed(): self.failure_code = FAILURE_INVALID_ACCOUNT_OPERATION_ERROR[0] return False def has_transaction_destination_succeed(self, event, **kwargs): """ :param args: :param kwargs: :return: """ dst_txn = self._get_dst_txn(event) if dst_txn.is_succeed(): return True if dst_txn.is_failed(): self.failure_code = FAILURE_INVALID_ACCOUNT_OPERATION_ERROR[0] return False def has_failure_code(self, event, **kwargs): """ :param args: :param kwargs: :return: """ return self.failure_code is not None def set_cancellation_data(self, event, **kwargs): """ :param args: :param kwargs: :return: """ reason = event.kwargs.get('reason') if reason is None: raise ReasonNotAvailable user = event.kwargs.get('user') if user is not None: self.cancellation_data = Cancellation(reason=reason, user=CancellationByUser(id=user.id)) return queue_name = event.kwargs.get('queue_name') task_name = event.kwargs.get('task_name') if queue_name is not None and task_name is not None: task = CancellationByTask(name=task_name, queue_name=queue_name) cancellation = Cancellation(reason=reason, task=task) self.cancellation_data = cancellation def has_cancellation_data(self, event, **kwargs): """ :param args: :param kwargs: :return: """ return self.cancellation_data.type is not None def execute_cancel(self, event, **kwargs): """ :param args: :param kwargs: :return: """ if self.has_cancellation_data(event): persist = event.kwargs.get('persist', False) # act_txn = self._get_act_txn(event) act_txn = self._cancel_txn(act_txn, persist) event.kwargs.update({'act_txn': act_txn}) # dst_txn = self._get_dst_txn(event) dst_txn = self._cancel_txn(dst_txn, persist) event.kwargs.update({'dst_txn': dst_txn}) def remove_expired_task(self, event, **kwargs): """ :param args: :param kwargs: :return: """ task_name = self.tasks.get('cancel') if task_name: queue_name = self.__table_name__ self.remove_task(queue_name=queue_name, name=task_name) del self.tasks['cancel'] # --------------- # Task Methods # --------------- def create_expired_task(self): kwargs = dict() action = 'cancel' url_params = {'id': self.id, 'action': action} url = url_for('tasks.transfer_actions', **url_params) kwargs['queue_name'] = self.__table_name__ kwargs['method'] = 'PUT' kwargs['url'] = url kwargs['eta'] = datetime.utcnow() + timedelta(hours=24) kwargs['payload'] = urllib.urlencode({'action': action, 'metadata': json.dumps({'reason': 'expired'})}) # context['target'] = modules.get_current_module_name() task = self.add_task(**kwargs) if isinstance(task, taskqueue.Task): self.tasks['cancel'] = task.name