commit bc8dac4e11af953ec5a7ec6056e2dffc7d81c1c4 Author: Francesco Luzzana Date: Thu Jun 2 01:07:13 2022 +0200 init and backend setup diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66bca12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +venv/ diff --git a/pad-bis.py b/pad-bis.py new file mode 100644 index 0000000..db3e199 --- /dev/null +++ b/pad-bis.py @@ -0,0 +1,132 @@ +import logging +logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) + + +# Flask application to serve the web pages +from flask import Flask, render_template, request, redirect, url_for + +# Mediawiki client to interact with the Wiki +import mwclient + +# BS to read the html table from the wiki +from bs4 import BeautifulSoup + + +# os and dotenv to store the mediawiki credentials in a safe place +import os +from dotenv import load_dotenv +from pathlib import Path + +# load the mediawiki credentials from the shared folder +dotenv_path = Path("/var/www/.mw-credentials") +load_dotenv(dotenv_path=dotenv_path) + + + + +# prefix to add /soupboat/padliography to all the routes +# and to leave the @app.route() decorator more clean + +class PrefixMiddleware(object): + def __init__(self, app, prefix=""): + self.app = app + self.prefix = prefix + + def __call__(self, environ, start_response): + + if environ["PATH_INFO"].startswith(self.prefix): + environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :] + environ["SCRIPT_NAME"] = self.prefix + return self.app(environ, start_response) + else: + start_response("404", [("Content-Type", "text/plain")]) + return ["This url does not belong to the app.".encode()] + + +# create flask application +app = Flask(__name__) + + +# Url prefix for the soupboat +base_url = "/soupboat/padliography" + +# Page of the wiki with the pads +padliography = 'Padliography2' + +# register the middleware to prefix all the requests with our base_url +app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix=base_url) + + +def add_pad(link, title, overview, categories, date): + + site = mwclient.Site('pzwiki.wdka.nl', path='/mw-mediadesign/') + + site.login( + username=os.environ.get('MW_BOT'), + password=os.environ.get('MW_KEY') + ) + + page = site.pages[padliography] + text = page.text() + + + new_row = f'|-\n| {link} || {title} || {overview} || {categories} || {date}\n|-\n' + '|}' + + text = text.replace('|}', new_row) + + page.edit(text, f'New pad in the {padliography}: {title}') + + +def get_pads(): + + site = mwclient.Site('pzwiki.wdka.nl', path='/mw-mediadesign/') + + site.login( + username=os.environ.get('MW_BOT'), + password=os.environ.get('MW_KEY') + ) + + html = site.api('parse', prop='text', page=padliography) + pads = BeautifulSoup(html['parse']['text']['*']).find("table", attrs={"class":"padliography"}) + + return pads + + + + +@app.route('/') +def home(): + + pads = get_pads() + + + + return render_template('index.html', pads=pads) + + +@app.route('/add') +def add(): + + link = request.args.get('link', None) + title = request.args.get('title', None) + overview = request.args.get('overview', '') + categories = request.args.get('categories', '') + date = request.args.get('date', None) + + if None not in (link, title, date): + add_pad(link, title, overview, categories, date) + redirect(url_for('home')) + + return render_template('index.html') + + + + + + + + + + + +app.run(port=3146) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7bb9203 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,22 @@ +beautifulsoup4==4.11.1 +certifi==2022.5.18.1 +charset-normalizer==2.0.12 +click==8.1.3 +colorama==0.4.4 +Flask==2.1.2 +idna==3.3 +itsdangerous==2.1.2 +Jinja2==3.1.2 +MarkupSafe==2.1.1 +mwclient==0.10.1 +numpy==1.22.4 +oauthlib==3.2.0 +python-dateutil==2.8.2 +python-dotenv==0.20.0 +pytz==2022.1 +requests==2.27.1 +requests-oauthlib==1.3.1 +six==1.16.0 +soupsieve==2.3.2.post1 +urllib3==1.26.9 +Werkzeug==2.1.2 diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..50c6be5 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,124 @@ +body { + font-size: 1.5rem; + margin: 0; + background: rgb(255, 244, 235); +} + +.contents { + margin: 32px; +} + +h1 { + color: #ecab72; + margin: 32px; + text-align: center; +} + +a { + color: currentColor; +} + +a.stretched-link::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; +} + +/* FILTERS */ + +.tag { + background: none; + font-size: 20px; + display: inline-block; + border: 1px solid currentColor; + padding: 0 0.5em; + border-radius: 1em; + margin: 4px; + text-transform: capitalize; + user-select: none; + cursor: pointer; + + transform: translateY(0); + transition: transform 0.4s ease-in; +} + +.tag.active { + background-color: white; +} + +.tag.active.all { + background: none; +} + +.filters .tag:focus, +.filters .tag:hover { + transform: translateY(-4px); + transition: transform 0.2s ease-out; +} + +ul { + padding: 0; +} + +/* TABLE */ + +table { + border-collapse: collapse; +} + +tr { + /* display: none; */ + position: relative; +} + +tr:nth-child(even) { + background-color: rgb(251, 237, 225); +} + +tr.active { + display: table-row; +} + +tr:hover { + background-color: rgb(250, 232, 217); +} + +tr .tag { + cursor: default; +} + +tr.header { + display: table-row; + text-align: left; +} + +.categories { + width: 20%; +} + +.overview { + font-style: italic; + width: 40%; +} + +.date { + width: 14ch; +} + +form { + margin: 32px; + display: inline-block; + padding: 2rem; + background-color: rgb(250, 232, 217); + border-radius: 50%; +} + +form h2 { + margin: 0; + margin-bottom: 16px; + font-size: 28px; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..14f10a2 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,28 @@ + + + + + + + + Padliography II + + +
+

Add a new pad

+ + + + + + + + + + + +
+ + {% if pads %} {{pads|safe}} {%endif%} + +