Show create/modify/update time
This commit is contained in:
77
db/sqlite.py
77
db/sqlite.py
@@ -6,31 +6,56 @@ class DB:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_subforums(self):
|
def get_subforums(self):
|
||||||
return self._db().execute('select forum_id, name, description from subforums')
|
return self._db().execute('''
|
||||||
|
select f.forum_id, name, description, thread_id, title, update_time
|
||||||
|
from subforums f
|
||||||
|
left join threads t
|
||||||
|
on t.thread_id = (
|
||||||
|
select tt.thread_id
|
||||||
|
from threads tt
|
||||||
|
where f.forum_id = tt.forum_id
|
||||||
|
order by update_time desc
|
||||||
|
limit 1
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
|
||||||
def get_subforum(self, subforum):
|
def get_subforum(self, subforum):
|
||||||
return self._db().execute('select name, description from subforums where forum_id = ?', (subforum,)).fetchone()
|
return self._db().execute('''
|
||||||
|
select name, description
|
||||||
|
from subforums
|
||||||
|
where forum_id = ?
|
||||||
|
''',
|
||||||
|
(subforum,)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
def get_threads(self, subforum):
|
def get_threads(self, subforum):
|
||||||
return self._db().execute('select thread_id, title from threads where forum_id = ?', (subforum,))
|
return self._db().execute('''
|
||||||
|
select t.thread_id, title, t.create_time, t.update_time, t.author_id, name, count(1)
|
||||||
|
from threads t, users, comments c
|
||||||
|
where forum_id = ? and user_id = t.author_id and t.thread_id = c.thread_id
|
||||||
|
group by t.thread_id
|
||||||
|
''',
|
||||||
|
(subforum,)
|
||||||
|
)
|
||||||
|
|
||||||
def get_thread(self, thread):
|
def get_thread(self, thread):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
title, text, author, author_id = db.execute('''
|
title, text, author, author_id, create_time, modify_time = db.execute('''
|
||||||
select title, text, name, author_id
|
select title, text, name, author_id, create_time, modify_time
|
||||||
from threads, users
|
from threads, users
|
||||||
where thread_id = ? and author_id = user_id
|
where thread_id = ? and author_id = user_id
|
||||||
''',
|
''',
|
||||||
(thread,)
|
(thread,)
|
||||||
).fetchone()
|
).fetchone()
|
||||||
comments = db.execute('''
|
comments = db.execute('''
|
||||||
select comment_id, parent_id, name, text
|
select comment_id, parent_id, name, text, create_time, modify_time
|
||||||
from comments, users
|
from comments, users
|
||||||
where thread_id = ? and author_id = user_id
|
where thread_id = ? and author_id = user_id
|
||||||
''',
|
''',
|
||||||
(thread,)
|
(thread,)
|
||||||
)
|
)
|
||||||
return title, text, author, author_id, comments
|
return title, text, author, author_id, create_time, modify_time, comments
|
||||||
|
|
||||||
def get_thread_title(self, thread_id):
|
def get_thread_title(self, thread_id):
|
||||||
return self._db().execute('''
|
return self._db().execute('''
|
||||||
@@ -41,6 +66,16 @@ class DB:
|
|||||||
(thread_id,)
|
(thread_id,)
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
|
def get_recent_threads(self, limit):
|
||||||
|
return self._db().execute('''
|
||||||
|
select thread_id, title, modify_date
|
||||||
|
from threads
|
||||||
|
order by modify_date
|
||||||
|
limit ?
|
||||||
|
''',
|
||||||
|
(limit,)
|
||||||
|
)
|
||||||
|
|
||||||
def get_comments(self, thread):
|
def get_comments(self, thread):
|
||||||
return self._db().execute('''
|
return self._db().execute('''
|
||||||
select text
|
select text
|
||||||
@@ -67,7 +102,7 @@ class DB:
|
|||||||
union
|
union
|
||||||
select comment_id from descendant_of, comments where id = parent_id
|
select comment_id from descendant_of, comments where id = parent_id
|
||||||
)
|
)
|
||||||
select id, parent_id, name, text from descendant_of, comments, users
|
select id, parent_id, name, text, create_time, modify_time from descendant_of, comments, users
|
||||||
where id = comment_id and user_id = author_id
|
where id = comment_id and user_id = author_id
|
||||||
''',
|
''',
|
||||||
(comment_id,)
|
(comment_id,)
|
||||||
@@ -155,8 +190,17 @@ class DB:
|
|||||||
''',
|
''',
|
||||||
(thread_id, author_id, text, time, time, thread_id)
|
(thread_id, author_id, text, time, time, thread_id)
|
||||||
)
|
)
|
||||||
|
if c.rowcount > 0:
|
||||||
|
c.execute('''
|
||||||
|
update threads
|
||||||
|
set update_time = ?
|
||||||
|
where threads.thread_id = ?
|
||||||
|
''',
|
||||||
|
(time, thread_id)
|
||||||
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return c.rowcount > 0
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def add_comment_to_comment(self, parent_id, author_id, text, time):
|
def add_comment_to_comment(self, parent_id, author_id, text, time):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
@@ -169,8 +213,21 @@ class DB:
|
|||||||
''',
|
''',
|
||||||
(parent_id, author_id, text, time, time, parent_id)
|
(parent_id, author_id, text, time, time, parent_id)
|
||||||
)
|
)
|
||||||
|
if c.rowcount > 0:
|
||||||
|
c.execute('''
|
||||||
|
update threads
|
||||||
|
set update_time = ?
|
||||||
|
where threads.thread_id = (
|
||||||
|
select c.thread_id
|
||||||
|
from comments c
|
||||||
|
where comment_id = ?
|
||||||
|
)
|
||||||
|
''',
|
||||||
|
(time, parent_id)
|
||||||
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return c.rowcount > 0
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _db(self):
|
def _db(self):
|
||||||
return sqlite3.connect(self.conn)
|
return sqlite3.connect(self.conn)
|
||||||
|
|||||||
61
main.py
61
main.py
@@ -1,8 +1,9 @@
|
|||||||
from flask import Flask, render_template, session, request, redirect, url_for, flash
|
from flask import Flask, render_template, session, request, redirect, url_for, flash, g
|
||||||
from db.sqlite import DB
|
from db.sqlite import DB
|
||||||
import os
|
import os
|
||||||
import passlib.hash
|
import passlib.hash
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
db = DB(os.getenv('DB'))
|
db = DB(os.getenv('DB'))
|
||||||
@@ -30,13 +31,15 @@ def subforum(forum_id):
|
|||||||
@app.route('/thread/<int:thread_id>/')
|
@app.route('/thread/<int:thread_id>/')
|
||||||
def thread(thread_id):
|
def thread(thread_id):
|
||||||
user_id = session.get('user_id')
|
user_id = session.get('user_id')
|
||||||
title, text, author, author_id, comments = db.get_thread(thread_id)
|
title, text, author, author_id, create_time, modify_time, comments = db.get_thread(thread_id)
|
||||||
comments = create_comment_tree(comments)
|
comments = create_comment_tree(comments)
|
||||||
return render_template(
|
return render_template(
|
||||||
'thread.html',
|
'thread.html',
|
||||||
title = title,
|
title = title,
|
||||||
text = text,
|
text = text,
|
||||||
author = author,
|
author = author,
|
||||||
|
create_time = create_time,
|
||||||
|
modify_time = modify_time,
|
||||||
comments = comments,
|
comments = comments,
|
||||||
manage = author_id == user_id,
|
manage = author_id == user_id,
|
||||||
)
|
)
|
||||||
@@ -174,24 +177,72 @@ def add_comment_parent(comment_id):
|
|||||||
|
|
||||||
|
|
||||||
class Comment:
|
class Comment:
|
||||||
def __init__(self, id, author, text):
|
def __init__(self, id, author, text, create_time, modify_time):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.author = author
|
self.author = author
|
||||||
self.text = text
|
self.text = text
|
||||||
self.children = []
|
self.children = []
|
||||||
|
self.create_time = create_time
|
||||||
|
self.modify_time = modify_time
|
||||||
|
|
||||||
def create_comment_tree(comments):
|
def create_comment_tree(comments):
|
||||||
# Collect comments first, then build the tree in case we encounter a child before a parent
|
# Collect comments first, then build the tree in case we encounter a child before a parent
|
||||||
comment_map = {
|
comment_map = {
|
||||||
comment_id: (Comment(comment_id, author, text), parent_id)
|
comment_id: (Comment(comment_id, author, text, create_time, modify_time), parent_id)
|
||||||
for comment_id, parent_id, author, text
|
for comment_id, parent_id, author, text, create_time, modify_time
|
||||||
in comments
|
in comments
|
||||||
}
|
}
|
||||||
root = []
|
root = []
|
||||||
|
# Build tree
|
||||||
for comment, parent_id in comment_map.values():
|
for comment, parent_id in comment_map.values():
|
||||||
parent = comment_map.get(parent_id)
|
parent = comment_map.get(parent_id)
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
parent[0].children.append(comment)
|
parent[0].children.append(comment)
|
||||||
else:
|
else:
|
||||||
root.append(comment)
|
root.append(comment)
|
||||||
|
# Sort each comment based on create time
|
||||||
|
def sort_time(l):
|
||||||
|
l.sort(key=lambda c: c.modify_time, reverse=True)
|
||||||
|
for c in l:
|
||||||
|
sort_time(c.children)
|
||||||
|
sort_time(root)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
@app.context_processor
|
||||||
|
def utility_processor():
|
||||||
|
def format_since(t):
|
||||||
|
n = time.time_ns()
|
||||||
|
if n < t:
|
||||||
|
return 'In a distant future'
|
||||||
|
|
||||||
|
# Try the sane thing first
|
||||||
|
dt = (n - t) // 10 ** 9
|
||||||
|
if dt < 1:
|
||||||
|
return "less than a second ago"
|
||||||
|
if dt < 2:
|
||||||
|
return f"1 second ago"
|
||||||
|
if dt < 60:
|
||||||
|
return f"{dt} seconds ago"
|
||||||
|
if dt < 119:
|
||||||
|
return f"1 minute ago"
|
||||||
|
if dt < 3600:
|
||||||
|
return f"{dt // 60} minutes ago"
|
||||||
|
if dt < 3600 * 2:
|
||||||
|
return f"1 hour ago"
|
||||||
|
if dt < 3600 * 24:
|
||||||
|
return f"{dt // 3600} hours ago"
|
||||||
|
if dt < 3600 * 24 * 31:
|
||||||
|
return f"{dt // (3600 * 24)} days ago"
|
||||||
|
|
||||||
|
# Try some very rough estimate, whatever
|
||||||
|
f = lambda x: datetime.utcfromtimestamp(x // 10 ** 9)
|
||||||
|
n, t = f(n), f(t)
|
||||||
|
def f(x, y, s):
|
||||||
|
return f'{y - x} {s}{"s" if y - x > 1 else ""} ago'
|
||||||
|
if t.year < n.year:
|
||||||
|
return f(t.year, n.year, "year")
|
||||||
|
if t.month < n.month:
|
||||||
|
return f(t.month, n.month, "month")
|
||||||
|
# This shouldn't be reachable, but it's still better to return something
|
||||||
|
return "incredibly long ago"
|
||||||
|
return {'format_since': format_since}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
{% macro author(name, ctime, mtime) %}
|
||||||
|
<p><sub><i>{{ name }} - {{ format_since(ctime) }}{% if ctime != mtime %} (last modified {{ format_since(mtime) }}){% endif %}</i></sub></p>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_comment_pre(comment) %}
|
{% macro render_comment_pre(comment) %}
|
||||||
<div class=comment>
|
<div class=comment>
|
||||||
<p><sub><i>{{ comment.author }}</i></sub></p>
|
{{ author(comment.author, comment.create_time, comment.modify_time) }}
|
||||||
<p>{{ comment.text }}</p>
|
<p>{{ comment.text }}</p>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% from 'comment.html' import render_comment, render_comment_pre, render_comment_post, reply %}
|
{% from 'comment.html' import render_comment, render_comment_pre, render_comment_post, reply with context %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<sup><a href="{{ url_for('thread', thread_id = thread_id) }}">thread</a></sup>
|
<sup><a href="{{ url_for('thread', thread_id = thread_id) }}">thread</a></sup>
|
||||||
|
|||||||
@@ -4,10 +4,22 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Forum</th>
|
<th>Forum</th>
|
||||||
|
<th>Last update</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for id, name, description in subforums %}
|
{% for id, name, description, t_id, t_title, t_mtime in subforums %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ url_for('subforum', forum_id = id) }}"><b>{{ name }}</b> - {{ description }}</a></td>
|
<td>
|
||||||
|
<p><a href="{{ url_for('subforum', forum_id = id) }}"><b>{{ name }}</b></a></p>
|
||||||
|
<p>{{ description }}</p>
|
||||||
|
</td>
|
||||||
|
{% if t_id %}
|
||||||
|
<td>
|
||||||
|
<p><a href="{{ url_for('thread', thread_id = t_id) }}"><b>{{ t_title }}</b></a></p>
|
||||||
|
<p>{{ format_since(t_mtime) }}</p>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td>No threads</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -6,10 +6,18 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Topic</th>
|
<th>Topic</th>
|
||||||
|
<th>Author</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Updated</th>
|
||||||
|
<th>Comments</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for id, title in threads %}
|
{% for id, title, ctime, utime, author_id, author, comment_count in threads %}
|
||||||
<tr>
|
<tr>
|
||||||
<th><a href="{{ url_for('thread', thread_id = id) }}">{{ title }}</a></th>
|
<th><a href="{{ url_for('thread', thread_id = id) }}">{{ title }}</a></th>
|
||||||
|
<td><a href="{{ url_for('user_info', user_id = author_id) }}">{{ author }}</a></td>
|
||||||
|
<td>{{ format_since(ctime) }}</td>
|
||||||
|
<td>{{ format_since(utime) }}</td>
|
||||||
|
<td>{{ comment_count }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% from 'comment.html' import render_comment, reply %}
|
{% from 'comment.html' import render_comment, reply, author as f_author with context %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if manage %}
|
{% if manage %}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<sup><i>{{ author }}</i></sup>
|
<i>{{ f_author(author, create_time, modify_time) }}</i>
|
||||||
<p>{{ text }}</p>
|
<p>{{ text }}</p>
|
||||||
|
|
||||||
{{ reply() }}
|
{{ reply() }}
|
||||||
|
|||||||
Reference in New Issue
Block a user