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")