Compare commits
16 Commits
25c0d2545c
...
rss
Author | SHA1 | Date | |
---|---|---|---|
4c49d4e96a | |||
c1b730d396 | |||
4321429dbf | |||
8c4cb8dad2 | |||
![]() |
ee19286ee8 | ||
945aa6e57b | |||
241c44a380 | |||
![]() |
1e30909e52 | ||
![]() |
ca58f85738 | ||
776e646f32 | |||
3bcb47769a | |||
![]() |
3c9c948fe2 | ||
![]() |
73760341d9 | ||
![]() |
e4b6b80fdb | ||
![]() |
13c72b8066 | ||
![]() |
2725f4257c |
@@ -1,5 +1,11 @@
|
||||
theme: './theme/default'
|
||||
author_name: 'Firstname Name'
|
||||
author_mail: 'name@example.com'
|
||||
draft: './draft'
|
||||
# python -m uuid -u uuid4
|
||||
id: 'a06cd9a6-3a48-479b-bf7f-40ddcdde7982'
|
||||
inbox: './inbox'
|
||||
outbox: './outbox'
|
||||
theme: './theme/default'
|
||||
title: 'Blog title'
|
||||
presentation: 'Blog presentation.'
|
||||
url: 'https://blog.example.com'
|
||||
outbox: './outbox'
|
||||
presentation: 'Blog presentation.'
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,6 +5,7 @@ build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
main.spec
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
@@ -13,3 +14,5 @@ wheels/
|
||||
.blog
|
||||
inbox/
|
||||
outbox/
|
||||
themes/
|
||||
draft/
|
||||
|
25
Makefile
Normal file
25
Makefile
Normal file
@@ -0,0 +1,25 @@
|
||||
default: build
|
||||
.PHONY: build install-tools install-dep init clean
|
||||
|
||||
init: install-dep install-tools
|
||||
|
||||
build:
|
||||
@uvx pyinstaller --hidden-import=yaml --hidden-import=yaml.loader --paths=.venv/lib/python3.13/site-packages/ --onefile ./main.py
|
||||
|
||||
install-tools:
|
||||
@uv tool install flake8
|
||||
@uv tool install black
|
||||
@uv tool install pyinstaller
|
||||
|
||||
install-dep:
|
||||
@uv sync
|
||||
|
||||
clean:
|
||||
@rm -r ./dist ./build
|
||||
|
||||
install:
|
||||
ifeq ($(USER), root)
|
||||
@cp ./dist/main /usr/local/bin/blog
|
||||
else
|
||||
@cp ./dist/main ~/.local/bin/blog
|
||||
endif
|
90
blog/blog.py
90
blog/blog.py
@@ -1,12 +1,12 @@
|
||||
import glob
|
||||
import shutil
|
||||
import datetime
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from blog.page import Page
|
||||
from blog.config import Config
|
||||
from blog.theme import Theme
|
||||
from jinja2 import Environment, FileSystemLoader, Template
|
||||
|
||||
|
||||
class Blog:
|
||||
|
||||
def __init__(self, conf: Config):
|
||||
@@ -14,27 +14,30 @@ class Blog:
|
||||
self.conf = conf
|
||||
self.pages = dict()
|
||||
|
||||
def load_pages(self):
|
||||
"""Charge tous les fichiers .md dans le dossier inbox"""
|
||||
files_list = glob.glob(f"{self.conf.inbox}/*.md")
|
||||
def make(self, draft: bool = False):
|
||||
"""Convertit les pages en un site html"""
|
||||
|
||||
self.pages = dict()
|
||||
for file in files_list:
|
||||
self.pages[Path(file).stem] = Page(Path(file))
|
||||
self._load_pages(self.conf.inbox)
|
||||
|
||||
def make(self):
|
||||
"""Convertit les pages en un site html"""
|
||||
if not self.pages:
|
||||
self.load_pages()
|
||||
if draft:
|
||||
self._load_pages(self.conf.draft)
|
||||
|
||||
env = Environment(loader=FileSystemLoader(self.conf.theme))
|
||||
|
||||
page_template = env.get_template("page.html.j2")
|
||||
index_template = env.get_template("index.html.j2")
|
||||
|
||||
self._copy_css()
|
||||
self._build_all_pages(page_template)
|
||||
self._build_index(index_template)
|
||||
self._copy_css()
|
||||
self._build_atom()
|
||||
|
||||
def _load_pages(self, path: Path):
|
||||
"""Charge tous les fichiers .md dans le dossier inbox"""
|
||||
files_list = glob.glob(f"{path}/*.md")
|
||||
for file in files_list:
|
||||
self.pages[Path(file).stem] = Page(Path(file))
|
||||
|
||||
def _build_all_pages(self, template: Template):
|
||||
"""Convertit les pages markdown dans conf.inbox en html dans conf.outbox"""
|
||||
@@ -44,7 +47,7 @@ class Blog:
|
||||
outbox_path.mkdir()
|
||||
|
||||
for filename in self.pages:
|
||||
html_content = self.pages[filename].html(template)
|
||||
html_content = self.pages[filename].html_template(template)
|
||||
with open(f"{self.conf.outbox}/pages/{filename}.html", "w+") as html_file:
|
||||
html_file.write(html_content)
|
||||
|
||||
@@ -56,21 +59,58 @@ class Blog:
|
||||
with open(f"{self.conf.outbox}/index.html", "w+") as html_file:
|
||||
html_file.write(html_content)
|
||||
|
||||
def _build_atom(self):
|
||||
""" """
|
||||
updated = datetime.date(1970, 1, 1)
|
||||
|
||||
articles = ""
|
||||
for filename in self.pages:
|
||||
date = self.pages[filename].date
|
||||
if updated < date:
|
||||
updated = date
|
||||
articles += textwrap.indent(
|
||||
textwrap.dedent(f"""\
|
||||
<entry>
|
||||
<title>{self.pages[filename].title}</title>
|
||||
<link href="{self.conf.url}/pages/{filename}.html"/>
|
||||
<updated>{date}T00:00:00Z</updated>
|
||||
<id>urn:uuid:{self.pages[filename].id}</id>
|
||||
<content type="xhtml">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
{self.pages[filename].html()}
|
||||
</div>
|
||||
</content>
|
||||
</entry>
|
||||
"""),
|
||||
" ")
|
||||
|
||||
header = textwrap.dedent(f""" <?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>{self.conf.title}</title>
|
||||
<subtitle>{self.conf.presentation}</subtitle>
|
||||
<link href="{self.conf.url}/atom.xml" rel="self"/>
|
||||
<link href="{self.conf.url}/index.html"/>
|
||||
<updated>{updated}T00:00:00Z</updated>
|
||||
<author>
|
||||
<name>{self.conf.author_name}</name>
|
||||
<email>{self.conf.author_mail}</email>
|
||||
</author>
|
||||
<id>urn:uuid:{self.conf.id}</id>
|
||||
""")
|
||||
|
||||
footer = "</feed>"
|
||||
|
||||
with open(Path(self.conf.outbox) / "atom.xml", 'w+') as rss_file:
|
||||
rss_file.write(header + articles + footer)
|
||||
|
||||
def _copy_css(self):
|
||||
css_path = Path(self.conf.theme) / 'css'
|
||||
dest_path = Path(self.conf.outbox) / 'css'
|
||||
|
||||
"""Copie les fichiers CSS du theme vers l'export"""
|
||||
css_path = Path(self.conf.theme) / "css"
|
||||
dest_path = Path(self.conf.outbox) / "css"
|
||||
|
||||
if not dest_path.exists():
|
||||
dest_path.mkdir()
|
||||
|
||||
for css_file in css_path.glob('*.css'):
|
||||
for css_file in css_path.glob("*.css"):
|
||||
dest_file = dest_path / css_file.name
|
||||
|
||||
shutil.copy(css_file, dest_file)
|
||||
|
||||
# dest = Path(str(dest_path) + '/' + Path(css_file).stem() + '.css')
|
||||
|
||||
# dest.write_text(Path(css_file).read_text)
|
||||
|
||||
|
||||
|
||||
|
@@ -5,7 +5,18 @@ from pathlib import Path
|
||||
class Config:
|
||||
|
||||
_conf = dict()
|
||||
_list_valid_parameters = {"inbox", "outbox", "theme", "title", "presentation"}
|
||||
_list_valid_parameters = {
|
||||
"inbox",
|
||||
"outbox",
|
||||
"theme",
|
||||
"title",
|
||||
"presentation",
|
||||
"url",
|
||||
"draft",
|
||||
"author_name",
|
||||
"author_mail",
|
||||
"id"
|
||||
}
|
||||
|
||||
def __init__(self, config_file: Path):
|
||||
"""Constructeur : charge les paramètres depuis le fichier de configuration"""
|
||||
|
44
blog/page.py
44
blog/page.py
@@ -1,34 +1,32 @@
|
||||
import re
|
||||
import os
|
||||
import yaml
|
||||
import uuid
|
||||
import jinja2
|
||||
import textwrap
|
||||
import markdown
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from datetime import date, datetime
|
||||
|
||||
# from bs4 import BeautifulSoup as bs
|
||||
from datetime import date
|
||||
|
||||
|
||||
def new_page(title: str):
|
||||
def new_page(title: str, path: Path) -> Path:
|
||||
"""Crée un nouvel article à partir du titre de celui ci."""
|
||||
|
||||
today = date.today().strftime("%Y-%m-%d")
|
||||
|
||||
filename = "./inbox/" + re.sub("[^a-zA-Z0-9-]", "_", title) + ".md"
|
||||
|
||||
filename = path + '/' + re.sub("[^a-zA-Z0-9-]", "_", title) + ".md"
|
||||
id = uuid.uuid4()
|
||||
content = textwrap.dedent(
|
||||
f"""\
|
||||
---
|
||||
date: {today}
|
||||
title: {title}
|
||||
category:
|
||||
id: {id}
|
||||
category:
|
||||
tags:
|
||||
-
|
||||
-
|
||||
---
|
||||
# {title}
|
||||
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -36,7 +34,7 @@ def new_page(title: str):
|
||||
with open(filename, "w") as file:
|
||||
file.write(content)
|
||||
|
||||
subprocess.run(["nvim", "+normal G$", "+startinsert", filename])
|
||||
return filename
|
||||
|
||||
|
||||
class Page:
|
||||
@@ -59,13 +57,10 @@ class Page:
|
||||
if "date" not in self.meta or type(self.meta["date"]) is not date:
|
||||
self.meta["date"] = date(1970, 1, 1)
|
||||
|
||||
def __getattr__(self, name):
|
||||
def __getattr__(self, name: str):
|
||||
if name not in self.meta:
|
||||
raise AttributeError(f"L'attribut '{name}' n'existe pas.")
|
||||
|
||||
# if name == date:
|
||||
# return datetime(self.meta['date'], "%Y-%m-%d")
|
||||
|
||||
return self.meta[name]
|
||||
|
||||
def _extract_meta_and_markdown(self, content: str) -> list:
|
||||
@@ -87,18 +82,19 @@ class Page:
|
||||
meta += line + "\n"
|
||||
return meta, markdown
|
||||
|
||||
def html(self, template: jinja2.Template) -> str:
|
||||
def html(self) -> str:
|
||||
"""Convertit le markdown en html"""
|
||||
return markdown.markdown(
|
||||
self.md_content, extensions=["codehilite", "fenced_code"]
|
||||
)
|
||||
|
||||
|
||||
def html_template(self, template: jinja2.Template) -> str:
|
||||
"""Convertit la page en html a partir d'un template jinja2"""
|
||||
title = self.meta["title"] or "Sans titre"
|
||||
date = self.meta["date"] or None
|
||||
return template.render(
|
||||
title=title,
|
||||
date=date,
|
||||
content=markdown.markdown(
|
||||
self.md_content, extensions=["codehilite", "fenced_code"]
|
||||
),
|
||||
content=self.html(),
|
||||
)
|
||||
|
||||
|
||||
# from bs4 import BeautifulSoup as bs
|
||||
# print(bs(html_string, 'html.parser').prettify())
|
||||
|
@@ -1,17 +0,0 @@
|
||||
import glob
|
||||
from pathlib import Path
|
||||
from blog.page import Page
|
||||
from blog.config import Config
|
||||
from jinja2 import Environment, FileSystemLoader, Template
|
||||
|
||||
|
||||
class Theme:
|
||||
|
||||
def __init__(self, path: Path):
|
||||
""" """
|
||||
self.path = path
|
||||
|
||||
|
||||
def _list_css(self)
|
||||
""" """
|
||||
pass
|
@@ -1,6 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def clean_output(path: Path):
|
||||
# TODO
|
||||
pass
|
44
main.py
44
main.py
@@ -2,15 +2,15 @@
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from blog.page import new_page
|
||||
from blog.config import Config
|
||||
from blog.utils import clean_output
|
||||
from blog.blog import Blog
|
||||
|
||||
# from blog.blog import Blog
|
||||
|
||||
actions_list = ["new", "make", "clean"]
|
||||
actions_list = ["new", "make"]
|
||||
blog_dir = "./inbox/"
|
||||
|
||||
|
||||
@@ -19,35 +19,42 @@ def load_args():
|
||||
parser = argparse.ArgumentParser(description="Gestion du blog")
|
||||
parser.add_argument("action", choices=actions_list, help="L'action a réaliser.")
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
default=".blog",
|
||||
help="Chemin vers le fichier de configuration",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--inbox",
|
||||
default="./inbox",
|
||||
help="Chemin vers les fichiers markdown du blog",
|
||||
)
|
||||
parser.add_argument("all", nargs=argparse.REMAINDER)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
action="store_true",
|
||||
help="Construit le blog avec les brouillons si spécifié.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--draft",
|
||||
default="./draft",
|
||||
help="Chemin vers les brouillons",
|
||||
)
|
||||
parser.add_argument("all", nargs=argparse.REMAINDER, help=argparse.SUPPRESS)
|
||||
return vars(parser.parse_args())
|
||||
|
||||
|
||||
def load_make_args(args: str) -> dict:
|
||||
"""Charge les paramêtres spécifiques à l'action make"""
|
||||
parser = argparse.ArgumentParser(description="Compile le blog")
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--theme",
|
||||
default="./themes/default",
|
||||
help="Chemin vers le theme utilisé pour exporter le blog",
|
||||
help="Chemin vers le theme utilisé",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
default="./output",
|
||||
help="Nom du dossier ou sera exporté le blog en html",
|
||||
help="Nom du dossier où sera exporté le blog en html",
|
||||
)
|
||||
return vars(parser.parse_args(args))
|
||||
|
||||
@@ -57,7 +64,6 @@ def main():
|
||||
args = load_args()
|
||||
|
||||
if not Path(args["config"]).is_file():
|
||||
# TODO gérer le cas où le fichier n'existe pas. Le créer ? Prendre les valeurs par défaut ?
|
||||
print("Le fichier de configuration n'existe pas.")
|
||||
os._exit(1)
|
||||
|
||||
@@ -67,22 +73,24 @@ def main():
|
||||
match args["action"]:
|
||||
case "new":
|
||||
page_title = " ".join(args["all"])
|
||||
new_page(page_title)
|
||||
|
||||
path = conf.inbox
|
||||
if args["d"]:
|
||||
path = conf.draft
|
||||
subprocess.run(
|
||||
["nvim", "+normal G$", "+startinsert", new_page(page_title, path)]
|
||||
)
|
||||
os._exit(0)
|
||||
|
||||
case "make":
|
||||
args = load_make_args(args["all"])
|
||||
conf.overload(args)
|
||||
make_args = load_make_args(args["all"])
|
||||
conf.overload(make_args)
|
||||
|
||||
blog = Blog(conf)
|
||||
blog.make()
|
||||
blog.make(draft=args["d"])
|
||||
|
||||
os._exit(0)
|
||||
|
||||
case "clean":
|
||||
clean_output(conf.inbox)
|
||||
os._exit(0)
|
||||
|
||||
case _:
|
||||
print("error")
|
||||
os._exit(1)
|
||||
|
@@ -5,8 +5,6 @@ description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"black>=25.1.0",
|
||||
"flake8>=7.2.0",
|
||||
"jinja2>=3.1.6",
|
||||
"markdown>=3.8",
|
||||
"pygments>=2.19.1",
|
||||
|
@@ -1,75 +0,0 @@
|
||||
pre { line-height: 125%; }
|
||||
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
.codehilite .hll { background-color: #ffffcc }
|
||||
.codehilite { background: #f8f8f8; }
|
||||
.codehilite .c { color: #3D7B7B; font-style: italic } /* Comment */
|
||||
.codehilite .err { border: 1px solid #F00 } /* Error */
|
||||
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
|
||||
.codehilite .o { color: #666 } /* Operator */
|
||||
.codehilite .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
|
||||
.codehilite .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
|
||||
.codehilite .cp { color: #9C6500 } /* Comment.Preproc */
|
||||
.codehilite .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
|
||||
.codehilite .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
|
||||
.codehilite .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
|
||||
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.codehilite .ge { font-style: italic } /* Generic.Emph */
|
||||
.codehilite .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||
.codehilite .gr { color: #E40000 } /* Generic.Error */
|
||||
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.codehilite .gi { color: #008400 } /* Generic.Inserted */
|
||||
.codehilite .go { color: #717171 } /* Generic.Output */
|
||||
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
.codehilite .gs { font-weight: bold } /* Generic.Strong */
|
||||
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.codehilite .gt { color: #04D } /* Generic.Traceback */
|
||||
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
|
||||
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||
.codehilite .kt { color: #B00040 } /* Keyword.Type */
|
||||
.codehilite .m { color: #666 } /* Literal.Number */
|
||||
.codehilite .s { color: #BA2121 } /* Literal.String */
|
||||
.codehilite .na { color: #687822 } /* Name.Attribute */
|
||||
.codehilite .nb { color: #008000 } /* Name.Builtin */
|
||||
.codehilite .nc { color: #00F; font-weight: bold } /* Name.Class */
|
||||
.codehilite .no { color: #800 } /* Name.Constant */
|
||||
.codehilite .nd { color: #A2F } /* Name.Decorator */
|
||||
.codehilite .ni { color: #717171; font-weight: bold } /* Name.Entity */
|
||||
.codehilite .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
|
||||
.codehilite .nf { color: #00F } /* Name.Function */
|
||||
.codehilite .nl { color: #767600 } /* Name.Label */
|
||||
.codehilite .nn { color: #00F; font-weight: bold } /* Name.Namespace */
|
||||
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||
.codehilite .nv { color: #19177C } /* Name.Variable */
|
||||
.codehilite .ow { color: #A2F; font-weight: bold } /* Operator.Word */
|
||||
.codehilite .w { color: #BBB } /* Text.Whitespace */
|
||||
.codehilite .mb { color: #666 } /* Literal.Number.Bin */
|
||||
.codehilite .mf { color: #666 } /* Literal.Number.Float */
|
||||
.codehilite .mh { color: #666 } /* Literal.Number.Hex */
|
||||
.codehilite .mi { color: #666 } /* Literal.Number.Integer */
|
||||
.codehilite .mo { color: #666 } /* Literal.Number.Oct */
|
||||
.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
|
||||
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
|
||||
.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
|
||||
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||
.codehilite .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
|
||||
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||
.codehilite .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
|
||||
.codehilite .sx { color: #008000 } /* Literal.String.Other */
|
||||
.codehilite .sr { color: #A45A77 } /* Literal.String.Regex */
|
||||
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
|
||||
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||
.codehilite .fm { color: #00F } /* Name.Function.Magic */
|
||||
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
|
||||
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
|
||||
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
|
||||
.codehilite .vm { color: #19177C } /* Name.Variable.Magic */
|
||||
.codehilite .il { color: #666 } /* Literal.Number.Integer.Long */
|
@@ -1,22 +0,0 @@
|
||||
body {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
background: red;
|
||||
color: white;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ lang|default("fr") }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<title>{{ title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ title }}</h1>
|
||||
<p id="intro">{{ presentation }}</p>
|
||||
{%- set ns = namespace(cur_month="") -%}
|
||||
{%- for key, page in pages.items()|sort(attribute='1.date', reverse = true) -%}
|
||||
{%- set month = page.date.strftime('%Y-%m') -%}
|
||||
|
||||
{%- if ns.cur_month != month -%}
|
||||
{%- if ns.cur_month != "" %}
|
||||
</ul>
|
||||
{%- endif%}
|
||||
<h2>{{ month }}</h2>
|
||||
<ul>
|
||||
{%- endif -%}
|
||||
{%- set ns.cur_month = month %}
|
||||
<li>{{ page.date.strftime('%d')}} : <a href="./pages/{{ key }}.html">{{ page.title }}</a>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="../css/codehilite.css">
|
||||
<title>{{ title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
{{ content }}
|
||||
</body>
|
||||
</html>
|
122
uv.lock
generated
122
uv.lock
generated
@@ -2,33 +2,11 @@ version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "25.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "platformdirs" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blog"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "black" },
|
||||
{ name = "flake8" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
{ name = "pygments" },
|
||||
@@ -37,49 +15,12 @@ dependencies = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "black", specifier = ">=25.1.0" },
|
||||
{ name = "flake8", specifier = ">=7.2.0" },
|
||||
{ name = "jinja2", specifier = ">=3.1.6" },
|
||||
{ name = "markdown", specifier = ">=3.8" },
|
||||
{ name = "pygments", specifier = ">=2.19.1" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "7.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mccabe" },
|
||||
{ name = "pycodestyle" },
|
||||
{ name = "pyflakes" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/c4/5842fc9fc94584c455543540af62fd9900faade32511fab650e9891ec225/flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426", size = 48177, upload-time = "2025-03-29T20:08:39.329Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/83/5c/0627be4c9976d56b1217cb5187b7504e7fd7d3503f8bfd312a04077bd4f7/flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343", size = 57786, upload-time = "2025-03-29T20:08:37.902Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
@@ -129,69 +70,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/6e/1f4a62078e4d95d82367f24e685aef3a672abfd27d1a868068fed4ed2254/pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae", size = 39312, upload-time = "2025-03-29T17:33:30.669Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/07/be/b00116df1bfb3e0bb5b45e29d604799f7b91dd861637e4d448b4e09e6a3e/pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9", size = 31424, upload-time = "2025-03-29T17:33:29.405Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/af/cc/1df338bd7ed1fa7c317081dcf29bf2f01266603b301e6858856d346a12b3/pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b", size = 64175, upload-time = "2025-03-31T13:21:20.34Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/15/40/b293a4fa769f3b02ab9e387c707c4cbdc34f073f945de0386107d4e669e6/pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a", size = 63164, upload-time = "2025-03-31T13:21:18.503Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.1"
|
||||
|
Reference in New Issue
Block a user