Skip to content

Commit

Permalink
Merge pull request #7 from HaudinFlorence/create_theme_exporter
Browse files Browse the repository at this point in the history
Create theme exporter
  • Loading branch information
HaudinFlorence authored Jan 25, 2023
2 parents b006662 + c186873 commit c070696
Show file tree
Hide file tree
Showing 22 changed files with 1,122 additions and 286 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

Extension to create a theme

This extension is composed of a Python package named `jupyter_theme_editor`
for the server extension and a NPM package named `jupyter-theme-editor`
for the frontend extension.

## Requirements

- JupyterLab >= 3.0
Expand All @@ -24,6 +28,22 @@ To remove the extension, execute:
pip uninstall jupyter_theme_editor
```

## Troubleshoot

If you are seeing the frontend extension, but it is not working, check
that the server extension is enabled:

```bash
jupyter server extension list
```

If the server extension is installed and enabled, but you are not seeing
the frontend extension, check the frontend extension is installed:

```bash
jupyter labextension list
```

## Contributing

### Development install
Expand All @@ -41,6 +61,8 @@ The `jlpm` command is JupyterLab's pinned version of
pip install -e .
# Link your development version of the extension with JupyterLab
jupyter labextension develop . --overwrite
# Server extension must be manually installed in develop mode
jupyter server extension enable jupyter_theme_editor
# Rebuild extension Typescript source after making changes
jlpm build
```
Expand All @@ -65,6 +87,8 @@ jupyter lab build --minimize=False
### Development uninstall

```bash
# Server extension must be manually disabled in develop mode
jupyter server extension disable jupyter_theme_editor
pip uninstall jupyter_theme_editor
```

Expand All @@ -74,6 +98,22 @@ folder is located. Then you can remove the symlink named `jupyter-theme-editor`

### Testing the extension

#### Server tests

This extension is using [Pytest](https://docs.pytest.org/) for Python code testing.

Install test dependencies (needed only once):

```sh
pip install -e ".[test]"
```

To execute them, run:

```sh
pytest -vv -r ap --cov jupyter_theme_editor
```

#### Frontend tests

This extension is using [Jest](https://jestjs.io/) for JavaScript code testing.
Expand Down
17 changes: 17 additions & 0 deletions binder/postBuild
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ _(sys.executable, "-m", "pip", "check")
# install the labextension
_(sys.executable, "-m", "pip", "install", "-e", ".")
_(sys.executable, "-m", "jupyter", "labextension", "develop", "--overwrite", ".")
_(
sys.executable,
"-m",
"jupyter",
"serverextension",
"enable",
"jupyter_theme_editor",
)
_(
sys.executable,
"-m",
"jupyter",
"server",
"extension",
"enable",
"jupyter_theme_editor",
)

# verify the environment the extension didn't break anything
_(sys.executable, "-m", "pip", "check")
Expand Down
8 changes: 8 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest

pytest_plugins = ("pytest_jupyter.jupyter_server", )


@pytest.fixture
def jp_server_config(jp_server_config):
return {"ServerApp": {"jpserver_extensions": {"jupyter_theme_editor": True}}}
6 changes: 5 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ module.exports = {
testPathIgnorePatterns,
transform,
automock: false,
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts'],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/.ipynb_checkpoints/*'
],
coverageDirectory: 'coverage',
coverageReporters: ['lcov', 'text'],
globals: {
Expand Down
7 changes: 7 additions & 0 deletions jupyter-config/nb-config/jupyter_theme_editor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"NotebookApp": {
"nbserver_extensions": {
"jupyter_theme_editor": true
}
}
}
7 changes: 7 additions & 0 deletions jupyter-config/server-config/jupyter_theme_editor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ServerApp": {
"jpserver_extensions": {
"jupyter_theme_editor": true
}
}
}
27 changes: 27 additions & 0 deletions jupyter_theme_editor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
from ._version import __version__
from .handlers import setup_handlers



def _jupyter_labextension_paths():
return [{
"src": "labextension",
"dest": "jupyter-theme-editor"
}]



def _jupyter_server_extension_points():
return [{
"module": "jupyter_theme_editor"
}]


def _load_jupyter_server_extension(server_app):
"""Registers the API handler to receive HTTP requests from the frontend extension.
Parameters
----------
server_app: jupyterlab.labapp.LabApp
JupyterLab application instance
"""
setup_handlers(server_app.web_app)
name = "jupyter_theme_editor"
server_app.log.info(f"Registered {name} server extension")


# For backward compatibility with notebook server - useful for Binder/JupyterHub
load_jupyter_server_extension = _load_jupyter_server_extension

47 changes: 47 additions & 0 deletions jupyter_theme_editor/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import json
from jupyter_server.base.handlers import APIHandler
from jupyter_server.utils import url_path_join
import tornado
from jinja2 import Environment, PackageLoader
from pathlib import Path

class RouteHandler(APIHandler):
# The following decorator should be present on all verb methods (head, get, post,
# patch, put, delete, options) to ensure only authorized user can request the
# Jupyter server

def initialize(self, env: Environment, path: str):
self._env = env
self._path = path

@tornado.web.authenticated
def post(self):
# input_data is a dictionary with a key "name"
input_data = self.get_json_body()
new_input_data = {}
for key, value in input_data.items():
value = str(input_data[key])
new_value = value.strip()
new_key = key.replace('--jp-', '').replace('-', '_')
new_input_data[new_key] = new_value


j2_template = self._env.get_template(self._path)
output_data = j2_template.render(new_input_data)
self.set_header("content-type", "text/css")
self.set_header("cache-control", "no-cache")
self.set_header("content-disposition",
"attachment; filename=variables.css")
self.set_header("content-length", len(output_data.encode()))
self.finish(output_data)


def setup_handlers(web_app):
host_pattern = ".*$"

base_url = web_app.settings["base_url"]
route_pattern = url_path_join(
base_url, 'jupyter-theme-editor', "send_cssProperties")
handlers = [(route_pattern, RouteHandler, {"env":Environment(loader=PackageLoader(
"jupyter_theme_editor", "templates")), "path": "template.css" })]
web_app.add_handlers(host_pattern, handlers)
Loading

0 comments on commit c070696

Please sign in to comment.