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.
Files changed (26) hide show
  1. teachbooks-0.0.1/PKG-INFO +20 -0
  2. teachbooks-0.0.1/README.md +5 -0
  3. teachbooks-0.0.1/pyproject.toml +44 -0
  4. teachbooks-0.0.1/setup.cfg +4 -0
  5. teachbooks-0.0.1/teachbooks/__init__.py +0 -0
  6. teachbooks-0.0.1/teachbooks/cli/__init__.py +0 -0
  7. teachbooks-0.0.1/teachbooks/cli/main.py +53 -0
  8. teachbooks-0.0.1/teachbooks/plugins/__init__.py +0 -0
  9. teachbooks-0.0.1/teachbooks/plugins/pybtex/__init__.py +0 -0
  10. teachbooks-0.0.1/teachbooks/plugins/pybtex/formatting/__init__.py +0 -0
  11. teachbooks-0.0.1/teachbooks/plugins/pybtex/formatting/apa.py +486 -0
  12. teachbooks-0.0.1/teachbooks/plugins/pybtex/labels/__init__.py +0 -0
  13. teachbooks-0.0.1/teachbooks/plugins/pybtex/labels/apa.py +67 -0
  14. teachbooks-0.0.1/teachbooks/plugins/pybtex/names/__init__.py +0 -0
  15. teachbooks-0.0.1/teachbooks/plugins/pybtex/names/firstlast.py +39 -0
  16. teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/__init__.py +0 -0
  17. teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/style/__init__.py +0 -0
  18. teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/style/referencing/__init__.py +0 -0
  19. teachbooks-0.0.1/teachbooks/plugins/sphinxcontrib_bibtex/style/referencing/author_year_apa.py +15 -0
  20. teachbooks-0.0.1/teachbooks/publish.py +38 -0
  21. teachbooks-0.0.1/teachbooks.egg-info/PKG-INFO +20 -0
  22. teachbooks-0.0.1/teachbooks.egg-info/SOURCES.txt +24 -0
  23. teachbooks-0.0.1/teachbooks.egg-info/dependency_links.txt +1 -0
  24. teachbooks-0.0.1/teachbooks.egg-info/entry_points.txt +14 -0
  25. teachbooks-0.0.1/teachbooks.egg-info/requires.txt +5 -0
  26. 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,5 @@
1
+ # TeachBooks Package
2
+
3
+ The TeachBooks package contains tools to make life easier when working with (interactive) Jupyter Books. It is essentially a wrapper around Jupyter Book.
4
+
5
+ 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"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
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
@@ -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
@@ -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))
@@ -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&nbsp;Joseph de&nbsp;la Vall<span class="bibtex-protected">é</span>e&nbsp;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.&nbsp;L. X.&nbsp;J. de&nbsp;la Vall<span class="bibtex-protected">é</span>e&nbsp;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
+ ]
@@ -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,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
@@ -0,0 +1,5 @@
1
+ jupyter-book~=1.0
2
+ numpy
3
+ matplotlib
4
+ scipy
5
+ pandas
@@ -0,0 +1,2 @@
1
+ dist
2
+ teachbooks