summaryrefslogtreecommitdiff
path: root/utab
diff options
context:
space:
mode:
Diffstat (limited to 'utab')
-rw-r--r--utab/__init__.py0
-rw-r--r--utab/__main__.py77
-rw-r--r--utab/const.py4
-rw-r--r--utab/data/config.yml2
-rw-r--r--utab/data/css/index.css32
-rw-r--r--utab/data/index.html11
-rw-r--r--utab/data/sites.csv0
-rw-r--r--utab/rendering.py35
8 files changed, 161 insertions, 0 deletions
diff --git a/utab/__init__.py b/utab/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/utab/__init__.py
diff --git a/utab/__main__.py b/utab/__main__.py
new file mode 100644
index 0000000..5e619ed
--- /dev/null
+++ b/utab/__main__.py
@@ -0,0 +1,77 @@
+from flask import Flask, Response, request, redirect, abort
+from appdirs import user_data_dir
+from pathlib import Path
+import urllib
+import sys
+import yaml
+import csv
+from .rendering import *
+
+app = Flask(__name__)
+
+# locate page template at e.g. $XDG_CONFIG_HOME/utab/index.html
+# and sites json file at utab/sites.json
+data_dir = user_data_dir(appname="utab")
+template_fp = Path(data_dir) / "index.html"
+css_dir = Path(data_dir) / "css"
+try:
+ template = Path.open(template_fp).read()
+except FileNotFoundError:
+ print("Template file not found.")
+ sys.exit(1)
+
+sites_fp = Path(data_dir) / "sites.csv"
+config_fp = Path(data_dir) / "config.yml"
+with open(config_fp) as f:
+ config = yaml.load(f.read())
+ f.close()
+
+
+def read_sites():
+ with open(sites_fp) as f:
+ sites = list(csv.reader(f))
+ f.close()
+ return sites
+
+
+@app.route("/")
+def index():
+ return render_page(
+ template,
+ sites=render_sites(
+ read_sites(), columns=config["columns"], rows=config["rows"]
+ ),
+ )
+
+
+@app.route("/go/<path:url>")
+def visit_site(url):
+ print(url)
+ url_unesc = urllib.parse.unquote(url) # unescaped url
+ print(url_unesc)
+ sites = read_sites()
+ for i, s in enumerate(sites):
+ if s[0] == url_unesc:
+ sites[i][VISITS] = str(int(sites[i][VISITS]) + 1)
+ with open(sites_fp, "w") as f:
+ # update visits
+ csv.writer(f).writerows(sites)
+ f.close()
+ return redirect(url_unesc, 302)
+
+
+@app.route("/css/<string:filename>")
+def serve_css(filename):
+ try:
+ with open(css_dir / filename) as f:
+ resp = Response(f.read(), 200, {"Content-Type": "text/css"})
+ f.close()
+ return resp
+ except FileNotFoundError:
+ return abort(404)
+ except:
+ return abort(500)
+
+
+# run on localhost only
+app.run("127.0.0.1", 64366)
diff --git a/utab/const.py b/utab/const.py
new file mode 100644
index 0000000..181f49a
--- /dev/null
+++ b/utab/const.py
@@ -0,0 +1,4 @@
+URL = 0
+TITLE = 1
+FAVICON = 2
+VISITS = 3
diff --git a/utab/data/config.yml b/utab/data/config.yml
new file mode 100644
index 0000000..824eb6c
--- /dev/null
+++ b/utab/data/config.yml
@@ -0,0 +1,2 @@
+columns: 8
+rows: 4
diff --git a/utab/data/css/index.css b/utab/data/css/index.css
new file mode 100644
index 0000000..5f03851
--- /dev/null
+++ b/utab/data/css/index.css
@@ -0,0 +1,32 @@
+body {
+ text-align: center;
+ background-color: #222;
+ color: white;
+ font-family: sans-serif;
+}
+
+.sites-grid {
+ position: relative;
+ left: 10%;
+ max-width: 80%;
+ max-height: 80%;
+}
+
+.sites-item {
+ border: #888 2px solid;
+ display: inline-block;
+ margin: 20px;
+ width: 80px;
+ height: 80px;
+}
+
+.sites-item:hover {
+ border: #ccc 2px solid;
+}
+
+.site-favicon {
+ width: 64px;
+ height: 64px;
+ position: relative;
+ top: 8px;
+}
diff --git a/utab/data/index.html b/utab/data/index.html
new file mode 100644
index 0000000..bc6b97b
--- /dev/null
+++ b/utab/data/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<head>
+ <title>utab</title>
+</head>
+<body>
+ <h2>Top Sites</h2>
+ <div id="sites">
+ %sites%
+ </div>
+ <link rel="stylesheet" type="text/css" href="/css/index.css" />
+</body>
diff --git a/utab/data/sites.csv b/utab/data/sites.csv
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/utab/data/sites.csv
diff --git a/utab/rendering.py b/utab/rendering.py
new file mode 100644
index 0000000..731524c
--- /dev/null
+++ b/utab/rendering.py
@@ -0,0 +1,35 @@
+import urllib
+from .const import *
+
+# dead simple template engine
+def render_page(template: str, **kwargs):
+ page = template
+ for k, v in kwargs.items():
+ page = page.replace(f"%{str(k)}%", str(v))
+ return page
+
+
+def render_sites(sites: list, columns=8, rows=4):
+ top_sites = sorted(sites, key=lambda s: int(s[VISITS]), reverse=True)[
+ : (columns * rows) # top col*row sites, default=32
+ ]
+ # site_rows: group sites into rows
+ if len(top_sites) < 32:
+ top_sites.extend([None] * (32 - len(top_sites)))
+
+ site_rows = list(zip(*[top_sites[n::columns] for n in range(columns)]))
+ html = '<div class="sites-grid">'
+ for row in site_rows:
+ html += '<div class="sites-row">'
+ for col in row:
+ if col is not None:
+ html += (
+ '<div class="sites-item">'
+ f'<a class="site" href="/go/{urllib.parse.quote(col[URL], safe="")}">'
+ f'<img class="site-favicon" src="{col[FAVICON]}" /></a>'
+ + f"<p>{col[TITLE]}</p>"
+ + "</div>"
+ )
+ html += "</div>"
+ html += "</div>"
+ return html