Skip to content

Commit f7d0021

Browse files
authored
Revert "delete content directory (#159)"
This reverts commit 24b289d.
1 parent 24b289d commit f7d0021

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+5641
-1
lines changed

content/Makefile

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Minimal makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line, and also
5+
# from the environment for the first two.
6+
SPHINXOPTS ?=
7+
SPHINXBUILD ?= sphinx-build
8+
SOURCEDIR = .
9+
BUILDDIR = _build
10+
11+
# Put it first so that "make" without argument is like "make help".
12+
help:
13+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14+
15+
live:
16+
sphinx-autobuild --ignore _build -b dirhtml . _build/dirhtml/
17+
18+
.PHONY: help Makefile
19+
20+
# Catch-all target: route all unknown targets to Sphinx using the new
21+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
22+
%: Makefile
23+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

content/_ext/nb_gallery_generator.py

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""
2+
Sphinx plugin to run generate a gallery for notebooks
3+
"""
4+
import base64
5+
import dataclasses
6+
import json
7+
import os
8+
import pathlib
9+
import random
10+
import shutil
11+
from textwrap import dedent
12+
13+
import matplotlib.image
14+
import matplotlib.pyplot as plt
15+
import pandas as pd
16+
import yaml
17+
18+
with open('lorem_ipsum.txt') as fid:
19+
descriptions = fid.read().split('\n\n')
20+
21+
22+
DOC_SRC = pathlib.Path(os.path.dirname(os.path.abspath(__file__))).parent
23+
default_img_loc = DOC_SRC / '_static/images/sphinx-logo.png'
24+
thumbnail_dir = DOC_SRC / '_static/thumbnails'
25+
thumbnail_dir.mkdir(parents=True, exist_ok=True)
26+
27+
28+
def create_thumbnail(infile, width=275, height=275, cx=0.5, cy=0.5, border=4):
29+
"""Overwrites `infile` with a new file of the given size"""
30+
im = matplotlib.image.imread(infile)
31+
rows, cols = im.shape[:2]
32+
size = min(rows, cols)
33+
if size == cols:
34+
xslice = slice(0, size)
35+
ymin = min(max(0, int(cx * rows - size // 2)), rows - size)
36+
yslice = slice(ymin, ymin + size)
37+
else:
38+
yslice = slice(0, size)
39+
xmin = min(max(0, int(cx * cols - size // 2)), cols - size)
40+
xslice = slice(xmin, xmin + size)
41+
thumb = im[yslice, xslice]
42+
thumb[:border, :, :3] = thumb[-border:, :, :3] = 0
43+
thumb[:, :border, :3] = thumb[:, -border:, :3] = 0
44+
45+
dpi = 100
46+
fig = plt.figure(figsize=(width / dpi, height / dpi), dpi=dpi)
47+
48+
ax = fig.add_axes([0, 0, 1, 1], aspect='auto', frameon=False, xticks=[], yticks=[])
49+
ax.imshow(thumb, aspect='auto', resample=True, interpolation='bilinear')
50+
fig.savefig(infile, dpi=dpi)
51+
plt.close(fig)
52+
return fig
53+
54+
55+
@dataclasses.dataclass
56+
class NotebookInfo:
57+
filepath: pathlib.Path
58+
default_img_loc: pathlib.Path
59+
thumbnail_dir: pathlib.Path
60+
src_dir: pathlib.Path
61+
62+
def __post_init__(self):
63+
self.thumbnail_dir.mkdir(parents=True, exist_ok=True)
64+
self.png_path = self.thumbnail_dir / f'{self.filepath.stem}.png'
65+
with open(self.filepath) as fid:
66+
self.json_source = json.load(fid)
67+
self.gen_preview()
68+
69+
nb_id = f'{self.filepath.relative_to(self.src_dir).parent}/{self.filepath.stem}'
70+
self.info = {
71+
'thumbnail': f'../{self.png_path.relative_to(self.src_dir).as_posix()}',
72+
'notebook': f'../{self.filepath.relative_to(self.src_dir).as_posix()}',
73+
'title': self.extract_title(),
74+
'url': f'../{nb_id}.html',
75+
'id': nb_id,
76+
'description': random.choice(descriptions)[:200].strip(),
77+
}
78+
79+
def gen_preview(self):
80+
preview = self.extract_preview_pic()
81+
if preview is not None:
82+
with open(self.png_path, 'wb') as buff:
83+
buff.write(preview)
84+
else:
85+
shutil.copy(self.default_img_loc, self.png_path)
86+
87+
create_thumbnail(self.png_path)
88+
89+
def extract_preview_pic(self):
90+
"""Use the last image in the notebook as preview pic"""
91+
pic = None
92+
for cell in self.json_source['cells']:
93+
for output in cell.get('outputs', []):
94+
if 'image/png' in output.get('data', []):
95+
pic = output['data']['image/png']
96+
if pic is not None:
97+
return base64.b64decode(pic)
98+
return None
99+
100+
def extract_title(self):
101+
for cell in self.json_source['cells']:
102+
if cell['cell_type'] == 'markdown':
103+
rows = [row.strip() for row in cell['source'] if row.strip()]
104+
for row in rows:
105+
if row.startswith('# '):
106+
return row[2:].replace(':', '-')
107+
return self.filepath.stem.replace('_', ' ').replace(':', '-')
108+
109+
110+
def build_gallery(srcdir, gallery, contains_notebooks):
111+
src_dir = pathlib.Path(srcdir)
112+
os.chdir(srcdir)
113+
target_dir = src_dir / f'{gallery}_gallery'
114+
image_dir = target_dir / '_thumbnails'
115+
image_dir.mkdir(parents=True, exist_ok=True)
116+
117+
if contains_notebooks:
118+
notebooks_path = src_dir / 'notebooks'
119+
notebooks = sorted(
120+
[notebook for notebook in notebooks_path.glob('**/*.ipynb') if 'checkpoint' not in notebook.name]
121+
)
122+
entries = [
123+
NotebookInfo(note, default_img_loc=default_img_loc, thumbnail_dir=image_dir, src_dir=src_dir).info
124+
for note in notebooks
125+
]
126+
df = pd.DataFrame(entries).sort_values(by=['title'])
127+
entries = df.to_dict(orient='records')
128+
with open(target_dir / f'{gallery}_gallery.yaml', 'w') as fid:
129+
yaml.dump(entries, fid)
130+
131+
panels_body = []
132+
for entry in entries:
133+
x = f"""\
134+
---
135+
:img-top: {entry["thumbnail"]}
136+
+++
137+
**{entry['title']}**
138+
139+
{entry['description'][:50]} ...
140+
141+
{{link-badge}}`{entry["url"]},"rendered-notebook",cls=badge-secondary text-white float-left p-2 mr-1`
142+
"""
143+
144+
panels_body.append(x)
145+
146+
panels_body = '\n'.join(panels_body)
147+
148+
gallery_content = f'''# {gallery.capitalize()} Gallery
149+
````{{panels}}
150+
:container: full-width
151+
:column: text-left col-6 col-lg-4
152+
:card: +my-2
153+
:img-top-cls: w-75 m-auto p-2
154+
:body: d-none
155+
156+
{dedent(panels_body)}
157+
````
158+
'''
159+
with open(target_dir / 'index.md', 'w') as fid:
160+
fid.write(dedent(gallery_content))
161+
162+
163+
def main(app):
164+
for gallery in [('notebooks', True)]:
165+
build_gallery(app.builder.srcdir, gallery[0], gallery[1])
166+
167+
168+
def setup(app):
169+
app.connect('builder-inited', main)
+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import itertools
2+
import pathlib
3+
from textwrap import dedent
4+
5+
import yaml
6+
7+
8+
def _tag_in_item(item, tag_str=None):
9+
if tag_str is None:
10+
return True
11+
all_tags = sorted(itertools.chain(*item['tags'].values()))
12+
return tag_str in all_tags
13+
14+
15+
def _generate_sorted_tag_keys(all_items):
16+
17+
key_set = set(itertools.chain(*[item['tags'].keys() for item in all_items]))
18+
return sorted(key_set)
19+
20+
21+
def _generate_tag_set(all_items, tag_key=None):
22+
23+
tag_set = set()
24+
for item in all_items:
25+
for k, e in item['tags'].items():
26+
if tag_key and k != tag_key:
27+
continue
28+
for t in e:
29+
tag_set.add(t)
30+
31+
return tag_set
32+
33+
34+
def _generate_tag_menu(all_items, tag_key):
35+
36+
tag_set = _generate_tag_set(all_items, tag_key)
37+
tag_list = sorted(tag_set)
38+
39+
hrefs = ''.join(
40+
f'<a class="dropdown-item" href="/pages/links/{tag.replace(" ", "-")}.html">{tag.title()}</a> \n'
41+
for tag in tag_list
42+
)
43+
44+
return f"""
45+
<div class="dropdown">
46+
<button class="btn btn-sm btn-primary m-2 dropdown-toggle" data-toggle="collapse" data-target="#{tag_key}" aria-haspopup="true">{tag_key.title()}</button>
47+
<div id="{tag_key}" class="collapse dropdown-menu">
48+
{hrefs}
49+
</div>
50+
</div>
51+
"""
52+
53+
54+
def _generate_menu(all_items, flt=None):
55+
56+
key_list = _generate_sorted_tag_keys(all_items)
57+
menu_html = '<div class="d-flex flex-row"> \n'
58+
for tag_key in key_list:
59+
menu_html += _generate_tag_menu(all_items, tag_key) + '\n'
60+
if flt:
61+
menu_html += '<a type="button" class="btn btn-link" href="/pages/links.html">Return to Full Gallery</a> \n'
62+
menu_html += '<a type="button" class="btn btn-link" style="position:absolute; right:0;" href="https://github.com/ProjectPythia/projectpythia.github.io/issues/new?assignees=&labels=external-links-gallery-submission&template=update-external-links-gallery.md&title=">Submit a Link</a> \n'
63+
menu_html += '</div> \n'
64+
menu_html += '<script> $(document).on("click",function(){$(".collapse").collapse("hide");}); </script> \n'
65+
return menu_html
66+
67+
68+
def build_from_items(items, filename, display_name, menu_html):
69+
70+
# Build the gallery file
71+
panels_body = []
72+
for item in items:
73+
if not item.get('thumbnail'):
74+
item['thumbnail'] = '/_static/images/ebp-logo.png'
75+
thumbnail = item['thumbnail']
76+
tag_list = sorted((itertools.chain(*item['tags'].values())))
77+
tags = [
78+
f'{{link-badge}}`"/pages/links/{tag.replace(" ", "-")}.html",{tag},cls=badge-primary badge-pill text-light`'
79+
for tag in tag_list
80+
]
81+
tags = '\n'.join(tags)
82+
83+
authors = [a.get('name', 'anonymous') for a in item['authors']]
84+
authors_str = f"Created by: {', '.join(authors)}"
85+
86+
email = [a.get('email') for a in item['authors']][0]
87+
email_str = '' if email is None else f'Email: {email}'
88+
89+
affiliation = [a.get('affiliation') for a in item['authors']][0]
90+
affiliation_str = '' if affiliation is None else f'Affiliation: {affiliation}'
91+
92+
affiliation_url = [a.get('affiliation_url') for a in item['authors']][0]
93+
affiliation_url_str = '' if affiliation_url is None else f'{affiliation} Site: <{affiliation_url}>'
94+
95+
panels_body.append(
96+
f"""\
97+
---
98+
:img-top: {thumbnail}
99+
+++
100+
**{item["title"]}**
101+
102+
<button class="modal-btn">See Details</button>
103+
<div class="modal">
104+
<div class="content">
105+
<p>
106+
<img src={thumbnail} />
107+
108+
**{item["title"]}**
109+
110+
{authors_str}
111+
112+
{email_str}
113+
114+
{affiliation_str}
115+
116+
{affiliation_url_str}
117+
118+
{item['description']}
119+
120+
```{{link-button}} {item["url"]}
121+
:type: url
122+
:text: Visit Website
123+
:classes: btn-outline-primary btn-block
124+
```
125+
126+
{tags}
127+
</p>
128+
</div>
129+
</div>
130+
131+
{tags}
132+
"""
133+
)
134+
135+
panels_body = '\n'.join(panels_body)
136+
137+
panels = f"""
138+
# {display_name}
139+
140+
{menu_html}
141+
142+
````{{panels}}
143+
:column: text-left col-6 col-lg-4
144+
:card: +my-2
145+
:img-top-cls: w-75 m-auto p-2
146+
:body: d-none
147+
148+
{dedent(panels_body)}
149+
````
150+
<div class="backdrop"></div>
151+
<script src="/_static/custom.js"> </script>
152+
"""
153+
154+
pathlib.Path(f'pages/{filename}.md').write_text(panels)
155+
156+
157+
def main(app):
158+
159+
with open('links.yaml') as fid:
160+
all_items = yaml.safe_load(fid)
161+
162+
menu_html = _generate_menu(all_items)
163+
build_from_items(all_items, 'links', 'External Links Gallery', menu_html)
164+
165+
menu_html_flt = _generate_menu(all_items, flt=True)
166+
tag_set = _generate_tag_set(all_items)
167+
168+
for tag in tag_set:
169+
items = [item for item in all_items if _tag_in_item(item, tag)]
170+
build_from_items(items, f'links/{tag.replace(" ", "-")}', f'External Links Gallery - "{tag}"', menu_html_flt)
171+
172+
173+
def setup(app):
174+
app.connect('builder-inited', main)

0 commit comments

Comments
 (0)