diff --git a/app.py b/app.py index 51fb489..ce7e779 100644 --- a/app.py +++ b/app.py @@ -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//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) diff --git a/init_data.py b/init_data.py index 79ef856..31d889f 100644 --- a/init_data.py +++ b/init_data.py @@ -1,5 +1,4 @@ from app import Story, Paragraph, db -from datetime import datetime db.drop_all() db.create_all() @@ -57,11 +56,11 @@ BEGIN_WORDS = [ ), ] -for idx, (title, text, avatar) in zip(range(10), BEGIN_WORDS): +for idx, (title, text, avatar) in zip(range(1, 11), BEGIN_WORDS): s = Story(id=idx, title=title, text=text, tail=idx, avatar="/ordinary/static/img/" + avatar) p = Paragraph(id=idx, text=text, story_id=idx, is_chosen=True, - author="初始设定", time=datetime.now()) + author="初始设定") db.session.add(s) db.session.add(p) diff --git a/static/css/ordinary.css b/static/css/ordinary.css index 05f929f..88d559c 100644 --- a/static/css/ordinary.css +++ b/static/css/ordinary.css @@ -27,6 +27,11 @@ body { vertical-align: top; } +.story-text { + font-size: 90%; + text-indent: 2rem; +} + .card-img-top { margin: 5% 25%; width: 50%; @@ -55,27 +60,31 @@ a:focus, .btn-link:focus { } .part1 { + width: 100%; max-width: 500px; float: left; - padding-right: 10px; position: relative; } .part2 { min-width: 250px; + max-width: 500px; overflow: hidden; padding-left: 25px; position: relative; } +.part-box { + min-width: 250px; + min-height: 450px; +} + .qbox { border: 2px black solid; background: white; padding: 5px; color: black; margin: 5px 5px 40px; - min-width: 250px; - min-height: 300px; } #answerLogin input { diff --git a/templates/story.html b/templates/story.html index 19a889b..012bef7 100644 --- a/templates/story.html +++ b/templates/story.html @@ -35,18 +35,18 @@ -
-
+
+

{{story.title}}


{% for p in paragraph_part %} -

{{p.text}}

+

{{p.text}}

{% endfor %}
-
+

备选后续

{% if sort_by == 'like' %} @@ -55,13 +55,29 @@ 按时间 | 按赞数 {% endif %} + + {% for p in pagination.items %} +
+ No. {{p.id}} +

{{p.text}}

+
+ {% for emoji, num, reacted, kind in p.reaction_status() %} + + {% endfor %} +
+ +
+
+ {% endfor %}
-
-

续!

+
+

续!

+
- +
@@ -73,7 +89,7 @@ @@ -121,24 +137,28 @@