teachbooks 0.0.1__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.
- teachbooks-0.0.1/PKG-INFO +20 -0
- teachbooks-0.0.1/README.md +5 -0
- teachbooks-0.0.1/pyproject.toml +44 -0
- teachbooks-0.0.1/setup.cfg +4 -0
- teachbooks-0.0.1/teachbooks/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/cli/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/cli/main.py +53 -0
- teachbooks-0.0.1/teachbooks/plugins/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/formatting/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/formatting/apa.py +486 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/labels/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/labels/apa.py +67 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/names/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/pybtex/names/firstlast.py +39 -0
- teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/style/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/style/referencing/__init__.py +0 -0
- teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/style/referencing/author_year_apa.py +15 -0
- teachbooks-0.0.1/teachbooks/publish.py +38 -0
- teachbooks-0.0.1/teachbooks.egg-info/PKG-INFO +20 -0
- teachbooks-0.0.1/teachbooks.egg-info/SOURCES.txt +24 -0
- teachbooks-0.0.1/teachbooks.egg-info/dependency_links.txt +1 -0
- teachbooks-0.0.1/teachbooks.egg-info/entry_points.txt +14 -0
- teachbooks-0.0.1/teachbooks.egg-info/requires.txt +5 -0
- teachbooks-0.0.1/teachbooks.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: teachbooks
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: TU Delft CiTG Jupyter Book Software Stack
|
|
5
|
+
Author-email: Caspar Jungbacker <c.a.a.jungbacker@tudelft.nl>
|
|
6
|
+
Project-URL: Homepage, https://gitlab.tudelft.nl/interactivetextbooks-citg/extensions/teachbooks-package
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: jupyter-book~=1.0
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Requires-Dist: matplotlib
|
|
13
|
+
Requires-Dist: scipy
|
|
14
|
+
Requires-Dist: pandas
|
|
15
|
+
|
|
16
|
+
# TeachBooks Package
|
|
17
|
+
|
|
18
|
+
The TeachBooks package contains tools to make life easier when working with (interactive) Jupyter Books. It is essentially a wrapper around Jupyter Book.
|
|
19
|
+
|
|
20
|
+
Work in progress...
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "teachbooks"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "TU Delft CiTG Jupyter Book Software Stack"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Caspar Jungbacker", email = "c.a.a.jungbacker@tudelft.nl"}
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"jupyter-book ~= 1.0",
|
|
19
|
+
"numpy",
|
|
20
|
+
"matplotlib",
|
|
21
|
+
"scipy",
|
|
22
|
+
"pandas"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
teachbooks = "teachbooks.cli.main:main"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages]
|
|
29
|
+
find = {}
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
"Homepage" = "https://gitlab.tudelft.nl/interactivetextbooks-citg/extensions/teachbooks-package"
|
|
33
|
+
|
|
34
|
+
[project.entry-points."pybtex.style.formatting"]
|
|
35
|
+
apa = "teachbooks.plugins.pybtex.formatting.apa:APAStyle"
|
|
36
|
+
|
|
37
|
+
[project.entry-points."pybtex.style.labels"]
|
|
38
|
+
apa = "teachbooks.plugins.pybtex.labels.apa:APALabelStyle"
|
|
39
|
+
|
|
40
|
+
[project.entry-points."pybtex.style.names"]
|
|
41
|
+
firstlast = "teachbooks.plugins.pybtex.names:FirstLastStyle"
|
|
42
|
+
|
|
43
|
+
[project.entry-points."sphinxcontrib.bibtex.style.referencing"]
|
|
44
|
+
author_year_round = "teachbooks.plugins.sphinxcontrib_bibtex.style.referencing.author_year_apa:AuthorYearRoundReferenceStyle"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from jupyter_book.cli.main import build as jupyter_book_build
|
|
5
|
+
|
|
6
|
+
from teachbooks.publish import make_publish
|
|
7
|
+
|
|
8
|
+
@click.group()
|
|
9
|
+
def main():
|
|
10
|
+
"""TeachBooks command line tools"""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@main.command()
|
|
14
|
+
@click.argument("path-source", type=click.Path(exists=True, file_okay=True))
|
|
15
|
+
@click.option(
|
|
16
|
+
"--publish",
|
|
17
|
+
is_flag=True,
|
|
18
|
+
help="Target publish version of the book. Removes any sections surrounded "
|
|
19
|
+
"by REMOVE-FROM-PUBLISH tags from _config.yml and _toc.yml"
|
|
20
|
+
)
|
|
21
|
+
@click.option(
|
|
22
|
+
"--process-only",
|
|
23
|
+
is_flag=True,
|
|
24
|
+
help="Only pre-process content, do not build the book"
|
|
25
|
+
)
|
|
26
|
+
@click.pass_context
|
|
27
|
+
def build(ctx, path_source, publish, process_only):
|
|
28
|
+
"""Pre-process book contents and run the Jupyter Book build command"""
|
|
29
|
+
|
|
30
|
+
strategy = "publish" if publish else "draft"
|
|
31
|
+
echo_info(f"running build with strategy '{strategy}'")
|
|
32
|
+
|
|
33
|
+
path_src_folder = Path(path_source).absolute()
|
|
34
|
+
if publish:
|
|
35
|
+
path_conf, path_toc = make_publish(path_src_folder)
|
|
36
|
+
else:
|
|
37
|
+
path_conf, path_toc = None, None
|
|
38
|
+
|
|
39
|
+
# Use default arguments for jb build, as we normally do
|
|
40
|
+
if not process_only:
|
|
41
|
+
ctx.invoke(
|
|
42
|
+
jupyter_book_build,
|
|
43
|
+
path_source=path_src_folder,
|
|
44
|
+
config=path_conf,
|
|
45
|
+
toc=path_toc
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def echo_info(message: str) -> None:
|
|
50
|
+
"""Wrapper for writing to stdout"""
|
|
51
|
+
prefix = click.style("TeachBooks: ", fg="cyan", bold=True)
|
|
52
|
+
click.echo(prefix + message)
|
|
53
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# -*- coding:Utf-8 -*-
|
|
2
|
+
from __future__ import unicode_literals
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
import six
|
|
6
|
+
|
|
7
|
+
from pybtex.plugin import find_plugin
|
|
8
|
+
from pybtex.style.formatting import BaseStyle, toplevel
|
|
9
|
+
from pybtex.style.template import (
|
|
10
|
+
field, first_of, href, join, optional, optional_field, sentence, tag,
|
|
11
|
+
together, words, node, FieldIsMissing
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from pybtex.richtext import Text, Symbol
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
firstlast = find_plugin('pybtex.style.names', 'lastfirst')()
|
|
18
|
+
|
|
19
|
+
if six.PY2:
|
|
20
|
+
def format_pages(text):
|
|
21
|
+
dash_re = re.compile(r'-+')
|
|
22
|
+
pages = Text(Symbol('ndash')).join(
|
|
23
|
+
re.split(dash_re, six.text_type(text)))
|
|
24
|
+
if re.search('[-‒–—―]', six.text_type(text)):
|
|
25
|
+
return Text("pp.", Symbol('nbsp'), pages)
|
|
26
|
+
return Text("p.", Symbol('nbsp'), pages)
|
|
27
|
+
else:
|
|
28
|
+
def format_pages(text):
|
|
29
|
+
dash_re = re.compile(r'-+')
|
|
30
|
+
pages = Text(Symbol('ndash')).join(text.split(dash_re))
|
|
31
|
+
if re.search('[-‒–—―]', str(text)):
|
|
32
|
+
return Text("pp.", Symbol('nbsp'), pages)
|
|
33
|
+
return Text("p.", Symbol('nbsp'), pages)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
pages = field('pages', apply_func=format_pages)
|
|
37
|
+
date = words[field('year'), optional[", ", field('month')]]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@node
|
|
41
|
+
def apa_names(children, context, role, **kwargs):
|
|
42
|
+
"""
|
|
43
|
+
Returns formatted names as an APA compliant reference list citation.
|
|
44
|
+
"""
|
|
45
|
+
assert not children
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
persons = context['entry'].persons[role]
|
|
49
|
+
except KeyError:
|
|
50
|
+
raise FieldIsMissing(role, context['entry'])
|
|
51
|
+
|
|
52
|
+
style = context['style']
|
|
53
|
+
|
|
54
|
+
if len(persons) > 7:
|
|
55
|
+
persons = persons[:6] + persons[-1:]
|
|
56
|
+
formatted_names = [style.format_name(
|
|
57
|
+
person, style.abbreviate_names) for person in persons]
|
|
58
|
+
return join(sep=', ', last_sep=', … ')[
|
|
59
|
+
formatted_names].format_data(context)
|
|
60
|
+
else:
|
|
61
|
+
formatted_names = [style.format_name(
|
|
62
|
+
person, style.abbreviate_names) for person in persons]
|
|
63
|
+
return join(sep=', ', sep2=', & ', last_sep=', & ')[
|
|
64
|
+
formatted_names].format_data(context)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@node
|
|
68
|
+
def editor_names(children, context, with_suffix=True, **kwargs):
|
|
69
|
+
"""
|
|
70
|
+
Returns formatted editor names for inbook.
|
|
71
|
+
"""
|
|
72
|
+
assert not children
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
editors = context['entry'].persons['editor']
|
|
76
|
+
except KeyError:
|
|
77
|
+
raise FieldIsMissing('editor', context['entry'])
|
|
78
|
+
|
|
79
|
+
formatted_names = [
|
|
80
|
+
firstlast.format(editor, True) for editor in editors]
|
|
81
|
+
|
|
82
|
+
if with_suffix:
|
|
83
|
+
return words[
|
|
84
|
+
join(sep=', ', sep2=', & ', last_sep=', & ')[formatted_names],
|
|
85
|
+
"(Eds.)" if len(editors) > 1 else "(Ed.)"
|
|
86
|
+
].format_data(context)
|
|
87
|
+
|
|
88
|
+
return join(sep=', ', sep2=', & ', last_sep=', & ')[
|
|
89
|
+
formatted_names
|
|
90
|
+
].format_data(context)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class APAStyle(BaseStyle):
|
|
94
|
+
name = 'apa'
|
|
95
|
+
default_name_style = 'lastfirst'
|
|
96
|
+
default_sorting_style = 'author_year_title'
|
|
97
|
+
default_label_style = 'apa'
|
|
98
|
+
|
|
99
|
+
def __init__(self, *args, **kwargs):
|
|
100
|
+
super(APAStyle, self).__init__(*args, **kwargs)
|
|
101
|
+
self.abbreviate_names = True
|
|
102
|
+
|
|
103
|
+
def format_names(self, role, as_sentence=True):
|
|
104
|
+
formatted_names = apa_names(role)
|
|
105
|
+
if as_sentence:
|
|
106
|
+
return sentence(capfirst=False)[formatted_names]
|
|
107
|
+
else:
|
|
108
|
+
return formatted_names
|
|
109
|
+
|
|
110
|
+
def format_author_or_editor_and_date(self, e):
|
|
111
|
+
if 'author' in e.persons and 'editor' in e.persons:
|
|
112
|
+
return sentence(sep=' ')[
|
|
113
|
+
self.format_names('author'), join["(", date, ")."],
|
|
114
|
+
self.format_editor(e, as_sentence=False)]
|
|
115
|
+
elif 'author' in e.persons:
|
|
116
|
+
return sentence(sep=' ')[
|
|
117
|
+
self.format_names('author'), join["(", date, ")"]]
|
|
118
|
+
else:
|
|
119
|
+
return sentence(sep=' ')[
|
|
120
|
+
self.format_editor(e, as_sentence=False),
|
|
121
|
+
join["(", date, ")"]]
|
|
122
|
+
|
|
123
|
+
def format_editor(self, e, as_sentence=True):
|
|
124
|
+
editors = self.format_names('editor', as_sentence=False)
|
|
125
|
+
if 'editor' not in e.persons:
|
|
126
|
+
# when parsing the template, a FieldIsMissing exception
|
|
127
|
+
# will be thrown anyway; no need to do anything now,
|
|
128
|
+
# just return the template that will throw the exception
|
|
129
|
+
return editors
|
|
130
|
+
if len(e.persons['editor']) > 1:
|
|
131
|
+
word = '(Eds.)'
|
|
132
|
+
else:
|
|
133
|
+
word = '(Ed.)'
|
|
134
|
+
result = join(sep=' ')[editors, word]
|
|
135
|
+
if as_sentence:
|
|
136
|
+
return sentence[result]
|
|
137
|
+
else:
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
def format_volume(self, e, for_article=False):
|
|
141
|
+
prefix = "Vol."
|
|
142
|
+
if for_article:
|
|
143
|
+
return join[
|
|
144
|
+
tag('em')[field('volume')],
|
|
145
|
+
optional['(', field('number'), ')'],
|
|
146
|
+
]
|
|
147
|
+
else:
|
|
148
|
+
return optional[together[prefix, field('volume')]]
|
|
149
|
+
|
|
150
|
+
def format_title(self, e, which_field, as_sentence=True):
|
|
151
|
+
formatted_title = field(
|
|
152
|
+
which_field, apply_func=lambda text: text.capitalize()
|
|
153
|
+
)
|
|
154
|
+
if as_sentence:
|
|
155
|
+
return sentence[formatted_title]
|
|
156
|
+
else:
|
|
157
|
+
return formatted_title
|
|
158
|
+
|
|
159
|
+
def format_btitle(self, e, which_field, as_sentence=True):
|
|
160
|
+
formatted_title = tag('em')[field(which_field)]
|
|
161
|
+
if as_sentence:
|
|
162
|
+
return sentence[formatted_title]
|
|
163
|
+
else:
|
|
164
|
+
return formatted_title
|
|
165
|
+
|
|
166
|
+
def format_web_refs(self, e):
|
|
167
|
+
# Based on urlbst output.web.refs
|
|
168
|
+
return sentence(add_period=False)[
|
|
169
|
+
optional[self.format_url(e)],
|
|
170
|
+
optional[self.format_eprint(e)],
|
|
171
|
+
optional[self.format_pubmed(e)],
|
|
172
|
+
optional[self.format_doi(e)],
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
def format_url(self, e):
|
|
176
|
+
# Based on urlbst format.url
|
|
177
|
+
return words[
|
|
178
|
+
'URL:',
|
|
179
|
+
href[
|
|
180
|
+
field('url', raw=True),
|
|
181
|
+
field('url', raw=True)
|
|
182
|
+
]
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
def format_pubmed(self, e):
|
|
186
|
+
# Based on urlbst format.pubmed
|
|
187
|
+
return href[
|
|
188
|
+
join[
|
|
189
|
+
'https://www.ncbi.nlm.nih.gov/pubmed/',
|
|
190
|
+
field('pubmed', raw=True)
|
|
191
|
+
],
|
|
192
|
+
join[
|
|
193
|
+
'PMID:',
|
|
194
|
+
field('pubmed', raw=True)
|
|
195
|
+
]
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
def format_doi(self, e):
|
|
199
|
+
# Based on urlbst format.doi
|
|
200
|
+
return href[
|
|
201
|
+
join[
|
|
202
|
+
'https://doi.org/',
|
|
203
|
+
field('doi', raw=True)
|
|
204
|
+
],
|
|
205
|
+
join[
|
|
206
|
+
'doi:',
|
|
207
|
+
field('doi', raw=True)
|
|
208
|
+
]
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
def format_eprint(self, e):
|
|
212
|
+
# Based on urlbst format.eprint
|
|
213
|
+
return href[
|
|
214
|
+
join[
|
|
215
|
+
'https://arxiv.org/abs/',
|
|
216
|
+
field('eprint', raw=True)
|
|
217
|
+
],
|
|
218
|
+
join[
|
|
219
|
+
'arXiv:',
|
|
220
|
+
field('eprint', raw=True)
|
|
221
|
+
]
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
def get_article_template(self, e):
|
|
225
|
+
# Required fields: author, title, journal, year
|
|
226
|
+
# Optional fields: volume, number, pages, month, note, key
|
|
227
|
+
volume_and_pages = first_of[
|
|
228
|
+
# Volume and pages, with optional issue number
|
|
229
|
+
optional[
|
|
230
|
+
join[
|
|
231
|
+
self.format_volume(e, for_article=True),
|
|
232
|
+
optional[', ', field('pages')]
|
|
233
|
+
],
|
|
234
|
+
],
|
|
235
|
+
# Pages only
|
|
236
|
+
pages,
|
|
237
|
+
]
|
|
238
|
+
template = toplevel[
|
|
239
|
+
self.format_names('author'),
|
|
240
|
+
sentence[
|
|
241
|
+
join["(", date, ")"]
|
|
242
|
+
],
|
|
243
|
+
self.format_title(e, 'title'),
|
|
244
|
+
sentence[
|
|
245
|
+
tag('em')[field('journal')],
|
|
246
|
+
optional[volume_and_pages],
|
|
247
|
+
],
|
|
248
|
+
sentence[optional_field('note')],
|
|
249
|
+
self.format_web_refs(e),
|
|
250
|
+
]
|
|
251
|
+
return template
|
|
252
|
+
|
|
253
|
+
def get_book_template(self, e):
|
|
254
|
+
# Required fields: author/editor, title, publisher, year
|
|
255
|
+
# Optional fields: volume, series, address, edition, month, note, key,
|
|
256
|
+
# isbn
|
|
257
|
+
return toplevel[
|
|
258
|
+
self.format_author_or_editor_and_date(e),
|
|
259
|
+
sentence(sep=' ')[
|
|
260
|
+
self.format_btitle(e, 'title'),
|
|
261
|
+
optional[
|
|
262
|
+
sentence[
|
|
263
|
+
optional[field('edition'), ' ed.'],
|
|
264
|
+
self.format_volume(e),
|
|
265
|
+
]
|
|
266
|
+
]
|
|
267
|
+
],
|
|
268
|
+
sentence(sep=': ')[
|
|
269
|
+
optional_field('address'),
|
|
270
|
+
field('publisher'),
|
|
271
|
+
],
|
|
272
|
+
sentence[optional_field('note')],
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
def get_booklet_template(self, e):
|
|
276
|
+
# Required fields: title
|
|
277
|
+
# Optional fields: author, howpublished, address, month, year, note,
|
|
278
|
+
# key
|
|
279
|
+
return toplevel[
|
|
280
|
+
sentence(sep=' ')[
|
|
281
|
+
first_of[
|
|
282
|
+
optional[self.format_names(
|
|
283
|
+
'author', as_sentence=False)],
|
|
284
|
+
"None to claim their bones"
|
|
285
|
+
],
|
|
286
|
+
join["(", first_of[optional[date], "n.d."], ")"]
|
|
287
|
+
],
|
|
288
|
+
self.format_title(e, 'title'),
|
|
289
|
+
sentence[optional_field('address')],
|
|
290
|
+
sentence[optional_field('note')],
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
def get_inbook_template(self, e):
|
|
294
|
+
# Required fields: author/editor, title, chapter/pages, publisher, year
|
|
295
|
+
# Optional fields: volume, series, address, edition, month, note, key
|
|
296
|
+
return toplevel[
|
|
297
|
+
sentence(sep=' ')[
|
|
298
|
+
self.format_names('author'),
|
|
299
|
+
join["(", date, ")"]
|
|
300
|
+
],
|
|
301
|
+
self.format_title(e, 'title'),
|
|
302
|
+
sentence(sep=' ')[
|
|
303
|
+
optional[
|
|
304
|
+
"In ",
|
|
305
|
+
editor_names(),
|
|
306
|
+
","
|
|
307
|
+
],
|
|
308
|
+
self.format_btitle(e, 'booktitle', as_sentence=False),
|
|
309
|
+
optional[
|
|
310
|
+
join[
|
|
311
|
+
"(",
|
|
312
|
+
sentence(add_period=False)[
|
|
313
|
+
optional[field('edition'), ' ed.'],
|
|
314
|
+
self.format_volume(e),
|
|
315
|
+
pages,
|
|
316
|
+
],
|
|
317
|
+
")"
|
|
318
|
+
]
|
|
319
|
+
]
|
|
320
|
+
],
|
|
321
|
+
sentence(sep=': ')[
|
|
322
|
+
optional_field('address'),
|
|
323
|
+
field('publisher'),
|
|
324
|
+
],
|
|
325
|
+
sentence[optional_field('note')],
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
def get_incollection_template(self, e):
|
|
329
|
+
# Required fields: author, title, booktitle, year
|
|
330
|
+
# Optional fields: editor, pages, organization, publisher, address,
|
|
331
|
+
# month, note, key
|
|
332
|
+
return toplevel[
|
|
333
|
+
self.format_author_or_editor_and_date(e),
|
|
334
|
+
self.format_title(e, 'title'),
|
|
335
|
+
sentence(sep=' ')[
|
|
336
|
+
self.format_btitle(e, 'booktitle', as_sentence=False),
|
|
337
|
+
optional["(", pages, ")"]
|
|
338
|
+
],
|
|
339
|
+
sentence(sep=': ')[
|
|
340
|
+
optional_field('address'),
|
|
341
|
+
optional_field('publisher'),
|
|
342
|
+
],
|
|
343
|
+
sentence[optional_field('note')],
|
|
344
|
+
]
|
|
345
|
+
|
|
346
|
+
def get_inproceedings_template(self, e):
|
|
347
|
+
# Required fields: author, title, booktitle, year
|
|
348
|
+
# Optional fields: editor, pages, organization, publisher, address,
|
|
349
|
+
# month, note, key
|
|
350
|
+
return toplevel[
|
|
351
|
+
self.format_author_or_editor_and_date(e),
|
|
352
|
+
self.format_title(e, 'title'),
|
|
353
|
+
sentence(sep=' ')[
|
|
354
|
+
self.format_btitle(e, 'booktitle', as_sentence=False),
|
|
355
|
+
optional["(", pages, ")"]
|
|
356
|
+
],
|
|
357
|
+
sentence(sep=': ')[
|
|
358
|
+
optional_field('address'),
|
|
359
|
+
optional_field('publisher'),
|
|
360
|
+
],
|
|
361
|
+
sentence[optional_field('note')],
|
|
362
|
+
self.format_web_refs(e)
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
def get_manual_template(self, e):
|
|
366
|
+
# Required fields: title
|
|
367
|
+
# Optional fields: author, organization, address, edition, month, year,
|
|
368
|
+
# note, key
|
|
369
|
+
return toplevel[
|
|
370
|
+
sentence(sep=' ')[
|
|
371
|
+
first_of[
|
|
372
|
+
optional[self.format_names('author', as_sentence=False)],
|
|
373
|
+
optional_field('organization'),
|
|
374
|
+
"None to claim their bones"
|
|
375
|
+
],
|
|
376
|
+
join["(", first_of[optional[date], "n.d."], ")"]
|
|
377
|
+
],
|
|
378
|
+
self.format_btitle(e, 'title'),
|
|
379
|
+
sentence[optional_field('address')],
|
|
380
|
+
sentence[optional_field('note')],
|
|
381
|
+
self.format_web_refs(e)
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
def get_mastersthesis_template(self, e):
|
|
385
|
+
# Required fields: author, title, school, year
|
|
386
|
+
# Optional fields: address, month, note, key
|
|
387
|
+
return toplevel[
|
|
388
|
+
sentence(sep=' ')[
|
|
389
|
+
self.format_names('author'),
|
|
390
|
+
join["(", date, ")"]
|
|
391
|
+
],
|
|
392
|
+
sentence(sep=' ')[
|
|
393
|
+
self.format_btitle(e, 'title', as_sentence=False),
|
|
394
|
+
"(Master's thesis)"
|
|
395
|
+
],
|
|
396
|
+
sentence[
|
|
397
|
+
field('school'),
|
|
398
|
+
optional_field('address'),
|
|
399
|
+
],
|
|
400
|
+
sentence[optional_field('note')],
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
def get_misc_template(self, e):
|
|
404
|
+
# Required fields: aucun
|
|
405
|
+
# Optional fields: author, title, howpublished, month, year, note, key,
|
|
406
|
+
# type
|
|
407
|
+
template = toplevel[
|
|
408
|
+
sentence(sep=' ')[
|
|
409
|
+
first_of[
|
|
410
|
+
optional[self.format_names('author', as_sentence=False)],
|
|
411
|
+
optional_field('publisher'),
|
|
412
|
+
"None to claim their bones"
|
|
413
|
+
],
|
|
414
|
+
join["(", first_of[optional[date], "n.d."], ")"]
|
|
415
|
+
],
|
|
416
|
+
optional[self.format_title(e, 'title')],
|
|
417
|
+
sentence[optional_field('note')],
|
|
418
|
+
self.format_web_refs(e)
|
|
419
|
+
]
|
|
420
|
+
return template
|
|
421
|
+
|
|
422
|
+
def get_phdthesis_template(self, e):
|
|
423
|
+
# Required fields: author, title, school, year
|
|
424
|
+
# Optional fields: address, month, note, key
|
|
425
|
+
return toplevel[
|
|
426
|
+
sentence(sep=' ')[
|
|
427
|
+
self.format_names('author'),
|
|
428
|
+
join["(", date, ")"]
|
|
429
|
+
],
|
|
430
|
+
sentence(sep=' ')[
|
|
431
|
+
self.format_btitle(e, 'title', as_sentence=False),
|
|
432
|
+
"(Doctoral dissertation)"
|
|
433
|
+
],
|
|
434
|
+
sentence[
|
|
435
|
+
field('school'),
|
|
436
|
+
optional_field('address'),
|
|
437
|
+
],
|
|
438
|
+
sentence[optional_field('note')],
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
def get_proceedings_template(self, e):
|
|
442
|
+
# Required fields: title, year
|
|
443
|
+
# Optional fields: editor, publisher, organization, address, month,
|
|
444
|
+
# note, key
|
|
445
|
+
return toplevel[
|
|
446
|
+
sentence(sep=' ')[
|
|
447
|
+
first_of[
|
|
448
|
+
optional[self.format_editor(e, as_sentence=False)],
|
|
449
|
+
optional_field('organization'),
|
|
450
|
+
"None to claim their bones"
|
|
451
|
+
],
|
|
452
|
+
join["(", first_of[optional[date], "n.d."], ")"]
|
|
453
|
+
],
|
|
454
|
+
self.format_btitle(e, 'title'),
|
|
455
|
+
sentence(sep=': ')[
|
|
456
|
+
optional_field('address'),
|
|
457
|
+
optional_field('publisher'),
|
|
458
|
+
],
|
|
459
|
+
sentence[optional_field('note')],
|
|
460
|
+
]
|
|
461
|
+
|
|
462
|
+
def get_techreport_template(self, e):
|
|
463
|
+
# Required fields: author, title, institution, year
|
|
464
|
+
# Optional fields: type, number, address, month, note, key
|
|
465
|
+
return toplevel[
|
|
466
|
+
sentence(sep=' ')[
|
|
467
|
+
self.format_names('author', as_sentence=False),
|
|
468
|
+
join["(", date, ")"]
|
|
469
|
+
],
|
|
470
|
+
self.format_btitle(e, 'title'),
|
|
471
|
+
sentence[field('institution')],
|
|
472
|
+
sentence[optional_field('note')],
|
|
473
|
+
]
|
|
474
|
+
|
|
475
|
+
def get_unpublished_template(self, e):
|
|
476
|
+
# Required fields: author, title, note
|
|
477
|
+
# Optional fields: month, year, key
|
|
478
|
+
template = toplevel[
|
|
479
|
+
sentence(sep=' ')[
|
|
480
|
+
self.format_names('author', as_sentence=False),
|
|
481
|
+
join["(", first_of[optional[date], "n.d."], ")"]
|
|
482
|
+
],
|
|
483
|
+
self.format_btitle(e, 'title'),
|
|
484
|
+
sentence[field('note')],
|
|
485
|
+
]
|
|
486
|
+
return template
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# -*- coding:Utf-8 -*-
|
|
2
|
+
from __future__ import unicode_literals
|
|
3
|
+
|
|
4
|
+
from collections import Counter
|
|
5
|
+
import re
|
|
6
|
+
import unicodedata
|
|
7
|
+
|
|
8
|
+
from pybtex.style.labels import BaseLabelStyle
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_nonalnum_pattern = re.compile('[^A-Za-z0-9 \-]+', re.UNICODE)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _strip_accents(s):
|
|
15
|
+
return "".join(
|
|
16
|
+
(c for c in unicodedata.normalize('NFD', s)
|
|
17
|
+
if not unicodedata.combining(c)))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _strip_nonalnum(parts):
|
|
21
|
+
"""Strip all non-alphanumerical characters from a list of strings.
|
|
22
|
+
|
|
23
|
+
>>> print(_strip_nonalnum([u"ÅA. B. Testing 12+}[.@~_", u" 3%"]))
|
|
24
|
+
AABTesting123
|
|
25
|
+
"""
|
|
26
|
+
s = "".join(parts)
|
|
27
|
+
return _nonalnum_pattern.sub("", _strip_accents(s))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class APALabelStyle(BaseLabelStyle):
|
|
31
|
+
def format_labels(self, sorted_entries):
|
|
32
|
+
labels = [self.format_label(entry) for entry in sorted_entries]
|
|
33
|
+
count = Counter(labels)
|
|
34
|
+
counted = Counter()
|
|
35
|
+
for label in labels:
|
|
36
|
+
if count[label] == 1:
|
|
37
|
+
yield label
|
|
38
|
+
else:
|
|
39
|
+
yield label # + chr(ord('a') + counted[label])
|
|
40
|
+
# counted.update([label])
|
|
41
|
+
|
|
42
|
+
def format_label(self, entry):
|
|
43
|
+
label = "Anonymous"
|
|
44
|
+
if 'author' in entry.persons:
|
|
45
|
+
label = self.format_author_or_editor_names(entry.persons['author'])
|
|
46
|
+
elif 'editor' in entry.persons:
|
|
47
|
+
label = self.format_author_or_editor_names(entry.persons['editor'])
|
|
48
|
+
elif 'organization' in entry.fields:
|
|
49
|
+
label = entry.fields['organization']
|
|
50
|
+
if label.startswith("The "):
|
|
51
|
+
label = label[4:]
|
|
52
|
+
|
|
53
|
+
if 'year' in entry.fields:
|
|
54
|
+
return "{}, {}".format(label, entry.fields['year'])
|
|
55
|
+
else:
|
|
56
|
+
return "{}, n.d.".format(label)
|
|
57
|
+
|
|
58
|
+
def format_author_or_editor_names(self, persons):
|
|
59
|
+
if len(persons) is 1:
|
|
60
|
+
return _strip_nonalnum(persons[0].last_names)
|
|
61
|
+
elif len(persons) is 2:
|
|
62
|
+
return "{} & {}".format(
|
|
63
|
+
_strip_nonalnum(persons[0].last_names),
|
|
64
|
+
_strip_nonalnum(persons[1].last_names))
|
|
65
|
+
else:
|
|
66
|
+
return "{} et al.".format(
|
|
67
|
+
_strip_nonalnum(persons[0].last_names))
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# -*- coding:Utf-8 -*-
|
|
2
|
+
from __future__ import unicode_literals
|
|
3
|
+
|
|
4
|
+
from pybtex.style.names import BaseNameStyle, name_part
|
|
5
|
+
from pybtex.style.template import join
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FirstLastStyle(BaseNameStyle):
|
|
9
|
+
|
|
10
|
+
def format(self, person, abbr=False):
|
|
11
|
+
r"""
|
|
12
|
+
Format names similarly to {vv~}{ll}{, jj}{, f.} in BibTeX.
|
|
13
|
+
|
|
14
|
+
>>> from pybtex.database import Person
|
|
15
|
+
>>> name = Person(string=r"Charles Louis Xavier Joseph de la Vall{\'e}e Poussin")
|
|
16
|
+
>>> firstlast = NameStyle().format
|
|
17
|
+
|
|
18
|
+
>>> print(firstlast(name).format().render_as('latex'))
|
|
19
|
+
Charles Louis Xavier~Joseph de~la Vall{é}e~Poussin
|
|
20
|
+
>>> print(firstlast(name).format().render_as('html'))
|
|
21
|
+
Charles Louis Xavier Joseph de la Vall<span class="bibtex-protected">é</span>e Poussin
|
|
22
|
+
|
|
23
|
+
>>> print(firstlast(name, abbr=True).format().render_as('latex'))
|
|
24
|
+
C.~L. X.~J. de~la Vall{é}e~Poussin
|
|
25
|
+
>>> print(firstlast(name, abbr=True).format().render_as('html'))
|
|
26
|
+
C. L. X. J. de la Vall<span class="bibtex-protected">é</span>e Poussin
|
|
27
|
+
|
|
28
|
+
>>> name = Person(first='First', last='Last', middle='Middle')
|
|
29
|
+
>>> print(firstlast(name).format().render_as('latex'))
|
|
30
|
+
First~Middle Last
|
|
31
|
+
>>> print(firstlast(name, abbr=True).format().render_as('latex'))
|
|
32
|
+
F.~M. Last
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
return join[
|
|
36
|
+
name_part(abbr=abbr)[person.rich_first_names + person.rich_middle_names],
|
|
37
|
+
name_part(before=' ')[person.rich_prelast_names],
|
|
38
|
+
name_part(before=' ')[person.rich_last_names],
|
|
39
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from sphinxcontrib.bibtex.style.referencing import BracketStyle
|
|
4
|
+
from sphinxcontrib.bibtex.style.referencing.author_year import AuthorYearReferenceStyle
|
|
5
|
+
|
|
6
|
+
def bracket_style() -> BracketStyle:
|
|
7
|
+
return BracketStyle(left='(', right=')')
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class AuthorYearRoundReferenceStyle(AuthorYearReferenceStyle):
|
|
11
|
+
bracket_parenthetical: BracketStyle = field(default_factory=bracket_style)
|
|
12
|
+
bracket_textual: BracketStyle = field(default_factory=bracket_style)
|
|
13
|
+
bracket_author: BracketStyle = field(default_factory=bracket_style)
|
|
14
|
+
bracket_label: BracketStyle = field(default_factory=bracket_style)
|
|
15
|
+
bracket_year: BracketStyle = field(default_factory=bracket_style)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def make_publish(sourcedir: Path) -> tuple[Path, Path]:
|
|
8
|
+
"""Pre-process files in a Jupyter Book directory"""
|
|
9
|
+
|
|
10
|
+
# Make hidden directory that will contain the cleaned-up files
|
|
11
|
+
workdir = sourcedir.joinpath(".psorbooks", "publish")
|
|
12
|
+
|
|
13
|
+
if not os.path.exists(workdir):
|
|
14
|
+
os.makedirs(workdir)
|
|
15
|
+
|
|
16
|
+
for file in ["_config.yml", "_toc.yml"]:
|
|
17
|
+
clean_yaml(
|
|
18
|
+
sourcedir.joinpath(file),
|
|
19
|
+
workdir.joinpath(file)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return workdir.joinpath("_config.yml"), workdir.joinpath("_toc.yml")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def clean_yaml(path_source: str | Path, path_output: str | Path) -> None:
|
|
26
|
+
"""Removes sections marked with # REMOVE-FROM-PUBLISH from a yaml file"""
|
|
27
|
+
|
|
28
|
+
with open(path_source, mode="r", encoding="utf8") as f:
|
|
29
|
+
yaml_source = f.read()
|
|
30
|
+
|
|
31
|
+
yaml_output = re.sub(
|
|
32
|
+
r"# START REMOVE-FROM-PUBLISH(.|\n)*?# END REMOVE-FROM-PUBLISH",
|
|
33
|
+
"",
|
|
34
|
+
yaml_source
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
with open(path_output, mode="w", encoding="utf8") as f:
|
|
38
|
+
f.write(yaml_output)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: teachbooks
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: TU Delft CiTG Jupyter Book Software Stack
|
|
5
|
+
Author-email: Caspar Jungbacker <c.a.a.jungbacker@tudelft.nl>
|
|
6
|
+
Project-URL: Homepage, https://gitlab.tudelft.nl/interactivetextbooks-citg/extensions/teachbooks-package
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: jupyter-book~=1.0
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Requires-Dist: matplotlib
|
|
13
|
+
Requires-Dist: scipy
|
|
14
|
+
Requires-Dist: pandas
|
|
15
|
+
|
|
16
|
+
# TeachBooks Package
|
|
17
|
+
|
|
18
|
+
The TeachBooks package contains tools to make life easier when working with (interactive) Jupyter Books. It is essentially a wrapper around Jupyter Book.
|
|
19
|
+
|
|
20
|
+
Work in progress...
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
teachbooks/__init__.py
|
|
4
|
+
teachbooks/publish.py
|
|
5
|
+
teachbooks.egg-info/PKG-INFO
|
|
6
|
+
teachbooks.egg-info/SOURCES.txt
|
|
7
|
+
teachbooks.egg-info/dependency_links.txt
|
|
8
|
+
teachbooks.egg-info/entry_points.txt
|
|
9
|
+
teachbooks.egg-info/requires.txt
|
|
10
|
+
teachbooks.egg-info/top_level.txt
|
|
11
|
+
teachbooks/cli/__init__.py
|
|
12
|
+
teachbooks/cli/main.py
|
|
13
|
+
teachbooks/plugins/__init__.py
|
|
14
|
+
teachbooks/plugins/pybtex/__init__.py
|
|
15
|
+
teachbooks/plugins/pybtex/formatting/__init__.py
|
|
16
|
+
teachbooks/plugins/pybtex/formatting/apa.py
|
|
17
|
+
teachbooks/plugins/pybtex/labels/__init__.py
|
|
18
|
+
teachbooks/plugins/pybtex/labels/apa.py
|
|
19
|
+
teachbooks/plugins/pybtex/names/__init__.py
|
|
20
|
+
teachbooks/plugins/pybtex/names/firstlast.py
|
|
21
|
+
teachbooks/plugins/sphinxcontrib_bibtex/__init__.py
|
|
22
|
+
teachbooks/plugins/sphinxcontrib_bibtex/style/__init__.py
|
|
23
|
+
teachbooks/plugins/sphinxcontrib_bibtex/style/referencing/__init__.py
|
|
24
|
+
teachbooks/plugins/sphinxcontrib_bibtex/style/referencing/author_year_apa.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
teachbooks = teachbooks.cli.main:main
|
|
3
|
+
|
|
4
|
+
[pybtex.style.formatting]
|
|
5
|
+
apa = teachbooks.plugins.pybtex.formatting.apa:APAStyle
|
|
6
|
+
|
|
7
|
+
[pybtex.style.labels]
|
|
8
|
+
apa = teachbooks.plugins.pybtex.labels.apa:APALabelStyle
|
|
9
|
+
|
|
10
|
+
[pybtex.style.names]
|
|
11
|
+
firstlast = teachbooks.plugins.pybtex.names:FirstLastStyle
|
|
12
|
+
|
|
13
|
+
[sphinxcontrib.bibtex.style.referencing]
|
|
14
|
+
author_year_round = teachbooks.plugins.sphinxcontrib_bibtex.style.referencing.author_year_apa:AuthorYearRoundReferenceStyle
|