crieur 1.3.1__tar.gz → 1.4.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.3.1 → crieur-1.4.0}/PKG-INFO +2 -1
- {crieur-1.3.1 → crieur-1.4.0}/crieur/__init__.py +1 -1
- {crieur-1.3.1 → crieur-1.4.0}/crieur/cli.py +18 -2
- {crieur-1.3.1 → crieur-1.4.0}/crieur/generator.py +39 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/models.py +24 -6
- {crieur-1.3.1 → crieur-1.4.0}/crieur/templates/base.html +2 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/templates/homepage.html +1 -1
- {crieur-1.3.1 → crieur-1.4.0}/crieur/templates/numero.html +1 -1
- {crieur-1.3.1 → crieur-1.4.0}/pyproject.toml +1 -0
- {crieur-1.3.1 → crieur-1.4.0}/.gitignore +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/LICENSE +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/README.md +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/__main__.py +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/statics/pico.css +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/templates/article.html +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/templates/author.html +0 -0
- {crieur-1.3.1 → crieur-1.4.0}/crieur/templates/keyword.html +0 -0
- {crieur-1.3.1 → crieur-1.4.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.4.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
|
|
@@ -684,6 +684,7 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
|
684
684
|
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
685
685
|
Requires-Python: >=3.8
|
|
686
686
|
Requires-Dist: dataclass-wizard
|
|
687
|
+
Requires-Dist: feedgen>=1.0.0
|
|
687
688
|
Requires-Dist: httpx
|
|
688
689
|
Requires-Dist: jinja2
|
|
689
690
|
Requires-Dist: minicli
|
|
@@ -9,7 +9,7 @@ import httpx
|
|
|
9
9
|
from minicli import cli, run
|
|
10
10
|
|
|
11
11
|
from . import VERSION
|
|
12
|
-
from .generator import generate_html
|
|
12
|
+
from .generator import generate_feed, generate_html
|
|
13
13
|
from .models import collect_authors, collect_keywords, configure_numero
|
|
14
14
|
from .utils import each_file_from, each_folder_from
|
|
15
15
|
|
|
@@ -58,6 +58,9 @@ def generate(
|
|
|
58
58
|
target_path,
|
|
59
59
|
templates_path,
|
|
60
60
|
)
|
|
61
|
+
generate_feed(
|
|
62
|
+
title, base_url, numeros, extra_vars, target_path, number=10, lang="fr"
|
|
63
|
+
)
|
|
61
64
|
|
|
62
65
|
if not without_statics:
|
|
63
66
|
static_path_local = Path(__file__).parent / "statics"
|
|
@@ -101,14 +104,27 @@ def stylo(
|
|
|
101
104
|
with httpx.stream("GET", url, timeout=None) as r:
|
|
102
105
|
for data in r.iter_bytes():
|
|
103
106
|
fd.write(data)
|
|
107
|
+
else:
|
|
108
|
+
print(
|
|
109
|
+
f"Source already exists: `{zip_path}` (no download). "
|
|
110
|
+
"Use the `--force` option to download it again"
|
|
111
|
+
)
|
|
104
112
|
|
|
105
113
|
target_path = sources_path / f"{i + 1}-{stylo_id}"
|
|
106
114
|
try:
|
|
107
115
|
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
108
116
|
zip_ref.extractall(target_path)
|
|
109
|
-
print(f"Data
|
|
117
|
+
print(f"Data extracted to {target_path}")
|
|
110
118
|
except zipfile.BadZipFile:
|
|
119
|
+
zip_problematic_path = Path() / f"problematic-export-{i + 1}-{stylo_id}.zip"
|
|
120
|
+
zip_path.rename(zip_problematic_path)
|
|
111
121
|
print(f"Unable to find corpus with id {stylo_id}!")
|
|
122
|
+
print(
|
|
123
|
+
f"Check out the content of {zip_problematic_path} to try to understand."
|
|
124
|
+
)
|
|
125
|
+
print(
|
|
126
|
+
"Either you use a wrong corpus id or there is an issue with the export."
|
|
127
|
+
)
|
|
112
128
|
return
|
|
113
129
|
|
|
114
130
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import locale
|
|
3
3
|
import shutil
|
|
4
|
+
from datetime import datetime, timedelta, timezone
|
|
4
5
|
|
|
5
6
|
import mistune
|
|
7
|
+
from feedgen.feed import FeedGenerator
|
|
6
8
|
from jinja2 import Environment as Env
|
|
7
9
|
from jinja2 import FileSystemLoader
|
|
8
10
|
from slugify import slugify
|
|
@@ -83,3 +85,40 @@ def generate_html(
|
|
|
83
85
|
author_folder = target_path / "auteur" / author.slug
|
|
84
86
|
author_folder.mkdir(parents=True, exist_ok=True)
|
|
85
87
|
(author_folder / "index.html").write_text(content)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def generate_feed(
|
|
91
|
+
title, base_url, numeros, extra_vars, target_path, number=10, lang="fr"
|
|
92
|
+
):
|
|
93
|
+
feed = FeedGenerator()
|
|
94
|
+
feed.id(base_url)
|
|
95
|
+
feed.title(title)
|
|
96
|
+
feed.link(href=base_url, rel="alternate")
|
|
97
|
+
feed.link(href=f"{base_url}feed.xml", rel="self")
|
|
98
|
+
feed.language(lang)
|
|
99
|
+
|
|
100
|
+
articles = sorted(
|
|
101
|
+
[article for numero in numeros for article in numero.articles], reverse=True
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
for article in articles[:number]:
|
|
105
|
+
feed_entry = feed.add_entry(order="append")
|
|
106
|
+
feed_entry.id(f"{base_url}{article.url}")
|
|
107
|
+
feed_entry.title(article.title_f)
|
|
108
|
+
feed_entry.link(href=f"{base_url}{article.url}")
|
|
109
|
+
feed_entry.updated(
|
|
110
|
+
datetime.combine(
|
|
111
|
+
article.date,
|
|
112
|
+
datetime.min.time(),
|
|
113
|
+
tzinfo=timezone(timedelta(hours=-4), "ET"),
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
for author in article.authors:
|
|
117
|
+
feed_entry.author(name=str(author))
|
|
118
|
+
feed_entry.summary(summary=md(article.content_md), type="html")
|
|
119
|
+
if article.keywords:
|
|
120
|
+
for keyword in article.keywords:
|
|
121
|
+
feed_entry.category(term=keyword.name)
|
|
122
|
+
|
|
123
|
+
feed.atom_file(target_path / "feed.xml", pretty=True)
|
|
124
|
+
print(f"Generated meta-feed with {number} items.")
|
|
@@ -2,6 +2,7 @@ from dataclasses import dataclass
|
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from dataclass_wizard import DatePattern, DumpMeta, YAMLWizard
|
|
5
|
+
from dataclass_wizard import errors as dw_errors
|
|
5
6
|
from slugify import slugify
|
|
6
7
|
from yaml.composer import ComposerError
|
|
7
8
|
|
|
@@ -29,11 +30,16 @@ class Numero(YAMLWizard):
|
|
|
29
30
|
)
|
|
30
31
|
article_yaml_path = article_folder / f"{article_slug}.yaml"
|
|
31
32
|
try:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
try:
|
|
34
|
+
loaded_article = Article.from_yaml_file(article_yaml_path)
|
|
35
|
+
except ComposerError:
|
|
36
|
+
loaded_article = Article.from_yaml(
|
|
37
|
+
article_yaml_path.read_text().split("---")[1]
|
|
38
|
+
)
|
|
39
|
+
except dw_errors.ParseError as e:
|
|
40
|
+
print(f"Metadata error in `{article['article']['title']}`:")
|
|
41
|
+
print(e)
|
|
42
|
+
exit(1)
|
|
37
43
|
if not loaded_article.id:
|
|
38
44
|
loaded_article.id = article_slug
|
|
39
45
|
loaded_article.content_md = (
|
|
@@ -46,7 +52,7 @@ class Numero(YAMLWizard):
|
|
|
46
52
|
)
|
|
47
53
|
loaded_article.numero = self
|
|
48
54
|
loaded_articles.append(loaded_article)
|
|
49
|
-
self.articles = loaded_articles
|
|
55
|
+
self.articles = sorted(loaded_articles, reverse=True)
|
|
50
56
|
|
|
51
57
|
|
|
52
58
|
@dataclass
|
|
@@ -65,6 +71,18 @@ class Article(YAMLWizard):
|
|
|
65
71
|
def __post_init__(self):
|
|
66
72
|
self.slug = slugify(self.title)
|
|
67
73
|
|
|
74
|
+
def __eq__(self, other):
|
|
75
|
+
return self.id == other.id
|
|
76
|
+
|
|
77
|
+
def __lt__(self, other: "Article"):
|
|
78
|
+
if not isinstance(other, Article):
|
|
79
|
+
return NotImplemented
|
|
80
|
+
return self.date < other.date
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def url(self):
|
|
84
|
+
return f"numero/{self.numero.slug}/article/{self.id}/"
|
|
85
|
+
|
|
68
86
|
|
|
69
87
|
def configure_numero(yaml_path):
|
|
70
88
|
# Preserves abstract_fr key (vs. abstract-fr) when converting to_yaml()
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
<link rel="icon" href="data:;base64,iVBORw0KGgo=" />
|
|
18
18
|
<link rel="stylesheet"
|
|
19
19
|
href="{{ base_url }}statics/pico.css" />
|
|
20
|
+
<link rel="alternate" type="application/atom+xml"
|
|
21
|
+
title="Feed" href="{{ base_url }}feed.xml">
|
|
20
22
|
<style>
|
|
21
23
|
h1 {
|
|
22
24
|
text-align: center;
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
</header>
|
|
98
98
|
<ul>
|
|
99
99
|
{% for article in numero.articles %}
|
|
100
|
-
<li><a href="{{ base_url }}
|
|
100
|
+
<li><a href="{{ base_url }}{{ article.url }}">{{ article.title_f }}</a></li>
|
|
101
101
|
{% endfor %}
|
|
102
102
|
</ul>
|
|
103
103
|
</article>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
{% for article in numero.articles %}
|
|
60
60
|
<article>
|
|
61
61
|
<header>
|
|
62
|
-
<h3><a href="{{ base_url }}
|
|
62
|
+
<h3><a href="{{ base_url }}{{ article.url }}">{{ article.title_f }}</a></h3>
|
|
63
63
|
</header>
|
|
64
64
|
{{ article.subtitle_f|markdown }}
|
|
65
65
|
{% if article.authors %}
|
|
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
|