aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2021-02-17 20:56:57 +0000
committerjwansek <eddie.atten.ea29@gmail.com>2021-02-17 20:56:57 +0000
commitb076881a8e899c67767eb6c9640f1da68f2d3454 (patch)
treedddf53dd12a6b919596556a381650137ae3e1fab
parent58fecf855d151ef0459a43ab9af271a3a4922ad5 (diff)
downloadeda.gay-b076881a8e899c67767eb6c9640f1da68f2d3454.tar.gz
eda.gay-b076881a8e899c67767eb6c9640f1da68f2d3454.zip
finised parser, finished rendering posts, finished sercving images
-rw-r--r--app.py72
-rw-r--r--database.py78
-rw-r--r--parser.py114
-rw-r--r--requirements.txt10
-rw-r--r--services.py2
-rw-r--r--static/images/t30.jpgbin0 -> 235413 bytes
-rw-r--r--static/style.css7
-rw-r--r--templates/template.html16
-rw-r--r--templates/thoughts.html12
9 files changed, 291 insertions, 20 deletions
diff --git a/app.py b/app.py
index e100689..24a6c64 100644
--- a/app.py
+++ b/app.py
@@ -1,8 +1,13 @@
+from PIL import Image
import configparser
import webbrowser
+import datetime
import database
import services
+import parser
import flask
+import os
+import io
app = flask.Flask(__name__)
CONFIG = configparser.ConfigParser()
@@ -11,7 +16,7 @@ CONFIG.read("edaweb.conf")
def get_template_items(title, db):
return {
"links": db.get_header_links(),
- "image": db.get_image("twitterpic"),
+ "image": db.get_pfp_image(),
"title": title,
"articles": db.get_header_articles()
}
@@ -45,14 +50,73 @@ def serve_services():
pihole = services.get_pihole_stats()
)
+@app.route("/thought")
+def get_thought():
+ thought_id = flask.request.args.get("id", type=int)
+ with database.Database() as db:
+ category_name, title, dt, parsed = parser.get_thought_from_id(db, thought_id)
+ return flask.render_template_string(
+ parsed,
+ **get_template_items(title, db),
+ thought = True,
+ dt = "published: " + str(dt),
+ category = category_name,
+ othercategories = db.get_categories_not(category_name)
+ )
+
+@app.route("/thoughts")
+def get_thoughts():
+ with database.Database() as db:
+ all_ = db.get_all_thoughts()
+ tree = {}
+ for id_, title, dt, category in all_:
+ if category not in tree.keys():
+ tree[category] = [(id_, title, dt)]
+ else:
+ tree[category].append((id_, title, str(dt)))
+
+ return flask.render_template(
+ "thoughts.html",
+ **get_template_items("thoughts", db),
+ tree = tree
+ )
+
+@app.route("/img/<filename>")
+def serve_image(filename):
+ imdirpath = os.path.join(".", "static", "images")
+ if filename in os.listdir(imdirpath):
+ try:
+ w = int(flask.request.args['w'])
+ h = int(flask.request.args['h'])
+ except (KeyError, ValueError):
+ return flask.send_from_directory(imdirpath, filename)
+
+ img = Image.open(os.path.join(imdirpath, filename))
+ img.thumbnail((w, h), Image.ANTIALIAS)
+ io_ = io.BytesIO()
+ img.save(io_, format='JPEG')
+ return flask.Response(io_.getvalue(), mimetype='image/jpeg')
+
+
+
+ else:
+ flask.abort(404)
+
+
@app.route("/preview")
def preview():
- import os
if "PREVIEW" in os.environ:
with database.Database() as db:
- return flask.render_template_string(os.environ["PREVIEW"], **get_template_items(os.environ["PREVIEW_TITLE"], db))
+ return flask.render_template_string(
+ os.environ["PREVIEW"],
+ **get_template_items(os.environ["PREVIEW_TITLE"], db),
+ thought = True,
+ dt = "preview rendered: " + str(datetime.datetime.now()),
+ category = os.environ["CATEGORY"],
+ othercategories = db.get_categories_not(os.environ["CATEGORY"])
+ )
else:
- return "page for internal use only"
+ flask.abort(404)
diff --git a/database.py b/database.py
index 93f7538..142d942 100644
--- a/database.py
+++ b/database.py
@@ -1,12 +1,28 @@
+from dataclasses import dataclass
import pymysql
+import random
import app
+@dataclass
class Database:
+ safeLogin:bool = True #automatically login with the user in the config file, who is read only
+ user:str = None #otherwise, login with the given username and passwd
+ passwd:str = None
+
def __enter__(self):
- self.__connection = pymysql.connect(
- **app.CONFIG["mysql"],
- charset = "utf8mb4"
- )
+ if self.safeLogin:
+ self.__connection = pymysql.connect(
+ **app.CONFIG["mysql"],
+ charset = "utf8mb4"
+ )
+ else:
+ self.__connection = pymysql.connect(
+ user = self.user,
+ passwd = self.passwd,
+ host = app.CONFIG["mysql"]["host"],
+ db = app.CONFIG["mysql"]["db"],
+ charset = "utf8mb4"
+ )
return self
def __exit__(self, type, value, traceback):
@@ -22,12 +38,64 @@ class Database:
cursor.execute("SELECT alt, url FROM images WHERE imageName = %s;", (imageName, ))
return cursor.fetchone()
+ def get_pfp_image(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT alt, url FROM images WHERE pfp_img = 1;")
+ return random.choice(cursor.fetchall())
+
def get_header_articles(self):
with self.__connection.cursor() as cursor:
cursor.execute("SELECT articleName, link FROM headerArticles;")
return cursor.fetchall()
+ def get_all_categories(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT category_name FROM categories;")
+ return [i[0] for i in cursor.fetchall()]
+
+ def add_category(self, category):
+ if not category in self.get_all_categories():
+ with self.__connection.cursor() as cursor:
+ cursor.execute("INSERT INTO categories (category_name) VALUES (%s);", (category, ))
+
+ self.__connection.commit()
+ return True
+
+ return False
+
+ def add_thought(self, category, title, markdown):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ INSERT INTO thoughts (category_id, title, markdown_text)
+ VALUES ((
+ SELECT category_id FROM categories WHERE category_name = %s
+ ), %s, %s);""", (category, title, markdown))
+ self.__connection.commit()
+
+ def get_thought(self, id_):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ SELECT categories.category_name, thoughts.title, thoughts.dt, thoughts.markdown_text
+ FROM thoughts INNER JOIN categories
+ ON thoughts.category_id = categories.category_id
+ WHERE thought_id = %s;""", (id_, ))
+ return cursor.fetchone()
+
+ def get_categories_not(self, category_name):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT category_name FROM categories WHERE category_name != %s;", (category_name, ))
+ return [i[0] for i in cursor.fetchall()]
+
+ def get_all_thoughts(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ SELECT thought_id, title, dt, category_name FROM thoughts
+ INNER JOIN categories ON thoughts.category_id = categories.category_id;
+ """)
+ return cursor.fetchall()
+
+
if __name__ == "__main__":
with Database() as db:
- print(db.get_image("headerImage")) \ No newline at end of file
+ print(db.get_all_thoughts()) \ No newline at end of file
diff --git a/parser.py b/parser.py
index 8677342..cce0cb0 100644
--- a/parser.py
+++ b/parser.py
@@ -1,6 +1,8 @@
import argparse
from urllib.parse import urlparse
import webbrowser
+import database
+import getpass
import app
import re
import os
@@ -8,13 +10,22 @@ import os
HEADER_INCREMENTER = 1
IMAGE_TYPES = [".png", ".jpg"]
+def get_thought_from_id(db, id_):
+ category_name, title, dt, markdown = db.get_thought(id_)
+ return category_name, title, dt, parse_text(markdown)
+
def parse_file(path):
with open(path, "r") as f:
unformatted = f.read()
+ return parse_text(unformatted)
+
+def parse_text(unformatted):
formatted = parse_headers(unformatted)
formatted = parse_asteriscs(formatted)
formatted = parse_links(formatted)
+ formatted = parse_code(formatted)
+ formatted = parse_lists(formatted)
formatted = add_linebreaks(formatted)
return '{% extends "template.html" %}\n{% block content %}\n' + formatted + '\n{% endblock %}'
@@ -77,17 +88,71 @@ def parse_links(test_str):
return test_str
+def parse_code(test_str):
+ regex = r"(?<!\\)`\w{1,}?`"
+ # this only matches single words, but escaping is less complicated
+ matches = re.finditer(regex, test_str, re.MULTILINE)
+ offset = 0
+
+ for match in matches:
+ replacement = "<em class=inlineCode style='font-family: monospace;font-style: normal;'>%s</em>" % match.group()[1:-1]
+ test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
+ offset += (len(replacement) - (match.end() - match.start()))
+
+ out = ""
+ inBlock = 0
+ for line in test_str.split("\n"):
+ if line == "```":
+ if inBlock % 2 == 0:
+ out += "<p class=codeBlock style='font-family: monospace;font-style: normal;white-space: pre-wrap;'>\n"
+ else:
+ out += "</p>\n"
+ inBlock += 1
+ else:
+ out += line + "\n"
+
+ return out
+
+def parse_lists(test_str):
+ regex = r"^[1-9][.)] .*$|- .*$"
+ matches = re.finditer(regex, test_str, re.MULTILINE)
+ offset = 0
+ theFirstOne = True
+
+ for match in matches:
+ if theFirstOne:
+ if match.group()[0].isdigit():
+ listType = "ol"
+ cutoff = 3
+ else:
+ listType = "ul"
+ cutoff = 2
+ replacement = "<%s>\n<li>%s</li>" % (listType, match.group()[cutoff:])
+ theFirstOne = False
+ else:
+ if re.match(regex, [i for i in test_str[match.end()+offset:].split("\n") if i != ''][0]) is None:
+ theFirstOne = True
+ replacement = "<li>%s</li>\n</%s>" % (match.group()[cutoff:], listType)
+ else:
+ replacement = "<li>%s</li>" % match.group()[cutoff:]
+ test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
+ offset += (len(replacement) - (match.end() - match.start()))
+
+ return test_str
+
def add_linebreaks(test_str):
return re.sub(r"^$", "<br><br>", test_str, 0, re.MULTILINE)
-def preview_markdown(path, title):
+def preview_markdown(path, title, category):
def startBrowser():
webbrowser.get("firefox").open("http://localhost:5000/preview")
del os.environ["PREVIEW"]
del os.environ["PREVIEW_TITLE"]
+ del os.environ["CATEGORY"]
os.environ["PREVIEW"] = parse_file(path)
os.environ["PREVIEW_TITLE"] = title
+ os.environ["CATEGORY"] = category
import threading
threading.Timer(1.25, startBrowser ).start()
@@ -97,8 +162,13 @@ def preview_markdown(path, title):
def main():
p = argparse.ArgumentParser()
p.add_argument(
- "-m", "--markdown",
+ "markdown",
help = "Path to a markdown file",
+ type = str
+ )
+ p.add_argument(
+ "-u", "--username",
+ help = "Username to use for the database",
required = True,
type = str
)
@@ -109,15 +179,47 @@ def main():
type = str
)
p.add_argument(
+ "-c", "--category",
+ help = "Article category",
+ required = True,
+ type = str
+ )
+ g = p.add_mutually_exclusive_group(required = True)
+ g.add_argument(
"-p", "--preview",
help = "Preview markdown rendering",
action='store_true'
)
+ g.add_argument(
+ "-s", "--save",
+ help = "Save markdown to database",
+ action='store_true'
+ )
+ g.add_argument(
+ "-e", "--echo",
+ help = "Print parsed markdown to stdout",
+ action='store_true'
+ )
args = vars(p.parse_args())
- if args["preview"]:
- preview_markdown(args["markdown"], args["title"])
- else:
- print(parse_file(args["markdown"]))
+
+ passwd = getpass.getpass("Enter password for %s@%s: " % (args["username"], app.CONFIG["mysql"]["host"]))
+
+ with database.Database(
+ safeLogin = False,
+ user = args["username"],
+ passwd = passwd
+ ) as db:
+
+ if args["preview"]:
+ preview_markdown(args["markdown"], args["title"], args["category"])
+ elif args["save"]:
+ if db.add_category(args["category"]):
+ print("Added category...")
+ with open(args["markdown"], "r") as f:
+ db.add_thought(args["category"], args["title"], f.read())
+ print("Added thought...")
+ else:
+ print(parse_file(args["markdown"]))
if __name__ == "__main__":
main() \ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index e86483b..fd89b0f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,10 @@
-python_qbittorrent==0.4.2
PyMySQL==1.0.2
+python_qbittorrent==0.4.2
Flask==1.1.2
-PiHole_api==2.6
-clutch==0.1a2
-docker_py==1.10.6
+PiHole_api
+transmission-clutch
+dataclasses
+docker
pihole==0.1.2
+Pillow==8.1.0
qbittorrent==0.1.6
diff --git a/services.py b/services.py
index b592427..32edb5d 100644
--- a/services.py
+++ b/services.py
@@ -33,7 +33,7 @@ def timeout(func):
# this works but Manager() uses an extra thread than Queue()
manager = multiprocessing.Manager()
returnVan = manager.list()
- ti = time.time()
+ # ti = time.time()
def runFunc(q, func):
q.append(func())
diff --git a/static/images/t30.jpg b/static/images/t30.jpg
new file mode 100644
index 0000000..949d14b
--- /dev/null
+++ b/static/images/t30.jpg
Binary files differ
diff --git a/static/style.css b/static/style.css
index 7bc2190..7a0bf68 100644
--- a/static/style.css
+++ b/static/style.css
@@ -90,6 +90,13 @@ article section table td {
text-align: right;
}
+aside {
+ width: 30%;
+ padding-left: 15px;
+ margin-left: 15px;
+ float: right;
+}
+
.running {
background-color: green;
padding: 1, 1, 1, 1;
diff --git a/templates/template.html b/templates/template.html
index 6b0d894..afbeefe 100644
--- a/templates/template.html
+++ b/templates/template.html
@@ -36,6 +36,22 @@
</a>
</div>
</header>
+ <!-- yes, this is stupid asf, using another template would be better (i cant get that to work) -->
+ {% if thought %}
+ <aside>
+ <h4>{{dt}}</h4>
+ <h5>this category:</h5>
+ <ul>
+ <li><b><a href={{'/thoughts#'+category.replace(' ', '_')}}>{{category}}</a></b></li>
+ </ul>
+ <h5>other categories:</h5>
+ {% for category_name in othercategories %}
+ <ul>
+ <li><a href={{'/thoughts#'+category_name.replace(' ', '_')}}>{{category_name}}</a></li>
+ </ul>
+ {% endfor %}
+ </aside>
+ {% endif %}
<article>
{% block content %}
{% endblock %}
diff --git a/templates/thoughts.html b/templates/thoughts.html
new file mode 100644
index 0000000..8717299
--- /dev/null
+++ b/templates/thoughts.html
@@ -0,0 +1,12 @@
+{% extends "template.html" %}
+{% block content %}
+ {% for category_name, thoughts in tree.items() %}
+ <h2 id={{category_name.replace(' ', '_')}}>{{category_name}}</h2>
+ <dl>
+ {% for id_, title, dt in thoughts %}
+ <dt><a href={{'/thought?id=%i' % id_}}>title</a></dt>
+ <dd>{{dt}}</dd>
+ {% endfor %}
+ </dl>
+ {% endfor %}
+{% endblock %} \ No newline at end of file