bitvend/bitvend/models.py

125 lines
3.3 KiB
Python

from flask import current_app as app
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import column_property
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import func, select, and_
db = SQLAlchemy()
class TransferException(Exception):
pass
class NoFunds(TransferException):
pass
class Transaction(db.Model):
__tablename__ = "transactions"
id = db.Column(db.Integer, primary_key=True)
tx_hash = db.Column(db.String)
uid = db.Column(db.String(64), db.ForeignKey("users.uid"))
amount = db.Column(db.Integer)
type = db.Column(db.String(32), default="manual")
related = db.Column(db.String)
related_user = db.relationship(
"User", foreign_keys=[related], primaryjoin="Transaction.related == User.uid"
)
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
@hybrid_property
def value(self):
return self.amount
product_id = db.Column(db.Integer)
product_value = db.Column(db.Integer)
@hybrid_property
def finished(self):
return (self.type != "purchase") | (self.product_id != None)
__mapper_args__ = {"order_by": created.desc()}
def __repr__(self):
return "<Transaction {0.uid} {0.type} {0.amount} {0.created}>".format(self)
class User(db.Model):
__tablename__ = "users"
uid = db.Column(db.String(64), primary_key=True)
transactions = db.relationship("Transaction", backref="user", lazy="dynamic")
def __str__(self):
return self.uid
def __repr__(self):
return "<User {0.uid} {0.balance}>".format(self)
balance = column_property(
select([func.coalesce(func.sum(Transaction.amount), 0)])
.where(Transaction.uid == uid)
.correlate_except(Transaction)
)
purchase_count = column_property(
select([func.coalesce(func.count(Transaction.amount), 0)])
.where(and_(Transaction.uid == uid, Transaction.type == "purchase"))
.correlate_except(Transaction)
)
@hybrid_property
def purchase_amount(self):
return -sum(
(tx.amount or 0)
for tx in self.transactions.filter(Transaction.type == "purchase")
)
purchase_amount = column_property(
select([func.coalesce(-func.sum(Transaction.amount), 0)])
.where(and_(Transaction.uid == uid, Transaction.type == "purchase"))
.correlate_except(Transaction)
)
def transfer(self, target, amount):
if amount > self.amount_available:
raise NoFunds()
self.transactions.append(
Transaction(amount=-amount, type="transfer", related=target.uid)
)
target.transactions.append(
Transaction(amount=amount, type="transfer", related=self.uid)
)
@property
def debt_limit(self):
return app.config.get("DEBT_LIMIT", 5000)
@hybrid_property
def amount_available(self):
return self.balance + self.debt_limit
is_authenticated = True
is_active = True
is_anonymous = False
def get_id(self):
return self.uid
@property
def transaction_in_progress(self):
return self.transactions.filter(Transaction.finished == False).count()
@classmethod
def find(cls, uid):
return cls.query.filter(func.lower(cls.uid) == func.lower(uid)).first()