annotate laterlinks3/laterlinks_flask_app.py @ 128:a26c4388749b

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