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

Added Documentation Folder #53

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
62ea570
first doc commit
mrzengel Aug 5, 2024
cd74a33
page test
mrzengel Aug 5, 2024
63256b1
theme test
mrzengel Aug 5, 2024
f157f3c
Section test
mrzengel Aug 5, 2024
18ae598
section test
mrzengel Aug 5, 2024
dec4809
removed test page
mrzengel Aug 5, 2024
1d44027
cleaned up folder
mrzengel Aug 5, 2024
a861c9d
renamed files
mrzengel Aug 6, 2024
c70abfe
developed basic installation instructions
mrzengel Aug 6, 2024
30a0451
added tags to subheadings in install
mrzengel Aug 6, 2024
a545ab9
fixed tags
mrzengel Aug 6, 2024
b2e5e7b
fix
mrzengel Aug 6, 2024
35752be
updated home page
mrzengel Aug 6, 2024
39fdef6
developed new page
mrzengel Aug 6, 2024
255e30c
added content
mrzengel Aug 6, 2024
a5664cf
updates
mrzengel Aug 6, 2024
fbef87a
adding content
mrzengel Aug 6, 2024
2176ebe
file structure test
mrzengel Aug 6, 2024
c29907d
folder structure
mrzengel Aug 6, 2024
f26e3d5
test
mrzengel Aug 6, 2024
c97474d
added more content
mrzengel Aug 6, 2024
ca7ab41
more content
mrzengel Aug 6, 2024
8caea77
developed backend md
mrzengel Aug 6, 2024
9c948b0
added content
mrzengel Aug 6, 2024
1da74c0
edit
mrzengel Aug 6, 2024
9a90a9c
added styling
mrzengel Aug 7, 2024
99ffb27
added frontend content
mrzengel Aug 7, 2024
0210b7d
test
mrzengel Aug 7, 2024
260d2d2
navigation test
mrzengel Aug 7, 2024
70d39e8
navigation test 2
mrzengel Aug 7, 2024
5b8ceec
slight fixes
mrzengel Aug 7, 2024
3649c5c
Merge branch 'main' into documentation
mrzengel Aug 8, 2024
87fb6e6
fixed schema
mrzengel Aug 9, 2024
3340f3d
requested changes
mrzengel Sep 15, 2024
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ Zenodo JupyterLab plugin
This project integrates [Zenodo](https://zenodo.org) into Jupyter Lab extension.

# Requirements
JupyterLab > 4, < 5
Notebook < 7
* JupyterLab > 4, < 5
* Notebook < 7
* [eOSSR](https://gitlab.com/escape-ossr/eossr)

# Install
You will need NodeJS >= 20 for these steps.
Expand Down
11 changes: 11 additions & 0 deletions docs/_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
title: "Zenodo Jupyter Lab Extension"
description: "Documentation for VRE Hub Zenodo Jupyter Lab Extension"
remote_theme: pmarsceill/just-the-docs
color_scheme: dark

sass:
load_paths:
- _sass

plugins:
- jekyll-sass-converter
4 changes: 4 additions & 0 deletions docs/_sass/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* _sass/custom.css */
h4 {
font-size: 1.5em; /* Adjust this value as needed */
}
176 changes: 176 additions & 0 deletions docs/documentation/backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
title: "Backend Documentation"
parent: "Full Documentation"
nav_order: 2
---

# Backend Documentation

## General Framework
The backend for this extension is built as a Jupyter Server Extension. The project entry points are specified with the `pyproject.toml` file in the root directory. These point to the `zenodo_jupyterlab.server` module, which contains the `extenion.py` and `__init__.py` files which run the function that sets up the API handlers defined within other files in that directory. This guide will go through each section, with explanation of functionality.

## Files in `zenodo_jupyterlab/server/`
* [`extension.py`](#extension)
* [`__init__.py`](#init)
* [`handlers.py`](#handlers)
* [`search.py`](#search)
* [`testConnection.py`](#testConnection)
* [`upload.py`](#upload)

## `extension.py` {#extension}
`_load_jupyter_server_extenion` is a basic function that calls on `setup_handlers` which is defined in `handlers.py` and passes the `server_app.web_app` object. This is automatically passed via the server extension points.

## `__init__.py` {#init}
Defines the `_jupyter_server_extension_points` function that signals to the primary extension in `pyproject.toml` how to access the server extension and to build when it built.

## `handlers.py` {#handlers}
This file generates the API endpoints for use by the frontend components. All handlers inherit from `jupyter_server.base.handlers.APIHandler` (except the XSRFTokenHandler inherits from JupyterHandler). For simplicity, parameters are placed within the function definitions here, though they are accessed via the `APIHandler.get_argument()` function because they are URI encoded in the API calls. In addition, return statements are defined plainly here, though they are actually returned via the `APIHandler.finish()` function.

### `class EnvHandler`
Interacts with environmental variables in the Jupyter instance. Exploits the `os` module.
#### `get(env_var: string)`
Simple function to return the value of a stored environmental variable. Returns a dict containing the variable name and value.
> **Returns:** `{env_var: *value*}`

#### `post(key: string, value: string)`
Simple function to set the value of an environmental variable. Returns a dict with the parameters.
> **Returns:** `{key, value}

### `class XSRFTokenHandler`
Note: this inherits from JupyterHandler, not APIHandler.

#### `get(self: JupyterHandler)`
Accesses JupyterHandler.xsrf_token and returns it.
> **Returns:** {'xsrfToken': xsrf_token}

### `class Search RecordHandler`
Handler designed to interact with `searchRecords` function defined in [`search.py`](#search).

#### `get(search_field: string, page: int, communities: string)`
Awaits response from `searchRecords` after passing all of the arguments and returns the list of corresponding records (max size: 25).
> **Returns:** {'records': *response*}

### `class SearchCommunityHandler`
Handler designed to interact with `searchCommunities` function defined in [`search.py`](#search).

#### `get(search_field: string, page: int)`
Awaits response from `searchCommunities` after passing all of the arguments and returns the list of corresponding communities (max size: 25).
> **Returns:** {'communities': *response*}

### `class RecordInfoHandler`
Handler designed to interact with `recordInformation` function defined in [`search.py`](#search).

#### `get(record-id: string)`
Awaits response from `recordInformation` after passing the `record-id` and returns the desired data.
> **Returns:** {'data': *response*}

### `class FileBrowserHandler`
Interacts with environmental information about the folder and files in the `$HOME` directory and child directories.

#### `get(path: string)
Pulls the Jupyter instance home directory from the `$HOME` environmental variable. Then appends the path passed to this to generate a relative path and verifies whether it exists (if not, returns an "error"). Exploits the `os.listdir` function to iterate through entries within the folder. Note: it explicitly ignores all entries that start with ".", though, in principle, these should be excluded by `listdir`. It then accumulates a list of directories of entry information:
```
entry = {
"name": file name,
"type": directory or file,
"path": relative path from home directory,
"modified": isoformatted timestamp of last modification,
"size": size
}
```
This information is all drawn from the returned data from `os.listdir`.
> **Returns:** {'entries': *list of entry dictionaries*}

### `class ZenodoAPIHandler`
Interacts with all functions that make calls to the `eossr.api.zenodo.ZenodoAPI` object. They are contained within this class to limit the need of generating new `ZenodoAPI` objects with each interaction.

#### `property zAPI`
Once logged in, this will hold a `ZenodoAPI` object initialized with the input access token and sandbox boolean. As long as they do not log in again, this object will remain for the lifetime of the Jupyter instance.

#### `post(form_data: json dictionary)`
Takes in a `form_data` JSON dictionary, that contains at least an `action` entry, which specifies which code to run.

##### `if action == 'check-connection'`
Verifies the validity of a Zenodo access token via the `checkZenodoConnection` function defined in [`testConnection.py`](#testConnection). If valid, sets `zAPI` to an initialized `ZenodoAPI` object. Returns the response from the called function.
> **Returns:** {'status': *response*}

##### `if action == 'upload'`
Interacts with the `upload` function defined in `upload.py`. If `zAPI` is not yet initialized, the function returns a status of "Please log in before attempting to upload." Otherwise, returns the response from the function.
> **Returns:** {'status': *response*}

### `class ServerInfoHandler`

#### `get`
Retrieves the home directory.
> **Returns:** {'root_dir': *home directory*}

### `setup_handlers(web_app)`
Defines the API endpoints for access from the frontend. Builds the urls off of the "web_app" base path and "zenodo-jupyterlab". Thus all handlers are of the form: base_path + "zenodo-jupyterlab-*action*". This function then adds these endpoints to the web_app.

## `search.py` {#search}
This files handles all search requests to Zenodo via the [`eOSSR`](https://gitlab.com/escape-ossr/eossr) library.

### `searchRecords(search_field: string, page: int, **kwargs)`
Calls the `eossr.api.zenodo.search_records` with the given arguments, as well as restricts the size of the response to 25 (i.e. passes `size = 25` as well). Parses the returned list of `eossr.api.zenodo.Record` objects and returns a list of the following kinds of dictionaries:
```
record = {
"id": Record ID,
"title": Record title,
"date": Date Published,
"resource_type": Records resource type (from within the metadata)
}
```
If this call to `eOSSR` fails, the function simply returns `["failed"]`.
> **Returns:** [list of records]

### `searchCommunities(search_field: string, page: int)`
Calls the `eossr.api.zenodo.search_communities` with the given arguments, as well as restricts the size of the response to 25 (i.e. passes `size = 25` as well). Parses the returned list of dictionaries and returns a list of the following kinds of dictionaries:
```
community = {
"id": Community ID,
"title": Community title,
"date": Date Published,
}
```
If this call to `eOSSR` fails, the function simply returns `["failed"]`.
> **Returns:** [list of communities]

### `recordInformation(recordID: string)`
Returns more specific information about a specified record via `eossr.api.zenodo.get_record`. Parses the returned data from this function and creates the following dictionary:
```
record= {
'authors': authors with affiliations as listed in the record,
'filelist': the list of files (full download links) attached to record
}
```
Note: The existing information such as title and id are still held on the frontend in the tabular display, so no need to repass that information. Secondary note: if this call fails, this function returns {"status": "failed"}
> **Returns:** *record*

## `testConnection.py` {#testConnection}
File devoted to validating Zenodo access tokens.

### `checkZenodoConnection`
Extracts the access token and sandbox boolean from the environmental variables `ZENODO_API_KEY` and `ZENODO_SANDBOX`, respectively. Parses the string value of the sandbox boolean into a Python boolean (stored as a string due to the mismatch is syntax between Typescript and Python booleans). Creates a `eossr.api.zenodo.ZenodoAPI` object with those extracted variables as arguments and stores it in `zAPI`. Then, uses the `eossr.api.zenodo.ZenodoAPI.query_user_deposits` function and extracts the status from this response, which indicates whether or not the access token is valid or if a connection can be made to the Zenodo REST API. If either stage fails, `zAPI` is initialized to `None` and the query status is set to 0.
> **Returns:** status code of the query, `zAPI`

## `upload.py` {#upload}
Devoted to generating and populating new Zenodo record deposits.

### `createDeposit(zAPI: eossr.api.zenodo.ZenodoAPI)`
Uses `eossr.api.zenodo.ZenodoAPI.create_new_deposit` to create an empty deposit. Note: whether or not this deposit exists on Zenodo or Zenodo Sandbox is entirely dependent on what option the user selected when logging in.
> **Returns:** ID of the newly created record

### `createMetadata(zAPI: eossr.api.zenodo.ZenodoAPI, recordID: int, form_data: FormData object)`
*Work in Progress*\
Extracts title from form_data and creates a JSON dict as follows:
```
json_metadata = {
"title": given title
}
```
Uses `eossr.api.zenodo.ZenodoAPI.set_deposit_metadata` to take in the recordID and the JSON data and add this metadata to the existing Zenodo object.
> **Returns:** *response*

### `upload(zAPI: eossr.api.zenodo.ZenodoAPI, form_data: FormData object)`
Verifies if zAPI has been initialized; if not, returns `None`. Calls `createDeposit` and passes `zAPI` and captures returns record ID. Then uses this record ID, form_data, and the `zAPI` to call `createMetadata`.
> **Returns:** "Success" if *response* doesn't equal `None`
Loading
Loading