Mercurial > hg > index.fcgi > www > www-1
comparison 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 |
comparison
equal
deleted
inserted
replaced
0:a4f973bf5d09 | 1:3ca39376ee6c |
---|---|
1 import csv | 1 import csv |
2 import datetime | 2 import datetime |
3 import io | |
3 import os | 4 import os |
5 import tempfile | |
4 | 6 |
5 import flask | 7 import flask |
6 | 8 import google.cloud.storage |
7 from html3.html3 import HTML | 9 from html3.html3 import HTML |
8 | 10 |
9 app = flask.Flask(__name__) | 11 app = flask.Flask(__name__) |
10 | 12 |
11 PIN = os.environ.get('LLPIN') | 13 GCS_BUCKET = google.cloud.storage.Client().get_bucket(os.environ.get("GCS_BUCKET")) |
14 PIN = os.environ.get("LLPIN") | |
12 STRTIME_FMT = "%Y-%m-%d %H:%M:%S" | 15 STRTIME_FMT = "%Y-%m-%d %H:%M:%S" |
13 | 16 |
14 | 17 |
15 class LLDialect(csv.Dialect): | 18 class LLDialect(csv.Dialect): |
16 delimiter = '\t' | 19 delimiter = '\t' |
37 class MissingFieldsError(LLError): | 40 class MissingFieldsError(LLError): |
38 def __str__(self): | 41 def __str__(self): |
39 return "MISSING FIELD(s)!" | 42 return "MISSING FIELD(s)!" |
40 | 43 |
41 | 44 |
42 def lldb_unread_load(): | 45 def gcs_download(fn): |
43 return csv.reader(open(LLDB_UNREAD_FN), LLDIALECT) | 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 | |
44 | 51 |
45 | 52 |
46 def lldb_add(inp): | 53 def gcs_upload(fn, tmp_f): |
54 blob = GCS_BUCKET.blob(fn) | |
55 blob.upload_from_file(tmp_f, rewind=True) | |
56 | |
57 | |
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) | |
61 | |
62 | |
63 def lldb_add(inp, lldb_tmp_f, lldb_unread_tmp_f): | |
47 title = inp.get("title") | 64 title = inp.get("title") |
48 url = inp.get("url") | 65 url = inp.get("url") |
49 if not (title and url): | 66 if not (title and url): |
50 raise MissingFieldsError() | 67 raise MissingFieldsError() |
51 | 68 |
52 dt_str = datetime.datetime.now().strftime(STRTIME_FMT) | 69 dt_str = datetime.datetime.now().strftime(STRTIME_FMT) |
53 with open(LLDB_FN, 'a') as lldb_f: | 70 |
54 csv.writer(lldb_f, LLDIALECT).writerow([title, url, dt_str]) | 71 lldb_tmp_f.seek(0, io.SEEK_END) |
55 with open(LLDB_UNREAD_FN, 'a') as lldb_f: | 72 csv.writer(lldb_tmp_f, LLDIALECT).writerow([title, url, dt_str]) |
56 csv.writer(lldb_f, LLDIALECT).writerow([title, url, dt_str]) | 73 gcs_upload(LLDB_FN, lldb_tmp_f) |
74 | |
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) | |
57 | 78 |
58 | 79 |
59 def lldb_unread_delete(inp): | 80 def lldb_unread_delete(inp, lldb_unread_tmp_f): |
60 delete = inp.getlist("delete") | 81 delete = inp.getlist("delete") |
61 if not delete: | 82 if not delete: |
62 raise MissingFieldsError() | 83 raise MissingFieldsError() |
63 | 84 |
64 lldb_unread = [i for i in lldb_unread_load()] | 85 lldb_unread = [i for i in lldb_unread_load(lldb_unread_tmp_f)] |
65 lldb_unread_f = open(LLDB_UNREAD_FN, 'w') | |
66 | 86 |
67 try: | 87 try: |
68 for i in delete: | 88 for i in delete: |
69 for j in lldb_unread: | 89 for j in lldb_unread: |
70 dt_str = j[2] | 90 dt_str = j[2] |
71 if i == dt_str: | 91 if i == dt_str: |
72 lldb_unread.remove(j) | 92 lldb_unread.remove(j) |
73 finally: | 93 finally: |
74 csv.writer(lldb_unread_f, LLDIALECT).writerows(lldb_unread) | 94 lldb_unread_tmp_f.truncate(0) |
75 lldb_unread_f.close() | 95 lldb_unread_tmp_f.seek(0) |
76 | 96 csv.writer(lldb_unread_tmp_f, LLDIALECT).writerows(lldb_unread) |
97 gcs_upload(LLDB_UNREAD_FN, lldb_unread_tmp_f) | |
77 | 98 |
78 | 99 |
79 @app.route("/", methods=["GET", "POST"]) | 100 @app.route("/", methods=["GET", "POST"]) |
80 def index(): | 101 def index(): |
81 is_post = (flask.request.method == "POST") | 102 is_post = (flask.request.method == "POST") |
82 inp = flask.request.form | 103 inp = flask.request.form |
83 cookies = flask.request.cookies | 104 cookies = flask.request.cookies |
84 | 105 |
85 if is_post: | 106 with gcs_download(LLDB_UNREAD_FN) as lldb_unread_tmp_f: |
86 if not PIN: | 107 if is_post: |
87 raise PinSetupError | 108 if not PIN: |
88 elif cookies.get("llpin") != PIN: | 109 raise PinSetupError |
89 raise PinFailError | 110 elif cookies.get("llpin") != PIN: |
111 raise PinFailError | |
90 | 112 |
91 if inp["submit"] == "Add": | 113 if inp["submit"] == "Add": |
92 lldb_add(inp) | 114 with gcs_download(LLDB_FN) as lldb_tmp_f: |
93 elif inp["submit"] == "Delete": | 115 lldb_add(inp, lldb_tmp_f, lldb_unread_tmp_f) |
94 lldb_unread_delete(inp) | 116 elif inp["submit"] == "Delete": |
117 lldb_unread_delete(inp, lldb_unread_tmp_f) | |
95 | 118 |
96 title = "later links..." | 119 title = "later links..." |
97 root = HTML("html") | 120 root = HTML("html") |
98 | 121 |
99 header = root.head | 122 header = root.head |
100 header.link(rel="stylesheet", type="text/css", href=flask.url_for("static", filename="index.css")) | 123 header.link(rel="stylesheet", type="text/css", href=flask.url_for("static", filename="index.css")) |
101 header.title(title) | 124 header.title(title) |
102 | 125 |
103 body = root.body | 126 body = root.body |
104 body.h1(title) | 127 body.h1(title) |
105 | 128 |
106 form = body.form(action="/", method="post") | 129 form = body.form(action="/", method="post") |
107 | 130 |
108 table = form.table | 131 table = form.table |
109 hrow = table.tr | 132 hrow = table.tr |
110 hrow.th("Link") | 133 hrow.th("Link") |
111 hrow.th("Created") | 134 hrow.th("Created") |
112 hrow.th.input(type="submit", name="submit", value="Delete") | 135 hrow.th.input(type="submit", name="submit", value="Delete") |
113 | 136 |
114 for (title, url, dt_str) in lldb_unread_load(): | 137 for (title, url, dt_str) in lldb_unread_load(lldb_unread_tmp_f): |
115 row = table.tr | 138 row = table.tr |
116 row.td.a(title, href=url) | 139 row.td.a(title, href=url) |
117 row.td(dt_str) | 140 row.td(dt_str) |
118 row.td.input(type="checkbox", name="delete", value=dt_str) | 141 row.td.input(type="checkbox", name="delete", value=dt_str) |
119 | 142 |
120 p1 = form.p | 143 p1 = form.p |
121 p1.label("Title").input(type="text", name="title", size="64") | 144 p1.label("Title").input(type="text", name="title", size="64") |
122 p1.br | 145 p1.br |
123 p1.label("URL").input(type="text", name="url", size="64") | 146 p1.label("URL").input(type="text", name="url", size="64") |
124 p1.br | 147 p1.br |
125 p1.input(type="submit", name="submit", value="Add") | 148 p1.input(type="submit", name="submit", value="Add") |
126 | 149 |
127 return str(root).encode("utf-8") | 150 return str(root).encode("utf-8") |