Added devision by semester

This commit is contained in:
alexander
2026-01-25 23:20:56 +01:00
parent bd21c6ad68
commit 0f1f9e84cb
13 changed files with 237 additions and 69 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
.venv .venv
out out
prod prod
cache
node_modules node_modules
__pycache__/ __pycache__/

View File

@@ -19,7 +19,7 @@ def build(trigger_list: list[str] | None = None):
# Clear output directory # Clear output directory
shutil.rmtree(settings.paths.output, ignore_errors=True) shutil.rmtree(settings.paths.output, ignore_errors=True)
shutil.copytree(settings.paths.static, settings.paths.output) shutil.copytree(settings.paths.static, settings.paths.output)
inv: list[CSItem] = prepare_cheatsheets(inv_raw, settings.paths.output) inv, cats, no_cat = prepare_cheatsheets(inv_raw)
if not os.path.exists(settings.paths.prod): if not os.path.exists(settings.paths.prod):
os.mkdir(settings.paths.prod) os.mkdir(settings.paths.prod)
@@ -38,8 +38,15 @@ def build(trigger_list: list[str] | None = None):
thisYear = datetime.datetime.now().year thisYear = datetime.datetime.now().year
print(cats)
with open(f"{settings.paths.output}/index.html", "w", encoding="utf-8") as f: with open(f"{settings.paths.output}/index.html", "w", encoding="utf-8") as f:
f.write(index.render(items=inv, thisYear=thisYear)) f.write(index.render(cats=list(
sorted(
map(lambda c: (c[0], list(c[1].items())), cats.items()),
key=lambda x: x[0]
)
), no_cat=no_cat, thisYear=thisYear))
with open(f"{settings.paths.output}/impressum.html", "w", encoding="utf-8") as f: with open(f"{settings.paths.output}/impressum.html", "w", encoding="utf-8") as f:
f.write(env.get_template("impressum.html.j2").render(thisYear=thisYear)) f.write(env.get_template("impressum.html.j2").render(thisYear=thisYear))

View File

@@ -10,6 +10,7 @@ class PathsConfig(BaseSettings):
static: str = Field(default="static", description="Static files directory") static: str = Field(default="static", description="Static files directory")
output: str = Field(default="out", description="Output directory") output: str = Field(default="out", description="Output directory")
prod: str = Field(default="prod", description="Production directory") prod: str = Field(default="prod", description="Production directory")
cache: str = Field(default="cache", description="Cache directory")
class Settings(BaseSettings): class Settings(BaseSettings):

View File

@@ -1,5 +1,6 @@
import os import os
import traceback import traceback
import re
from sources import CSInventoryConfig, CSItem, CheatsheetSourceType from sources import CSInventoryConfig, CSItem, CheatsheetSourceType
from sources.plain import process_plain_url from sources.plain import process_plain_url
@@ -23,7 +24,7 @@ def load_cheatsheet_inventory(file: str) -> CSInventoryConfig:
return res return res
def prepare_cheatsheets(config: CSInventoryConfig, outdir: str) -> list[CSItem]: def prepare_cheatsheets(config: CSInventoryConfig) -> list[CSItem]:
res: list[CSItem] = [] res: list[CSItem] = []
logger = get_worker_thread_logger() logger = get_worker_thread_logger()
@@ -34,13 +35,13 @@ def prepare_cheatsheets(config: CSInventoryConfig, outdir: str) -> list[CSItem]:
try: try:
match item.source.type: match item.source.type:
case CheatsheetSourceType.GITEA_SOURCE: case CheatsheetSourceType.GITEA_SOURCE:
new_items += process_gitea(item, outdir) new_items += process_gitea(item)
case CheatsheetSourceType.PLAIN_URL: case CheatsheetSourceType.PLAIN_URL:
new_items.append(process_plain_url(item, outdir)) new_items.append(process_plain_url(item))
case CheatsheetSourceType.CODEBERG_SOURCE: case CheatsheetSourceType.CODEBERG_SOURCE:
new_items += process_codeberg(item, outdir) new_items += process_codeberg(item)
case _: case _:
logger.warning("Unknown Source Type: %s", item.source.type) logger.warning("Unknown Source Type: %s", item.source.type)
@@ -55,6 +56,28 @@ def prepare_cheatsheets(config: CSInventoryConfig, outdir: str) -> list[CSItem]:
logger.debug(f"-> {new_item.title} ({new_item.url})") logger.debug(f"-> {new_item.title} ({new_item.url})")
res.append(new_item) res.append(new_item)
return res no_cat: list[CSItem] = []
cats: dict[int, dict[str, list[CSItem]]] = {}
for item in res:
m = re.match(r"[sS][eE][mM]([eE][sS][tT][eE][rR])?(\d+)-(.*)", item.title.strip())
if m:
semester = int(m.group(2))
module = str(m.group(3)).lower().strip() \
.replace("_", " ").replace("-", " ").title()
if semester not in cats:
cats[semester] = {}
if module not in cats[semester]:
cats[semester][module] = []
item.semester = semester
item.module = module
cats[semester][module].append(item)
else:
no_cat.append(item)
return res, cats, no_cat

View File

@@ -59,4 +59,6 @@ class CSItem(BaseModel):
id: str id: str
git_repo: str git_repo: str
git_repo_type: str git_repo_type: str
semester: int | None = 0
module: str | None = ""

View File

@@ -7,7 +7,7 @@ from pathlib import Path
from logger import get_worker_thread_logger from logger import get_worker_thread_logger
def process_codeberg(item: CSInventoryItem, outdir: str) -> list[CSItem] | None: def process_codeberg(item: CSInventoryItem) -> list[CSItem] | None:
logger = get_worker_thread_logger() logger = get_worker_thread_logger()
logger.info(f"Processing Gitea cheatsheet: {item.source.owner}/{item.source.repo}@{item.source.tag}") logger.info(f"Processing Gitea cheatsheet: {item.source.owner}/{item.source.repo}@{item.source.tag}")
source: CSSourceGitea = item.source source: CSSourceGitea = item.source
@@ -29,7 +29,7 @@ def process_codeberg(item: CSInventoryItem, outdir: str) -> list[CSItem] | None:
for fetch_url, real_url in assets_urls: for fetch_url, real_url in assets_urls:
if item.cache: if item.cache:
cache_url = cache_cheatsheet(fetch_url, outdir) cache_url = cache_cheatsheet(fetch_url)
if cache_url: if cache_url:
real_url = cache_url real_url = cache_url
else: else:

View File

@@ -6,7 +6,7 @@ from pathlib import Path
from logger import get_worker_thread_logger from logger import get_worker_thread_logger
def process_gitea(item: CSInventoryItem, outdir: str) -> list[CSItem] | None: def process_gitea(item: CSInventoryItem) -> list[CSItem] | None:
logger = get_worker_thread_logger() logger = get_worker_thread_logger()
logger.info(f"Processing Gitea cheatsheet: {item.source.owner}/{item.source.repo}@{item.source.tag}") logger.info(f"Processing Gitea cheatsheet: {item.source.owner}/{item.source.repo}@{item.source.tag}")
source: CSSourceGitea = item.source source: CSSourceGitea = item.source
@@ -28,7 +28,7 @@ def process_gitea(item: CSInventoryItem, outdir: str) -> list[CSItem] | None:
for fetch_url, real_url in assets_urls: for fetch_url, real_url in assets_urls:
if item.cache: if item.cache:
cache_url = cache_cheatsheet(fetch_url, outdir) cache_url = cache_cheatsheet(fetch_url)
if cache_url: if cache_url:
real_url = cache_url real_url = cache_url
else: else:

View File

@@ -1,12 +1,13 @@
from sources import CSInventoryItem, CSSourcePlainURL, CSItem from sources import CSInventoryItem, CSSourcePlainURL, CSItem
from sources.util import cache_cheatsheet, get_datestring from sources.util import cache_cheatsheet, get_datestring
from config import get_settings
async def process_plain_url(item: CSInventoryItem, outdir: str) -> CSItem | None: async def process_plain_url(item: CSInventoryItem) -> CSItem | None:
source: CSSourcePlainURL = item.source source: CSSourcePlainURL = item.source
res_url = source.url res_url = source.url
if item.cache: if item.cache:
cache_url = await cache_cheatsheet(source.url, outdir) cache_url = await cache_cheatsheet(source.url)
if cache_url: if cache_url:
res_url = cache_url res_url = cache_url
else: else:

View File

@@ -4,12 +4,13 @@ import os
from pathlib import Path from pathlib import Path
from logger import get_worker_thread_logger from logger import get_worker_thread_logger
from urllib.parse import urlparse from urllib.parse import urlparse
from config import get_settings
def get_datestring() -> str: def get_datestring() -> str:
return datetime.datetime.now().strftime("%d.%m.%y") return datetime.datetime.now().strftime("%d.%m.%y (%H:%M:%S)")
def cache_cheatsheet(url) -> str | None:
def cache_cheatsheet(url, outdir: str) -> str | None: settings = get_settings()
logger = get_worker_thread_logger() logger = get_worker_thread_logger()
logger.info(f"Caching cheatsheet from {url}") logger.info(f"Caching cheatsheet from {url}")
@@ -27,14 +28,22 @@ def cache_cheatsheet(url, outdir: str) -> str | None:
url_base_name = Path(urlparse(url).path).stem url_base_name = Path(urlparse(url).path).stem
filesname = os.path.join("cache", f"{url_base_name}.pdf") filesname = os.path.join(f"{url_base_name}.pdf")
if not os.path.exists(os.path.join(outdir, "cache")): if not os.path.exists(os.path.join(settings.paths.cache)):
os.mkdir(os.path.join(outdir, "cache")) os.mkdir(os.path.join(settings.paths.cache))
with open(os.path.join(outdir, filesname), "wb") as f:
if not os.path.exists(os.path.join(settings.paths.output, "cache")):
os.mkdir(os.path.join(settings.paths.output, "cache"))
with open(os.path.join(settings.paths.output, "cache", filesname), "wb") as f:
f.write(data)
with open(os.path.join(settings.paths.cache, filesname), "wb") as f:
f.write(data) f.write(data)
logger.info(f"Saved file to {filesname}") logger.info(f"Saved file to {filesname}")
return filesname return os.path.join("cache", filesname)

View File

@@ -47,8 +47,7 @@ nav.navbar a:hover {
} }
/* Main content area padding */ /* Main content area padding */
body > h1, body > h1, body > h2, body > h3, body > table, body > p {
body > table {
margin-left: 20px; margin-left: 20px;
margin-right: 20px; margin-right: 20px;
} }
@@ -61,17 +60,30 @@ h1 {
padding-bottom: 10px; padding-bottom: 10px;
} }
h2 {
color: #444;
font-size: 1.2em;
text-align: center;
font-weight: bold;
margin-top: 20px;
border-top: 1px solid #999;
padding-top: 10px;
padding-bottom: 5px;
}
h3 { h3 {
color: #333; color: #333;
text-align: center; text-align: start;
font-weight: normal; font-weight: normal;
margin-bottom: 5px;
margin-top: 5px; margin-top: 5px;
} }
table { table {
width: calc(100% - 40px); width: calc(100% - 40px);
border-collapse: collapse; border-collapse: collapse;
margin-top: 20px; margin-top: 0;
margin-bottom: 20px;
background-color: #ffffff; background-color: #ffffff;
} }
@@ -84,11 +96,35 @@ th, td {
text-align: left; text-align: left;
} }
thead {
height: 15px;
}
th { th {
background-color: #c0c0c0; background-color: #c0c0c0;
font-weight: bold; font-weight: bold;
} }
tr > :nth-child(1), tr > :nth-child(1) {
width: 40%;
}
tr > :nth-child(2), tr > :nth-child(2) {
width: 15%;
}
tr > :nth-child(3), tr > :nth-child(3) {
width: 15%;
}
tr > :nth-child(4), tr > :nth-child(4) {
width: 15%;
}
tr > :nth-child(5), tr > :nth-child(5) {
width: 15%;
}
tbody tr:hover { tbody tr:hover {
background-color: #e8e8ff; background-color: #e8e8ff;
} }

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../styles/main.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAKA;EACI;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AACA;AAAA;EAEI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA","file":"main.css"} {"version":3,"sourceRoot":"","sources":["../../styles/main.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAKA;EACI;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AACA;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA","file":"main.css"}

View File

@@ -50,8 +50,7 @@ nav.navbar a:hover {
} }
/* Main content area padding */ /* Main content area padding */
body > h1, body > h1, body > h2, body > h3, body > table, body > p {
body > table {
margin-left: 20px; margin-left: 20px;
margin-right: 20px; margin-right: 20px;
} }
@@ -64,17 +63,30 @@ h1 {
padding-bottom: 10px; padding-bottom: 10px;
} }
h2 {
color: #444;
font-size: 1.2em;
text-align: center;
font-weight: bold;
margin-top: 20px;
border-top: 1px solid #999;
padding-top: 10px;
padding-bottom: 5px;
}
h3 { h3 {
color: #333; color: #333;
text-align: center; text-align: start;
font-weight: normal; font-weight: normal;
margin-bottom: 5px;
margin-top: 5px; margin-top: 5px;
} }
table { table {
width: calc(100% - 40px); width: calc(100% - 40px);
border-collapse: collapse; border-collapse: collapse;
margin-top: 20px; margin-top: 0;
margin-bottom: 20px;
background-color: #ffffff; background-color: #ffffff;
} }
@@ -87,11 +99,35 @@ th, td {
text-align: left; text-align: left;
} }
thead {
height: 15px;
}
th { th {
background-color: #c0c0c0; background-color: #c0c0c0;
font-weight: bold; font-weight: bold;
} }
tr > :nth-child(1), tr > :nth-child(1) {
width: 40%;
}
tr > :nth-child(2), tr > :nth-child(2) {
width: 15%;
}
tr > :nth-child(3), tr > :nth-child(3) {
width: 15%;
}
tr > :nth-child(4), tr > :nth-child(4) {
width: 15%;
}
tr > :nth-child(5), tr > :nth-child(5) {
width: 15%;
}
tbody tr:hover { tbody tr:hover {
background-color: #e8e8ff; background-color: #e8e8ff;
} }

View File

@@ -10,7 +10,7 @@
{% include "navbar.j2" %} {% include "navbar.j2" %}
<h1>typst4ei</h1> <h1>typst4ei</h1>
<h3>Eine Sammlung von Formelsammlung für/von EI Stundenten der TUM</h3> <p>Eine Sammlung von Formelsammlung für/von EI Stundenten der TUM</p>
<p> <p>
Disclaimer: Die Richtigkeit des Materials kann nicht garantiert werden. Disclaimer: Die Richtigkeit des Materials kann nicht garantiert werden.
@@ -19,44 +19,96 @@
Aber Feedback und Korrekturen sind immer willkommen! Aber Feedback und Korrekturen sind immer willkommen!
</p> </p>
<table>
<thead>
<tr>
<th>Title</th>
<th>Repo</th>
<th>Upload Date</th>
<th>Git commit</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>
<a href="{{ item.url }}">{{ item.title }}</a>
{% if item.author %} {% for item in cats %}
<br>by {{ item.author }} <h2>{{ item[0] }}. Semester</h2>
{% endif %} <table>
</td> <thead>
<td> <tr>
{% if item.git_repo %} <th>Title</th>
<a href="{{ item.git_repo }}">{{ item.git_repo_type }}</a> <th>Autor</th>
{% else %} <th>Repo</th>
N/A <th>Upload Date</th>
{% endif %} <th>Git commit</th>
</td> </tr>
<td> </thead>
{{ item.date }} <tbody>
</td> {% for module in item[1] %}
<td> {% for item in module[1] %}
{% if item.git_repo %} <tr>
{{ item.commit }} <td>
{% endif %} <a href="{{ item.url }}">{{ module[0] }}</a>
</td> </td>
</tr> <td>
{% endfor %} {% if item.author %}
</tbody> {{ item.author }}
</table> {% else %}
N/A
{% endif %}
</td>
<td>
{% if item.git_repo %}
<a href="{{ item.git_repo }}">{{ item.git_repo_type }}</a>
{% else %}
N/A
{% endif %}
</td>
<td>
{{ item.date }}
</td>
<td>
{% if item.git_repo %}
{{ item.commit }}
{% endif %}
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endfor%}
{% if no_cat %}
<h2>Verschiedenes</h2>
<table>
<thead>
<tr>
<th>Title</th>
<th>Repo</th>
<th>Upload Date</th>
<th>Git commit</th>
</tr>
</thead>
<tbody>
{% for item in no_cat %}
<tr>
<td>
<a href="{{ item.url }}">{{ item.title }}</a>
{% if item.author %}
<br>by {{ item.author }}
{% endif %}
</td>
<td>
{% if item.git_repo %}
<a href="{{ item.git_repo }}">{{ item.git_repo_type }}</a>
{% else %}
N/A
{% endif %}
</td>
<td>
{{ item.date }}
</td>
<td>
{% if item.git_repo %}
{{ item.commit }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</body> </body>
</html> </html>