2018-06-26 21:48:25 +00:00
|
|
|
from datetime import datetime
|
2020-06-09 02:32:51 +00:00
|
|
|
import string
|
|
|
|
import random
|
|
|
|
|
|
|
|
import requests
|
|
|
|
from sqlalchemy.orm import relationship, backref
|
2018-06-26 21:48:25 +00:00
|
|
|
import sqlalchemy as sa
|
|
|
|
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
|
|
|
|
from sqlalchemy.ext.declarative import declarative_base
|
2020-06-09 02:32:51 +00:00
|
|
|
from sqlalchemy.types import Float
|
|
|
|
from sqlalchemy_json import MutableJson
|
|
|
|
|
2018-06-26 21:48:25 +00:00
|
|
|
import settings
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
base = declarative_base(name="Model")
|
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
|
|
|
|
class User(db.Model):
|
2018-06-26 21:48:25 +00:00
|
|
|
__tablename__ = "users"
|
2020-06-09 02:32:51 +00:00
|
|
|
id = db.Column('user_id', db.Integer, primary_key=True)
|
|
|
|
username = db.Column(db.String(20), unique=True, index=True)
|
|
|
|
password = db.Column(db.String(60))
|
|
|
|
email = db.Column(db.String(50), unique=True, index=True)
|
|
|
|
registered_on = db.Column(db.DateTime)
|
|
|
|
admin = db.Column(db.Boolean, default=False)
|
2018-06-26 21:48:25 +00:00
|
|
|
proposals = relationship('Proposal', back_populates="user")
|
2018-06-30 23:38:13 +00:00
|
|
|
comments = relationship("Comment", back_populates="user")
|
|
|
|
|
2018-06-26 21:48:25 +00:00
|
|
|
def __init__(self, username, password, email):
|
2018-09-05 22:27:26 +00:00
|
|
|
from funding.factory import bcrypt
|
2018-06-26 21:48:25 +00:00
|
|
|
self.username = username
|
|
|
|
self.password = bcrypt.generate_password_hash(password).decode('utf8')
|
|
|
|
self.email = email
|
|
|
|
self.registered_on = datetime.utcnow()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_authenticated(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_active(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_anonymous(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_admin(self):
|
|
|
|
return self.admin
|
|
|
|
|
|
|
|
def get_id(self):
|
|
|
|
return self.id
|
|
|
|
|
|
|
|
def __repr__(self):
|
2018-09-05 22:27:26 +00:00
|
|
|
return self.username
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2018-06-30 23:16:37 +00:00
|
|
|
@classmethod
|
|
|
|
def add(cls, username, password, email):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
2018-09-05 22:27:26 +00:00
|
|
|
from funding.validation import val_username, val_email
|
2018-06-30 23:16:37 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
# validate incoming username/email
|
|
|
|
val_username(username)
|
|
|
|
val_email(email)
|
|
|
|
|
|
|
|
user = User(username, password, email)
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.add(user)
|
|
|
|
db.session.commit()
|
|
|
|
db.session.flush()
|
2018-06-30 23:16:37 +00:00
|
|
|
return user
|
|
|
|
except Exception as ex:
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.rollback()
|
2018-06-30 23:16:37 +00:00
|
|
|
raise
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2018-06-30 23:38:13 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
class Proposal(db.Model):
|
2018-06-26 21:48:25 +00:00
|
|
|
__tablename__ = "proposals"
|
2020-06-09 02:32:51 +00:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
archived = db.Column(db.Boolean, default=False)
|
|
|
|
headline = db.Column(db.VARCHAR, nullable=False)
|
|
|
|
content = db.Column(db.VARCHAR, nullable=False)
|
|
|
|
category = db.Column(db.VARCHAR, nullable=False)
|
|
|
|
date_added = db.Column(db.TIMESTAMP, default=datetime.now)
|
|
|
|
html = db.Column(db.VARCHAR)
|
|
|
|
last_edited = db.Column(db.TIMESTAMP)
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
# the FFS target
|
2020-06-09 02:32:51 +00:00
|
|
|
funds_target = db.Column(db.Float, nullable=False)
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
# the FFS progress (cached)
|
2020-06-09 02:32:51 +00:00
|
|
|
funds_progress = db.Column(db.Float, nullable=False, default=0)
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
# the FFS withdrawal amount (paid to the author)
|
2020-06-09 02:32:51 +00:00
|
|
|
funds_withdrew = db.Column(db.Float, nullable=False, default=0)
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
# the FFS receiving and withdrawal addresses
|
2020-06-09 02:32:51 +00:00
|
|
|
addr_donation = db.Column(db.VARCHAR)
|
|
|
|
addr_receiving = db.Column(db.VARCHAR)
|
|
|
|
payment_id = db.Column(db.VARCHAR)
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
# proposal status:
|
2018-07-04 17:50:36 +00:00
|
|
|
# 0: disabled
|
|
|
|
# 1: proposed
|
|
|
|
# 2: funding required
|
|
|
|
# 3: wip
|
|
|
|
# 4: completed
|
2020-06-09 02:32:51 +00:00
|
|
|
status = db.Column(db.INTEGER, default=1)
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
user_id = db.Column(db.Integer, db.ForeignKey('users.user_id'))
|
2018-06-26 21:48:25 +00:00
|
|
|
user = relationship("User", back_populates="proposals")
|
|
|
|
|
2018-06-30 23:18:00 +00:00
|
|
|
payouts = relationship("Payout", back_populates="proposal")
|
2018-07-02 21:14:10 +00:00
|
|
|
comments = relationship("Comment", back_populates="proposal", lazy='select')
|
2018-06-30 23:18:00 +00:00
|
|
|
|
2018-06-26 21:48:25 +00:00
|
|
|
def __init__(self, headline, content, category, user: User):
|
|
|
|
if not headline or not content:
|
|
|
|
raise Exception('faulty proposal')
|
|
|
|
self.headline = headline
|
|
|
|
self.content = content
|
|
|
|
self.user_id = user.id
|
|
|
|
if category not in settings.FUNDING_CATEGORIES:
|
|
|
|
raise Exception('wrong category')
|
|
|
|
self.category = category
|
|
|
|
|
|
|
|
@property
|
|
|
|
def json(self):
|
|
|
|
return {
|
|
|
|
'date_posted_epoch': self.date_added.strftime('%s'),
|
|
|
|
'date_posted': self.date_added.strftime('%b %d %Y %H:%M:%S'),
|
|
|
|
'headline': self.headline,
|
2018-07-12 16:11:17 +00:00
|
|
|
'content_markdown': self.content,
|
2018-06-26 21:48:25 +00:00
|
|
|
'category': self.category,
|
|
|
|
'funds_target': self.funds_target,
|
|
|
|
'funded_pct': self.funds_progress,
|
|
|
|
'addr_donation': self.addr_donation,
|
|
|
|
'status': self.status,
|
2018-07-12 16:11:17 +00:00
|
|
|
'user': self.user.username,
|
|
|
|
'id': self.id
|
2018-06-26 21:48:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def find_by_id(cls, pid: int):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
2018-06-26 21:48:25 +00:00
|
|
|
q = cls.query
|
|
|
|
q = q.filter(Proposal.id == pid)
|
|
|
|
result = q.first()
|
|
|
|
if not result:
|
|
|
|
return
|
2018-07-03 23:17:23 +00:00
|
|
|
return result
|
|
|
|
|
2018-07-12 00:39:49 +00:00
|
|
|
@property
|
|
|
|
def funds_target_usd(self):
|
2018-09-05 22:27:26 +00:00
|
|
|
from funding.bin.utils import Summary, coin_to_usd
|
2018-07-12 00:39:49 +00:00
|
|
|
prices = Summary.fetch_prices()
|
|
|
|
if not prices:
|
|
|
|
return
|
2018-09-05 22:27:26 +00:00
|
|
|
return coin_to_usd(amt=self.funds_target, btc_per_coin=prices['coin-btc'], usd_per_btc=prices['btc-usd'])
|
2018-07-12 00:39:49 +00:00
|
|
|
|
2018-07-04 17:50:36 +00:00
|
|
|
@property
|
|
|
|
def comment_count(self):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
|
|
|
q = db.session.query(db.func.count(Comment.id))
|
2018-07-04 17:50:36 +00:00
|
|
|
q = q.filter(Comment.proposal_id == self.id)
|
|
|
|
return q.scalar()
|
|
|
|
|
2018-07-03 23:17:23 +00:00
|
|
|
def get_comments(self):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
|
|
|
q = db.session.query(Comment)
|
2018-07-03 23:17:23 +00:00
|
|
|
q = q.filter(Comment.proposal_id == self.id)
|
2020-06-09 02:32:51 +00:00
|
|
|
q = q.filter(Comment.replied_to.is_(None))
|
2018-08-18 19:53:41 +00:00
|
|
|
q = q.order_by(Comment.date_added.desc())
|
2018-07-02 21:14:10 +00:00
|
|
|
comments = q.all()
|
|
|
|
|
|
|
|
for c in comments:
|
2020-06-09 02:32:51 +00:00
|
|
|
q = db.session.query(Comment)
|
2018-07-03 23:17:23 +00:00
|
|
|
q = q.filter(Comment.proposal_id == self.id)
|
2018-07-02 21:14:10 +00:00
|
|
|
q = q.filter(Comment.replied_to == c.id)
|
|
|
|
_c = q.all()
|
|
|
|
setattr(c, 'comments', _c)
|
|
|
|
|
2018-07-03 23:17:23 +00:00
|
|
|
setattr(self, '_comments', comments)
|
|
|
|
return self
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
@property
|
|
|
|
def spends(self):
|
|
|
|
amount = sum([p.amount for p in self.payouts])
|
|
|
|
pct = amount / 100 * self.balance['sum']
|
|
|
|
return {"amount": amount, "pct": pct}
|
|
|
|
|
2018-06-26 21:48:25 +00:00
|
|
|
@property
|
|
|
|
def balance(self):
|
|
|
|
"""This property retrieves the current funding status
|
|
|
|
of this proposal. It uses Redis cache to not spam the
|
2018-09-05 22:27:26 +00:00
|
|
|
daemon too much. Returns a nice dictionary containing
|
2018-06-26 21:48:25 +00:00
|
|
|
all relevant proposal funding info"""
|
2018-09-05 22:27:26 +00:00
|
|
|
from funding.bin.utils import Summary, coin_to_usd
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import cache, db
|
|
|
|
rtn = {'sum': 0.0, 'txs': [], 'pct': 0.0, 'available': 0}
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
if self.archived:
|
|
|
|
return rtn
|
|
|
|
|
|
|
|
try:
|
|
|
|
r = requests.get(f'http://{settings.RPC_HOST}:{settings.RPC_PORT}/json_rpc', json={
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"id": "0",
|
|
|
|
"method": "get_payments",
|
|
|
|
"params": {
|
|
|
|
"payment_id": self.payment_id
|
|
|
|
}
|
|
|
|
})
|
|
|
|
r.raise_for_status()
|
|
|
|
blob = r.json()
|
|
|
|
|
|
|
|
assert 'result' in blob
|
|
|
|
assert 'payments' in blob['result']
|
|
|
|
assert isinstance(blob['result']['payments'], list)
|
|
|
|
except Exception as ex:
|
|
|
|
return rtn
|
|
|
|
|
|
|
|
txs = blob['result']['payments']
|
|
|
|
for tx in txs:
|
|
|
|
tx['amount_human'] = float(tx['amount'])/1e11
|
|
|
|
tx['txid'] = tx['tx_hash']
|
|
|
|
tx['type'] = 'in'
|
|
|
|
|
|
|
|
data = {
|
|
|
|
'sum': sum([float(z['amount']) / 1e11 for z in txs]),
|
|
|
|
'txs': txs
|
|
|
|
}
|
|
|
|
|
|
|
|
if not isinstance(data, dict):
|
|
|
|
print('error; get_transfers_in; %d' % self.id)
|
|
|
|
return rtn
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2018-07-12 00:39:49 +00:00
|
|
|
prices = Summary.fetch_prices()
|
2018-06-26 21:48:25 +00:00
|
|
|
for tx in data['txs']:
|
2018-07-12 00:39:49 +00:00
|
|
|
if prices:
|
2018-09-05 22:27:26 +00:00
|
|
|
tx['amount_usd'] = coin_to_usd(amt=tx['amount_human'], btc_per_coin=prices['coin-btc'], usd_per_btc=prices['btc-usd'])
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
if data.get('sum', 0.0):
|
|
|
|
data['pct'] = 100 / float(self.funds_target / data.get('sum', 0.0))
|
2018-09-05 22:27:26 +00:00
|
|
|
data['available'] = data['sum']
|
2018-06-26 21:48:25 +00:00
|
|
|
else:
|
|
|
|
data['pct'] = 0.0
|
2018-09-05 22:27:26 +00:00
|
|
|
data['available'] = 0.0
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
if data['pct'] != self.funds_progress:
|
|
|
|
self.funds_progress = data['pct']
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.commit()
|
|
|
|
db.session.flush()
|
2018-06-26 21:48:25 +00:00
|
|
|
|
2018-09-05 22:27:26 +00:00
|
|
|
if data['available']:
|
|
|
|
data['remaining_pct'] = 100 / float(data['sum'] / data['available'])
|
|
|
|
else:
|
|
|
|
data['remaining_pct'] = 0.0
|
|
|
|
|
|
|
|
return data
|
|
|
|
|
2018-06-26 21:48:25 +00:00
|
|
|
@classmethod
|
2018-07-04 17:50:36 +00:00
|
|
|
def find_by_args(cls, status: int = None, cat: str = None, limit: int = 20, offset=0):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
2018-07-04 17:50:36 +00:00
|
|
|
if isinstance(status, int) and status not in settings.FUNDING_STATUSES.keys():
|
|
|
|
raise NotImplementedError('invalid status')
|
|
|
|
if isinstance(cat, str) and cat not in settings.FUNDING_CATEGORIES:
|
|
|
|
raise NotImplementedError('invalid cat')
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
q = cls.query
|
2018-07-04 17:50:36 +00:00
|
|
|
if isinstance(status, int):
|
|
|
|
q = q.filter(Proposal.status == status)
|
2018-06-26 21:48:25 +00:00
|
|
|
if cat:
|
|
|
|
q = q.filter(Proposal.category == cat)
|
|
|
|
q = q.order_by(Proposal.date_added.desc())
|
|
|
|
q = q.limit(limit)
|
|
|
|
if isinstance(offset, int):
|
|
|
|
q = q.offset(offset)
|
|
|
|
|
2018-07-04 17:50:36 +00:00
|
|
|
return q.all()
|
2018-06-26 21:48:25 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def search(cls, key: str):
|
2020-06-09 02:32:51 +00:00
|
|
|
key_ilike = f"%{key.replace('%', '')}%"
|
2018-06-26 21:48:25 +00:00
|
|
|
q = Proposal.query
|
2020-06-09 02:32:51 +00:00
|
|
|
q = q.filter(db.or_(
|
2018-06-26 21:48:25 +00:00
|
|
|
Proposal.headline.ilike(key_ilike),
|
|
|
|
Proposal.content.ilike(key_ilike)))
|
|
|
|
return q.all()
|
2018-06-30 23:18:00 +00:00
|
|
|
|
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
class Payout(db.Model):
|
2018-06-30 23:18:00 +00:00
|
|
|
__tablename__ = "payouts"
|
2020-06-09 02:32:51 +00:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
2018-06-30 23:18:00 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
proposal_id = db.Column(db.Integer, db.ForeignKey('proposals.id'))
|
2018-06-30 23:18:00 +00:00
|
|
|
proposal = relationship("Proposal", back_populates="payouts")
|
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
amount = db.Column(db.Integer, nullable=False)
|
|
|
|
to_address = db.Column(db.VARCHAR, nullable=False)
|
|
|
|
|
|
|
|
date_sent = db.Column(db.TIMESTAMP, default=datetime.now)
|
2018-06-30 23:18:00 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
ix_proposal_id = db.Index("ix_proposal_id", proposal_id)
|
2018-06-30 23:18:00 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def add(cls, proposal_id, amount, to_address):
|
|
|
|
# @TODO: validate that we can make this payout; check previous payouts
|
2020-06-09 02:32:51 +00:00
|
|
|
from flask_login import current_user
|
2018-06-30 23:18:00 +00:00
|
|
|
if not current_user.admin:
|
|
|
|
raise Exception("user must be admin to add a payout")
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
2018-06-30 23:18:00 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
payout = Payout(propsal_id=proposal_id, amount=amount, to_address=to_address)
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.add(payout)
|
|
|
|
db.session.commit()
|
|
|
|
db.session.flush()
|
2018-06-30 23:18:00 +00:00
|
|
|
return payout
|
|
|
|
except Exception as ex:
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.rollback()
|
2018-06-30 23:18:00 +00:00
|
|
|
raise
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_payouts(proposal_id):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
|
|
|
return db.session.query(Payout).filter(Payout.proposal_id == proposal_id).all()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def as_tx(self):
|
|
|
|
return {
|
|
|
|
"block_height": "-",
|
|
|
|
"type": "out",
|
|
|
|
"amount_human": self.amount,
|
|
|
|
"amount": self.amount
|
|
|
|
}
|
2018-06-30 23:38:13 +00:00
|
|
|
|
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
class Comment(db.Model):
|
2018-06-30 23:38:13 +00:00
|
|
|
__tablename__ = "comments"
|
2020-06-09 02:32:51 +00:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
2018-06-30 23:38:13 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
proposal_id = db.Column(db.Integer, db.ForeignKey('proposals.id'))
|
2018-07-02 21:14:10 +00:00
|
|
|
proposal = relationship("Proposal", back_populates="comments")
|
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
user_id = db.Column(db.Integer, db.ForeignKey('users.user_id'), nullable=False)
|
2018-06-30 23:38:13 +00:00
|
|
|
user = relationship("User", back_populates="comments")
|
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
date_added = db.Column(db.TIMESTAMP, default=datetime.now)
|
2018-07-02 21:14:10 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
message = db.Column(db.VARCHAR, nullable=False)
|
|
|
|
replied_to = db.Column(db.ForeignKey("comments.id"))
|
2018-07-01 00:14:41 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
locked = db.Column(db.Boolean, default=False)
|
2018-06-30 23:38:13 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
automated = db.Column(db.Boolean, default=False)
|
2018-07-04 18:37:32 +00:00
|
|
|
|
2020-06-09 02:32:51 +00:00
|
|
|
ix_comment_replied_to = db.Index("ix_comment_replied_to", replied_to)
|
|
|
|
ix_comment_proposal_id = db.Index("ix_comment_proposal_id", proposal_id)
|
2018-07-02 21:14:10 +00:00
|
|
|
|
2018-10-25 21:48:49 +00:00
|
|
|
@property
|
|
|
|
def message_html(self):
|
|
|
|
return [line for line in self.message.strip().split('\r\n') if line]
|
|
|
|
|
2018-07-09 23:14:16 +00:00
|
|
|
@property
|
|
|
|
def ago(self):
|
2018-09-05 22:27:26 +00:00
|
|
|
from funding.bin.utils_time import TimeMagic
|
2018-07-09 23:14:16 +00:00
|
|
|
return TimeMagic().ago(self.date_added)
|
|
|
|
|
2018-07-01 00:14:41 +00:00
|
|
|
@staticmethod
|
2018-07-02 21:14:10 +00:00
|
|
|
def find_by_id(cid: int):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
|
|
|
return db.session.query(Comment).filter(Comment.id == cid).first()
|
2018-07-01 00:14:41 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2018-07-02 21:14:10 +00:00
|
|
|
def remove(cid: int):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
|
|
|
from flask_login import current_user
|
2018-07-02 21:14:10 +00:00
|
|
|
comment = Comment.get(cid=cid)
|
2020-06-09 02:32:51 +00:00
|
|
|
|
|
|
|
if current_user.id != comment.user_id and not current_user.admin:
|
|
|
|
raise Exception("no rights to remove this comment")
|
|
|
|
|
2018-07-01 00:14:41 +00:00
|
|
|
try:
|
|
|
|
comment.delete()
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.commit()
|
|
|
|
db.session.flush()
|
2018-07-01 00:14:41 +00:00
|
|
|
except:
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.rollback()
|
2018-07-01 00:14:41 +00:00
|
|
|
raise
|
|
|
|
|
|
|
|
@staticmethod
|
2018-07-02 21:14:10 +00:00
|
|
|
def lock(cid: int):
|
2020-06-09 02:32:51 +00:00
|
|
|
from funding.factory import db
|
|
|
|
from flask_login import current_user
|
2018-07-01 00:14:41 +00:00
|
|
|
if not current_user.admin:
|
|
|
|
raise Exception("admin required")
|
2018-07-02 21:14:10 +00:00
|
|
|
comment = Comment.find_by_id(cid=cid)
|
2018-07-01 00:14:41 +00:00
|
|
|
if not comment:
|
|
|
|
raise Exception("comment by that id not found")
|
|
|
|
comment.locked = True
|
|
|
|
try:
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.commit()
|
|
|
|
db.session.flush()
|
2018-07-01 00:14:41 +00:00
|
|
|
return comment
|
|
|
|
except:
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.rollback()
|
2018-07-01 00:14:41 +00:00
|
|
|
raise
|
|
|
|
|
2018-06-30 23:38:13 +00:00
|
|
|
@classmethod
|
2018-07-04 18:37:32 +00:00
|
|
|
def add_comment(cls, pid: int, user_id: int, message: str, cid: int = None, message_id: int = None, automated=False):
|
2020-06-09 02:32:51 +00:00
|
|
|
from flask_login import current_user
|
|
|
|
from funding.factory import db
|
2018-06-30 23:38:13 +00:00
|
|
|
if not message:
|
|
|
|
raise Exception("empty message")
|
|
|
|
|
|
|
|
if current_user.id != user_id and not current_user.admin:
|
|
|
|
raise Exception("no rights to add or modify this comment")
|
|
|
|
|
|
|
|
if not message_id:
|
2018-07-02 21:14:10 +00:00
|
|
|
proposal = Proposal.find_by_id(pid=pid)
|
|
|
|
if not proposal:
|
|
|
|
raise Exception("no proposal by that id")
|
2018-07-04 18:37:32 +00:00
|
|
|
comment = Comment(user_id=user_id, proposal_id=proposal.id, automated=automated)
|
2018-07-02 21:14:10 +00:00
|
|
|
if cid:
|
|
|
|
parent = Comment.find_by_id(cid=cid)
|
2018-07-01 00:14:41 +00:00
|
|
|
if not parent:
|
|
|
|
raise Exception("cannot reply to a non-existent comment")
|
|
|
|
comment.replied_to = parent.id
|
2018-06-30 23:38:13 +00:00
|
|
|
else:
|
|
|
|
try:
|
2020-06-09 02:32:51 +00:00
|
|
|
user = db.session.query(User).filter(User.id == user_id).first()
|
2018-06-30 23:38:13 +00:00
|
|
|
if not user:
|
|
|
|
raise Exception("no user by that id")
|
2018-07-02 21:14:10 +00:00
|
|
|
comment = next(c for c in user.comments if c.id == message_id)
|
2018-06-30 23:38:13 +00:00
|
|
|
if comment.locked and not current_user.admin:
|
|
|
|
raise Exception("your comment has been locked/removed")
|
|
|
|
except StopIteration:
|
|
|
|
raise Exception("no message by that id")
|
|
|
|
except:
|
|
|
|
raise Exception("unknown error")
|
|
|
|
try:
|
|
|
|
comment.message = message
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.add(comment)
|
|
|
|
db.session.commit()
|
|
|
|
db.session.flush()
|
2018-07-12 01:03:05 +00:00
|
|
|
except Exception as ex:
|
2020-06-09 02:32:51 +00:00
|
|
|
db.session.rollback()
|
2018-07-12 01:03:05 +00:00
|
|
|
raise Exception(str(ex))
|
2020-06-09 02:32:51 +00:00
|
|
|
return comment
|