├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── test.yml ├── .gitignore ├── .npmrc ├── .yarnrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── binder ├── postBuild └── requirements.txt ├── datasets.yml ├── docs ├── development.md ├── img │ ├── interface.png │ ├── notebook.png │ └── screenshot.png └── usage.md ├── etc ├── jest │ ├── jest-environment.js │ ├── jest-puppeteer.config.js │ └── jest.config.js ├── lint-staged │ └── lint-staged.config.js ├── prettier │ ├── .prettierignore │ └── .prettierrc ├── stylelint │ └── .stylelintrc.json └── tslint │ └── tslint.json ├── notebooks └── demo.ipynb ├── package.json ├── press_release.md ├── src ├── default_graph.ts ├── find_entity.ts ├── index.ts ├── internal_url.tsx ├── linked_data_browser.tsx ├── linked_data_registry.ts ├── node_entry.tsx ├── node_object.tsx ├── sample_provider.ts ├── types.ts ├── value_object.tsx └── viewer.tsx ├── style.css ├── test └── ui │ └── test.ts ├── tsconfig.json └── tsconfig.test.json /.editorconfig: -------------------------------------------------------------------------------- 1 | #/ 2 | # @license BSD-3-Clause 3 | # 4 | # Copyright (c) 2019 Project Jupyter Contributors. 5 | # Distributed under the terms of the 3-Clause BSD License. 6 | #/ 7 | 8 | # EditorConfig configuration file (see ). 9 | 10 | # Indicate that this file is a root-level configuration file: 11 | root = true 12 | 13 | # Set properties for all files: 14 | [*] 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | # Set properties for JavaScript files: 21 | [*.js] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | # Set properties for TypeScript files: 26 | [*.ts] 27 | indent_style = space 28 | indent_size = 2 29 | 30 | # Set properties for TSX files: 31 | [*.tsx] 32 | indent_style = space 33 | indent_size = 2 34 | 35 | # Set properties for Python files: 36 | [*.py] 37 | indent_style = space 38 | indent_size = 4 39 | 40 | # Set properties for Julia files: 41 | [*.jl] 42 | indent_style = tab 43 | 44 | # Set properties for R files: 45 | [*.R] 46 | indent_style = tab 47 | 48 | # Set properties for C files: 49 | [*.c] 50 | indent_style = tab 51 | 52 | # Set properties for C header files: 53 | [*.h] 54 | indent_style = tab 55 | 56 | # Set properties for C++ files: 57 | [*.cpp] 58 | indent_style = tab 59 | 60 | # Set properties for C++ header files: 61 | [*.hpp] 62 | indent_style = tab 63 | 64 | # Set properties for Fortran files: 65 | [*.f] 66 | indent_style = space 67 | indent_size = 2 68 | insert_final_newline = false 69 | 70 | # Set properties for shell files: 71 | [*.sh] 72 | indent_style = tab 73 | 74 | # Set properties for AWK files: 75 | [*.awk] 76 | indent_style = tab 77 | 78 | # Set properties for HTML files: 79 | [*.html] 80 | indent_style = tab 81 | tab_width = 2 82 | 83 | # Set properties for CSS files: 84 | [*.css] 85 | indent_style = tab 86 | 87 | # Set properties for Makefiles: 88 | [Makefile] 89 | indent_style = tab 90 | 91 | [*.mk] 92 | indent_style = tab 93 | 94 | # Set properties for Markdown files: 95 | [*.md] 96 | indent_style = space 97 | indent_size = 4 98 | trim_trailing_whitespace = false 99 | 100 | # Set properties for `package.json` files: 101 | [package.json] 102 | indent_style = space 103 | indent_size = 2 104 | 105 | # Set properties for `datapackage.json` files: 106 | [datapackage.json] 107 | indent_style = space 108 | indent_size = 2 109 | 110 | # Set properties for `lerna.json` files: 111 | [lerna.json] 112 | indent_style = space 113 | indent_size = 2 114 | 115 | # Set properties for `tslint.json` files: 116 | [tslint.json] 117 | indent_style = space 118 | indent_size = 2 119 | 120 | # Set properties for `tsconfig.json` files: 121 | [tsconfig.json] 122 | indent_style = space 123 | indent_size = 2 124 | 125 | # Set properties for LaTeX files: 126 | [*.tex] 127 | indent_style = tab 128 | 129 | # Set properties for LaTeX Bibliography files: 130 | [*.bib] 131 | indent_style = tab 132 | 133 | # Set properties for YAML files: 134 | [*.yml] 135 | indent_style = space 136 | indent_size = 2 137 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Something isn't working as I expected. 🤔 4 | --- 5 | 6 | 7 | 8 | ## Checklist 9 | 10 | > Please ensure the following tasks are completed before filing a bug report. 11 | 12 | - [ ] Read and understood the [Code of Conduct][code-of-conduct]. 13 | - [ ] Searched for existing issues and pull requests. 14 | 15 | ## Description 16 | 17 | > Description of the issue. 18 | 19 | Encountered an error when . 20 | 21 | ## Related Issues 22 | 23 | > Does this issue have any related issues? 24 | 25 | Related issues # , # , and # . 26 | 27 | ## Questions 28 | 29 | > Any questions for reviewers? 30 | 31 | No. 32 | 33 | ## Other 34 | 35 | > Any other information relevant to this issue? This may include screenshots, references, stack traces, sample output, and/or implementation notes. 36 | 37 | #### Demo 38 | 39 | > If relevant, provide a link to a live demo. 40 | 41 | For a live demo of the issue, see 42 | 43 | - (link) 44 | 45 | #### Reproduction 46 | 47 | > What steps are required to reproduce the unexpected output? 48 | 49 | In order to reproduce this bug, do the following: 50 | 51 | - Go to '...' 52 | - Click on '...' 53 | - Scroll down to '...' 54 | 55 | #### Expected Results 56 | 57 | > What are the expected results? 58 | 59 | The following results are expected: 60 | 61 | ```text 62 | (insert expected results here) 63 | ``` 64 | 65 | #### Actual Results 66 | 67 | > What are the actual results? 68 | 69 | The following are the actual results: 70 | 71 | ```text 72 | (insert actual results here) 73 | ``` 74 | 75 | #### Environments 76 | 77 | > What environments are affected (e.g., if unique to a particular browser, `Chrome`, `IE 11`)? Include the Python package version, extension version, and any other potentially relevant platform information. 78 | 79 | The following environments are affected: 80 | 81 | - Device: 82 | - OS: 83 | - Browser: 84 | - Version: 85 | 86 | 87 | 88 | [code-of-conduct]: https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md 89 | 90 | 91 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | about: I have a suggestion (and may want to help contribute 🙂)! 4 | --- 5 | 6 | 7 | 8 | ## Checklist 9 | 10 | > Please ensure the following tasks are completed before submitting a feature request. 11 | 12 | - [ ] Read and understood the [Code of Conduct][code-of-conduct]. 13 | - [ ] Searched for existing issues and pull requests. 14 | 15 | ## Description 16 | 17 | > Description of the feature request. 18 | 19 | This feature request proposes . 20 | 21 | ## Related Issues 22 | 23 | > Does this feature request have any related issues? 24 | 25 | Related issues # , # , and # . 26 | 27 | ## Questions 28 | 29 | > Any questions for reviewers? 30 | 31 | No. 32 | 33 | ## Other 34 | 35 | > Any other information relevant to this feature request? This may include screenshots, references, sample output, and/or implementation notes. 36 | 37 | No. 38 | 39 | 40 | 41 | [code-of-conduct]: https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md 42 | 43 | 44 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Resolves # . 2 | 3 | 4 | 5 | ## Checklist 6 | 7 | > Please ensure the following tasks are completed before submitting this pull request. 8 | 9 | - [ ] Read and understand the [Code of Conduct][code-of-conduct]. 10 | - [ ] Read and understood the [licensing terms][license]. 11 | - [ ] Searched for existing issues and pull requests **before** submitting this pull request. 12 | - [ ] Filed an issue (or an issue already existed) **prior to** submitting this pull request. 13 | - [ ] Submitted against `master` branch. 14 | 15 | ## Description 16 | 17 | > What is the purpose of this pull request? 18 | 19 | This pull request: 20 | 21 | - a 22 | - b 23 | - c 24 | 25 | ## Related Issues 26 | 27 | > Does this pull request have any related issues? 28 | 29 | This pull request: 30 | 31 | - resolves # 32 | - fixes # 33 | 34 | ## Questions 35 | 36 | > Any questions for reviewers of this pull request? 37 | 38 | No. 39 | 40 | ## Other 41 | 42 | > Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes. 43 | 44 | No. 45 | 46 | 47 | 48 | [code-of-conduct]: https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md 49 | [license]: https://github.com/jupyterlab/jupyterlab-metadata-service/blob/master/LICENSE 50 | 51 | 52 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | jobs: 3 | lint: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - uses: actions/checkout@v1 7 | - uses: actions/setup-python@v1 8 | with: 9 | python-version: "3.6" 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: "10.x" 13 | - run: python -m pip install --upgrade pip 14 | - run: pip install jupyterlab 15 | - run: jlpm 16 | - run: jlpm run lint:check 17 | test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v1 21 | - uses: actions/setup-python@v1 22 | with: 23 | python-version: "3.6" 24 | - uses: actions/setup-node@v1 25 | with: 26 | node-version: "10.x" 27 | - run: python -m pip install --upgrade pip 28 | - run: pip install jupyterlab 29 | - run: jlpm run build 30 | - run: jupyter labextension install . @jupyterlab/dataregistry-extension --debug-log-path log.txt 31 | - if: failure() 32 | run: cat log.txt 33 | - run: jlpm run test 34 | - name: upload screenshots 35 | if: failure() 36 | uses: actions/upload-artifact@v1 37 | with: 38 | name: screenshots 39 | path: screenshots 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #/ 2 | # @license BSD-3-Clause 3 | # 4 | # Copyright (c) 2019 Project Jupyter Contributors. 5 | # Distributed under the terms of the 3-Clause BSD License. 6 | #/ 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # celery beat schedule file 86 | celerybeat-schedule 87 | 88 | # SageMath parsed files 89 | *.sage.py 90 | 91 | # Environments 92 | .env 93 | .venv 94 | env/ 95 | venv/ 96 | ENV/ 97 | env.bak/ 98 | venv.bak/ 99 | 100 | # Spyder project settings 101 | .spyderproject 102 | .spyproject 103 | 104 | # Rope project settings 105 | .ropeproject 106 | 107 | # mkdocs documentation 108 | /site 109 | 110 | # mypy 111 | .mypy_cache/ 112 | *.bundle.* 113 | lib/ 114 | node_modules/ 115 | *.egg-info/ 116 | .ipynb_checkpoints 117 | 118 | # npm 119 | yarn.lock 120 | package-lock.json 121 | screenshots 122 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | #/ 2 | # @license BSD-3-Clause 3 | # 4 | # Copyright (c) 2019 Project Jupyter Contributors. 5 | # Distributed under the terms of the 3-Clause BSD License. 6 | #/ 7 | 8 | # Configuration for [npm][1]. 9 | # 10 | # [1]: https://docs.npmjs.com/files/npmrc 11 | 12 | # Disable the creation of a lock file: 13 | package-lock = false 14 | shrinkwrap = false 15 | 16 | # Disable automatically "saving" dependencies on install: 17 | save = false -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | #/ 2 | # @license BSD-3-Clause 3 | # 4 | # Copyright (c) 2019 Project Jupyter Contributors. 5 | # Distributed under the terms of the 3-Clause BSD License. 6 | #/ 7 | 8 | # Configuration for [yarn][1]. 9 | # 10 | # [1]: https://yarnpkg.com/lang/en/docs/yarnrc/ 11 | 12 | # Disable the creation of a lock file: 13 | --install.no-lockfile true 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | # Contribution Guidelines 11 | 12 | > Project contribution guidelines. 13 | 14 | ## Introduction 15 | 16 | First off, thanks for your interest! While this guide focuses on technical development, if you are looking to contribute to the project but are non-technical, you can still contribute! For example, you can contribute by filing issues, writing feature requests, updating documentation, providing build and infrastructure support, and helping market and promote the project, among other things. Every bit helps, and we are grateful for your time and effort! 17 | 18 | ## Code of Conduct 19 | 20 | **Before** contributing, read the [Code of Conduct][jupyter-code-of-conduct], which details the _bare minimum_ behavior expectations that the project requires of its contributors.## Contributions 21 | 22 | ### Issues 23 | 24 | When filing new issues and commenting on existing issues on this repository, please ensure that discussions are related to concrete technical issues. 25 | 26 | **Before** filing a potential bug report, 27 | 28 | - Search for existing issues and pull requests. 29 | - Try some debugging techniques to help isolate the problem, including logging inputs and outputs. 30 | 31 | If the source of the problem is a third party package, file a bug report with the relevant package author, rather than on this repository. 32 | 33 | When filing an issue, provide the following, where possible: 34 | 35 | - A description of the issue. 36 | - Links to any related issues. 37 | - The full error message, including the stacktrace. 38 | - The sequence of steps required to reproduce the issue. 39 | - A minimal working example; i.e., the smallest chunk of code that triggers the error. If the code is larger than `50` lines, consider creating a [gist][github-gist]. 40 | - The expected results. 41 | - List of affected environments; e.g., browser, browser version, `npm` version, Node.js version, operating system, and the project version. 42 | 43 | When pasting code blocks or output, use triple backticks to enable proper formatting. Surround inline code with single backticks. For other Markdown formatting tips and trips, see GitHub's [Markdown guide][github-markdown-guide]. 44 | 45 | Be aware that the `@` symbol tags users on GitHub, so **always** surround package names with backticks (e.g., `@jupyterlab/metadata-extension`). 46 | 47 | ### Code 48 | 49 | > By contributing code to the project, you are agreeing to release it under the project [license][license]. 50 | 51 | **Before** contributing code, be sure to 52 | 53 | - read and understand the [licensing terms][license]. 54 | 55 | For instructions on how to setup and configure your environment, be sure to 56 | 57 | - read and follow the [development guide][development-guide]. 58 | 59 | If you want to contribute a new feature or a breaking change to this project, be sure to 60 | 61 | - file an issue detailing the proposed change. 62 | - wait for feature request approval. 63 | - adhere to the guidance set forth in the feature request comments. 64 | 65 | If you are unfamiliar with [Git][git], the version control system used by GitHub and this project, 66 | 67 | - see the [Git][git] docs. 68 | - try a tutorial, such as the [tutorial][github-git-tutorial] provided by GitHub. 69 | 70 | Next, take a look around the project, noting the style and organization of documentation, tests, examples, and source implementations. Consistency is highly **prioritized** within this project. Thus, the more you are able to match and adhere to project conventions and style, the more likely your contribution will be accepted. While we have done our best to automate linting and style guidelines, such automation is not perfect and cannot adequately capture the inevitable exceptions and nuance to many rules. In short, the more you study existing practice, the better prepared you will be to contribute to this project. 71 | 72 | #### Step 0: GitHub 73 | 74 | Create a [GitHub account][github-signup]. The project uses GitHub exclusively for hosting source code, managing issues and pull requests, triggering continuous integration, and reporting. 75 | 76 | #### Step 1: Fork 77 | 78 | [Fork][github-fork] the repository on GitHub and clone the repository to your local machine. 79 | 80 | ```bash 81 | $ git clone https://github.com//juptyerlab-metadata-service.git 82 | ``` 83 | 84 | where `` is your GitHub username. The repository may have a large commit history, leading to slow download times. If you are not interested in code archeology, you can reduce the download time by limiting the clone [depth][git-clone-depth]. 85 | 86 | ```bash 87 | $ git clone --depth= https://github.com//juptyerlab-metadata-service.git 88 | ``` 89 | 90 | where `` refers to the number of commits you want to download (as few as `1` and as many as the entire project history). 91 | 92 | If you are behind a firewall, you may need to use the `https` protocol, rather than the `git` protocol. 93 | 94 | ```bash 95 | $ git config --global url."https://".insteadOf git:// 96 | ``` 97 | 98 | Once you have finished cloning the repository into the destination directory, you should see the folder `jupyterlab-metadata-service`. To proceed with configuring your environment, navigate to the project folder. 99 | 100 | ```bash 101 | $ cd jupyterlab-metadata-service 102 | ``` 103 | 104 | And finally, add an `upstream` [remote][git-remotes] to allow syncing changes between this repository and your local version. 105 | 106 | ```bash 107 | $ git remote add upstream git://github.com/jupyterlab/jupyterlab-metadata-service.git 108 | ``` 109 | 110 | #### Step 2: Branch 111 | 112 | For modifications intended to be included in this repository, create a new local branch. 113 | 114 | ```bash 115 | $ git checkout -b 116 | ``` 117 | 118 | where `` is the branch name. The `master` branch for this repository is protected, and direct modifications to this branch will **not** be accepted. Instead, all contributions should be made on non-master local branches, including documentation changes and other non-code modifications. 119 | 120 | #### Step 3: Write 121 | 122 | Start making your changes and/or implementing the new feature. 123 | 124 | #### Step 4: Commit 125 | 126 | Ensure that you have configured [Git][git] to know your name and email address. 127 | 128 | ```bash 129 | $ git config --global user.name "Jane Doe" 130 | $ git config --global user.email "jane.doe@example.com" 131 | ``` 132 | 133 | Add changed files and commit. 134 | 135 | ```bash 136 | $ git add files/which/changed 137 | $ git commit 138 | ``` 139 | 140 | #### Step 5: Sync 141 | 142 | To incorporate recent changes from the `upstream` repository during development, you should [rebase][git-rebase] your local branch, reapplying your local commits on top of the current upstream `HEAD`. This procedure is in contrast to performing a standard [merge][git-merge], which may interleave development histories. The rationale is twofold: 143 | 144 | 1. interleaved histories make [squashing][git-rewriting-history] commits more difficult 145 | 2. a standard merge increases the risk of incomplete/broken commits appearing in the Git history. 146 | 147 | An ideal commit history is one in which, at no point in time, is the project in a broken state. While not always possible (mistakes happen), striving for this ideal facilitates time travel and software archeology. 148 | 149 | ```bash 150 | $ git fetch upstream 151 | $ git rebase upstream/develop 152 | ``` 153 | 154 | #### Step 6: Test 155 | 156 | Tests should accompany **all** bug fixes and features. For guidance on how to write tests, consult existing tests within the project. 157 | 158 | **Before** submitting a [pull request][github-pull-request] to the `upstream` repository, ensure that all tests pass, including linting. 159 | 160 | Any [pull requests][github-pull-request] which include failing tests and/or lint errors will **not** be accepted. 161 | 162 | To run the tests: 163 | 164 | ```bash 165 | $ jlpm run test 166 | ``` 167 | 168 | You can also run the tests with a browser GUI for debugging with: 169 | 170 | ```bash 171 | $ jlpm run test:debug 172 | ``` 173 | 174 | If a test fails, a screenshot will be taken of the current state of the browser and 175 | saved in the `./screenshots` folder for debugging. 176 | 177 | If they fail in CI, this folder will be provided as an archive you can download from the Github Actions UI. 178 | 179 | #### Step 7: Push 180 | 181 | Push your changes to your remote GitHub repository. 182 | 183 | ```bash 184 | $ git push origin 185 | ``` 186 | 187 | where `` is the name of your branch. 188 | 189 | #### Step 8: Pull Request 190 | 191 | Once your contribution is ready to be incorporated in the `upstream` repository, open a [pull request][github-pull-request] against the `master` branch. A project contributor will review the contribution, provide feedback, and potentially request changes. 192 | 193 | > Receiving feedback is the most **important**, and often the most **valuable**, part of the submission process. Don't get disheartened! 194 | 195 | To make changes to your [pull request][github-pull-request], make changes to your branch. Each time you push changes to your forked repository, GitHub will automatically update the [pull request][github-pull-request]. 196 | 197 | ```bash 198 | $ git add files/which/changed 199 | $ git commit 200 | $ git push origin 201 | ``` 202 | 203 | Note that, once a [pull request][github-pull-request] has been made (i.e., your local repository commits have been pushed to a remote server), you should **not** perform any further [rewriting][git-rewriting-history] of Git history. If the history needs modification, a contributor will modify the history during the merge process. The rationale for **not** rewriting public history is that doing so invalidates the commit history for anyone else who has pulled your changes, thus imposing additional burdens on collaborators to ensure that their local versions match the modified history. 204 | 205 | #### Step 9: Land 206 | 207 | After any changes have been resolved, a contributor will approve a [pull request][github-pull-request] for inclusion in the project. Once merged, the [pull request][github-pull-request] will be updated with the merge commit, and the [pull request][github-pull-request] will be closed. 208 | 209 | Note that, during the merge process, multiple commits will often be [squashed][git-rewriting-history]. 210 | 211 | #### Step 10: Celebrate 212 | 213 | **Congratulations**! You are an official contributor to this project! Thank you for your hard work and patience! 214 | 215 | ## Notes 216 | 217 | ### GitHub 218 | 219 | - When linking to specific lines of code in an issue or a pull request, hit the `y` key while viewing a file on GitHub. Doing so reloads the page with a URL that includes the specific version of the file you are viewing. This ensures that, when you refer to specific lines, these same lines can be easily viewed in the future, even if the content of the file changes. 220 | - GitHub does not send notifications when you push a commit and update a [pull request][github-pull-request], so be sure to comment on the pull request thread to inform reviewers that you have made changes. 221 | 222 | ### Writing Tests 223 | 224 | > By contributing tests to the project, you are agreeing to release them under the project [license][license]. 225 | 226 | ### Writing Documentation 227 | 228 | > By contributing documentation to the project, you are agreeing to release it under the project [license][license]. 229 | 230 | Project documentation is localized within each package. Similar to code, you should modify documentation using [Git][git]. 231 | 232 | ## Developer's Certificate of Origin 1.1 233 | 234 | By making a contribution to this project, I certify that: 235 | 236 | - (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or 237 | - (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or 238 | - (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. 239 | - (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. 240 | 241 | ## Conclusion 242 | 243 | Phew. While the above may be a lot to remember, even for what seem like minor changes, eventually it becomes routine and part of the normal development flow. Part of the motivation for enforcing process is to ensure that all code contributions meet a certain quality threshold, thus helping reviewers focus less on non-substantive issues like style and failing tests and more on substantive issues such as contribution content and merit. Know that your patience, hard work, time, and effort are greatly appreciated! 244 | 245 | 246 | 247 | [jupyter-of-conduct]: https://jupyter.org/conduct/ 248 | [license]: https://github.com/jupyter/jupyterlab-metadata-service/blob/master/LICENSE 249 | [development-guide]: https://github.com/jupyter/jupyterlab-metadata-service/blob/master/docs/development.md 250 | [github-signup]: https://github.com/signup/free 251 | [github-pull-request]: https://help.github.com/articles/creating-a-pull-request/ 252 | [github-gist]: https://gist.github.com/ 253 | [github-markdown-guide]: https://guides.github.com/features/mastering-markdown/ 254 | [github-fork]: https://help.github.com/articles/fork-a-repo/ 255 | [github-git-tutorial]: http://try.github.io/levels/1/challenges/1 256 | [git]: http://git-scm.com/ 257 | [git-clone-depth]: https://git-scm.com/docs/git-clone#git-clone---depthltdepthgt 258 | [git-remotes]: https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes 259 | [git-rebase]: https://git-scm.com/docs/git-rebase 260 | [git-merge]: https://git-scm.com/docs/git-merge 261 | [git-rewriting-history]: https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History 262 | 263 | 264 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019 Project Jupyter Contributors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **This project has been archived by lack of maintainers** 2 | 3 | # JupyterLab Metadata Extension 4 | 5 | ![Stability Experimental][badge-stability] 6 | [![Binder][badge-binder]][binder] 7 | 8 | ```bash 9 | jupyter labextension install @jupyterlab/metadata-extension @jupyterlab/dataregistry-extension 10 | ``` 11 | 12 | This JupyterLab extension 13 | 14 | - displays linked data about the resources you are interacting with in JuyterLab. 15 | - enables other extensions to register as linked data providers to expose [JSON LD][json-ld] about an entity given the entity's URL. 16 | - exposes linked data to the user as a Linked Data viewer in the Data Browser pane. 17 | - Check out the project vision in the ["Press Release from the Future"](./press_release.md)! 18 | 19 | ## Usage 20 | 21 | [Usage docs](./docs/usage.md) 22 | 23 | ## Contributing 24 | 25 | This repository is in active development, and we welcome collaboration. For development guidance, please consult the [development guide](./docs/development.md). 26 | 27 | If you have ideas or questions, feel free to open an issue, or, if you feel like getting your hands dirty, feel free to tackle an existing issue by contributing a pull request. 28 | 29 | We try to keep the current issues relevant and matched to relevant milestones. 30 | 31 | 32 | 33 | [badge-stability]: https://img.shields.io/badge/stability-experimental-red.svg 34 | [badge-binder]: https://mybinder.org/badge_logo.svg 35 | [binder]: https://mybinder.org/v2/gh/jupyterlab/jupyterlab-metadata-service/master?urlpath=lab 36 | [json-ld]: https://json-ld.org/ 37 | 38 | 39 | -------------------------------------------------------------------------------- /binder/postBuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # @license BSD-3-Clause 4 | # 5 | # Copyright (c) 2019 Project Jupyter Contributors. 6 | # Distributed under the terms of the 3-Clause BSD License. 7 | 8 | set -o errexit 9 | set -o xtrace 10 | 11 | jlpm run build 12 | jupyter labextension link . @jupyterlab/dataregistry-extension 13 | -------------------------------------------------------------------------------- /binder/requirements.txt: -------------------------------------------------------------------------------- 1 | jupyterlab>=1.0 2 | -------------------------------------------------------------------------------- /datasets.yml: -------------------------------------------------------------------------------- 1 | children: 2 | - "https://github.com/Coleridge-Initiative/adrf-onto/wiki/Vocabulary#publication-5322c3a9d95a8dda1f1b" 3 | datasets: 4 | - url: "https://github.com/Coleridge-Initiative/adrf-onto/wiki/Vocabulary#publication-5322c3a9d95a8dda1f1b" 5 | label: "A Publication" 6 | children: 7 | - "https://github.com/Coleridge-Initiative/adrf-onto/wiki/Vocabulary#dataset-2cea7e830ce94a923473" 8 | - url: "https://github.com/Coleridge-Initiative/adrf-onto/wiki/Vocabulary#dataset-2cea7e830ce94a923473" 9 | label: "Some other dataset" 10 | snippets: 11 | Load in Pandas: "import pandas as pd; ..." -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | # Development 11 | 12 | > Development guide. 13 | 14 | ## Introduction 15 | 16 | Before we begin, development requires some setup and configuration. What follows is an overview of environment requirements and a sequence of steps for getting up and running. We use [Git][git] for version control, and for most tasks, we use [npm][npm] scripts to help us get things done quickly and effectively. For the most part, the project is able to internally manage dependencies for testing and linting, so, once you follow the steps below, you should be ready to start developing! 17 | 18 | So, without further ado, let's get you started! 19 | 20 | ## Prerequisites 21 | 22 | Development requires the following prerequisites: 23 | 24 | - [Git][git]: version control 25 | - [Python][python]: general purpose language (version `>= 3.6`) 26 | - [pip][pip]: Python package manager (version `>= 9.0.0`) 27 | - [Node.js][node-js]: JavaScript runtime (latest stable version is **strongly** recommended) 28 | - [npm][npm]: package manager (version `> 2.7.0`) 29 | - [JupyterLab][jupyterlab]: computational environment (version `>= 1.0.0`) 30 | 31 | While not required, you are encouraged to create an [Anaconda][anaconda] environment. 32 | 33 | ```bash 34 | $ conda create -n jupyterlab-metadata-service -c conda-forge python=3.6 jupyterlab nodejs 35 | ``` 36 | 37 | To activate the environment, 38 | 39 | ```bash 40 | $ conda activate jupyterlab-metadata-service 41 | ``` 42 | 43 | > **NOTE**: for each new terminal window, you'll need to explicitly activate the [Anaconda][anaconda] environment. 44 | 45 | ## Download 46 | 47 | To acquire the source code, first navigate to the parent directory in which you want to place the project [Git][git] repository. 48 | 49 | > **NOTE**: avoid directory paths which include spaces or any other shell meta characters such as `$` or `:`, as these characters can be problematic for certain build tools. 50 | 51 | ```bash 52 | $ cd /path/to/parent/destination/directory 53 | ``` 54 | 55 | Next, clone the repository. 56 | 57 | ```bash 58 | $ git clone https://github.com/jupyterlab/jupyterlab-metadata-service.git 59 | ``` 60 | 61 | If you are wanting to contribute to this GitHub repository, first [fork][github-fork] the repository and amend the previous command. 62 | 63 | ```bash 64 | $ git clone https://github.com//jupyterlab-metadata-service.git 65 | ``` 66 | 67 | where `` is your GitHub username (assuming you are using GitHub to manage public repositories). The repository may have a large commit history, leading to slow download times. If you are not interested in code archeology, you can reduce the download time by limiting the [depth][git-clone-depth]. 68 | 69 | ```bash 70 | $ git clone --depth= https://github.com//jupyterlab-metadata-service.git 71 | ``` 72 | 73 | where `` refers to the number of commits you want to download (as few as 1 and as many as the entire project history). 74 | 75 | If you are behind a firewall, you may need to use the `http` protocol, rather than the [`git`][git] protocol. 76 | 77 | ```bash 78 | $ git config --global url."https://".insteadOf git:// 79 | ``` 80 | 81 | Once you have finished cloning the repository into the destination directory, you should see the folder `jupyterlab-metadata-service`. To proceed with configuring your environment, navigate to the project folder. 82 | 83 | ```bash 84 | $ cd jupyterlab-metadata-service 85 | ``` 86 | 87 | ## Installation 88 | 89 | To install development dependencies (e.g., [Node.js][node-js] module dependencies), 90 | 91 | ```bash 92 | $ jlpm install 93 | $ jlpm install:extensions 94 | ``` 95 | 96 | where `jlpm` is the JupyterLab package manager which is bundled with [JupyterLab][jupyterlab]. 97 | 98 | ## Build 99 | 100 | To build the metadata extension, 101 | 102 | ```bash 103 | $ jlpm run build 104 | ``` 105 | 106 | If your environment has been configured correctly, the previous command should complete without errors. 107 | 108 | To build the [JupyterLab][jupyterlab] extensions found in this repository and to launch the [JupyterLab][jupyterlab] environment, 109 | 110 | ```bash 111 | $ jlpm run build:jupyter 112 | ``` 113 | 114 | ## Clean 115 | 116 | To clean your local environment, including [Node.js][node-js] module dependencies, 117 | 118 | ```bash 119 | $ jlpm run clean 120 | ``` 121 | 122 | To remove build artifacts, such as compiled JavaScript files, from repository packages, 123 | 124 | ```bash 125 | $ jlpm run clean:packages 126 | ``` 127 | 128 | To remove [JupyterLab][jupyterlab] extension artifacts, such as linked extensions, 129 | 130 | ```bash 131 | $ jlpm run clean:jupyter 132 | ``` 133 | 134 | ## Reset 135 | 136 | To clean and rebuild the metadata extension, 137 | 138 | ```bash 139 | $ jlpm run all 140 | ``` 141 | 142 | ## Watch 143 | 144 | During development, you'll likely want the metadata extension to automatically recompile and update. Accordingly, in a separate terminal window, 145 | 146 | ```bash 147 | $ jlpm run build:watch 148 | ``` 149 | 150 | which will automatically trigger recompilation upon updates to source files. 151 | 152 | In another terminal window, 153 | 154 | ```bash 155 | $ jlpm run build:jupyter:watch 156 | ``` 157 | 158 | which will launch the [JupyterLab][jupyterlab] environment and automatically update the running lab environment upon recompilation changes. 159 | 160 | ## Update 161 | 162 | If you have previously downloaded the repository using `git clone`, you can update an existing source tree from the base project directory using `git pull`. 163 | 164 | ```bash 165 | $ git pull 166 | ``` 167 | 168 | If you are working with a [forked][github-fork] repository and wish to [sync][github-fork-sync] your local repository with the [upstream][git-remotes] project (i.e., incorporate changes from the main project repository into your local repository), assuming you have [configured a remote][github-remote] which points to the upstream repository, 169 | 170 | ```bash 171 | $ git fetch upstream 172 | $ git merge upstream/ 173 | ``` 174 | 175 | where `upstream` is the remote name and `` refers to the branch you want to merge into your local copy. 176 | 177 | ## Organization 178 | 179 | The repository is organized as follows: 180 | 181 | ```text 182 | binder Binder configuration 183 | docs top-level documentation 184 | notebooks Jupyter notebooks 185 | src extension source code 186 | ``` 187 | 188 | ## Editors 189 | 190 | - This repository uses [EditorConfig][editorconfig] to maintain consistent coding styles between different editors and IDEs, including [browsers][editorconfig-chrome]. 191 | 192 | 193 | 194 | [git]: http://git-scm.com/ 195 | [python]: https://www.python.org/ 196 | [pip]: https://github.com/pypa/pip 197 | [node-js]: https://nodejs.org/en/ 198 | [npm]: https://www.npmjs.com/ 199 | [jupyterlab]: https://github.com/jupyterlab/jupyterlab 200 | [anaconda]: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html 201 | [github-fork]: https://help.github.com/articles/fork-a-repo/ 202 | [github-fork-sync]: https://help.github.com/articles/syncing-a-fork/ 203 | [github-remote]: https://help.github.com/articles/configuring-a-remote-for-a-fork/ 204 | [git-clone-depth]: https://git-scm.com/docs/git-clone#git-clone---depthltdepthgt 205 | [git-remotes]: https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes 206 | [editorconfig]: http://editorconfig.org/ 207 | [editorconfig-chrome]: https://chrome.google.com/webstore/detail/github-editorconfig/bppnolhdpdfmmpeefopdbpmabdpoefjh?hl=en-US 208 | 209 | 210 | -------------------------------------------------------------------------------- /docs/img/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterlab/jupyterlab-metadata-service/9925f6af5b1f74a6a0226687bc85a3501f508bbd/docs/img/interface.png -------------------------------------------------------------------------------- /docs/img/notebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterlab/jupyterlab-metadata-service/9925f6af5b1f74a6a0226687bc85a3501f508bbd/docs/img/notebook.png -------------------------------------------------------------------------------- /docs/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterlab/jupyterlab-metadata-service/9925f6af5b1f74a6a0226687bc85a3501f508bbd/docs/img/screenshot.png -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | The JupyterLab Metadata extension adds a Linked Data viewer to the [Data Registry][data-registry]. To view metadata about a dataset, 4 | find it in the data explorer and then select the "Linked Data" view: 5 | 6 | ![](./img/interface.png) 7 | 8 | You can also register Linked Data about entities from a notebook by outputting the `application/ld+json` MIME type: 9 | 10 | ![](./img/notebook.png) 11 | 12 | Third party extensions can depend on this extension to expose other linked data providers to register other metadata sources for users. 13 | 14 | 15 | 16 | [data-registry]: https://github.com/jupyterlab/jupyterlab-data-explorer 17 | 18 | 19 | -------------------------------------------------------------------------------- /etc/jest/jest-environment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | // Based on from https://yarnpkg.com/en/package/@rws-air/jestscreenshot 9 | 10 | const resolve = require('path').resolve; 11 | const PuppeteerEnvironment = require('jest-environment-puppeteer'); 12 | const JestScreenshot = require('@rws-air/jestscreenshot'); 13 | 14 | class CustomEnvironment extends PuppeteerEnvironment { 15 | async setup() { 16 | await super.setup(); 17 | } 18 | async teardown() { 19 | await this.global.page.waitFor(2000); 20 | await super.teardown(); 21 | } 22 | 23 | async handleTestEvent(event, state) { 24 | if (event.name === 'test_fn_failure') { 25 | const testName = state.currentlyRunningTest.name; 26 | 27 | const jestScreenshot = new JestScreenshot({ 28 | page: this.global.page, 29 | dirName: resolve(__dirname, '..', '..'), 30 | testName 31 | }); 32 | 33 | await jestScreenshot.setup(); 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Exports. 40 | */ 41 | module.exports = CustomEnvironment; 42 | -------------------------------------------------------------------------------- /etc/jest/jest-puppeteer.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | const config = { 9 | launch: { 10 | headless: process.env.HEADLESS !== 'false', 11 | slowMo: process.env.SLOWMO === 'true' 12 | }, 13 | // https://github.com/smooth-code/jest-puppeteer/tree/master/packages/jest-dev-server#options 14 | server: { 15 | command: "jupyter lab --port 8080 --no-browser --LabApp.token=''", 16 | port: 8080 17 | } 18 | }; 19 | 20 | /** 21 | * Exports. 22 | */ 23 | module.exports = config; 24 | -------------------------------------------------------------------------------- /etc/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | const resolve = require('path').resolve; 9 | const { defaults: tsjPreset } = require('ts-jest/presets'); 10 | 11 | // Resolve the root project directory: 12 | const ROOT = resolve(__dirname, '..', '..'); 13 | 14 | const config = { 15 | rootDir: ROOT, 16 | 17 | // Needed for jest-screenshots 18 | testRunner: 'jest-circus/runner', 19 | 20 | testEnvironment: resolve(__dirname, 'jest-environment.js'), 21 | globalSetup: 'jest-environment-puppeteer/setup', 22 | globalTeardown: 'jest-environment-puppeteer/teardown', 23 | setupFilesAfterEnv: ['expect-puppeteer'], 24 | transform: { 25 | ...tsjPreset.transform 26 | }, 27 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 28 | testMatch: ['**/test/**/test*.ts?(x)'], 29 | testPathIgnorePatterns: ['/build/', '/lib/', '/node_modules/'], 30 | globals: { 31 | 'ts-jest': { 32 | tsConfig: resolve(ROOT, 'tsconfig.test.json') 33 | } 34 | } 35 | }; 36 | 37 | /** 38 | * Exports. 39 | */ 40 | module.exports = config; 41 | -------------------------------------------------------------------------------- /etc/lint-staged/lint-staged.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | const resolve = require('path').resolve; 9 | 10 | const rc = resolve(__dirname, '..', 'prettier', '.prettierrc'); 11 | const ignore = resolve(__dirname, '..', 'prettier', '.prettierignore'); 12 | 13 | const config = { 14 | '**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}': [ 15 | 'prettier --config=' + rc + ' --ignore-path=' + ignore + ' --write', 16 | 'git add' 17 | ] 18 | }; 19 | 20 | /** 21 | * Exports. 22 | */ 23 | module.exports = config; 24 | -------------------------------------------------------------------------------- /etc/prettier/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/node_modules 3 | **/lib 4 | **/build 5 | **/static 6 | tests/**/coverage 7 | **/package.json 8 | .eggs 9 | .mypy_cache 10 | .ipynb_checkpoints 11 | -------------------------------------------------------------------------------- /etc/prettier/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /etc/stylelint/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard", "stylelint-config-prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /etc/tslint/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["tslint-plugin-prettier"], 3 | "rules": { 4 | "prettier": [true, { "singleQuote": true }], 5 | "align": [true, "parameters", "statements"], 6 | "await-promise": true, 7 | "ban": [ 8 | true, 9 | ["_", "forEach"], 10 | ["_", "each"], 11 | ["$", "each"], 12 | ["angular", "forEach"] 13 | ], 14 | "class-name": true, 15 | "comment-format": [true, "check-space"], 16 | "curly": true, 17 | "eofline": true, 18 | "forin": false, 19 | "indent": [true, "spaces", 2], 20 | "interface-name": [true, "always-prefix"], 21 | "jsdoc-format": true, 22 | "label-position": true, 23 | "max-line-length": [false], 24 | "member-access": false, 25 | "member-ordering": [false], 26 | "new-parens": true, 27 | "no-angle-bracket-type-assertion": true, 28 | "no-any": false, 29 | "no-arg": true, 30 | "no-bitwise": true, 31 | "no-conditional-assignment": true, 32 | "no-consecutive-blank-lines": false, 33 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 34 | "no-construct": true, 35 | "no-debugger": true, 36 | "no-default-export": false, 37 | "no-duplicate-variable": true, 38 | "no-empty": true, 39 | "no-eval": true, 40 | "no-floating-promises": true, 41 | "no-inferrable-types": false, 42 | "no-internal-module": true, 43 | "no-invalid-this": [true, "check-function-in-method"], 44 | "no-null-keyword": false, 45 | "no-reference": true, 46 | "no-require-imports": false, 47 | "no-shadowed-variable": false, 48 | "no-string-literal": false, 49 | "no-switch-case-fall-through": true, 50 | "no-trailing-whitespace": true, 51 | "no-use-before-declare": false, 52 | "no-var-keyword": true, 53 | "no-var-requires": true, 54 | "object-literal-sort-keys": false, 55 | "one-line": [ 56 | true, 57 | "check-open-brace", 58 | "check-catch", 59 | "check-else", 60 | "check-finally", 61 | "check-whitespace" 62 | ], 63 | "one-variable-per-declaration": [true, "ignore-for-loop"], 64 | "quotemark": { 65 | "options": [true, "single", "avoid-escape"], 66 | "severity": "off" 67 | }, 68 | "radix": true, 69 | "semicolon": [true, "always", "ignore-bound-class-methods"], 70 | "switch-default": true, 71 | "trailing-comma": [ 72 | false, 73 | { 74 | "multiline": "never", 75 | "singleline": "never" 76 | } 77 | ], 78 | "triple-equals": [true, "allow-null-check", "allow-undefined-check"], 79 | "typedef": [false], 80 | "typedef-whitespace": [ 81 | false, 82 | { 83 | "call-signature": "nospace", 84 | "index-signature": "nospace", 85 | "parameter": "nospace", 86 | "property-declaration": "nospace", 87 | "variable-declaration": "nospace" 88 | }, 89 | { 90 | "call-signature": "space", 91 | "index-signature": "space", 92 | "parameter": "space", 93 | "property-declaration": "space", 94 | "variable-declaration": "space" 95 | } 96 | ], 97 | "use-isnan": true, 98 | "use-strict": [false], 99 | "variable-name": [ 100 | true, 101 | "check-format", 102 | "allow-leading-underscore", 103 | "ban-keywords", 104 | "allow-pascal-case" 105 | ], 106 | "whitespace": [ 107 | true, 108 | "check-branch", 109 | "check-operator", 110 | "check-separator", 111 | "check-type" 112 | ] 113 | }, 114 | "linterOptions": { 115 | "exclude": ["../../node_modules/**/*", "../../lib/**/*"] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /notebooks/demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import IPython.display" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 2, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "data": { 19 | "application/ld+json": { 20 | "@id": "file:///output.png", 21 | "http://schema.org/name": "Some Name!" 22 | } 23 | }, 24 | "metadata": {}, 25 | "output_type": "display_data" 26 | } 27 | ], 28 | "source": [ 29 | "IPython.display.publish_display_data(\n", 30 | " {\"application/ld+json\": {\n", 31 | " \"@id\": \"file:///output.png\",\n", 32 | " \"http://schema.org/name\": \"Some Name!\"\n", 33 | " }}\n", 34 | ")" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": {}, 41 | "outputs": [ 42 | { 43 | "data": { 44 | "application/x.jupyter.relative-dataset-urls+json": [ 45 | "file:///output.png" 46 | ] 47 | }, 48 | "metadata": {}, 49 | "output_type": "display_data" 50 | } 51 | ], 52 | "source": [ 53 | "IPython.display.publish_display_data(\n", 54 | " {\"application/x.jupyter.relative-dataset-urls+json\": [\"file:///output.png\"]}\n", 55 | ")" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [] 64 | } 65 | ], 66 | "metadata": { 67 | "kernelspec": { 68 | "display_name": "Python 3", 69 | "language": "python", 70 | "name": "python3" 71 | }, 72 | "language_info": { 73 | "codemirror_mode": { 74 | "name": "ipython", 75 | "version": 3 76 | }, 77 | "file_extension": ".py", 78 | "mimetype": "text/x-python", 79 | "name": "python", 80 | "nbconvert_exporter": "python", 81 | "pygments_lexer": "ipython3", 82 | "version": "3.6.7" 83 | } 84 | }, 85 | "nbformat": 4, 86 | "nbformat_minor": 4 87 | } 88 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jupyterlab/metadata-extension", 3 | "version": "2.0.0", 4 | "description": "A JupyterLab extension to display linked data about the resources you are interacting with in JuyterLab.", 5 | "keywords": [ 6 | "jupyter", 7 | "jupyterlab", 8 | "jupyterlab-extension" 9 | ], 10 | "homepage": "https://github.com/jupyterlab/jupyterlab-metadata-service", 11 | "bugs": { 12 | "url": "https://github.com/jupyterlab/jupyterlab-metadata-service/issues" 13 | }, 14 | "license": "BSD-3-Clause", 15 | "author": "CalPoly-Quansight", 16 | "files": [ 17 | "lib/**/", 18 | "style.css" 19 | ], 20 | "style": "style.css", 21 | "main": "lib/index.js", 22 | "types": "lib/index.d.ts", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/jupyterlab/jupyterlab-metadata-service.git" 26 | }, 27 | "scripts": { 28 | "all": "jlpm run clean && jlpm run build && jlpm run build:jupyter", 29 | "all:watch": "jlpm run clean && jlpm run build && jlpm run build:jupyter:watch", 30 | "build": "jlpm run build:dev", 31 | "build:dev": "jlpm install && jlpm run clean:packages && jlpm run build:packages && jlpm run link:packages && jlpm run install:extensions && jupyter labextension list", 32 | "build:jupyter": "jupyter lab build && jupyter lab", 33 | "build:jupyter:watch": "jupyter lab build && jupyter lab --watch", 34 | "build:packages": "tsc --build", 35 | "build:watch": "tsc --build --watch --listEmittedFiles", 36 | "clean": "jlpm run clean:jupyter && jlpm clean:packages && jlpm clean:node", 37 | "clean:jupyter": "jlpm run uninstall:extensions && jlpm run unlink:packages && jupyter lab clean", 38 | "clean:node": "rimraf node_modules", 39 | "clean:packages": "rimraf lib tsconfig.tsbuildinfo", 40 | "install:extensions": "jupyter labextension install @jupyterlab/dataregistry-extension", 41 | "link": "jlpm run link:packages", 42 | "link:packages": "jupyter labextension link ./ --no-build", 43 | "lint": "jlpm run prettier && jlpm run lint:css && jlpm run lint:typescript", 44 | "lint:check": "jlpm run prettier:check && jlpm run lint:css:check && jlpm run lint:typescript:check", 45 | "lint:css": "stylelint *.css --fix --config ./etc/stylelint/.stylelintrc.json", 46 | "lint:css:check": "stylelint *.css --config ./etc/stylelint/.stylelintrc.json", 47 | "lint:typescript": "tslint --fix -c ./etc/tslint/tslint.json --project tsconfig.json '**/*{.ts,.tsx}'", 48 | "lint:typescript:check": "tslint -c ./etc/tslint/tslint.json --project tsconfig.json '**/*{.ts,.tsx}'", 49 | "prepublishOnly": "npm run clean && npm install && npm run build:packages", 50 | "prettier": "prettier --write '**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}' --config ./etc/prettier/.prettierrc --ignore-path ./etc/prettier/.prettierignore", 51 | "prettier:check": "prettier --list-different '**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}' --config ./etc/prettier/.prettierrc --ignore-path ./etc/prettier/.prettierignore", 52 | "rebuild:packages": "jlpm run clean:packages && jlpm run build:packages", 53 | "test": "env JEST_PUPPETEER_CONFIG=./etc/jest/jest-puppeteer.config.js jest --runInBand --config ./etc/jest/jest.config.js", 54 | "test:debug": "env HEADLESS=false SLOWMO=true jlpm test", 55 | "uninstall:extensions": "jupyter labextension uninstall --all --no-build", 56 | "unlink": "jlpm run unlink:packages", 57 | "unlink:packages": "jupyter labextension unlink ./ --no-build || echo 'Unlink command failed, but continuing...'" 58 | }, 59 | "husky": { 60 | "hooks": { 61 | "pre-commit": "lint-staged --config ./etc/lint-staged/lint-staged.config.js" 62 | } 63 | }, 64 | "dependencies": { 65 | "@jupyterlab/application": "^1.0.0", 66 | "@phosphor/coreutils": "^1.3.0", 67 | "jsonld": "1.6.2", 68 | "react": "^16.4.2" 69 | }, 70 | "peerDependencies": { 71 | "@jupyterlab/dataregistry": "^3.0.0", 72 | "@jupyterlab/dataregistry-extension": "^3.0.0", 73 | "rxjs": "^6.5.2" 74 | }, 75 | "devDependencies": { 76 | "@jupyterlab/dataregistry-extension": "^3.0.0", 77 | "@rws-air/jestscreenshot": "^3.0.3", 78 | "@types/expect-puppeteer": "^3.3.2", 79 | "@types/jest": "^24.0.19", 80 | "@types/jest-environment-puppeteer": "^4.3.1", 81 | "@types/jsonld": "^1.5.0", 82 | "@types/puppeteer": "^1.20.2", 83 | "husky": "^3.0.9", 84 | "jest": "^24.9.0", 85 | "jest-circus": "^24.9.0", 86 | "jest-puppeteer": "^4.3.0", 87 | "lint-staged": "^9.4.2", 88 | "prettier": "^1.18.2", 89 | "puppeteer": "^2.0.0", 90 | "rimraf": "~2.6.2", 91 | "stylelint": "^11.0.0", 92 | "stylelint-config-prettier": "^6.0.0", 93 | "stylelint-config-standard": "^19.0.0", 94 | "ts-jest": "^24.1.0", 95 | "tslint": "^5.20.0", 96 | "tslint-config-prettier": "^1.18.0", 97 | "tslint-plugin-prettier": "^2.0.1", 98 | "typescript": "~3.3.1" 99 | }, 100 | "publishConfig": { 101 | "access": "public" 102 | }, 103 | "jupyterlab": { 104 | "extension": true 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /press_release.md: -------------------------------------------------------------------------------- 1 | [back to project](./README.md) 2 | 3 | # Press Release from the Future 4 | 5 | ## JupyterLab Metadata Explorer Press Release 6 | 7 | The JupyterLab Metadata Explorer is a step forward in providing _rich context_ to data stewards, analysts, and scientists into the world of meaningful, semantically enriched linked scientific documents and data. 8 | Jupyter Notebooks and JupyterLab have succeeded as general-purpose tools to wrangle, manipulate, and analyze documents and data with programming languages. 9 | The JupyterLab audiences combine various computational documents (eg. narrative, code, applications, notebooks) and media types (e.g. figures, data) to contextualize their expertise in different scientific method. 10 | _Rich context_ metadata connects vocabularies and schema that enrich the context and meaning of information in computational documents. 11 | The JupyterLab Metadata Explorer, a feature of the rich context ecosystem, is a purpose-driven extension that exposes supplementary meaning related to data. It is designed for individuals and organizations capturing knowledge in JupyterLab and Jupyter notebooks. 12 | 13 | There is valuable contextual information–metadata–surrounding all of JupyterLab entities (notebooks, datasets, file, etc) which we call “rich context”. 14 | This _rich context_, when visible, enables the collaborative authoring of an emergent narrative around your work within JupyterLab. 15 | It empowers you to collaborate with your peers, discover new information, techniques, and results to help you make informed decisions. 16 | It gets to the heart of the underlying value of a dataset for stakeholder agencies and organizations and enables researchers to work with the data in a more effective manner. 17 | With the introduction of the JupyterLab Metadata Explorer, we enter a new phase of tooling which will surround the practitioner with _rich context_. 18 | 19 | Classical sciences, the information poor predecessor to modern science, can manage their metadata knowledge without information systems. 20 | The modern information rich landscape of data science is accelerated by global asynchronous collaborations. 21 | The velocity of modern science is too fast for individuals to manage the complex meaning in data and computational documents; the rich context ecosystem is needed for managing meaning. 22 | 23 | Specifically, we are defining metadata as multi-vocabulary knowledge graphs encoded as RDF, through JSON-LD, served by HTTP. 24 | Organizations - with their developers and scientists - collaboratively contribute to collections of interlinked descriptions of entities into knowledge graphs. 25 | These entities range from things, publications, datasets, events and people, along with other objects used in coding languages and scientific frameworks. 26 | The JupyterLab Metadata Explorer consumes an organization’s metadata catalog as knowledge graphs, to enhance interactive computing practices with supplemental meaning about the fundamental entities of data and their relationships. 27 | Ultimately, it connects knowledge generated by multi-faceted organizations of data consumers and creators. 28 | 29 | Taken together, the result is a flexible system that serves an organization’s scientists, analysts, and data stewards. 30 | Organizations with existing metadata catalogs can serve their catalogs to their JupyterLab users by linking knowledge though the metadata provider; data stewards can also supplement their organization’s knowledge through links to various external metadata providers. 31 | Projects in JupyterLab benefit doubly from the auto-generated context supplied by the previous notebooks while maintaining the agency to curate personalized domain knowledge graphs. 32 | Rich Context helps organizations, data stewards, and scientists discover information, techniques, and previous analysis extracted from datasets, notebooks, publications etc., to access the underlying value of data in a more effective manner. 33 | The end result of linking a Metadata Provider for your organization is less duplicated work and faster innovation. 34 | 35 | Metadata Catalogs already exist in various formats, are hosted in various ways by various organizations, and have many uses outside of JupyterLab. 36 | The Metadata Explorer connects the Metadata Service and Metadata Providers. 37 | It relies on well-tested and documented web standards to expose catalogs through the JupyterLab interface to end-users. 38 | The result is that the Metadata Explorer can merge many catalogs together into a unified view! 39 | **Insert gif of Metadata Explorer here** 40 | The Metadata Explorer, and the general rich context suite, aids you as your JupyterLab assistant. 41 | When the Metadata Explorer is open in JupyterLab it automatically accesses the knowledge graph, to surface links between what an end-user is working on and any associated knowledge within the graph. 42 | Keep your hands free as you browse the data that the software has linked for you. 43 | Boom, you’re a scientist collaborating with other Jupyter communities. 44 | 45 | Go download and install the JupyterLab Metadata Explorer, and the other tools in the Jupyterlab rich context ecosystem. Your metadata awaits! 46 | -------------------------------------------------------------------------------- /src/find_entity.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import { LinkedData } from './types'; 9 | 10 | /** 11 | * Returns a linked data entity associated with a specified URL. 12 | * 13 | * @private 14 | * @param entities - list of entities 15 | * @param id - URL 16 | * @returns linked data entity 17 | */ 18 | function findEntity(entities: Array, id: URL): LinkedData | undefined { 19 | return entities.find(o => o['@id'] === id.toString()); 20 | } 21 | 22 | /** 23 | * Exports. 24 | */ 25 | export { findEntity }; 26 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import { JupyterFrontEndPlugin } from '@jupyterlab/application'; 9 | import LinkedDataBrowser from './linked_data_browser'; 10 | import LinkedDataRegistry from './linked_data_registry'; 11 | import SampleProvider from './sample_provider'; 12 | 13 | export default [ 14 | LinkedDataRegistry, 15 | SampleProvider, 16 | LinkedDataBrowser 17 | ] as JupyterFrontEndPlugin[]; 18 | -------------------------------------------------------------------------------- /src/internal_url.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import React from 'react'; 9 | 10 | /** 11 | * Interface describing internal URL properties. 12 | * 13 | * @private 14 | */ 15 | interface IProps { 16 | /** 17 | * URL. 18 | */ 19 | url: string; 20 | 21 | /** 22 | * Callback invoked upon a "click" event. 23 | * 24 | * @param url - URL 25 | */ 26 | onClick: (url: URL) => void; 27 | } 28 | 29 | /** 30 | * Renders a URL. 31 | * 32 | * @private 33 | * @param props - property values 34 | * @returns a rendered URL 35 | */ 36 | function InternalURL(props: IProps) { 37 | return ( 38 | props.onClick(new URL(props.url))} 41 | > 42 | {props.url} 43 | 44 | ); 45 | } 46 | 47 | /** 48 | * Exports. 49 | */ 50 | export { InternalURL }; 51 | -------------------------------------------------------------------------------- /src/linked_data_browser.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import React from 'react'; 9 | import { Observable, combineLatest } from 'rxjs'; 10 | import { switchMap, map } from 'rxjs/operators'; 11 | import { 12 | JupyterFrontEnd, 13 | JupyterFrontEndPlugin 14 | } from '@jupyterlab/application'; 15 | import { 16 | createConverter, 17 | resolveDataType, 18 | DataTypeNoArgs 19 | } from '@jupyterlab/dataregistry'; 20 | import { 21 | IActiveDataset, 22 | IRegistry, 23 | reactDataType 24 | } from '@jupyterlab/dataregistry-extension'; 25 | import { LinkedData, LinkedDataThunk } from './types'; 26 | import { 27 | LinkedDataRegistry, 28 | LinkedDataRegistryToken 29 | } from './linked_data_registry'; 30 | import { findEntity } from './find_entity'; 31 | import { Viewer } from './viewer'; 32 | 33 | // // Define a conversion data type for compacted JSON-LD (see https://w3c.github.io/json-ld-syntax/#example-162-http-request-with-profile-requesting-a-compacted-document): 34 | // const jsonLDCompactedDataType = new DataTypeNoArgs>( 35 | // "application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted" 36 | // ); 37 | 38 | // Define a conversion data type for a linked data thunk: 39 | const linkedDataThunkDataType = new DataTypeNoArgs( 40 | 'application/x.jupyter.linked-data-thunk' 41 | ); 42 | 43 | // Define a conversion data type for JSON-LD: 44 | const jsonLDDataType = new DataTypeNoArgs>( 45 | 'application/ld+json' 46 | ); 47 | 48 | /** 49 | * Activates the plugin. 50 | * 51 | * @private 52 | * @param app - Jupyter front-end application instance 53 | * @param registry - linked data registry 54 | * @param dataRegistry - JupyterLab data registry 55 | * @param activate - activate dataset 56 | */ 57 | function activate( 58 | app: JupyterFrontEnd, 59 | registry: LinkedDataRegistry, 60 | dataRegistry: IRegistry, 61 | active: IActiveDataset 62 | ) { 63 | // Get linked data from all data types and register them in the graph: 64 | let linkedData: Array = []; 65 | dataRegistry.URLs$.pipe( 66 | switchMap(urls => 67 | combineLatest( 68 | ...([...urls] 69 | .map(url => { 70 | const res = jsonLDDataType.getDataset(dataRegistry.getURL(url)); 71 | if (!res) { 72 | return null; 73 | } 74 | return (res as Observable<{ '@id': string }>).pipe( 75 | map(r => ({ 76 | ...r, 77 | // Replace by setting base to current URL: 78 | ...{ '@id': new URL(r['@id'], url).toString() } 79 | })) 80 | ); 81 | }) 82 | .filter(e => e !== null) 83 | .flat() as Array>) 84 | ) 85 | ) 86 | ).subscribe({ 87 | next: value => { 88 | linkedData = value; 89 | } 90 | }); 91 | 92 | const provider = { 93 | get: resolver 94 | }; 95 | registry.register(provider); 96 | 97 | dataRegistry.addConverter( 98 | /** 99 | * Convert json ld to compacted form and resolve base url, so that the @id 100 | * can be relative to the dataset 101 | * 102 | * https://github.com/digitalbazaar/jsonld.js/issues/329#issuecomment-532475466 103 | */ 104 | // createConverter( 105 | // { from: jsonLDDataType, to: jsonLDCompactedDataType }, 106 | // ({ data, url }) => 107 | // data.pipe(flatMap(doc => compact(doc, {}, { base: url.toString() }))) 108 | // ), 109 | createConverter( 110 | { from: resolveDataType, to: linkedDataThunkDataType }, 111 | ({ url }) => registry.get(url) 112 | ), 113 | createConverter( 114 | { from: linkedDataThunkDataType, to: reactDataType }, 115 | ({ url, data }) => ({ 116 | type: 'Linked Data', 117 | data: ( 118 | active.next(url.toString())} 122 | /> 123 | ) 124 | }) 125 | ) 126 | ); 127 | 128 | /** 129 | * Returns a thunk to fetch linked data associated with a specified URL. 130 | * 131 | * @private 132 | * @param url - URL 133 | * @returns a function returning a promise which resolves linked data 134 | */ 135 | function resolver(url: URL) { 136 | const result = findEntity(linkedData, url); 137 | if (!result) { 138 | return null; 139 | } 140 | return async () => result; 141 | } 142 | } 143 | 144 | /** 145 | * Plugin registration data. 146 | */ 147 | const extension: JupyterFrontEndPlugin = { 148 | id: 'jupyterlab-metadata-service:data-browser', 149 | activate: activate, 150 | autoStart: true, 151 | requires: [LinkedDataRegistryToken, IRegistry, IActiveDataset] 152 | }; 153 | 154 | /** 155 | * Exports. 156 | */ 157 | export default extension; 158 | -------------------------------------------------------------------------------- /src/linked_data_registry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import { expand, flatten } from 'jsonld'; 9 | import { Token } from '@phosphor/coreutils'; 10 | import { JupyterFrontEndPlugin } from '@jupyterlab/application'; 11 | import { LinkedDataThunk } from './types'; 12 | import { findEntity } from './find_entity'; 13 | 14 | /** 15 | * Interface describing a linked data "provider". 16 | * 17 | * @private 18 | */ 19 | interface ILinkedDataProvider { 20 | /** 21 | * Returns a thunk to fetch the linked data associated with a specified URL. 22 | * 23 | * ## Notes 24 | * 25 | * - If no information is associated with a specified URL, the method should return `null`. 26 | * 27 | * @param url - URL 28 | * @returns a function returning a promise which resolves linked data 29 | */ 30 | get(url: URL): LinkedDataThunk | null; 31 | } 32 | 33 | /** 34 | * Linked data registry class. 35 | * 36 | * @private 37 | */ 38 | class LinkedDataRegistry { 39 | /** 40 | * Registers a linked data provider. 41 | * 42 | * @param provider - linked data provider 43 | */ 44 | register(provider: ILinkedDataProvider): void { 45 | this._providers.add(provider); 46 | } 47 | 48 | /** 49 | * Returns a promise for resolving linked data associated with a specified URL. 50 | * 51 | * ## Notes 52 | * 53 | * - If a URL is not associated with linked data, the method returns `null`. 54 | * 55 | * @param url - URL 56 | * @returns a function returning a promise which resolves linked data 57 | */ 58 | get(url: URL): LinkedDataThunk | null { 59 | const thunks = [...this._providers] 60 | .map(p => p.get(url)) 61 | .filter(v => v) as Array; 62 | if (thunks.length === 0) { 63 | return null; 64 | } 65 | return async () => 66 | findEntity( 67 | (await expand( 68 | await flatten(await Promise.all(thunks.map(t => t())), null) 69 | )) as Array, 70 | url 71 | )!; 72 | } 73 | 74 | /** 75 | * List of linked data "providers". 76 | */ 77 | private _providers = new Set(); 78 | } 79 | 80 | /** 81 | * Linked data registry token. 82 | * 83 | * @private 84 | */ 85 | const LinkedDataRegistryToken = new Token('LinkedDataRegistry'); 86 | 87 | /** 88 | * Activates the plugin. 89 | * 90 | * @private 91 | * @returns a linked data registry 92 | */ 93 | function activate() { 94 | return new LinkedDataRegistry(); 95 | } 96 | 97 | /** 98 | * Plugin registration data. 99 | */ 100 | const extension: JupyterFrontEndPlugin = { 101 | id: 'jupyterlab-metadata-service:linked-data-registry', 102 | activate: activate, 103 | autoStart: true, 104 | requires: [], 105 | provides: LinkedDataRegistryToken 106 | }; 107 | 108 | /** 109 | * Exports. 110 | */ 111 | export { LinkedDataRegistry }; 112 | export { LinkedDataRegistryToken }; 113 | export default extension; 114 | -------------------------------------------------------------------------------- /src/node_entry.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import React from 'react'; 9 | import { InternalURL } from './internal_url'; 10 | import { ValueObject } from './value_object'; 11 | import { NodeObject } from './node_object'; 12 | 13 | /** 14 | * Interface describing node entry properties. 15 | * 16 | * ## Notes 17 | * 18 | * - The entries of a node object whose keys are not keywords are also called properties of the node object. See [W3C][w3c]. 19 | * 20 | * [w3c]: https://w3c.github.io/json-ld-syntax/#syntax-tokens-and-keywords 21 | * 22 | * @private 23 | */ 24 | interface IProps { 25 | /** 26 | * Keyword. 27 | */ 28 | keyword: string; 29 | 30 | /** 31 | * Node object. 32 | */ 33 | object: object; 34 | 35 | /** 36 | * Callback invoked upon a "click" event. 37 | * 38 | * @param url - URL 39 | */ 40 | onClick: (url: URL) => void; 41 | } 42 | 43 | /** 44 | * Renders a node entry. 45 | * 46 | * @private 47 | * @param props - node entry property values 48 | * @returns rendered node entry 49 | */ 50 | function NodeEntry(props: IProps) { 51 | if (props.keyword === '@id') { 52 | return ( 53 | <> 54 |
55 |
56 | 60 |
61 | 62 | ); 63 | } 64 | if (props.keyword === '@type') { 65 | return ( 66 | <> 67 |
type
68 | {(Array.isArray(props.object) ? props.object : [props.object]).map( 69 | type => ( 70 |
71 | 72 |
73 | ) 74 | )} 75 | 76 | ); 77 | } 78 | return ( 79 | <> 80 |
{props.keyword}
81 | {(Array.isArray(props.object) ? props.object : [props.object]).map( 82 | (innerObject, idx) => ( 83 |
84 | {'@value' in innerObject ? ( 85 | 86 | ) : ( 87 | 88 | )} 89 |
90 | ) 91 | )} 92 | 93 | ); 94 | } 95 | 96 | /** 97 | * Exports. 98 | */ 99 | export { NodeEntry }; 100 | -------------------------------------------------------------------------------- /src/node_object.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import React from 'react'; 9 | import { NodeEntry } from './node_entry'; 10 | 11 | /** 12 | * Interface describing [node object][1] properties. 13 | * 14 | * [1]:https://w3c.github.io/json-ld-syntax/#node-objects 15 | */ 16 | interface IProps { 17 | /** 18 | * [Node object][1]. 19 | * 20 | * [1]:https://w3c.github.io/json-ld-syntax/#node-objects 21 | */ 22 | nodeObject: object; 23 | 24 | /** 25 | * Callback invoked upon a "click" event. 26 | * 27 | * @param url - URL 28 | */ 29 | onClick: (url: URL) => void; 30 | } 31 | 32 | /** 33 | * Renders a node object. 34 | * 35 | * @private 36 | * @param props - node object property values 37 | * @returns rendered node object 38 | */ 39 | function NodeObject(props: IProps) { 40 | const entries = Object.entries(props.nodeObject); 41 | if (entries.length === 0) { 42 | return
No properties.
; 43 | } 44 | return ( 45 |
46 | {entries.map(([keyword, object]) => ( 47 | 51 | ))} 52 |
53 | ); 54 | } 55 | 56 | /** 57 | * Exports. 58 | */ 59 | export { NodeObject }; 60 | -------------------------------------------------------------------------------- /src/sample_provider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import { 9 | JupyterFrontEnd, 10 | JupyterFrontEndPlugin 11 | } from '@jupyterlab/application'; 12 | import { 13 | LinkedDataRegistry, 14 | LinkedDataRegistryToken 15 | } from './linked_data_registry'; 16 | import { findEntity } from './find_entity'; 17 | import defaultGraph from './default_graph'; 18 | 19 | /** 20 | * Returns a thunk to fetch linked data associated with a specified URL. 21 | * 22 | * @private 23 | * @param url - URL 24 | * @returns a function returning a promise which resolves linked data 25 | */ 26 | function resolver(url: URL) { 27 | const result = findEntity(defaultGraph, url); 28 | if (!result) { 29 | return null; 30 | } 31 | return async () => result; 32 | } 33 | 34 | /** 35 | * Activates the plugin. 36 | * 37 | * @private 38 | * @param _ - Jupyter front-end application instance 39 | * @param registry - linked data registry 40 | */ 41 | function activate(app: JupyterFrontEnd, registry: LinkedDataRegistry) { 42 | const provider = { 43 | get: resolver 44 | }; 45 | registry.register(provider); 46 | } 47 | 48 | /** 49 | * Plugin registration data. 50 | */ 51 | const extension: JupyterFrontEndPlugin = { 52 | id: 'jupyterlab-metadata-service:sample-provider', 53 | activate: activate, 54 | autoStart: true, 55 | requires: [LinkedDataRegistryToken] 56 | }; 57 | 58 | /** 59 | * Exports. 60 | */ 61 | export default extension; 62 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | /** 9 | * Linked data object. 10 | * 11 | * @private 12 | */ 13 | type LinkedData = object; 14 | 15 | /** 16 | * Returns a promise which resolves a linked data object. 17 | * 18 | * @private 19 | * @returns a promise which resolves a linked data object 20 | */ 21 | type LinkedDataThunk = () => Promise; 22 | 23 | /** 24 | * Exports. 25 | */ 26 | export { LinkedData }; 27 | export { LinkedDataThunk }; 28 | -------------------------------------------------------------------------------- /src/value_object.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import React from 'react'; 9 | 10 | /** 11 | * Interface describing properties for an object containing a [value object][1]. 12 | * 13 | * [1]: https://w3c.github.io/json-ld-syntax/#value-objects 14 | */ 15 | interface IProps { 16 | /** 17 | * Value object. 18 | */ 19 | valueObject: { '@value': any }; 20 | } 21 | 22 | /** 23 | * Renders a [value object][1]. 24 | * 25 | * [1]: https://w3c.github.io/json-ld-syntax/#value-objects 26 | * 27 | * @private 28 | * @param props - value object property values 29 | * @returns a rendered value object 30 | */ 31 | function ValueObject(props: IProps) { 32 | const value = props.valueObject['@value']; 33 | return ( 34 | 35 | {typeof value === 'object' ? JSON.stringify(value, null, ' ') : value} 36 | 37 | ); 38 | } 39 | 40 | /** 41 | * Exports. 42 | */ 43 | export { ValueObject }; 44 | -------------------------------------------------------------------------------- /src/viewer.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import React from 'react'; 9 | import { LinkedData, LinkedDataThunk } from './types'; 10 | import { NodeObject } from './node_object'; 11 | 12 | /** 13 | * Interface describing viewer properties. 14 | * 15 | * @private 16 | */ 17 | interface IProps { 18 | /** 19 | * URL. 20 | */ 21 | url: URL; 22 | 23 | /** 24 | * Callback invoked upon a "click" event. 25 | * 26 | * @param url - URL 27 | */ 28 | onClick: (url: URL) => void; 29 | 30 | /** 31 | * Function returning a promise which resolves linked data. 32 | */ 33 | thunk: LinkedDataThunk; 34 | } 35 | 36 | /** 37 | * Interface describing viewer state. 38 | */ 39 | interface IState { 40 | /** 41 | * Linked data. 42 | */ 43 | data?: LinkedData; 44 | } 45 | 46 | /** 47 | * Component for viewing linked data. 48 | */ 49 | class Viewer extends React.Component { 50 | /** 51 | * Viewer state. 52 | */ 53 | readonly state: IState = {}; 54 | 55 | /** 56 | * Component hook invoked prior to mounting. 57 | */ 58 | async componentWillMount() { 59 | this.setState({ data: await this.props.thunk() }); 60 | } 61 | 62 | /** 63 | * Component hook invoked after updating. 64 | * 65 | * @param prevProps - previous properties 66 | */ 67 | componentDidUpdate(prevProps: IProps) { 68 | if (this.props.url.toString() !== prevProps.url.toString()) { 69 | return this.componentWillMount(); 70 | } 71 | } 72 | 73 | /** 74 | * Renders the component. 75 | * 76 | * @returns rendered component 77 | */ 78 | render() { 79 | const { data } = this.state; 80 | if (data === undefined) { 81 | return
...
; 82 | } 83 | return ( 84 |
85 | 86 |
87 | ); 88 | } 89 | } 90 | 91 | /** 92 | * Exports. 93 | */ 94 | export { Viewer }; 95 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | .jl-metadata { 9 | padding: 16px; 10 | } 11 | 12 | .jl-metadata-node { 13 | color: var(--jp-content-font-color1); 14 | } 15 | 16 | .jl-metadata-node dt { 17 | font-weight: bold; 18 | float: left; 19 | clear: left; 20 | padding-right: 8px; 21 | max-width: 108px; 22 | overflow: hidden; 23 | text-overflow: ellipsis; 24 | white-space: nowrap; 25 | direction: rtl; 26 | } 27 | 28 | .jl-metadata-node dt::before { 29 | content: ':'; 30 | } 31 | 32 | .jl-metadata-node dd { 33 | margin: 0; 34 | padding-bottom: 8px; 35 | overflow: hidden; 36 | direction: ltr; 37 | } 38 | 39 | .jl-metadata-value { 40 | color: var(--jp-content-font-color1); 41 | } 42 | 43 | .jl-metadata-internal-url { 44 | color: #2196f3; 45 | direction: rtl; 46 | } 47 | 48 | .jl-metadata-internal-url a { 49 | color: #2196f3; 50 | overflow: hidden; 51 | white-space: nowrap; 52 | direction: rtl; 53 | text-overflow: ellipsis; 54 | display: block; 55 | } 56 | 57 | .jl-metadata-internal-url a:hover { 58 | color: #2196f3; 59 | } 60 | 61 | .jl-metadata-internal-url a:active { 62 | color: #2196f3; 63 | } 64 | .jl-metadata-id-keyword { 65 | display: none; 66 | } 67 | 68 | .jl-metadata-id-field { 69 | color: var(--jp-content-font-color1); 70 | } 71 | -------------------------------------------------------------------------------- /test/ui/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license BSD-3-Clause 3 | * 4 | * Copyright (c) 2019 Project Jupyter Contributors. 5 | * Distributed under the terms of the 3-Clause BSD License. 6 | */ 7 | 8 | import { ElementHandle } from 'puppeteer'; 9 | 10 | const { setDefaultOptions } = require('expect-puppeteer'); 11 | 12 | const timeout = 15 * 1000; 13 | 14 | jest.setTimeout(timeout); 15 | setDefaultOptions({ timeout }); 16 | 17 | async function getXPath(xpath: string): Promise> { 18 | await page.waitForXPath(xpath); 19 | const elements = await page.$x(xpath); 20 | expect(elements.length).toBe(1); 21 | return elements[0]; 22 | } 23 | 24 | function sleep(ms: number): Promise { 25 | return new Promise(resolve => setTimeout(resolve, ms)); 26 | } 27 | describe('JupyterLab', () => { 28 | beforeAll(async () => { 29 | // Load JupyterLab: 30 | await page.goto('http://localhost:8080/lab?reset'); 31 | 32 | // NOTE: depending on system resource constraints, this may NOT be enough time for JupyterLab to load and get "settled", so to speak. If CI tests begin inexplicably failing due to timeout failures, may want to consider increasing the sleep duration... 33 | await sleep(3000); 34 | 35 | // Attempt to find the data explorer tab on the page (all tests essentially presume that we can load the data explorer via the tab bar button): 36 | const el = await page.$('[title="Data Explorer"]'); 37 | if (el !== null) { 38 | // Clicking on the data explorer tab should open the data explorer, thus allowing us to test data explorer UI interactions: 39 | el.click(); 40 | } else { 41 | console.log('Unable to find expected tab.'); 42 | } 43 | }); 44 | 45 | it('should show JupyterLab logo', async () => { 46 | expect.assertions(1); 47 | await expect(page).toMatchElement('#jp-MainLogo', { visible: true } as any); 48 | }); 49 | 50 | it("show be able to show 'Data Explorer' tab", async () => { 51 | expect.assertions(1); 52 | await expect(page).toMatchElement('.jl-explorer-heading', { 53 | text: 'Datasets', 54 | visible: true 55 | } as any); 56 | }); 57 | 58 | it('should see files marker', async () => { 59 | expect.assertions(1); 60 | await expect(page).toMatchElement('h3', { 61 | text: 'file:///', 62 | visible: true 63 | } as any); 64 | }); 65 | 66 | it('should be able to expand files', async () => { 67 | expect.assertions(1); 68 | const filebutton = await getXPath('//button[../h3/text()="file:///"]'); 69 | await filebutton.click(); 70 | }); 71 | 72 | it('should see datasets.yml marker', async () => { 73 | expect.assertions(1); 74 | await expect(page).toMatchElement('h3', { 75 | text: 'datasets.yml', 76 | visible: true 77 | } as any); 78 | }); 79 | 80 | it('should be able to expand datasets.yml', async () => { 81 | expect.assertions(1); 82 | const datasetsButton = await getXPath( 83 | '//button[../h3/text()="datasets.yml"]' 84 | ); 85 | await datasetsButton.click(); 86 | }); 87 | 88 | it('should show datasets label', async () => { 89 | expect.assertions(1); 90 | await expect(page).toMatchElement('h3', { 91 | text: 'A Publication', 92 | visible: true 93 | } as any); 94 | }); 95 | 96 | it("show be able to show 'Data Browser' tab", async () => { 97 | expect.assertions(2); 98 | await expect(page).toClick('[title="Data Browser"]'); 99 | await expect(page).toMatchElement('.jl-dr-browser', { 100 | text: 'Follow active?', 101 | visible: true 102 | } as any); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "composite": true, 5 | "declaration": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "jsx": "react", 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "noUnusedLocals": true, 14 | "preserveWatchOutput": true, 15 | "resolveJsonModule": true, 16 | "outDir": "lib", 17 | "rootDir": "src", 18 | "lib": ["dom", "esnext"], 19 | "strict": true, 20 | "strictNullChecks": true, 21 | "target": "es2017" 22 | }, 23 | "include": ["src/*"] 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build/test", 5 | "rootDir": "test" 6 | }, 7 | "include": ["test/*", "test/**/*"] 8 | } 9 | --------------------------------------------------------------------------------