Compare commits
11 Commits
d90da792b4
...
6cae30eab4
Author | SHA1 | Date |
---|---|---|
Julien Palard | 6cae30eab4 | |
mscherer | e905f05880 | |
Julien Palard | 4c2db3dbbf | |
Julien Palard | 7ad22828f4 | |
Julien Palard | 577131fc3b | |
Mindiell | c80e88a4dd | |
Mindiell | 40dcf336c5 | |
Mindiell | 699aa7b71d | |
Mindiell | e8c7cb313e | |
Mindiell | d115cb92b7 | |
Mindiell | f7704b65b4 |
|
@ -1,2 +1,3 @@
|
||||||
.venv/
|
.venv/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
config-*.py
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# AfpyLogs
|
# AfpyLogs
|
||||||
|
|
||||||
Web view of IRC logs from #afpy channel on Freenode.
|
Web view of IRC logs from #afpy channel on [Libera](https://libera.chat/).
|
||||||
|
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
36
app.py
36
app.py
|
@ -3,24 +3,33 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from bleach import Cleaner
|
||||||
|
from bleach.linkifier import LinkifyFilter
|
||||||
from flask import Flask, g, redirect, render_template, url_for
|
from flask import Flask, g, redirect, render_template, url_for
|
||||||
|
|
||||||
|
|
||||||
application = Flask(__name__, template_folder=".")
|
application = Flask(__name__, template_folder=".")
|
||||||
application.config.from_object("config")
|
application.config.from_object("config")
|
||||||
|
try:
|
||||||
|
application.config.from_object(f"config-{application.config['ENV']}")
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
"Starting without specific configuration"
|
||||||
|
f"file config-{application.config['ENV']}.py"
|
||||||
|
)
|
||||||
application.jinja_env.trim_blocks = application.config["JINJA_ENV"]["TRIM_BLOCKS"]
|
application.jinja_env.trim_blocks = application.config["JINJA_ENV"]["TRIM_BLOCKS"]
|
||||||
application.jinja_env.lstrip_blocks = application.config["JINJA_ENV"]["LSTRIP_BLOCKS"]
|
application.jinja_env.lstrip_blocks = application.config["JINJA_ENV"]["LSTRIP_BLOCKS"]
|
||||||
|
|
||||||
LOG_PATTERN = re.compile(application.config["LOG_PATTERN"])
|
LOG_PATTERN = re.compile(application.config["LOG_PATTERN"])
|
||||||
LINK_PATTERN = re.compile(application.config["LINK_PATTERN"])
|
|
||||||
BOLD_PATTERN = re.compile(application.config["BOLD_PATTERN"])
|
BOLD_PATTERN = re.compile(application.config["BOLD_PATTERN"])
|
||||||
|
|
||||||
|
|
||||||
def get_archives():
|
def get_archives():
|
||||||
archives = []
|
archives = []
|
||||||
dates = {"years": [], "months": {}, "days": {}}
|
dates = {"years": [], "months": {}, "days": {}}
|
||||||
for filename in sorted(os.listdir(application.config["LOG_PATH"])):
|
for filename in sorted(Path(application.config["LOG_PATH"]).glob("log-*-*-*.txt")):
|
||||||
date = filename[:-4].split("-")[1:]
|
date = filename.name[:-4].split("-")[1:]
|
||||||
archives.append(date)
|
archives.append(date)
|
||||||
if date[0] not in dates["years"]:
|
if date[0] not in dates["years"]:
|
||||||
dates["years"].append(date[0])
|
dates["years"].append(date[0])
|
||||||
|
@ -41,36 +50,35 @@ def archives(year=None, month=None, day=None):
|
||||||
# Récupération des fichiers disponibles
|
# Récupération des fichiers disponibles
|
||||||
archives, g.dates = get_archives()
|
archives, g.dates = get_archives()
|
||||||
# Récupération de la date souhaitée
|
# Récupération de la date souhaitée
|
||||||
if year is None or month is None or day is None:
|
if (
|
||||||
# Si date mal ou non fournie, on prend la dernière existante
|
year is None
|
||||||
|
or month is None
|
||||||
|
or day is None
|
||||||
|
or [year, month, day] not in archives
|
||||||
|
):
|
||||||
|
# Si date mal ou non fournie ou inexistante, on prend la dernière
|
||||||
year = archives[-1][0]
|
year = archives[-1][0]
|
||||||
month = archives[-1][1]
|
month = archives[-1][1]
|
||||||
day = archives[-1][2]
|
day = archives[-1][2]
|
||||||
# Et on redirige proprement
|
# Et on redirige proprement
|
||||||
return redirect(url_for("archives", year=year, month=month, day=day))
|
return redirect(url_for("archives", year=year, month=month, day=day))
|
||||||
# On vérifie que le tuple existe
|
|
||||||
if [year, month, day] not in archives:
|
|
||||||
# TODO: Renvoyer une 404 ou mettre un message d'erreur ?
|
|
||||||
return "404"
|
|
||||||
# Ok, on charge et on affiche le contenu du fichier
|
# Ok, on charge et on affiche le contenu du fichier
|
||||||
filename = "log-%s-%s-%s.txt" % (year, month, day)
|
filename = "log-%s-%s-%s.txt" % (year, month, day)
|
||||||
filepath = os.path.join(application.config["LOG_PATH"], filename)
|
filepath = os.path.join(application.config["LOG_PATH"], filename)
|
||||||
with open(filepath) as f:
|
with open(filepath, encoding="utf-8") as f:
|
||||||
lines = f.read().splitlines()
|
lines = f.read().splitlines()
|
||||||
g.lines = []
|
g.lines = []
|
||||||
g.year, g.month, g.day = year, month, day
|
g.year, g.month, g.day = year, month, day
|
||||||
|
cleaner = Cleaner(tags=["b"], filters=[LinkifyFilter])
|
||||||
for line in lines:
|
for line in lines:
|
||||||
result = LOG_PATTERN.match(line)
|
result = LOG_PATTERN.match(line)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
message = result.group("message")
|
message = result.group("message")
|
||||||
for link in LINK_PATTERN.findall(message):
|
|
||||||
message = message.replace(
|
|
||||||
link, application.config["LINK_HTML"].format(link=link)
|
|
||||||
)
|
|
||||||
for text in BOLD_PATTERN.findall(message):
|
for text in BOLD_PATTERN.findall(message):
|
||||||
message = message.replace(
|
message = message.replace(
|
||||||
text, application.config["BOLD_HTML"].format(text=text)
|
text, application.config["BOLD_HTML"].format(text=text)
|
||||||
)
|
)
|
||||||
|
message = cleaner.clean(message)
|
||||||
g.lines.append(
|
g.lines.append(
|
||||||
{
|
{
|
||||||
"time": result.group("time"),
|
"time": result.group("time"),
|
||||||
|
|
10
config.py
10
config.py
|
@ -13,11 +13,5 @@ LOG_PATH = "/var/www/logs.afpy.org"
|
||||||
DATE_FORMAT = "(\d+-\d+-\d+ )?(?P<time>\d\d:\d\d)"
|
DATE_FORMAT = "(\d+-\d+-\d+ )?(?P<time>\d\d:\d\d)"
|
||||||
LOG_PATTERN = r"^%s\s+[<*]\s*(?P<nick>[^> ]+)[> ]\s+(?P<message>.*)$" % DATE_FORMAT
|
LOG_PATTERN = r"^%s\s+[<*]\s*(?P<nick>[^> ]+)[> ]\s+(?P<message>.*)$" % DATE_FORMAT
|
||||||
|
|
||||||
# Patterns
|
BOLD_PATTERN = r"\*[^*\s]+\*"
|
||||||
LINK_PATTERN = r"https?://\S+"
|
BOLD_HTML = "<b>{text}</b>"
|
||||||
BOLD_PATTERN = r"\*[^\*\s]+\*"
|
|
||||||
|
|
||||||
# html
|
|
||||||
LINK_HTML = '<a href="{link}">{link}</a>'
|
|
||||||
BOLD_HTML = "<b>{text}</b>"
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
flask
|
flask
|
||||||
|
bleach
|
||||||
gunicorn
|
gunicorn
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Logs du chan #afpy pour le </title>
|
<title>Logs du chan #afpy pour le </title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {background-color: #000; color: #fff; font-family: "Verdana"}
|
body {background-color: #000; color: #fff; font-family: "Verdana"}
|
||||||
|
@ -67,7 +69,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
{% for line in g.lines %}
|
{% for line in g.lines %}
|
||||||
<span class="time">{{ line.time }}</span> <span class="bracket"><</span><span class="nick">{{ line.nick }}</span><span class="bracket">></span> <span class="message">{{ line.message |safe }}</span><br />
|
<span class="time">{{ line.time }}</span> <span class="bracket"><</span><span class="nick">{{ line.nick }}</span><span class="bracket">></span> <span class="message">{{ line.message|safe }}</span><br />
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in New Issue