Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
galaxyproject
GitHub Repository: galaxyproject/training-material
Path: blob/main/_plugins/notebook-jupyter.rb
1677 views
1
require 'json'
2
require 'fileutils'
3
require './_plugins/notebook'
4
require './_plugins/gtn'
5
6
def json_boxify(h, page)
7
h['cells'].each do |cell|
8
# If it's a list, loop
9
if cell['source'].is_a? Array
10
cell['source'].each do |line|
11
# rubocop:disable Layout/LineLength
12
line.gsub!(%r{<(?<boxclass>#{Gtn::Boxify.box_classes})-title( ?(?<noprefix>noprefix))>(?<title>.*?)</\s*\k<boxclass>-title\s*>}) do
13
# rubocop:enable Layout/LineLength
14
m = Regexp.last_match
15
box_type = m[:boxclass]
16
title = m[:title]
17
noprefix = m[:noprefix]
18
_, box = Gtn::Boxify.generate_title(box_type, title, lang, page.path, noprefix: noprefix)
19
box
20
end
21
end
22
else
23
# rubocop:disable Layout/LineLength
24
cell['source'].gsub!(%r{<(?<boxclass>#{Gtn::Boxify.box_classes})-title(?<noprefix>\s+noprefix)?>(?<title>.*?)</\s*\k<boxclass>-title\s*>}) do
25
# rubocop:enable Layout/LineLength
26
m = Regexp.last_match
27
box_type = m[:boxclass]
28
title = m[:title]
29
noprefix = m[:noprefix]
30
_, box = Gtn::Boxify.generate_title(box_type, title, 'en', page.path, noprefix: noprefix)
31
box
32
end
33
end
34
end
35
h
36
end
37
38
def jupyter_pre_render(site)
39
Jekyll.logger.info '[GTN/Notebooks] Rendering'
40
41
site.config['__rendered_notebook_cache'] = {}
42
43
# For every tutorial with the 'notebook' key in the page data
44
site.pages.select { |page| Gtn::Notebooks.notebook_filter(page.data) }.each do |page|
45
# We get the path to the tutorial source
46
dir = File.dirname(File.join('.', page.url))
47
fn = File.join('.', page.url).sub(/html$/, 'md')
48
notebook_language = page.data['notebook'].fetch('language', 'python')
49
50
# Tag our source page
51
page.data['tags'] = page.data['tags'] || []
52
page.data['tags'].push('jupyter-notebook')
53
54
Jekyll.logger.info "[GTN/Notebooks] Rendering #{notebook_language} #{fn}"
55
last_modified = Gtn::ModificationTimes.obtain_time(page.path)
56
notebook = Gtn::Notebooks.render_jupyter_notebook(page.data, page.content, page.url, last_modified,
57
notebook_language, site, dir)
58
59
topic_id = dir.split('/')[-3]
60
tutorial_id = dir.split('/')[-1]
61
with_solutions = notebook.clone
62
63
with_solutions['cells'] = with_solutions['cells'].map do |cell|
64
if cell.fetch('cell_type') == 'markdown' && (cell['source'].is_a? String)
65
m = cell['source'].match(/<blockquote class="solution"[^>]*>/)
66
if m
67
cell['source'].gsub!(/<blockquote class="solution"[^>]*>/,
68
'<br/><details style="border: 2px solid #B8C3EA; margin: 1em 0.2em;' \
69
'padding: 0.5em; cursor: pointer;"><summary>👁 View solution</summary>')
70
71
idx = m.begin(0)
72
q = cell['source'][0..idx]
73
w = cell['source'][idx + 1..]
74
e = w.index('</blockquote>')
75
r = "#{w[0..e - 1]}</details>#{w[e + 13..]}"
76
77
cell['source'] = q + r
78
end
79
end
80
cell
81
end
82
83
# Write it out!
84
ipynb_dir = File.join(site.dest, dir)
85
ipynb_path = File.join(ipynb_dir, "#{topic_id}-#{tutorial_id}.ipynb")
86
# page2 = PageWithoutAFile.new(site, '', dir, "#{topic_id}-#{tutorial_id}.ipynb")
87
# page2.content = JSON.pretty_generate(with_solutions)
88
# page2.data['layout'] = nil
89
# page2.data['citation_target'] = 'jupyter'
90
# site.pages << page2
91
92
# Create a no-solutions version:
93
no_solutions = notebook.clone
94
95
no_solutions['cells'] = no_solutions['cells'].map do |cell|
96
if cell.fetch('cell_type') == 'markdown' && (cell['source'].is_a? String)
97
cell['source'].gsub!(/<blockquote class="solution"[^>]*>/,
98
'<blockquote class="solution" style="display:none">')
99
end
100
cell
101
end
102
103
ipynb_path2 = File.join(ipynb_dir, "#{topic_id}-#{tutorial_id}-course.ipynb")
104
# page2 = PageWithoutAFile.new(site, '', dir, "#{topic_id}-#{tutorial_id}-course.ipynb")
105
# page2.content = JSON.pretty_generate(no_solutions)
106
# page2.data['layout'] = nil
107
# page2.data['citation_target'] = 'jupyter'
108
# site.pages << page2
109
110
site.config['__rendered_notebook_cache'][page.path] = {
111
'dir' => ipynb_dir,
112
'path1' => ipynb_path,
113
'content1' => JSON.pretty_generate(json_boxify(with_solutions, page)),
114
'path2' => ipynb_path2,
115
'content2' => JSON.pretty_generate(json_boxify(no_solutions, page)),
116
}
117
end
118
end
119
120
def jupyter_post_write(site)
121
site.config['__rendered_notebook_cache'].each do |_path, info|
122
# Create if missing
123
FileUtils.mkdir_p(info['dir'])
124
# Write it out!
125
File.write(info['path1'], info['content1'])
126
File.write(info['path2'], info['content2'])
127
end
128
end
129
130
Jekyll::Hooks.register :site, :pre_render do |site|
131
jupyter_pre_render(site)
132
end
133
134
# Basically like `PageWithoutAFile`, we just write out the ones we'd created earlier.
135
Jekyll::Hooks.register :site, :post_write do |site|
136
jupyter_post_write(site)
137
end
138
139