Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voilite POC #1187

Merged
merged 10 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Switch to CSS-based style for light and dark theme
  • Loading branch information
trungleduc authored and martinRenou committed Feb 20, 2023
commit 6f73e67ec5f57d1937c076629afac5f2c916976c
16 changes: 11 additions & 5 deletions share/jupyter/voila/templates/lab/tree.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,19 @@
<script id="jupyter-config-data" type="application/json">
{{ page_config | tojson }}
</script>
<script src="{{ page_config['fullStaticUrl'] | e }}/treepage.js"></script>
<div id="voila-tree-main", style="display: None;">
{% if (theme != 'dark' and theme != 'light') %}
<script src="{{ page_config['fullStaticUrl'] | e }}/treepage.js"></script>
<div id="voila-tree-main" style="display: None;">
{% set mainStyle = 'style="display: None;"' %}
{% else %}
{% set mainStyle = '' %}
{% endif %}
{% elif frontend == 'voila' %}
{% set openInNewTab = "" %}
<div id="voila-tree-main">
{% set openInNewTab = '' %}
{% set mainStyle = '' %}
{% endif %}


<div id="voila-tree-main" {{mainStyle}}>
<div class="list-header">
<div class="list-header-text">
Select items to open with Voilà.
Expand Down
36 changes: 30 additions & 6 deletions voila/voilite/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import os
import shutil
from copy import deepcopy
from distutils.dir_util import copy_tree
from pathlib import Path
from typing import Dict
from typing import List as TypeList
Expand Down Expand Up @@ -156,9 +155,10 @@ def copy_static_files(self, federated_extensions: TypeList[str]) -> None:
dest_static_path = os.path.join(os.getcwd(), self.output_prefix)
if os.path.isdir(dest_static_path):
shutil.rmtree(dest_static_path, ignore_errors=False)
copy_tree(
shutil.copytree(
os.path.join(lite_static_path),
os.path.join(dest_static_path, 'build'),
dirs_exist_ok=True,
)
shutil.copyfile(
os.path.join(lite_static_path, 'services.js'),
Expand All @@ -179,17 +179,41 @@ def copy_static_files(self, federated_extensions: TypeList[str]) -> None:
name = extension['name']
full_path = os.path.join(root, name)
if os.path.isdir(full_path):
copy_tree(
full_path, os.path.join(dest_extension_path, name)
shutil.copytree(
full_path,
os.path.join(dest_extension_path, name),
dirs_exist_ok=True,
)

template_name = self.voilite_configuration.template
ignore_func = lambda dir, files: [
f
for f in files
if os.path.isfile(os.path.join(dir, f)) and f[-3:] != 'css'
]
for root in self.static_paths:
abspath = os.path.abspath(root)
if os.path.exists(abspath):
shutil.copytree(
abspath,
os.path.join(
dest_static_path,
'voila',
'templates',
template_name,
'static',
),
ignore=ignore_func,
dirs_exist_ok=True,
)

# Copy themes files
all_themes = find_all_lab_theme()
for theme in all_themes:
theme_dst = os.path.join(
dest_static_path, 'build', 'themes', theme[0]
)
copy_tree(theme[1], theme_dst)
shutil.copytree(theme[1], theme_dst, dirs_exist_ok=True)

def convert_notebook(
self,
Expand Down Expand Up @@ -237,7 +261,7 @@ def convert_directory(self, page_config):
voilite_configuration=self.voilite_configuration,
contents_manager=self.contents_manager,
base_url=self.base_url,
page_config=page_config
page_config=page_config,
)
nb_paths = tree_exporter.from_contents()
for nb in nb_paths:
Expand Down
92 changes: 53 additions & 39 deletions voila/voilite/voilite_tree_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from jupyter_server.services.contents.largefilemanager import LargeFileManager
from jupyter_server.utils import url_path_join, url_escape
from ..configuration import VoilaConfiguration
from ..utils import find_all_lab_theme
from ..utils import find_all_lab_theme, create_include_assets_functions
from copy import deepcopy


Expand All @@ -17,7 +17,7 @@ def __init__(
contents_manager: LargeFileManager,
base_url: str,
page_config: Dict,
output_prefix: str = "_output",
output_prefix: str = '_output',
**kwargs,
):

Expand All @@ -29,7 +29,7 @@ def __init__(

self.allowed_extensions = list(
voilite_configuration.extension_language_mapping.keys()
) + [".ipynb"]
) + ['.ipynb']
self.output_prefix = output_prefix
self.notebook_paths = []

Expand All @@ -42,73 +42,75 @@ def filter_extensions(self, page_config: Dict) -> Dict:
all_theme_name = [x[0] for x in all_themes]
filtered_extension = list(
filter(
lambda x: x["name"] in all_theme_name,
page_config_copy["federated_extensions"],
lambda x: x['name'] in all_theme_name,
page_config_copy['federated_extensions'],
)
)
page_config_copy["federated_extensions"] = filtered_extension
page_config_copy['federated_extensions'] = filtered_extension
return page_config_copy

def allowed_content(self, content: Dict) -> bool:
if content["type"] == "notebook":
if content['type'] == 'notebook':
return True
if content["type"] == "directory" and content["name"] != "_output":
if content['type'] == 'directory' and content['name'] != '_output':
return True
__, ext = os.path.splitext(content.get("path"))
__, ext = os.path.splitext(content.get('path'))
return ext in self.allowed_extensions

def from_contents(self, **kwargs) -> Tuple[str, List[str]]:

self.resources = self.init_resources(**kwargs)
self.template = self.jinja2_env.get_template("tree.html")
self.template = self.jinja2_env.get_template('tree.html')

return self.write_contents()

def generate_breadcrumbs(self, path: str) -> List:
breadcrumbs = [(url_path_join(self.base_url, "voila/tree"), "")]
parts = path.split("/")
breadcrumbs = [(url_path_join(self.base_url, 'voila/tree'), '')]
parts = path.split('/')
for i in range(len(parts)):
if parts[i]:
link = url_path_join(
self.base_url,
"voila/tree",
'voila/tree',
url_escape(url_path_join(*parts[: i + 1])),
)
breadcrumbs.append((link, parts[i]))
return breadcrumbs

def generate_page_title(self, path: str) -> str:
parts = path.split("/")
parts = path.split('/')
if len(parts) > 3: # not too many parts
parts = parts[-2:]
page_title = url_path_join(*parts)
if page_title:
return page_title + "/"
return page_title + '/'
else:
return "Voilà Home"
return 'Voilà Home'

def write_contents(self, path="") -> Tuple[Dict, List[str]]:
def write_contents(self, path='') -> Tuple[Dict, List[str]]:
cm = self.contents_manager
if cm.dir_exists(path=path):
if cm.is_hidden(path) and not cm.allow_hidden:
print("Refusing to serve hidden directory, via 404 Error")
print('Refusing to serve hidden directory, via 404 Error')
return

breadcrumbs = self.generate_breadcrumbs(path)
page_title = self.generate_page_title(path)
contents = cm.get(path)

contents["content"] = sorted(contents["content"], key=lambda i: i["name"])
contents["content"] = list(
filter(self.allowed_content, contents["content"])
contents['content'] = sorted(
contents['content'], key=lambda i: i['name']
)
contents['content'] = list(
filter(self.allowed_content, contents['content'])
)

for file in contents["content"]:
if file["type"] == "notebook":
self.notebook_paths.append(file["path"])
file["path"] = file["path"].replace(".ipynb", ".html")
elif file["type"] == "directory":
self.write_contents(file["path"])
for file in contents['content']:
if file['type'] == 'notebook':
self.notebook_paths.append(file['path'])
file['path'] = file['path'].replace('.ipynb', '.html')
elif file['type'] == 'directory':
self.write_contents(file['path'])

page_content = self.template.render(
contents=contents,
Expand All @@ -117,30 +119,42 @@ def write_contents(self, path="") -> Tuple[Dict, List[str]]:
**self.resources,
)

output_dir = os.path.join(self.output_prefix, "voila", "tree", path)
output_dir = os.path.join(
self.output_prefix, 'voila', 'tree', path
)
if not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
with open(os.path.join(output_dir, "index.html"), "w") as f:
with open(os.path.join(output_dir, 'index.html'), 'w') as f:
f.write(page_content)

if path == "":
with open(os.path.join(self.output_prefix, "index.html"), "w") as f:
if path == '':
with open(
os.path.join(self.output_prefix, 'index.html'), 'w'
) as f:
f.write(page_content)

return self.notebook_paths

def init_resources(self, **kwargs) -> Dict:

resources = {
"base_url": self.base_url,
"page_config": self.page_config,
"frontend": "voilite",
"theme": self.theme,
"include_css": lambda x: "",
"include_js": lambda x: "",
"include_url": lambda x: "",
"include_lab_theme": lambda x: "",
'base_url': self.base_url,
'page_config': self.page_config,
'frontend': 'voilite',
'theme': self.theme,
'include_css': lambda x: '',
'include_js': lambda x: '',
'include_url': lambda x: '',
'include_lab_theme': lambda x: '',
**kwargs,
}
if self.page_config['labThemeName'] in [
'JupyterLab Light',
'JupyterLab Dark',
]:
include_assets_functions = create_include_assets_functions(
self.template_name, self.base_url
)
resources.update(include_assets_functions)

return resources