paulo@117: import csv paulo@117: import datetime paulo@118: import io paulo@117: import os paulo@118: import tempfile paulo@117: paulo@117: import flask paulo@118: import google.cloud.storage paulo@117: from html3.html3 import HTML paulo@117: paulo@117: app = flask.Flask(__name__) paulo@117: paulo@118: GCS_BUCKET = google.cloud.storage.Client().get_bucket(os.environ.get("GCS_BUCKET")) paulo@118: PIN = os.environ.get("LLPIN") paulo@117: STRTIME_FMT = "%Y-%m-%d %H:%M:%S" paulo@117: paulo@117: paulo@117: class LLDialect(csv.Dialect): paulo@117: delimiter = '\t' paulo@117: quoting = csv.QUOTE_NONE paulo@117: lineterminator = '\n' paulo@117: paulo@117: paulo@117: LLDIALECT = LLDialect() paulo@117: LLDB_FN = "lldb.tsv" paulo@117: LLDB_UNREAD_FN = "lldb_unread.tsv" paulo@117: paulo@117: paulo@117: class LLError(Exception): paulo@117: pass paulo@117: paulo@117: class PinFailError(LLError): paulo@117: def __str__(self): paulo@117: return "PIN FAIL!" paulo@117: paulo@117: class PinSetupError(LLError): paulo@117: def __str__(self): paulo@117: return "PIN SETUP ERROR!" paulo@117: paulo@117: class MissingFieldsError(LLError): paulo@117: def __str__(self): paulo@117: return "MISSING FIELD(s)!" paulo@117: paulo@117: paulo@118: def gcs_download(fn): paulo@118: tmp_f = tempfile.TemporaryFile("w+") paulo@118: blob = GCS_BUCKET.get_blob(fn) paulo@118: if blob: paulo@118: tmp_f.write(str(blob.download_as_string(), encoding="utf-8")) paulo@118: return tmp_f paulo@117: paulo@117: paulo@118: def gcs_upload(fn, tmp_f): paulo@118: blob = GCS_BUCKET.blob(fn) paulo@121: tmp_f.seek(0) paulo@121: blob.upload_from_string(tmp_f.read()) paulo@118: paulo@118: paulo@118: def lldb_unread_load(lldb_unread_tmp_f): paulo@118: lldb_unread_tmp_f.seek(0) paulo@118: return csv.reader(lldb_unread_tmp_f, LLDIALECT) paulo@118: paulo@118: paulo@118: def lldb_add(inp, lldb_tmp_f, lldb_unread_tmp_f): paulo@117: title = inp.get("title") paulo@117: url = inp.get("url") paulo@117: if not (title and url): paulo@117: raise MissingFieldsError() paulo@117: paulo@117: dt_str = datetime.datetime.now().strftime(STRTIME_FMT) paulo@117: paulo@118: lldb_tmp_f.seek(0, io.SEEK_END) paulo@118: csv.writer(lldb_tmp_f, LLDIALECT).writerow([title, url, dt_str]) paulo@118: gcs_upload(LLDB_FN, lldb_tmp_f) paulo@117: paulo@118: lldb_unread_tmp_f.seek(0, io.SEEK_END) paulo@118: csv.writer(lldb_unread_tmp_f, LLDIALECT).writerow([title, url, dt_str]) paulo@118: gcs_upload(LLDB_UNREAD_FN, lldb_unread_tmp_f) paulo@118: paulo@118: paulo@118: def lldb_unread_delete(inp, lldb_unread_tmp_f): paulo@117: delete = inp.getlist("delete") paulo@117: if not delete: paulo@117: raise MissingFieldsError() paulo@117: paulo@118: lldb_unread = [i for i in lldb_unread_load(lldb_unread_tmp_f)] paulo@117: paulo@117: try: paulo@117: for i in delete: paulo@117: for j in lldb_unread: paulo@117: dt_str = j[2] paulo@117: if i == dt_str: paulo@117: lldb_unread.remove(j) paulo@117: finally: paulo@118: lldb_unread_tmp_f.truncate(0) paulo@118: lldb_unread_tmp_f.seek(0) paulo@118: csv.writer(lldb_unread_tmp_f, LLDIALECT).writerows(lldb_unread) paulo@118: gcs_upload(LLDB_UNREAD_FN, lldb_unread_tmp_f) paulo@117: paulo@117: paulo@117: @app.route("/", methods=["GET", "POST"]) paulo@117: def index(): paulo@117: is_post = (flask.request.method == "POST") paulo@117: inp = flask.request.form paulo@117: cookies = flask.request.cookies paulo@117: paulo@118: with gcs_download(LLDB_UNREAD_FN) as lldb_unread_tmp_f: paulo@118: if is_post: paulo@118: if not PIN: paulo@118: raise PinSetupError paulo@118: elif cookies.get("llpin") != PIN: paulo@118: raise PinFailError paulo@117: paulo@118: if inp["submit"] == "Add": paulo@118: with gcs_download(LLDB_FN) as lldb_tmp_f: paulo@118: lldb_add(inp, lldb_tmp_f, lldb_unread_tmp_f) paulo@118: elif inp["submit"] == "Delete": paulo@118: lldb_unread_delete(inp, lldb_unread_tmp_f) paulo@117: paulo@118: title = "later links..." paulo@118: root = HTML("html") paulo@117: paulo@118: header = root.head paulo@118: header.link(rel="stylesheet", type="text/css", href=flask.url_for("static", filename="index.css")) paulo@118: header.title(title) paulo@118: paulo@118: body = root.body paulo@118: body.h1(title) paulo@117: paulo@118: form = body.form(action="/", method="post") paulo@117: paulo@118: table = form.table paulo@118: hrow = table.tr paulo@118: hrow.th("Link") paulo@118: hrow.th("Created") paulo@118: hrow.th.input(type="submit", name="submit", value="Delete") paulo@117: paulo@118: for (title, url, dt_str) in lldb_unread_load(lldb_unread_tmp_f): paulo@118: row = table.tr paulo@118: row.td.a(title, href=url) paulo@118: row.td(dt_str) paulo@118: row.td.input(type="checkbox", name="delete", value=dt_str) paulo@117: paulo@118: p1 = form.p paulo@118: p1.label("Title").input(type="text", name="title", size="64") paulo@118: p1.br paulo@118: p1.label("URL").input(type="text", name="url", size="64") paulo@118: p1.br paulo@118: p1.input(type="submit", name="submit", value="Add") paulo@117: paulo@118: return str(root).encode("utf-8")