Skip to content
Snippets Groups Projects
Commit 860b4ad5 authored by Steffen Vogel's avatar Steffen Vogel :santa_tone2:
Browse files

move dynamic content to JupyterHub servces

parent 81e4c9dc
No related branches found
No related tags found
No related merge requests found
Pipeline #395884 failed
...@@ -13,7 +13,6 @@ create-missing = true ...@@ -13,7 +13,6 @@ create-missing = true
git-repository-url = "https://git.rwth-aachen.de/jupyter/documentation" git-repository-url = "https://git.rwth-aachen.de/jupyter/documentation"
git-repository-icon = "fa-gitlab" git-repository-icon = "fa-gitlab"
additional-css = [ "style.css" ] additional-css = [ "style.css" ]
additional-js = [ "script.js" ]
[output.html.redirect] [output.html.redirect]
"instructors/PreperationNewCourses.html" = "NewProfiles.html" "instructors/PreperationNewCourses.html" = "NewProfiles.html"
const serviceUrl = 'https://jupyter.rwth-aachen.de/services';
const spawnBaseUrl = "https://jupyter.rwth-aachen.de/hub/spawn";
const badgeURL = "https://jupyter.pages.rwth-aachen.de/documentation/images/badge-launch-rwth-jupyter.svg";
// taken from https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
async function getProfiles() {
let response = await fetch(serviceUrl + '/profile/api/all');
let json = await response.json();
return json.profiles;
}
function updateLink(event) {
var profileSlug = document.getElementById("link-wizard-profile");
var filePath = document.getElementById("link-wizard-path");
var outputDiv = document.getElementById("link-wizard-output");
var directLink = document.getElementById("link-wizard-direct-link");
var badgeDiv = document.getElementById("link-wizard-badge");
var badgePre_md = document.getElementById("link-wizard-badge-md");
var badgePreHtml = document.getElementById("link-wizard-badge-html");
// Strip leading slash
var path = filePath.value.replace(/^\//, '');
var slug = profileSlug.value;
if (profileSlug.length > 0) {
var link = updateDirectLink(slug, path);
// Badge Code
var badge_md = '[![](' + badgeURL + ')](' + link +')';
var badgeHtml = '<a href="' + link + '"><img src="' + badgeURL + '" /></a>';
badgePre_md.innerHTML = badge_md;
badgePreHtml.innerHTML = escapeHtml(badgeHtml);
// Link
directLink.innerHTML = '<a href="' + link + '">' + link + '</a>';
// Badge
badgeDiv.innerHTML = badgeHtml
outputDiv.style.display = 'block';
}
}
async function generateProfileDropdown(profilesSelect) {
// Fill profiles select
let profiles = await getProfiles();
for(var i = 0; i < profiles.length; i++) {
var o = document.createElement("option");
o.value = profiles[i].slug;
o.textContent = profiles[i].display_name;
profilesSelect.appendChild(o);
}
}
function updateDirectLink(profileSlug, path) {
var url = spawnBaseUrl + '?profile=' + profileSlug;
if (path && path.length > 0)
url = url + '&next=/user-redirect/lab/tree/' + encodeURIComponent(path);
return url;
}
function loadBuildStatus() {
getProfiles().then(profiles => {
for (const i in profiles) {
let profile = profiles[i];
let elm = document.getElementById('build-' + profile.slug);
if (elm) {
let build = profile.last_build;
if (build) {
elm.classList.add(build.status);
elm.innerText = build.status;
let date = new Date(build.started_at).toLocaleString();
let text = '<a href="' + build.web_url + '">#' + build.id + '</a> from ' + date + ' ran for ' + build.duration + ' secs, triggered by <a href="' + build.user.web_url + '">' + build.user.name + '</a>';
elm.insertAdjacentHTML('afterend', '<span title="' + build.commit.message + '" class="build-details">' + text + '</span>');
}
else {
elm.classList.add('unknown');
elm.innerText = 'unknown';
}
}
}
});
}
function insertVersionCollapsibles() {
let seps = this.document.getElementsByClassName('profile-sep');
for (const sep of seps) {
let slug = sep.getAttribute('data-slug');
sep.insertAdjacentHTML('beforebegin', '<h4>Package Versions</h4>');
repos = {
pip: 'PyPI / pip',
conda: 'Anaconda',
'jupyter-labextensions': 'Jupyter Lab Extensions',
'jupyter-serverextensions': 'Jupyter Server / Notebook Extensions',
'jupyter-kernelspecs': 'Jupyter Kernel'
}
for (const i in repos) {
let repo = i;
let name = repos[i];
sep.insertAdjacentHTML('beforebegin', '<div data-display-name="' + name + '" data-repo="' + repo + '" data-slug="' + slug + '" class="collapsible-content"></div>');
}
}
var colls = document.getElementsByClassName('collapsible-content');
for (const coll of colls) {
let btn = this.document.createElement('button');
let slug = coll.getAttribute('data-slug');
let repo = coll.getAttribute('data-repo');
let displayName = coll.getAttribute('data-display-name');
btn.classList.add('collapsible');
btn.innerText = displayName;
coll.insertAdjacentElement('beforebegin', btn)
btn.addEventListener('click', () => {
fetch(serviceUrl + '/profile/api/' + slug + '/versions/' + repo)
.then(response => response.json())
.then(pkgs => {
tbl = document.createElement('table');
tbl.classList.add('versions')
tbl.insertAdjacentHTML('afterbegin', '<th>Name</th><th>Version</th>');
for (const i in pkgs) {
let pkg = pkgs[i];
let url;
switch (repo) {
case 'pip': url = 'https://pypi.org/project/' + pkg.name + '/' + pkg.version + '/'; break;
case 'conda': url = 'https://anaconda.org/anaconda/' + pkg.name; break;
case 'jupyter-labextensions': url = 'https://www.npmjs.com/package/' + pkg.name + '/v/' + pkg.version; break;
case 'jupyter-serverextensions': url = 'https://pypi.org/project/' + pkg.name + (pkg.version ? '/' + pkg.version + '/' : ''); break;
}
let name = url
? '<a href="' + url + '">' + pkg.name + '</a>'
: pkg.name;
let row;
switch (repo) {
case 'pip':
case 'conda':
case 'jupyter-labextensions':
case 'jupyter-serverextensions':
row = '<tr><td>' + name + '</td><td>' + pkg.version + '</td></tr>';
break;
case 'jupyter-kernelspecs':
row = '<tr><td>' + pkg.spec.display_name + '</td></tr>';
break;
default:
row = '<tr><td>' + pkg.name + '</td></tr>';
}
tbl.insertAdjacentHTML('beforeend', row);
}
coll.innerHTML = '';
coll.appendChild(tbl);
});
btn.classList.toggle('collapsible-active');
if (coll.style.display === 'block')
coll.style.display = 'none';
else
coll.style.display = 'block';
});
}
}
window.addEventListener('load', e => {
if (window.location.pathname.endsWith('/Profiles.html')) {
insertVersionCollapsibles();
loadBuildStatus();
}
if (window.location.pathname.endsWith('/Links.html')) {
var profileSelect = document.getElementById("link-wizard-profile");
generateProfileDropdown(profileSelect);
}
});
# Jupyter Profiles
The _RWTHjupyter_ cluster provides a variety of different profiles (run-time environments) for different Jupyter kernels or lectures.
The following list is automatically updated by our continous integration system.
## Dependencies
[![Dependency Graph](graph.svg)](graph.svg)
(Click on the figure to zoom in)
## List
{{ #include ProfileList.md }}
...@@ -5,14 +5,15 @@ ...@@ -5,14 +5,15 @@
- [Access](Access.md) - [Access](Access.md)
- [Terms Of Use](TermsOfUse.md) - [Terms Of Use](TermsOfUse.md)
- [Frequently Asked Questions](FAQ.md) - [Frequently Asked Questions](FAQ.md)
- [Profiles](Profiles.md) - [Profiles](https://jupyter.rwth-aachen.de/services/profile/list)
- [Usage](usage/README.md) - [Usage](usage/README.md)
- [Shared Folders](usage/SharedFolders.md) - [Shared Folders](usage/SharedFolders.md)
- [Shared Notebooks](usage/SharedNotebooks.md) - [Shared Notebooks](usage/SharedNotebooks.md)
- [Links](usage/Links.md) - [Links](usage/Links.md)
- [Link Wizard](https://jupyter.rwth-aachen.de/services/link)
- [Instructors](instructors/README.md) - [Instructors](instructors/README.md)
- [New Profiles](instructors/NewProfiles.md) - [New Profiles](instructors/NewProfiles.md)
- [Statistics](Statistics.md) - [Statistics](https://jupyter.rwth-aachen.de/services/statistics/)
- [Contact & Support](Contact.md) - [Contact & Support](Contact.md)
[Legal](Legal.md) [Legal](Legal.md)
# Statistics
<iframe src="https://grafana.jupyter.rwth-aachen.de/d/AN4KQwWiz/jupyterhub?from=now-1d&to=now&orgId=1&refresh=1m&kiosk" width="100%" height="1600" frameborder="0">
</iframe>
...@@ -5,34 +5,7 @@ They can be used for your README.md, Websites or Moodle. ...@@ -5,34 +5,7 @@ They can be used for your README.md, Websites or Moodle.
## Link creation wizard ## Link creation wizard
You can use the following wizard to generate links and badges which open specific notebooks and profiles. Feel free to use the [link creation wizard](https://jupyter.rwth-aachen.de/services/link) to create links the the schemes described below.
<html>
<div class="link-wizard">
<form>
<fieldset>
<label for="link-wizard-profile">Jupyter Profile</label><br>
<select onchange="updateLink(event)" id="link-wizard-profile">
<option selected="selected" value="">Select profile</option>
</select>
</fieldset>
<fieldset>
<label for="link-wizard-path">Path to a notebook file (optional)</label><br>
<input oninput="updateLink(event)" onkeydown="event.stopPropagation()" type="text" id="link-wizard-path" placeholder="Index.ipynb">
</fieldset>
</form>
<div id="link-wizard-output">
<h3>Link</h3>
<pre id="link-wizard-direct-link"></pre>
<h3>Badge</h3>
<div id="link-wizard-badge"><img src="https://jupyter.pages.rwth-aachen.de/documentation/images/badge-launch-rwth-jupyter.svg" alt="RWTHjupyter badge"></div>
<h5>Markdown Snippet</h5>
<pre id="link-wizard-badge-md"></pre>
<h5>HTML Snippet</h5>
<pre id="link-wizard-badge-html"></pre>
</div>
</div>
</html>
## Spawn and open Notebook ## Spawn and open Notebook
......
.content main { .content main {
max-width: 90%; max-width: 90%;
} }
span.build-badge {
font-weight: bold;
border-radius: 6px;
padding: 0px 6px;
}
span.build-details {
padding-left: 10px;
}
span.success {
background-color: lightgreen;
}
span.failed {
background-color: red;
}
span.canceled {
background-color: lightgrey;
}
span.skipped {
background-color: blue;
}
span.manual {
background-color: violet;
}
.collapsible {
background-color: #f1f1f1;
color: #444;
cursor: pointer;
border: none;
padding: 6px;
width: 100%;
text-align: left;
outline: none;
font-size: 15px;
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.collapsible-active, .collapsible:hover {
background-color: #ccc;
}
/* Style the collapsible content. Note: hidden by default */
.collapsible-content {
padding: 0 18px;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
.collapsible:after {
content: '\02795'; /* Unicode character for "plus" sign (+) */
font-size: 13px;
color: white;
float: right;
margin-left: 5px;
}
.collapsible-active:after {
content: "\2796"; /* Unicode character for "minus" sign (-) */
}
table.versions {
margin: 5px;
}
/* Link Wizard */
.link-wizard form {
border: 1px grey solid;
}
.link-wizard h3 {
margin-top: 0.5em;
}
.link-wizard fieldset {
border: 0;
}
.link-wizard #link-wizard-path {
width: 100%;
}
.link-wizard #link-wizard-output {
display: none;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment