Path: blob/main/_plugins/notebook-jupyter.rb
1677 views
require 'json'1require 'fileutils'2require './_plugins/notebook'3require './_plugins/gtn'45def json_boxify(h, page)6h['cells'].each do |cell|7# If it's a list, loop8if cell['source'].is_a? Array9cell['source'].each do |line|10# rubocop:disable Layout/LineLength11line.gsub!(%r{<(?<boxclass>#{Gtn::Boxify.box_classes})-title( ?(?<noprefix>noprefix))>(?<title>.*?)</\s*\k<boxclass>-title\s*>}) do12# rubocop:enable Layout/LineLength13m = Regexp.last_match14box_type = m[:boxclass]15title = m[:title]16noprefix = m[:noprefix]17_, box = Gtn::Boxify.generate_title(box_type, title, lang, page.path, noprefix: noprefix)18box19end20end21else22# rubocop:disable Layout/LineLength23cell['source'].gsub!(%r{<(?<boxclass>#{Gtn::Boxify.box_classes})-title(?<noprefix>\s+noprefix)?>(?<title>.*?)</\s*\k<boxclass>-title\s*>}) do24# rubocop:enable Layout/LineLength25m = Regexp.last_match26box_type = m[:boxclass]27title = m[:title]28noprefix = m[:noprefix]29_, box = Gtn::Boxify.generate_title(box_type, title, 'en', page.path, noprefix: noprefix)30box31end32end33end34h35end3637def jupyter_pre_render(site)38Jekyll.logger.info '[GTN/Notebooks] Rendering'3940site.config['__rendered_notebook_cache'] = {}4142# For every tutorial with the 'notebook' key in the page data43site.pages.select { |page| Gtn::Notebooks.notebook_filter(page.data) }.each do |page|44# We get the path to the tutorial source45dir = File.dirname(File.join('.', page.url))46fn = File.join('.', page.url).sub(/html$/, 'md')47notebook_language = page.data['notebook'].fetch('language', 'python')4849# Tag our source page50page.data['tags'] = page.data['tags'] || []51page.data['tags'].push('jupyter-notebook')5253Jekyll.logger.info "[GTN/Notebooks] Rendering #{notebook_language} #{fn}"54last_modified = Gtn::ModificationTimes.obtain_time(page.path)55notebook = Gtn::Notebooks.render_jupyter_notebook(page.data, page.content, page.url, last_modified,56notebook_language, site, dir)5758topic_id = dir.split('/')[-3]59tutorial_id = dir.split('/')[-1]60with_solutions = notebook.clone6162with_solutions['cells'] = with_solutions['cells'].map do |cell|63if cell.fetch('cell_type') == 'markdown' && (cell['source'].is_a? String)64m = cell['source'].match(/<blockquote class="solution"[^>]*>/)65if m66cell['source'].gsub!(/<blockquote class="solution"[^>]*>/,67'<br/><details style="border: 2px solid #B8C3EA; margin: 1em 0.2em;' \68'padding: 0.5em; cursor: pointer;"><summary>👁 View solution</summary>')6970idx = m.begin(0)71q = cell['source'][0..idx]72w = cell['source'][idx + 1..]73e = w.index('</blockquote>')74r = "#{w[0..e - 1]}</details>#{w[e + 13..]}"7576cell['source'] = q + r77end78end79cell80end8182# Write it out!83ipynb_dir = File.join(site.dest, dir)84ipynb_path = File.join(ipynb_dir, "#{topic_id}-#{tutorial_id}.ipynb")85# page2 = PageWithoutAFile.new(site, '', dir, "#{topic_id}-#{tutorial_id}.ipynb")86# page2.content = JSON.pretty_generate(with_solutions)87# page2.data['layout'] = nil88# page2.data['citation_target'] = 'jupyter'89# site.pages << page29091# Create a no-solutions version:92no_solutions = notebook.clone9394no_solutions['cells'] = no_solutions['cells'].map do |cell|95if cell.fetch('cell_type') == 'markdown' && (cell['source'].is_a? String)96cell['source'].gsub!(/<blockquote class="solution"[^>]*>/,97'<blockquote class="solution" style="display:none">')98end99cell100end101102ipynb_path2 = File.join(ipynb_dir, "#{topic_id}-#{tutorial_id}-course.ipynb")103# page2 = PageWithoutAFile.new(site, '', dir, "#{topic_id}-#{tutorial_id}-course.ipynb")104# page2.content = JSON.pretty_generate(no_solutions)105# page2.data['layout'] = nil106# page2.data['citation_target'] = 'jupyter'107# site.pages << page2108109site.config['__rendered_notebook_cache'][page.path] = {110'dir' => ipynb_dir,111'path1' => ipynb_path,112'content1' => JSON.pretty_generate(json_boxify(with_solutions, page)),113'path2' => ipynb_path2,114'content2' => JSON.pretty_generate(json_boxify(no_solutions, page)),115}116end117end118119def jupyter_post_write(site)120site.config['__rendered_notebook_cache'].each do |_path, info|121# Create if missing122FileUtils.mkdir_p(info['dir'])123# Write it out!124File.write(info['path1'], info['content1'])125File.write(info['path2'], info['content2'])126end127end128129Jekyll::Hooks.register :site, :pre_render do |site|130jupyter_pre_render(site)131end132133# Basically like `PageWithoutAFile`, we just write out the ones we'd created earlier.134Jekyll::Hooks.register :site, :post_write do |site|135jupyter_post_write(site)136end137138139