crieur 1.4.0__tar.gz → 1.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of crieur might be problematic. Click here for more details.
- {crieur-1.4.0 → crieur-1.6.0}/PKG-INFO +5 -1
- {crieur-1.4.0 → crieur-1.6.0}/README.md +3 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/__init__.py +1 -1
- {crieur-1.4.0 → crieur-1.6.0}/crieur/cli.py +4 -4
- {crieur-1.4.0 → crieur-1.6.0}/crieur/generator.py +2 -4
- {crieur-1.4.0 → crieur-1.6.0}/crieur/models.py +76 -3
- {crieur-1.4.0 → crieur-1.6.0}/crieur/templates/article.html +1 -1
- {crieur-1.4.0 → crieur-1.6.0}/pyproject.toml +1 -0
- {crieur-1.4.0 → crieur-1.6.0}/.gitignore +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/LICENSE +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/__main__.py +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/statics/pico.css +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/templates/author.html +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/templates/base.html +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/templates/homepage.html +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/templates/keyword.html +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/templates/numero.html +0 -0
- {crieur-1.4.0 → crieur-1.6.0}/crieur/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crieur
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: A Static Revue Generator.
|
|
5
5
|
Project-URL: Homepage, https://gitlab.huma-num.fr/ecrinum/crieur
|
|
6
6
|
Project-URL: Issues, https://gitlab.huma-num.fr/ecrinum/crieur/-/issues
|
|
@@ -689,6 +689,7 @@ Requires-Dist: httpx
|
|
|
689
689
|
Requires-Dist: jinja2
|
|
690
690
|
Requires-Dist: minicli
|
|
691
691
|
Requires-Dist: mistune
|
|
692
|
+
Requires-Dist: pillow
|
|
692
693
|
Requires-Dist: python-slugify
|
|
693
694
|
Requires-Dist: pytz>=2025.2
|
|
694
695
|
Requires-Dist: pyyaml>=6.0.2
|
|
@@ -754,6 +755,7 @@ cog.out(f"```\n{help}\n```")
|
|
|
754
755
|
[--extra-vars EXTRA_VARS] [--target-path TARGET_PATH]
|
|
755
756
|
[--source-path SOURCE_PATH]
|
|
756
757
|
[--templates-path TEMPLATES_PATH] [--without-statics]
|
|
758
|
+
[--feed-limit FEED_LIMIT]
|
|
757
759
|
|
|
758
760
|
options:
|
|
759
761
|
-h, --help show this help message and exit
|
|
@@ -768,6 +770,8 @@ options:
|
|
|
768
770
|
/sources/).
|
|
769
771
|
--templates-path TEMPLATES_PATH
|
|
770
772
|
--without-statics Do not copy statics if True (default: False).
|
|
773
|
+
--feed-limit FEED_LIMIT
|
|
774
|
+
Number of max items in the feed (default: 10).
|
|
771
775
|
|
|
772
776
|
```
|
|
773
777
|
<!-- [[[end]]] -->
|
|
@@ -49,6 +49,7 @@ cog.out(f"```\n{help}\n```")
|
|
|
49
49
|
[--extra-vars EXTRA_VARS] [--target-path TARGET_PATH]
|
|
50
50
|
[--source-path SOURCE_PATH]
|
|
51
51
|
[--templates-path TEMPLATES_PATH] [--without-statics]
|
|
52
|
+
[--feed-limit FEED_LIMIT]
|
|
52
53
|
|
|
53
54
|
options:
|
|
54
55
|
-h, --help show this help message and exit
|
|
@@ -63,6 +64,8 @@ options:
|
|
|
63
64
|
/sources/).
|
|
64
65
|
--templates-path TEMPLATES_PATH
|
|
65
66
|
--without-statics Do not copy statics if True (default: False).
|
|
67
|
+
--feed-limit FEED_LIMIT
|
|
68
|
+
Number of max items in the feed (default: 10).
|
|
66
69
|
|
|
67
70
|
```
|
|
68
71
|
<!-- [[[end]]] -->
|
|
@@ -29,6 +29,7 @@ def generate(
|
|
|
29
29
|
source_path: Path = Path() / "sources",
|
|
30
30
|
templates_path: Path = Path(__file__).parent / "templates",
|
|
31
31
|
without_statics: bool = False,
|
|
32
|
+
feed_limit: int = 10,
|
|
32
33
|
):
|
|
33
34
|
"""Generate a new revue website.
|
|
34
35
|
|
|
@@ -39,11 +40,12 @@ def generate(
|
|
|
39
40
|
:source_path: Path where stylo source were downloaded (default: /sources/).
|
|
40
41
|
:template_path: Path where templates are located (default: @crieur/templates/).
|
|
41
42
|
:without_statics: Do not copy statics if True (default: False).
|
|
43
|
+
:feed_limit: Number of max items in the feed (default: 10).
|
|
42
44
|
"""
|
|
43
45
|
numeros = []
|
|
44
46
|
for numero in each_folder_from(source_path):
|
|
45
47
|
for corpus_yaml in each_file_from(numero, pattern="*.yaml"):
|
|
46
|
-
numero = configure_numero(corpus_yaml)
|
|
48
|
+
numero = configure_numero(corpus_yaml, base_url)
|
|
47
49
|
numeros.append(numero)
|
|
48
50
|
|
|
49
51
|
keywords = collect_keywords(numeros)
|
|
@@ -58,9 +60,7 @@ def generate(
|
|
|
58
60
|
target_path,
|
|
59
61
|
templates_path,
|
|
60
62
|
)
|
|
61
|
-
generate_feed(
|
|
62
|
-
title, base_url, numeros, extra_vars, target_path, number=10, lang="fr"
|
|
63
|
-
)
|
|
63
|
+
generate_feed(title, base_url, numeros, extra_vars, target_path, number=feed_limit)
|
|
64
64
|
|
|
65
65
|
if not without_statics:
|
|
66
66
|
static_path_local = Path(__file__).parent / "statics"
|
|
@@ -87,9 +87,7 @@ def generate_html(
|
|
|
87
87
|
(author_folder / "index.html").write_text(content)
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def generate_feed(
|
|
91
|
-
title, base_url, numeros, extra_vars, target_path, number=10, lang="fr"
|
|
92
|
-
):
|
|
90
|
+
def generate_feed(title, base_url, numeros, extra_vars, target_path, number, lang="fr"):
|
|
93
91
|
feed = FeedGenerator()
|
|
94
92
|
feed.id(base_url)
|
|
95
93
|
feed.title(title)
|
|
@@ -115,7 +113,7 @@ def generate_feed(
|
|
|
115
113
|
)
|
|
116
114
|
for author in article.authors:
|
|
117
115
|
feed_entry.author(name=str(author))
|
|
118
|
-
feed_entry.summary(summary=
|
|
116
|
+
feed_entry.summary(summary=article.content_html, type="html")
|
|
119
117
|
if article.keywords:
|
|
120
118
|
for keyword in article.keywords:
|
|
121
119
|
feed_entry.category(term=keyword.name)
|
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from textwrap import dedent
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
6
|
+
import mistune
|
|
4
7
|
from dataclass_wizard import DatePattern, DumpMeta, YAMLWizard
|
|
5
8
|
from dataclass_wizard import errors as dw_errors
|
|
9
|
+
from PIL import Image, UnidentifiedImageError
|
|
6
10
|
from slugify import slugify
|
|
7
11
|
from yaml.composer import ComposerError
|
|
8
12
|
|
|
9
13
|
|
|
14
|
+
class ImgsWithSizesRenderer(mistune.HTMLRenderer):
|
|
15
|
+
"""Renders images as <figure>s and add sizes."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
escape=True,
|
|
20
|
+
allow_harmful_protocols=None,
|
|
21
|
+
base_url=None,
|
|
22
|
+
article=None,
|
|
23
|
+
):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self._base_url = base_url
|
|
26
|
+
self._article = article
|
|
27
|
+
|
|
28
|
+
def paragraph(self, text):
|
|
29
|
+
# In case of a figure, we do not want the (non-standard) paragraph.
|
|
30
|
+
if text.strip().startswith("<figure>"):
|
|
31
|
+
return text
|
|
32
|
+
return super().paragraph(text)
|
|
33
|
+
|
|
34
|
+
def image(self, alt, url):
|
|
35
|
+
if self._article.images_path is None:
|
|
36
|
+
print(f"Image with URL `{url}` is discarded.")
|
|
37
|
+
return ""
|
|
38
|
+
full_path = self._article.images_path.resolve().parent / url
|
|
39
|
+
try:
|
|
40
|
+
image = Image.open(full_path)
|
|
41
|
+
except (IsADirectoryError, FileNotFoundError, UnidentifiedImageError):
|
|
42
|
+
print(f"`{full_path}` is not a valid image.")
|
|
43
|
+
return ""
|
|
44
|
+
width, height = image.size
|
|
45
|
+
caption = f"<figcaption>{alt}</figcaption>" if alt else ""
|
|
46
|
+
full_url = f"{self._base_url}{self._article.url}{url}"
|
|
47
|
+
return dedent(
|
|
48
|
+
f"""\
|
|
49
|
+
<figure>
|
|
50
|
+
<a href="{full_url}"
|
|
51
|
+
title="Cliquer pour une version haute résolution">
|
|
52
|
+
<img
|
|
53
|
+
src="{full_url}"
|
|
54
|
+
width="{width}" height="{height}"
|
|
55
|
+
loading="lazy"
|
|
56
|
+
decoding="async"
|
|
57
|
+
alt="{alt}">
|
|
58
|
+
</a>
|
|
59
|
+
{caption}
|
|
60
|
+
</figure>
|
|
61
|
+
"""
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
10
65
|
@dataclass
|
|
11
66
|
class Numero(YAMLWizard):
|
|
12
67
|
_id: str
|
|
@@ -18,7 +73,7 @@ class Numero(YAMLWizard):
|
|
|
18
73
|
def __post_init__(self):
|
|
19
74
|
self.slug = slugify(self.name)
|
|
20
75
|
|
|
21
|
-
def configure_articles(self, yaml_path):
|
|
76
|
+
def configure_articles(self, yaml_path, base_url):
|
|
22
77
|
# Preserves abstract_fr key (vs. abstract-fr) when converting to_yaml()
|
|
23
78
|
DumpMeta(key_transform="SNAKE").bind_to(Article)
|
|
24
79
|
|
|
@@ -40,6 +95,15 @@ class Numero(YAMLWizard):
|
|
|
40
95
|
print(f"Metadata error in `{article['article']['title']}`:")
|
|
41
96
|
print(e)
|
|
42
97
|
exit(1)
|
|
98
|
+
if not loaded_article.date:
|
|
99
|
+
print(f"Article `{loaded_article.title}` skipped (no date).")
|
|
100
|
+
continue
|
|
101
|
+
if loaded_article.date > datetime.today().date():
|
|
102
|
+
print(
|
|
103
|
+
f"Article `{loaded_article.title}` skipped "
|
|
104
|
+
f"(future date: {loaded_article.date})."
|
|
105
|
+
)
|
|
106
|
+
continue
|
|
43
107
|
if not loaded_article.id:
|
|
44
108
|
loaded_article.id = article_slug
|
|
45
109
|
loaded_article.content_md = (
|
|
@@ -51,6 +115,15 @@ class Numero(YAMLWizard):
|
|
|
51
115
|
else None
|
|
52
116
|
)
|
|
53
117
|
loaded_article.numero = self
|
|
118
|
+
md = mistune.create_markdown(
|
|
119
|
+
renderer=ImgsWithSizesRenderer(
|
|
120
|
+
escape=False,
|
|
121
|
+
base_url=base_url,
|
|
122
|
+
article=loaded_article,
|
|
123
|
+
),
|
|
124
|
+
plugins=["footnotes", "superscript"],
|
|
125
|
+
)
|
|
126
|
+
loaded_article.content_html = md(loaded_article.content_md)
|
|
54
127
|
loaded_articles.append(loaded_article)
|
|
55
128
|
self.articles = sorted(loaded_articles, reverse=True)
|
|
56
129
|
|
|
@@ -84,7 +157,7 @@ class Article(YAMLWizard):
|
|
|
84
157
|
return f"numero/{self.numero.slug}/article/{self.id}/"
|
|
85
158
|
|
|
86
159
|
|
|
87
|
-
def configure_numero(yaml_path):
|
|
160
|
+
def configure_numero(yaml_path, base_url):
|
|
88
161
|
# Preserves abstract_fr key (vs. abstract-fr) when converting to_yaml()
|
|
89
162
|
DumpMeta(key_transform="SNAKE").bind_to(Numero)
|
|
90
163
|
|
|
@@ -93,7 +166,7 @@ def configure_numero(yaml_path):
|
|
|
93
166
|
except ComposerError:
|
|
94
167
|
numero = Numero.from_yaml(yaml_path.read_text().split("---")[1])
|
|
95
168
|
|
|
96
|
-
numero.configure_articles(yaml_path)
|
|
169
|
+
numero.configure_articles(yaml_path, base_url)
|
|
97
170
|
return numero
|
|
98
171
|
|
|
99
172
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|