init and backend setup
commit
bc8dac4e11
@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
venv/
|
@ -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)
|
@ -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
|
@ -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;
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}" />
|
||||||
|
<title>Padliography II</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="add">
|
||||||
|
<h2>Add a new pad</h2>
|
||||||
|
<label for="link">link</label>
|
||||||
|
<input type="text" name="link" />
|
||||||
|
<label for="title">title</label>
|
||||||
|
<input type="text" name="title" />
|
||||||
|
<label for="overview">overview</label>
|
||||||
|
<input type="text" name="overview" />
|
||||||
|
<label for="categories">categories</label>
|
||||||
|
<input type="text" name="categories" />
|
||||||
|
<label for="date">date</label>
|
||||||
|
<input type="date" name="date" />
|
||||||
|
<input type="submit" value="Add" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if pads %} {{pads|safe}} {%endif%}
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue