Mercurial > hg > index.fcgi > www > www-1
view laterlinks3/laterlinks_flask_app.py @ 118:65db090a697e
laterlinks3: add GCS support
author | paulo |
---|---|
date | Fri, 18 Sep 2020 01:13:29 -0700 |
parents | 476cb019ad9f |
children | 26cc1a16e7a3 |
line source
1 import csv
2 import datetime
3 import io
4 import os
5 import tempfile
7 import flask
8 import google.cloud.storage
9 from html3.html3 import HTML
11 app = flask.Flask(__name__)
13 GCS_BUCKET = google.cloud.storage.Client().get_bucket(os.environ.get("GCS_BUCKET"))
14 PIN = os.environ.get("LLPIN")
15 STRTIME_FMT = "%Y-%m-%d %H:%M:%S"
18 class LLDialect(csv.Dialect):
19 delimiter = '\t'
20 quoting = csv.QUOTE_NONE
21 lineterminator = '\n'
24 LLDIALECT = LLDialect()
25 LLDB_FN = "lldb.tsv"
26 LLDB_UNREAD_FN = "lldb_unread.tsv"
29 class LLError(Exception):
30 pass
32 class PinFailError(LLError):
33 def __str__(self):
34 return "PIN FAIL!"
36 class PinSetupError(LLError):
37 def __str__(self):
38 return "PIN SETUP ERROR!"
40 class MissingFieldsError(LLError):
41 def __str__(self):
42 return "MISSING FIELD(s)!"
45 def gcs_download(fn):
46 tmp_f = tempfile.TemporaryFile("w+")
47 blob = GCS_BUCKET.get_blob(fn)
48 if blob:
49 tmp_f.write(str(blob.download_as_string(), encoding="utf-8"))
50 return tmp_f
53 def gcs_upload(fn, tmp_f):
54 blob = GCS_BUCKET.blob(fn)
55 blob.upload_from_file(tmp_f, rewind=True)
58 def lldb_unread_load(lldb_unread_tmp_f):
59 lldb_unread_tmp_f.seek(0)
60 return csv.reader(lldb_unread_tmp_f, LLDIALECT)
63 def lldb_add(inp, lldb_tmp_f, lldb_unread_tmp_f):
64 title = inp.get("title")
65 url = inp.get("url")
66 if not (title and url):
67 raise MissingFieldsError()
69 dt_str = datetime.datetime.now().strftime(STRTIME_FMT)
71 lldb_tmp_f.seek(0, io.SEEK_END)
72 csv.writer(lldb_tmp_f, LLDIALECT).writerow([title, url, dt_str])
73 gcs_upload(LLDB_FN, lldb_tmp_f)
75 lldb_unread_tmp_f.seek(0, io.SEEK_END)
76 csv.writer(lldb_unread_tmp_f, LLDIALECT).writerow([title, url, dt_str])
77 gcs_upload(LLDB_UNREAD_FN, lldb_unread_tmp_f)
80 def lldb_unread_delete(inp, lldb_unread_tmp_f):
81 delete = inp.getlist("delete")
82 if not delete:
83 raise MissingFieldsError()
85 lldb_unread = [i for i in lldb_unread_load(lldb_unread_tmp_f)]
87 try:
88 for i in delete:
89 for j in lldb_unread:
90 dt_str = j[2]
91 if i == dt_str:
92 lldb_unread.remove(j)
93 finally:
94 lldb_unread_tmp_f.truncate(0)
95 lldb_unread_tmp_f.seek(0)
96 csv.writer(lldb_unread_tmp_f, LLDIALECT).writerows(lldb_unread)
97 gcs_upload(LLDB_UNREAD_FN, lldb_unread_tmp_f)
100 @app.route("/", methods=["GET", "POST"])
101 def index():
102 is_post = (flask.request.method == "POST")
103 inp = flask.request.form
104 cookies = flask.request.cookies
106 with gcs_download(LLDB_UNREAD_FN) as lldb_unread_tmp_f:
107 if is_post:
108 if not PIN:
109 raise PinSetupError
110 elif cookies.get("llpin") != PIN:
111 raise PinFailError
113 if inp["submit"] == "Add":
114 with gcs_download(LLDB_FN) as lldb_tmp_f:
115 lldb_add(inp, lldb_tmp_f, lldb_unread_tmp_f)
116 elif inp["submit"] == "Delete":
117 lldb_unread_delete(inp, lldb_unread_tmp_f)
119 title = "later links..."
120 root = HTML("html")
122 header = root.head
123 header.link(rel="stylesheet", type="text/css", href=flask.url_for("static", filename="index.css"))
124 header.title(title)
126 body = root.body
127 body.h1(title)
129 form = body.form(action="/", method="post")
131 table = form.table
132 hrow = table.tr
133 hrow.th("Link")
134 hrow.th("Created")
135 hrow.th.input(type="submit", name="submit", value="Delete")
137 for (title, url, dt_str) in lldb_unread_load(lldb_unread_tmp_f):
138 row = table.tr
139 row.td.a(title, href=url)
140 row.td(dt_str)
141 row.td.input(type="checkbox", name="delete", value=dt_str)
143 p1 = form.p
144 p1.label("Title").input(type="text", name="title", size="64")
145 p1.br
146 p1.label("URL").input(type="text", name="url", size="64")
147 p1.br
148 p1.input(type="submit", name="submit", value="Add")
150 return str(root).encode("utf-8")