예제 #1
0
class Cryptocurrency(Currency):
    __tablename__ = 'cryptocurrency'
    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
    }

    symbol = Field(
        Unicode(10),
        ForeignKey(Currency.symbol),
        min_length=1,
        max_length=10,
        pattern=r'^[A-Z0-9]{1,10}$',
        primary_key=True
    )

    # A reference to an external cryptocurrency wallet
    wallet_id = Field(Unicode(32))
    wallet_latest_sync = Field(Integer(), default=0)

    withdraw_min = Field(DECIMAL(18, 8), default=Decimal('0.00001000'))
    withdraw_max = Field(DECIMAL(18, 8), default=Decimal('100.00000000'))
    withdraw_static_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))
    withdraw_commission_rate = Field(Unicode(10), default="0.005")
    withdraw_max_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))

    deposit_min = Field(DECIMAL(18, 8), default=Decimal('0.00000001'))
    deposit_max = Field(DECIMAL(18, 8), default=Decimal('100.00000000'))
    deposit_static_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))
    deposit_commission_rate = Field(Unicode(10), default="0.000")
    deposit_max_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))

    def calculate_withdraw_commission(self, amount: Decimal):
        commission = self.withdraw_static_commission
        if self.withdraw_commission_rate != Decimal(0):
            commission += amount * Decimal(self.withdraw_commission_rate)
        return min(
            commission, self.withdraw_max_commission
        ) if self.withdraw_max_commission != Decimal(0) else commission

    def calculate_deposit_commission(self, amount: Decimal):
        commission = self.deposit_static_commission
        if self.deposit_commission_rate != Decimal(0):
            commission += amount * Decimal(self.deposit_commission_rate)
        return min(
            commission, self.deposit_max_commission
        ) if self.deposit_max_commission != Decimal(0) else commission

    def to_dict(self):
        result = super().to_dict()
        # TODO: Get the current user's wallet_tier_policy about this currency
        # result['tirePolicy'] = {}
        result['withdrawMin'] = self.normalized_to_output(self.withdraw_min)
        result['withdrawMax'] = self.normalized_to_output(self.withdraw_max)
        result['withdrawStaticCommission'] = self.normalized_to_output(self.withdraw_static_commission)
        result['withdrawMaxCommission'] = self.normalized_to_output(self.withdraw_max_commission)
        result['depositMin'] = self.normalized_to_output(self.deposit_min)
        result['depositMax'] = self.normalized_to_output(self.deposit_max)
        result['depositStaticCommission'] = self.normalized_to_output(self.deposit_static_commission)
        result['depositMaxCommission'] = self.normalized_to_output(self.deposit_max_commission)
        return result
예제 #2
0
class BankAccount(BankingId):
    __tablename__ = 'bank_account'
    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
    }

    id = Field(Integer(), ForeignKey(BankingId.id), primary_key=True)
    fiat_symbol = Field(Unicode(10), ForeignKey('fiat.symbol'))

    iban = Field(Unicode(50), pattern=r'^[A-Z]{2}[A-Z0-9]{4,10}[0-9]{5,40}$')  # TODO: Should be unique if is_valid
    owner = Field(Unicode(100))
    bic = Field(Unicode(20), pattern=r'^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$', nullable=True)
예제 #3
0
class BankCard(BankingId):
    __tablename__ = 'bank_card'
    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
    }

    id = Field(Integer(), ForeignKey(BankingId.id), primary_key=True)
    fiat_symbol = Field(Unicode(10), ForeignKey('fiat.symbol'))

    pan = Field(Unicode(30), pattern=r'^([0-9]{4}-){3}[0-9]{4}$')  # TODO: Should be unique if is_valid
    holder = Field(Unicode(100))
    expiration = Field(Unicode(7), pattern=r'^[0-1]{1}/[0-9]{2,4}$', nullable=True, protected=True)  # mm/yy or mm/yyyy
예제 #4
0
class PaymentGateway(DeclarativeBase):
    __tablename__ = 'payment_gateway'

    name = Field(Unicode(30), primary_key=True)
    fiat_symbol = Field(Unicode(10), ForeignKey('fiat.symbol'))

    # # TODO: Will be deprecated and replaced by tiers
    cashin_min = Field(DECIMAL(18, 8), default=Decimal('0.00001000'))
    cashin_max = Field(DECIMAL(18, 8), default=Decimal('100.00000000'))
    cashin_static_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))
    cashin_commission_rate = Field(Unicode(10), default="0.000")
    cashin_max_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))

    # # TODO: Will be deprecated and replaced by tiers
    cashout_min = Field(DECIMAL(18, 8), default=Decimal('0.00010000'))
    cashout_max = Field(DECIMAL(18, 8), default=Decimal('100.00000000'))
    cashout_static_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))
    cashout_commission_rate = Field(Unicode(10), default="0.005")
    cashout_max_commission = Field(DECIMAL(18, 8), default=Decimal('0.00000000'))

    fiat = relationship('Fiat')

    def to_dict(self):
        result = super().to_dict()
        # TODO: Get the current user's fiat_tier_policy about this currency
        # result['tirePolicy'] = {}
        result['cashoutMin'] = self.fiat.normalized_to_output(self.cashout_min)
        result['cashoutMax'] = self.fiat.normalized_to_output(self.cashout_max)
        result['cashoutStaticCommission'] = self.fiat.normalized_to_output(self.cashout_static_commission)
        result['cashoutMaxCommission'] = self.fiat.normalized_to_output(self.cashout_max_commission)
        result['cashinMin'] = self.fiat.normalized_to_output(self.cashin_min)
        result['cashinMax'] = self.fiat.normalized_to_output(self.cashin_max)
        result['cashinStaticCommission'] = self.fiat.normalized_to_output(self.cashin_static_commission)
        result['cashinMaxCommission'] = self.fiat.normalized_to_output(self.cashin_max_commission)
        return result

    def calculate_cashout_commission(self, amount: Decimal) -> Decimal:
        commission = self.cashout_static_commission
        if self.cashout_commission_rate != Decimal(0):
            commission += amount * Decimal(self.cashout_commission_rate)
        return min(
            commission, self.cashout_max_commission
        ) if self.cashout_max_commission != Decimal(0) else commission

    def calculate_cashin_commission(self, amount: Decimal) -> Decimal:
        commission = self.cashin_static_commission
        if self.cashin_commission_rate != Decimal(0):
            commission += amount * Decimal(self.cashin_commission_rate)
        return min(
            commission, self.cashin_max_commission
        ) if self.cashin_max_commission != Decimal(0) else commission
예제 #5
0
class Invitation(ActivationMixin, DeclarativeBase):
    __tablename__ = 'invitation'

    code = Field(Unicode(), primary_key=True)

    total_sits = Field(Integer())
    filled_sits = Field(Integer(), default=0)

    __table_args__ = (CheckConstraint(
        'total_sits >= filled_sits',
        name='cc_invitation_total_sits_grater_that_filled'), )

    @property
    def unfilled_sits(self):
        return self.total_sits - self.filled_sits

    @classmethod
    def ensure_code(cls, code):
        invitation = Invitation.query.filter(
            Invitation.code == code).with_for_update().one_or_none()

        if invitation is None:
            raise HttpNotFound()

        return invitation
예제 #6
0
class Board(Versioned, Base):
    """Model class for board. This model serve as a category to topic and
    also holds settings regarding how posts are created and displayed. It
    should always be accessed using :attr:`slug`.
    """

    __tablename__ = "board"

    id = Column(Integer, primary_key=True)
    created_at = Column(DateTime(timezone=True), default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())
    slug = Column(String(64), unique=True, nullable=False)
    title = Column(Unicode(255), nullable=False)
    _settings = Column("settings", JSON, nullable=False, default={})
    agreements = Column(Text, nullable=True)
    description = Column(Text, nullable=True)
    status = Column(BoardStatusEnum, default="open", nullable=False)

    def get_settings(self):
        if self._settings is None:
            return DEFAULT_BOARD_CONFIG
        settings = DEFAULT_BOARD_CONFIG.copy()
        settings.update(self._settings)
        return settings

    def set_settings(self, value):
        self._settings = value

    @declared_attr
    def settings(self):
        return synonym("_settings",
                       descriptor=property(self.get_settings,
                                           self.set_settings))
예제 #7
0
class TicketMessage(ModifiedMixin, PaginationMixin, DeclarativeBase):
    __tablename__ = 'ticket_message'

    id = Field(Integer(), primary_key=True)
    ticket_id = Field(Integer(), ForeignKey(Ticket.id))
    member_id = Field(Integer(), ForeignKey('member.id'))
    text = Field(Unicode())

    is_answer = Field(Boolean(), default=False)

    _attachment = Field(TicketAttachment.as_mutable(JSON), nullable=True, protected=True)

    ticket = relationship(Ticket, lazy='select', uselist=False, protected=True)

    @property
    def attachment(self):
        return self._attachment.locate() if self._attachment else None

    @attachment.setter
    def attachment(self, value):
        if value is not None:
            self._attachment = TicketAttachment.create_from(value)
        else:
            self._attachment = None

    def to_dict(self):
        result = super().to_dict()
        result['attachment'] = self.attachment
        return result
예제 #8
0
class Cashin(BankingTransaction):
    __tablename__ = 'cashin'
    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
    }

    id = Field(Integer(), ForeignKey(BankingTransaction.id), primary_key=True)

    # TODO: Add some salt to prevent man in the middle (Extra field to send on creation and check on verification)
    transaction_id = Field(Unicode())
예제 #9
0
class Fiat(Currency):
    __tablename__ = 'fiat'
    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
    }

    symbol = Field(
        Unicode(10), ForeignKey(Currency.symbol),
        min_length=1,
        max_length=10,
        pattern=r'^[A-Z0-9]{1,10}$',
        primary_key=True
    )
예제 #10
0
class BankingTransaction(ModifiedMixin, OrderingMixin, FilteringMixin, PaginationMixin, DeclarativeBase):
    __tablename__ = 'banking_transaction'

    id = Field(Integer(), primary_key=True)
    fiat_symbol = Field(Unicode(10), ForeignKey('fiat.symbol'))
    member_id = Field(Integer(), ForeignKey('member.id'))  # FIXME: Change the name to `member_id`
    payment_gateway_name = Field(Unicode(30), ForeignKey('payment_gateway.name'))
    amount = Field(DECIMAL(18, 8))  # Value without commission
    commission = Field(DECIMAL(18, 8), default=Decimal(0))
    error = Field(Unicode(), nullable=True)
    reference_id = Field(Unicode(260), nullable=True)
    banking_id_id = Field(Integer(), ForeignKey('banking_id.id'), protected=True)

    payment_gateway = relationship('PaymentGateway')
    banking_id = relationship('BankingId')

    type = Field(Unicode(50))

    member = relationship('Member')

    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
        'polymorphic_on': type
    }
예제 #11
0
class BankingId(TimestampMixin, DeclarativeBase):
    __tablename__ = 'banking_id'

    id = Field(Integer(), primary_key=True)
    client_id = Field(Integer(), ForeignKey('client.id'))

    is_verified = Field(Boolean(), default=False)
    error = Field(Unicode(), nullable=True)

    client = relationship('Client', lazy='select', protected=True)

    type = Field(Enum('bank_account', 'bank_card', name='banking_id_type'))

    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
        'polymorphic_on': type
    }
예제 #12
0
class Ticket(ModifiedMixin, PaginationMixin, DeclarativeBase):
    __tablename__ = 'ticket'

    id = Field(Integer(), primary_key=True)

    title = Field(Unicode())
    member_id = Field(Integer(), ForeignKey('member.id'))
    department_id = Field(Integer(), ForeignKey('ticket_department.id'))

    closed_at = Field(DateTime(), nullable=True)

    department = relationship('TicketDepartment', uselist=False)

    @hybrid_property
    def is_closed(self):
        return self.closed_at is not None

    @is_closed.setter
    def is_closed(self, value):
        self.closed_at = datetime.now() if value else None

    @is_closed.expression
    def is_closed(self):
        # noinspection PyUnresolvedReferences
        return self.closed_at.isnot(None)

    @classmethod
    def import_value(cls, column, v):
        # noinspection PyUnresolvedReferences
        if column.key == cls.is_closed.key and not isinstance(v, bool):
            return str(v).lower() == 'true'
        # noinspection PyUnresolvedReferences
        return super().import_value(column, v)

    def to_dict(self):
        result = super().to_dict()
        first_message = DBSession.query(TicketMessage) \
            .filter(TicketMessage.ticket_id == self.id) \
            .order_by(TicketMessage.created_at) \
            .first()
        result['firstMessage'] = first_message.to_dict() if (first_message is not None) else None
        return result
예제 #13
0
class Board(Versioned, Base):
    """Model class for board. This model serve as a category to topic and
    also holds settings regarding how posts are created and displayed. It
    should always be accessed using :attr:`slug`.
    """

    __tablename__ = 'board'

    id = Column(Integer, primary_key=True)
    created_at = Column(DateTime(timezone=True), default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())
    slug = Column(String(64), unique=True, nullable=False)
    title = Column(Unicode(255), nullable=False)
    _settings = Column('settings', JsonType, nullable=False, default={})
    agreements = Column(Text, nullable=True)
    description = Column(Text, nullable=True)
    status = Column(Enum('open',
                         'restricted',
                         'locked',
                         'archived',
                         name='board_status'),
                    default='open',
                    nullable=False)

    def get_settings(self):
        settings = DEFAULT_BOARD_CONFIG.copy()
        settings.update(self._settings)
        return settings

    def set_settings(self, value):
        self._settings = value

    @declared_attr
    def settings(cls):
        return synonym('_settings', descriptor=property(cls.get_settings,
                                                        cls.set_settings))
예제 #14
0
파일: topic.py 프로젝트: scomma/fanboi2
class Topic(Versioned, Base):
    """Model class for topic. This model only holds topic metadata such as
    title or its associated board. The actual content of a topic belongs
    to :class:`Post`.
    """

    __tablename__ = "topic"

    id = Column(Integer, primary_key=True)
    created_at = Column(DateTime(timezone=True), default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())
    board_id = Column(Integer, ForeignKey("board.id"), nullable=False)
    title = Column(Unicode(255), nullable=False)
    status = Column(TopicStatusEnum, default="open", nullable=False)

    board = relationship(
        "Board",
        backref=backref(
            "topics",
            lazy="dynamic",
            cascade="all,delete",
            order_by=desc(
                func.coalesce(
                    select([TopicMeta.bumped_at
                            ]).where(TopicMeta.topic_id == id).as_scalar(),
                    created_at,
                )),
        ),
    )

    QUERY = (
        ("single_post", re.compile("^(\d+)$")),
        ("ranged_posts", re.compile("^(\d+)?\-(\d+)?$")),
        ("recent_posts", re.compile("^l(\d+)$")),
        ("recent_posts", re.compile("^recent$")),
    )

    def scoped_posts(self, query=None):
        """Return single post or multiple posts according to `query`. If
        `query` is not given, this method is an equivalent of calling
        :attr:`posts` directly. This method always returns an iterator.

        Single numeric (e.g. "253")
          Returns a single post that matches the number. For example if
          "253" is given, then an iterator containing post number "253" is
          returned.

        Ranged query (e.g. "100-150")
          Returns all posts within range. If start number is missing ("-150")
          or end number is missing ("100-") then the first post and last post
          are automatically assumed.

        Recent query (e.g. "l30", "recent")
          Returns the n last posts where n is the number after "l" letter.
          If named "recent" is given, then a default value of last 20 posts
          is used instead.
        """
        if query is None:
            return self.posts.all()
        else:
            for handler, matcher in self.QUERY:
                match = matcher.match(str(query))
                if match:
                    fn = getattr(self, handler)
                    return fn(*match.groups())
        return []

    def single_post(self, number=None):
        """Returns an iterator that contains a single post that matches
        `number`. If post with such number could not be found, an empty
        iterator is returned.
        """
        if not number:
            number = -1
        return self.posts.filter_by(number=int(number)).all()

    def ranged_posts(self, start=None, end=None):
        """Returns a range of post between `start` and `end`. When `start` or
        `end` is empty, the first and last posts are assumed respectively.
        """
        if start is None:
            start = 1
        if end is None:
            query = Post.number >= start
        else:
            query = Post.number.between(start, end)
        return self.posts.filter(query).all()

    def recent_posts(self, count=30):
        """Returns recent `count` number of posts associated with this topic.
        Defaults to 30 posts if `count` is not given.
        """
        return (self.posts.order_by(False).order_by(desc(
            Post.number)).limit(count).all()[::-1])
예제 #15
0
class Client(Member):
    __tablename__ = 'client'
    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
    }

    # noinspection PyArgumentList
    def __init__(self):
        # TODO: Move it to somewhere better
        self.evidence = ClientEvidence()

    id = Field(Integer, ForeignKey(Member.id), primary_key=True)

    has_second_factor = Field(Boolean(), default=False)

    invitation_code = Field(Unicode(),
                            ForeignKey('invitation.code'),
                            nullable=True)

    email_verified_at = Field(DateTime(), nullable=True, protected=True)
    evidence_verified_at = Field(DateTime(), nullable=True, protected=True)

    # FIXME: is `select` the best choice?
    evidence = relationship('ClientEvidence',
                            lazy='select',
                            uselist=False,
                            protected=True)

    @hybrid_property
    def is_email_verified(self):
        return self.email_verified_at is not None

    @is_email_verified.setter
    def is_email_verified(self, value):
        self.email_verified_at = datetime.now() if value else None

    @is_email_verified.expression
    def is_email_verified(self):
        # noinspection PyUnresolvedReferences
        return self.email_verified_at.isnot(None)

    @hybrid_property
    def is_evidence_verified(self):
        return self.evidence_verified_at is not None

    @is_evidence_verified.setter
    def is_evidence_verified(self, value):
        self.evidence_verified_at = datetime.now() if value else None

    @is_evidence_verified.expression
    def is_evidence_verified(self):
        # noinspection PyUnresolvedReferences
        return self.evidence_verified_at.isnot(None)

    @classmethod
    def import_value(cls, column, v):
        if column.key == cls.is_email_verified.key and not isinstance(v, bool):
            return str(v).lower() == 'true'
        if column.key == cls.is_evidence_verified.key and not isinstance(
                v, bool):
            return str(v).lower() == 'true'
        return super().import_value(column, v)

    @property
    def roles(self):
        roles = ['client']
        if self.is_evidence_verified:
            return roles + ['trusted_client']
        elif self.is_email_verified:
            return roles + ['semitrusted_client']
        else:
            return roles + ['distrusted_client']

    @classmethod
    def is_exists(cls, client_id):
        return cls.query.filter(cls.id == client_id).count() > 0
예제 #16
0
class Currency(OrderingMixin, FilteringMixin, DeclarativeBase):
    """
    For exp. for 'BTC' we'll use:
        * smallest_unit_scale = -8
        * normalization_scale = 0

    For exp. for 'ETH' we'll use:
        * smallest_unit_scale = -18
        * normalization_scale = -1

    For exp. for 'IRR' we'll use:
        * smallest_unit_scale = 0
        * normalization_scale = -8

    """
    __tablename__ = 'currency'

    symbol = Field(Unicode(10), min_length=1, max_length=10, pattern=r'^[A-Z0-9]{1,10}$', primary_key=True)
    name = Field(Unicode(25), min_length=1, max_length=25)

    normalization_scale = Field(Integer(), default=0)
    smallest_unit_scale = Field(Integer(), default=0)

    type = Field(Enum('fiat', 'cryptocurrency', name='currency_type'))

    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
        'polymorphic_on': type
    }

    def smallest_unit_to_normalized(self, number):
        """
        :return:
        """
        if number is None:
            return None
        return Decimal(number).scaleb(self.smallest_unit_scale + self.normalization_scale)

    def normalized_to_output(self, number: Decimal):
        """
        :return:
        """
        if number is None:
            return None
        if not isinstance(number, Decimal):
            number = Decimal(number)

        return ('{:.' + str(max(0, -self.smallest_unit_scale)) + 'f}') \
            .format(number.scaleb(- self.normalization_scale))

    def normalized_smallest_unit(self, number: Decimal):
        """
        :return:
        """
        if number is None:
            return None
        if not isinstance(number, Decimal):
            number = Decimal(number)

        return '{:.0f}'.format(number.scaleb(-self.smallest_unit_scale - self.normalization_scale))

    def input_to_normalized(self, number: str, strict=True):
        """
        :return:
        """
        if number is None:
            return None

        if strict and \
                (
                        (not (self.smallest_unit_scale >= 0 and len(number.split('.')) == 1))
                        and (len(number.split('.')) == 1 or len(number.split('.')[1]) != -self.smallest_unit_scale)
                ):
            raise HttpBadRequest(f'This number is in {self.symbol} unit and this currency should be presented with '
                                 f'the exact ${-self.smallest_unit_scale} digits behind the floating-point',
                                 'bad-number-format')

        number = Decimal(number)

        return number.scaleb(-self.normalization_scale)

    def smallest_unit_to_output(self, number):
        if number is None:
            return None
        return self.normalized_to_output(self.smallest_unit_to_normalized(number))

    # def normalized_to_smallest_unit(self, number):
    #     if number is None:
    #         return None
    #     if not isinstance(number, Decimal):
    #         number = Decimal(number)
    #     return self.normalized_to_output(self.smallest_unit_to_normalized(number))

    def to_dict(self):
        result = super().to_dict()
        # TODO: Get the current user's wallet_tier_policy about this currency
        # result['tirePolicy'] = {}
        return result

    @classmethod
    def format_normalized_string(cls, number: Decimal):
        return '{:.8f}'.format(number)
예제 #17
0
class ClientEvidence(ModifiedMixin, DeclarativeBase):
    __tablename__ = 'client_evidence'

    client_id = Field(Integer(), ForeignKey('member.id'), primary_key=True)

    first_name = Field(Unicode(50), nullable=True)
    last_name = Field(Unicode(50), nullable=True)

    mobile_phone = Field(Unicode(50),
                         min_length=10,
                         pattern=r'^\+[1-9]{1}[0-9]{3,14}$',
                         watermark='Mobile Phone',
                         example='+989876543210',
                         nullable=True,
                         unique=True)

    fixed_phone = Field(Unicode(50),
                        min_length=10,
                        pattern=r'^\+[1-9]{1}[0-9]{3,14}$',
                        watermark='Fixed Phone',
                        example='+982167653945',
                        nullable=True,
                        unique=True)

    gender = Field(Enum('male', 'female', name='gender'), nullable=True)
    birthday = Field(DateTime(), nullable=True)

    city_id = Field(Integer, ForeignKey('city.id'), nullable=True)
    city = relationship('City', uselist=False)
    national_code = Field(Unicode(50), nullable=True)

    address = Field(Unicode(), nullable=True)

    _id_card = Field(ClientIdCard.as_mutable(JSON),
                     nullable=True,
                     protected=True)  # Attachment
    _id_card_secondary = Field(ClientIdCard.as_mutable(JSON),
                               nullable=True,
                               protected=True)  # Attachment

    error = Field(Unicode(), nullable=True)

    @property
    def id_card(self):
        return self._id_card.locate() if self._id_card else None

    @id_card.setter
    def id_card(self, value):
        if value is not None:
            self._id_card = ClientIdCard.create_from(value)
        else:
            self._id_card = None

    @property
    def id_card_secondary(self):
        return self._id_card_secondary.locate(
        ) if self._id_card_secondary else None

    @id_card_secondary.setter
    def id_card_secondary(self, value):
        if value is not None:
            self._id_card_secondary = ClientIdCard.create_from(value)
        else:
            self._id_card_secondary = None

    def to_dict(self):
        result = super().to_dict()
        result['idCard'] = self.id_card
        result['idCardSecondary'] = self.id_card_secondary
        return result
예제 #18
0
class Market(OrderingMixin, FilteringMixin, DeclarativeBase):
    """
    Currency pairs are sometimes then written by concatenating the ISO currency codes (ISO 4217) of the base currency
    and the counter currency, separating them with a slash character. Often the slash character is omitted,
    alternatively the slash may be replaced by and etc. A widely traded currency pair is the relation of the euro
    against the US dollar, designated as EUR/USD. The quotation EUR/USD 1.2500 means that one euro is exchanged for
    1.2500 US dollars. Here, EUR is the base currency and USD is the quote currency(counter currency). This means that
    1 Euro can be exchangeable to 1.25 US Dollars.

    Reference: https://en.wikipedia.org/wiki/Currency_pair

    """
    __tablename__ = 'market'

    name = Field(Unicode(20),
                 pattern=r'^[A-Z0-9]{1,10}_[A-Z0-9]{1,10}$',
                 primary_key=True)  # e.g. btc_usd

    base_currency_symbol = Field(Unicode(),
                                 ForeignKey('currency.symbol'),
                                 protected=True)
    quote_currency_symbol = Field(Unicode(),
                                  ForeignKey('currency.symbol'),
                                  protected=True)

    base_currency = relationship('Currency',
                                 foreign_keys=[base_currency_symbol])
    quote_currency = relationship('Currency',
                                  foreign_keys=[quote_currency_symbol])

    buy_amount_min = Field(DECIMAL(18, 8), default=Decimal('0.00000100'))
    buy_amount_max = Field(DECIMAL(18, 8), default=Decimal('100.00000000'))

    sell_amount_min = Field(DECIMAL(18, 8), default=Decimal('0.00000100'))
    sell_amount_max = Field(DECIMAL(18, 8), default=Decimal('100.00000000'))

    taker_commission_rate = Field(Unicode(10), default='0.4')
    maker_commission_rate = Field(Unicode(10), default='0.1')

    # taker_static_commission = Field(BigInteger(), default=0)
    # taker_permille_commission = Field(Integer(), default=0)
    # taker_max_commission = Field(BigInteger(), default=0)

    # maker_static_commission = Field(BigInteger(), default=0)
    # maker_permille_commission = Field(Integer(), default=0)
    # maker_max_commission = Field(BigInteger(), default=0)

    def to_dict(self):
        result = super().to_dict()
        # TODO: Get the current user's wallet_tier_policy about this currency
        # result['tirePolicy'] = {}
        result['buyAmountMin'] = self.base_currency.normalized_to_output(
            self.buy_amount_min)
        result['buyAmountMax'] = self.base_currency.normalized_to_output(
            self.buy_amount_max)
        result['sellAmountMin'] = self.base_currency.normalized_to_output(
            self.sell_amount_min)
        result['sellAmountMax'] = self.base_currency.normalized_to_output(
            self.sell_amount_max)
        return result

    def get_last_price(self):
        try:
            return Decimal(stexchange_client.market_last(self.name))
        except StexchangeException as e:
            raise stexchange_http_exception_handler(e)

    def validate_ranges(self, type_, total_amount, price=None):

        # TODO: Review and rewrite the price threshold validator
        # threshold = Decimal(settings.trader.price_threshold_permille)
        # price_rate = Decimal(1000 * (price or self.get_last_price()) / self.get_last_price()) - Decimal(1000)

        if type_ == 'buy':
            # if price_rate > threshold:
            #     raise HttpBadRequest('Price not in valid range', 'price-not-in-range')

            if total_amount < self.buy_amount_min or \
                    (self.buy_amount_max != 0 and total_amount > self.buy_amount_max):
                raise HttpBadRequest('Amount not in range',
                                     'amount-not-in-range')

        elif type_ == 'sell':
            # if price_rate < -threshold:
            #     raise HttpBadRequest('Price not in valid range', 'price-not-in-range')

            if total_amount < self.sell_amount_min or \
                    (self.sell_amount_max != 0 and total_amount > self.sell_amount_max):
                raise HttpBadRequest('Amount not in range',
                                     'amount-not-in-range')
예제 #19
0
    user='******',
    pwd='root',
    host='localhost',
    port='3306',
    db_name='iaasms'
)
# pool_recycle=3600 连接超时参数
engine = create_engine(url)

table = Table(
    'tom_test', MetaData(),
    Column('id', INTEGER, primary_key=True),
    Column('start_time', INTEGER, index=False),
    Column('last_time', INTEGER, nullable=False),
    Column('count', INTEGER, nullable=False),
    Column('region', Unicode(20, _warn_on_bytestring=False))
)

# 创建表
table.create(engine, True)


def _formatter_data(res):
    """
    sqlalchemy.engine.result.ResultProxy 对象数据提取

    res.cursor._rows   # 数据
    res._metadata.keys 或 res.cursor.description # 数据库表字段名
    :param res:
    :return: list
    """
예제 #20
0
class TicketDepartment(DeclarativeBase):
    __tablename__ = 'ticket_department'

    id = Field(Integer(), primary_key=True)
    title = Field(Unicode(50))
예제 #21
0
class Member(ModifiedMixin, ActivationMixin, OrderingMixin, FilteringMixin,
             DeclarativeBase):
    __tablename__ = 'member'

    id = Field(Integer, primary_key=True)
    _password = Field('password',
                      Unicode(),
                      index=True,
                      json='password',
                      protected=True,
                      min_length=6)

    email = Field(
        Unicode(),
        unique=True,
        index=True,
        pattern=r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)')

    type = Field(Unicode(50))

    @classmethod
    def _hash_password(cls, password):
        salt = hashlib.sha256()
        salt.update(os.urandom(60))
        salt = salt.hexdigest()

        hashed_pass = hashlib.sha256()
        # Make sure password is a str because we cannot hash unicode objects
        hashed_pass.update((password + salt).encode('utf-8'))
        hashed_pass = hashed_pass.hexdigest()

        password = salt + hashed_pass
        return password

    def _set_password(self, password):
        """Hash ``password`` on the fly and store its hashed version."""
        min_length = self.__class__.password.info['min_length']
        if len(password) < min_length:
            raise HttpBadRequest(
                'Please enter at least %d characters for password field.' %
                min_length)
        self._password = self._hash_password(password)

    def _get_password(self):
        """Return the hashed version of the password."""
        return self._password

    password = synonym('_password',
                       descriptor=property(_get_password, _set_password),
                       info=dict(protected=True))

    def validate_password(self, password):
        """
        Check the password against existing credentials.

        :param password: the password that was provided by the user to
            try and authenticate. This is the clear text version that we will
            need to match against the hashed one in the database.
        :type password: unicode object.
        :return: Whether the password is valid.
        :rtype: bool

        """
        hashed_pass = hashlib.sha256()
        hashed_pass.update((password + self.password[:64]).encode('utf-8'))
        return self.password[64:] == hashed_pass.hexdigest()

    def create_jwt_principal(self, session_id=None):
        # FIXME: IMPORTANT Include user password as salt in signature

        if session_id is None:
            session_id = str(uuid.uuid4())

        return JwtPrincipal(
            dict(id=self.id,
                 roles=self.roles,
                 email=self.email,
                 sessionId=session_id))

    @classmethod
    def current(cls):
        if context.identity is None:
            raise HttpUnauthorized()

        return cls.query.filter(cls.email == context.identity.email).one()

    def change_password(self, current_password, new_password):
        if not self.validate_password(current_password):
            raise HttpBadRequest()

        self.password = new_password

    def create_refresh_principal(self):
        return JwtRefreshToken(dict(id=self.id))

    @property
    def sessions(self):
        return context.application.__authenticator__.get_member_sessions_info(
            self.id)

    __mapper_args__ = {
        'polymorphic_identity': __tablename__,
        'polymorphic_on': type
    }
예제 #22
0

# Local copy of SQLAlchemy's private type map
# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors
_type_map = {
    int: Integer(),
    float: Float(),
    bool: BOOLEANTYPE,
    decimal.Decimal: Numeric(),
    dt.date: Date(),
    dt.datetime: DateTime(),
    dt.time: Time(),
    dt.timedelta: Interval(),
    type(None): NULLTYPE,
    bytes: LargeBinary(),
    str: Unicode(),
}


def _to_sql(
    func: Callable[..., Any],
    argtypes: Optional[Sequence[Type_]] = None,
    rettype: Type_ = "",
) -> text:
    class ListAppender(list):
        def __call__(self, *args: Any) -> None:
            [self.append(arg) for arg in args]

    SQL_INDENT = "  "  # 2 spaces
    PY_INDENT = "    "  # 4 spaces
    SPACE = " "  # Single Space