|
|
@ -1,5 +1,5 @@ |
|
|
|
# -*- coding: utf-8 -*- |
|
|
|
|
|
|
|
from datetime import datetime |
|
|
|
from functools import wraps |
|
|
|
from flask import (Flask, request, render_template, send_from_directory, abort, |
|
|
|
redirect, session, Blueprint, url_for) |
|
|
@ -57,12 +57,12 @@ class Story(db.Model): |
|
|
|
class Paragraph(db.Model): |
|
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
|
parent_id = db.Column(db.Integer, default=0, index=True) |
|
|
|
story_id = db.Column(db.Integer, default=0, index=True) |
|
|
|
story_id = db.Column(db.Integer, index=True) |
|
|
|
is_hidden = db.Column(db.Boolean, default=False) |
|
|
|
is_chosen = db.Column(db.Boolean, default=False) |
|
|
|
text = db.Column(db.Text) |
|
|
|
author = db.Column(db.String(30)) |
|
|
|
time = db.Column(db.DateTime) |
|
|
|
time = db.Column(db.DateTime, default=datetime.now) |
|
|
|
like_num = db.Column(db.Integer, default=0, index=True) |
|
|
|
angry_num = db.Column(db.Integer, default=0) |
|
|
|
fun_num = db.Column(db.Integer, default=0) |
|
|
@ -72,13 +72,22 @@ class Paragraph(db.Model): |
|
|
|
def create_at(self): |
|
|
|
return self.time.strftime("%m-%d %H:%M") |
|
|
|
|
|
|
|
# always increment 1 for the id of a new record |
|
|
|
__table_args__ = {'sqlite_autoincrement': True} |
|
|
|
def reaction_status(self): |
|
|
|
user = session.get('username') |
|
|
|
return list(zip( |
|
|
|
'👍😡🤣😅👎', |
|
|
|
[self.like_num, self.angry_num, self.fun_num, self.sweat_num, 0], |
|
|
|
[ |
|
|
|
user and bool(Reaction.query.filter_by(pid=self.id, user=user, kind=i).first()) |
|
|
|
for i in range(1, 6) |
|
|
|
], |
|
|
|
range(1, 6) |
|
|
|
)) |
|
|
|
|
|
|
|
|
|
|
|
class Reaction(db.Model): |
|
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
|
kind = db.Column(db.SmallInteger) # 1: like 2: angry 3: funny 4: sweat |
|
|
|
kind = db.Column(db.SmallInteger) # 1: like 2: angry 3: funny 4: sweat 5:dislike |
|
|
|
pid = db.Column(db.Integer, index=True) # id of paragraph |
|
|
|
user = db.Column(db.String(30)) # username of user |
|
|
|
|
|
|
@ -180,7 +189,7 @@ def story(story_id): |
|
|
|
sort_by = request.args.get('sort_by', 'time') |
|
|
|
|
|
|
|
q = Paragraph.query.filter_by(parent_id=story.tail, is_hidden=False) |
|
|
|
q = q.order_by(Paragraph.like_num if sort_by == 'like' else |
|
|
|
q = q.order_by(Paragraph.like_num.desc() if sort_by == 'like' else |
|
|
|
Paragraph.id.desc()) |
|
|
|
pagination = q.paginate(max_per_page=100) |
|
|
|
|
|
|
@ -192,110 +201,68 @@ def story(story_id): |
|
|
|
|
|
|
|
return render_template('story.html', **locals()) |
|
|
|
|
|
|
|
''' |
|
|
|
key = request.args.get('key') |
|
|
|
sort_by = request.args.get('sort_by', 'time') |
|
|
|
final_list = request.args.get('final_list', '') |
|
|
|
|
|
|
|
if 'uid' not in session: |
|
|
|
return redirect('set_session') |
|
|
|
uid = session.get('uid') |
|
|
|
|
|
|
|
q = Candidate.query |
|
|
|
if final_list and C.step2.get('final_list'): |
|
|
|
q = q.filter(Candidate.id.in_(C.step2['final_list'])) |
|
|
|
q = q.order_by(db.desc('likeNum')) if sort_by=='likeNum' else q.order_by(db.desc('id')) |
|
|
|
pag = q.paginate(max_per_page=100) |
|
|
|
|
|
|
|
def check_like(c): |
|
|
|
c.liked = 'liked' if Like.query.filter_by(uid=uid, cid=c.id).count() else 'like' |
|
|
|
return c |
|
|
|
|
|
|
|
pag.items = map(check_like, pag.items) |
|
|
|
|
|
|
|
vs = [{ |
|
|
|
'name': name, |
|
|
|
'ques': ques, |
|
|
|
'hint': hint |
|
|
|
} for name, ques, hint, ans in C.verify |
|
|
|
] |
|
|
|
|
|
|
|
return render_template('list.html', pagination=pag, vs=vs, verified=session.get('verified'), showPrivate=(key==C.key), sort_by=sort_by, key=key, final_list=final_list,base_toot_url='https://%s/web/statuses/' % C.domain, step2=C.step2, text1=C.text1, text2=C.text2) |
|
|
|
|
|
|
|
@app.route('/ordinary/new', methods=['POST']) |
|
|
|
@limiter.limit("5 / hour; 1 / 2 second") |
|
|
|
@need_verify |
|
|
|
def new_one(): |
|
|
|
content = request.form.get('text') |
|
|
|
private = request.form.get('privateText') |
|
|
|
url = request.form.get('url') |
|
|
|
|
|
|
|
if not content or len(content)>4000: abort(422) |
|
|
|
if private and len(private)>1000: abort(422) |
|
|
|
if url and not re.match('https://(cloud\.tsinghua\.edu\.cn/f/[0-9a-z]+/(\?dl=1)?|closed\.social/safeShare/\d([a-zA-Z]+)?)', url): abort(422) |
|
|
|
|
|
|
|
if not Candidate.query.filter_by(content=content).first(): |
|
|
|
toot = th.status_post( |
|
|
|
f"有新的自荐报名(大家可以直接在此处评论):\n\n{content}", |
|
|
|
visibility=C.visibility |
|
|
|
) |
|
|
|
|
|
|
|
c = Candidate( |
|
|
|
content=content, |
|
|
|
private=private, |
|
|
|
url=url, |
|
|
|
toot=toot.id, |
|
|
|
time=datetime.now() |
|
|
|
) |
|
|
|
db.session.add(c) |
|
|
|
db.session.commit() |
|
|
|
@bp.route('/create', methods=['POST']) |
|
|
|
@login_required |
|
|
|
@limiter.limit("15 / hour") |
|
|
|
def create(): |
|
|
|
story_id = request.form.get('story-id') |
|
|
|
text = request.form.get('text') |
|
|
|
if not text or len(text) > 140: |
|
|
|
abort(422) |
|
|
|
story = Story.query.get_or_404(story_id) |
|
|
|
|
|
|
|
return redirect(".") |
|
|
|
|
|
|
|
|
|
|
|
context = th.status_context(toot) |
|
|
|
replies = [ |
|
|
|
{ |
|
|
|
'disp': (t.account.display_name or t.account.acct), |
|
|
|
'url': t.account.url, |
|
|
|
'content': h2t.handle(t.content).replace(C.bot_name,'').strip(), |
|
|
|
'time': str(t.created_at) |
|
|
|
} |
|
|
|
for t in context.descendants |
|
|
|
] |
|
|
|
d = list(filter( |
|
|
|
lambda r: r['content'] == '删除' and r['url'].split('/@')[1] in C.admins, |
|
|
|
replies |
|
|
|
)) |
|
|
|
if d: |
|
|
|
db.session.delete(c) |
|
|
|
db.session.commit() |
|
|
|
th.status_delete(toot) |
|
|
|
return '该内容已被删除', 404 |
|
|
|
|
|
|
|
return {'replies': replies} |
|
|
|
|
|
|
|
@limiter.limit("100 / hour") |
|
|
|
@app.route('/ordinary/<int:toot>/like', methods=['POST']) |
|
|
|
def like(toot): |
|
|
|
c = Candidate.query.filter_by(toot=toot).first() |
|
|
|
if not c: |
|
|
|
abort(404) |
|
|
|
|
|
|
|
uid = session['uid'] |
|
|
|
if not uid: abort(401) |
|
|
|
if Like.query.filter_by(uid=uid, cid=c.id).first(): |
|
|
|
return '点赞过了', 403 |
|
|
|
|
|
|
|
l = Like(uid=uid, cid=c.id) |
|
|
|
c.likeNum += 1 |
|
|
|
db.session.add(l) |
|
|
|
p = Paragraph( |
|
|
|
parent_id=story.tail, |
|
|
|
story_id=story_id, |
|
|
|
text=text, |
|
|
|
author=session['username'] |
|
|
|
) |
|
|
|
db.session.add(p) |
|
|
|
db.session.commit() |
|
|
|
|
|
|
|
return str(c.likeNum) |
|
|
|
return redirect(story_id) |
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/react', methods=['POST']) |
|
|
|
@login_required |
|
|
|
@limiter.limit("4 / minute") |
|
|
|
def react(): |
|
|
|
kind = request.form.get('kind', type=int) |
|
|
|
pid = request.form.get('pid', type=int) |
|
|
|
if kind not in range(1, 6): |
|
|
|
abort(422) |
|
|
|
p = Paragraph.query.get_or_404(pid) |
|
|
|
|
|
|
|
d = dict(kind=kind, user=session['username'], pid=pid) |
|
|
|
if Reaction.query.filter_by(**d).first(): |
|
|
|
return '' |
|
|
|
db.session.add(Reaction(**d)) |
|
|
|
|
|
|
|
n = '' |
|
|
|
if kind == 1: |
|
|
|
p.like_num += 1 |
|
|
|
n = p.like_num |
|
|
|
elif kind == 2: |
|
|
|
p.angry_num += 1 |
|
|
|
n = p.angry_num |
|
|
|
elif kind == 3: |
|
|
|
p.fun_num += 1 |
|
|
|
n = p.fun_num |
|
|
|
elif kind == 4: |
|
|
|
p.sweat_num += 1 |
|
|
|
n = p.sweat_num |
|
|
|
|
|
|
|
db.session.commit() |
|
|
|
|
|
|
|
return str(n) |
|
|
|
|
|
|
|
''' |
|
|
|
|
|
|
|
@bp.route('/choose') |
|
|
|
def choose_next(): |
|
|
|
min_like = request.args.get('min_like', type=int) |
|
|
|
choose_new_next(min_like) |
|
|
|
return 'ok' |
|
|
|
|
|
|
|
app.register_blueprint(bp) |
|
|
|
|
|
|
|