Compare commits

...

3 commits

Author SHA1 Message Date
Edward Betts 187214cfa1 Add Web UI to see when conference websites appeared
Closes: #4
2024-07-21 10:53:22 +09:00
Edward Betts 87009c2247 Make mypy happy 2024-07-15 00:55:18 +08:00
Edward Betts 708c5d6f58 urllib3 now has types 2024-07-14 23:28:16 +08:00
4 changed files with 117 additions and 28 deletions

View file

@ -2,11 +2,9 @@
"""Check if conference websites are live.""" """Check if conference websites are live."""
import configparser
import os import os
import re import re
import smtplib import smtplib
import typing
import warnings import warnings
from datetime import date from datetime import date
from email.mime.text import MIMEText from email.mime.text import MIMEText
@ -15,16 +13,10 @@ from email.utils import formatdate, make_msgid
import requests import requests
import yaml import yaml
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from urllib3.exceptions import InsecureRequestWarning # type: ignore from urllib3.exceptions import InsecureRequestWarning
from urllib3.util.url import parse_url # type: ignore from urllib3.util.url import parse_url
from conference import LiveConference, config, load_yaml
class LiveConference(typing.TypedDict):
"""Live conference."""
conference: str
year: int
live: date
class AbsoluteDNSAdapter(HTTPAdapter): class AbsoluteDNSAdapter(HTTPAdapter):
@ -36,6 +28,7 @@ class AbsoluteDNSAdapter(HTTPAdapter):
# Append a dot to the hostname if it's not already there. # Append a dot to the hostname if it's not already there.
hostname = parsed_url.host hostname = parsed_url.host
assert hostname
if not hostname.endswith("."): if not hostname.endswith("."):
hostname += "." hostname += "."
@ -50,16 +43,6 @@ class AbsoluteDNSAdapter(HTTPAdapter):
return super().send(request, **kwargs) return super().send(request, **kwargs)
config_file_path = os.path.expanduser(
os.path.join(
os.getenv("XDG_CONFIG_HOME", "~/.config"), "conference-check", "config"
)
)
config = configparser.ConfigParser()
config.read(os.path.expanduser(config_file_path))
# Suppress only the single InsecureRequestWarning from urllib3 # Suppress only the single InsecureRequestWarning from urllib3
warnings.filterwarnings("ignore", category=InsecureRequestWarning) warnings.filterwarnings("ignore", category=InsecureRequestWarning)
@ -95,6 +78,7 @@ not_here_list = [
"Wikimedia Error", "Wikimedia Error",
"The page you requested could not be found", "The page you requested could not be found",
"Ooops! Could Not Find It", "Ooops! Could Not Find It",
"OpenStreetMap Authentication Proxy",
] ]
@ -144,13 +128,6 @@ def send_mail(subject: str, body: str) -> None:
s.quit() s.quit()
def load_yaml(name: str) -> typing.Any:
"""Load YAML."""
filename = os.path.expanduser(config["data"][name])
assert os.path.exists(filename)
return yaml.safe_load(open(filename))
def check_conference_web_site(name: str, src_url: str, year: int) -> bool: def check_conference_web_site(name: str, src_url: str, year: int) -> bool:
"""Check if an individual web site is live.""" """Check if an individual web site is live."""
assert "{year}" in src_url assert "{year}" in src_url

34
conference/__init__.py Normal file
View file

@ -0,0 +1,34 @@
"""Conference classes and functions."""
import configparser
import os
import typing
from datetime import date
import yaml
config_file_path = os.path.expanduser(
os.path.join(
os.getenv("XDG_CONFIG_HOME", "~/.config"), "conference-check", "config"
)
)
assert os.path.exists(config_file_path)
config = configparser.ConfigParser()
config.read(os.path.expanduser(config_file_path))
class LiveConference(typing.TypedDict, total=False):
"""Live conference."""
conference: str
year: int
live: date
url: str | None
def load_yaml(name: str) -> typing.Any:
"""Load YAML."""
filename = os.path.expanduser(config["data"][name])
assert os.path.exists(filename)
return yaml.safe_load(open(filename))

53
templates/index.html Normal file
View file

@ -0,0 +1,53 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Conference check</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
h1 {
font-size: 2.5rem;
font-weight: 500;
margin-top: 0;
margin-bottom: 0.5rem;
}
a {
color: #0d6efd;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.text-right {
text-align: right;
}
</style>
</head>
<body>
<h1>Conference check</h1>
<p>Date when website went live.</p>
<table>
{% for l in live %}
<td class="text-right">
{{ l.live.strftime("%a, %d %b %Y") }}
🌍🎉
</td>
<td>
<a href="{{ l.url }}">{{ l.conference }} {{ l.year }}</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>

25
web_view.py Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/python3
"""When conference websites appeared."""
import flask
from conference import LiveConference, load_yaml
app = flask.Flask(__name__)
app.debug = False
@app.route("/")
async def index() -> str:
"""Index page."""
conferences = load_yaml("conferences")
live: list[LiveConference] = load_yaml("live")
for c in live:
c["url"] = conferences[c["conference"]].format(year=c["year"])
return flask.render_template("index.html", live=live, conferences=conferences)
if __name__ == "__main__":
app.run(host="0.0.0.0")