diff --git a/.gitignore b/.gitignore index 04bc8b15119d6e0ef53ce3f81cca25ed620aa25d..05625297ddc348ef051201f9fbfc116994e11d9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ client_secret .env uhepp_org/uhepp_vault/static/react +api_dev.sh +uhepp_org/oidc_dev.sh +*.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4dbdb04022c18887dfc06bba1fcbe601dc070bfa..8124642a40a6cfe200a0a762a53ec719f195fc81 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -76,6 +76,7 @@ build: - wasenweiler.sit-servers.net variables: GIT_STRATEGY: none + dependencies: [] image: name: docker/compose:1.24.0 entrypoint: ["/bin/sh", "-c"] @@ -99,6 +100,11 @@ deploy_staging: SECRET_KEY: ${STAGING_SECRET_KEY} OIDC_RP_CLIENT_SECRET: ${STAGING_OIDC_RP_CLIENT_SECRET} STAGING: 1 + BORG_PASSPHRASE: ${STAGING_BORG_PASSPHRASE} + SSH_ID: ${STAGING_SSH_ID} + BORG_REPO: ${STAGING_BORG_REPO} + CRON_PATTERN: ${STAGING_CRON_PATTERN} + PRUNE_ARGS: ${STAGING_PRUNE_ARGS} before_script: - mkdir -p .remote @@ -124,6 +130,11 @@ deploy_production: SECRET_KEY: ${PRODUCTION_SECRET_KEY} OIDC_RP_CLIENT_SECRET: ${PRODUCTION_OIDC_RP_CLIENT_SECRET} PRODUCTION: 1 + BORG_PASSPHRASE: ${PRODUCTION_BORG_PASSPHRASE} + SSH_ID: ${PRODUCTION_SSH_ID} + BORG_REPO: ${PRODUCTION_BORG_REPO} + CRON_PATTERN: ${PRODUCTION_CRON_PATTERN} + PRUNE_ARGS: ${PRODUCTION_PRUNE_ARGS} only: - master diff --git a/backup/Dockerfile b/backup/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..481aeaad068ab482f10a064362a7ede7a33a2c77 --- /dev/null +++ b/backup/Dockerfile @@ -0,0 +1,10 @@ +FROM centos:7 + +RUN yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm +RUN yum install -y cronie epel-release openssh-clients postgresql12 +RUN yum install -y borgbackup +ADD entrypoint.sh /usr/local/bin/entrypoint.sh +ADD do_backup.sh /usr/local/bin/do_backup.sh +ADD init_repo.sh /usr/local/bin/init_repo.sh +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["tail", "-f", "/var/log/borg"] diff --git a/backup/do_backup.sh b/backup/do_backup.sh new file mode 100755 index 0000000000000000000000000000000000000000..93e4b7fa3428acbb14980c9a42febfa171cf50d0 --- /dev/null +++ b/backup/do_backup.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set +e + +source /etc/borg/.env + +mkdir -p /var/dumps +pg_dump > /var/dumps/backup.sql + +export ARCHIVE=$(date --iso-8601=minutes) + +borg create ::${ARCHIVE} /var/dumps/* +borg prune --stats --list ${PRUNE_ARGS} +echo +echo diff --git a/backup/entrypoint.sh b/backup/entrypoint.sh new file mode 100755 index 0000000000000000000000000000000000000000..38d1a714d9567ba8787d5da06e469544bd8fc893 --- /dev/null +++ b/backup/entrypoint.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set +xe + +mkdir -p /etc/borg + +export BORG_RSH="ssh -o UserKnownHostsFile=/etc/borg/persistent/known_hosts -i /etc/borg/id_rsa" + +echo "export PGUSER=\"${DB_USER}\"" >> /etc/borg/.env +echo "export PGHOST=\"${DB_HOST}\"" >> /etc/borg/.env +echo "export PGPASSWORD=\"${DB_PASSWORD}\"" >> /etc/borg/.env +echo "export PGDATABASE=\"${DB_NAME}\"" >> /etc/borg/.env + +echo "export BORG_PASSPHRASE=\"${BORG_PASSPHRASE}\"" >> /etc/borg/.env +echo "export BORG_REPO=\"${BORG_REPO}\"" >> /etc/borg/.env +echo "export BORG_RSH=\"${BORG_RSH}\"" >> /etc/borg/.env +echo "export PRUNE_ARGS=\"${PRUNE_ARGS}\"" >> /etc/borg/.env + +echo "${SSH_ID}" | sed -e 's/\\n/\n/g' > /etc/borg/id_rsa +chmod 400 /etc/borg/id_rsa + +if ! borg info &> /dev/null; then + echo "Repository not ready. Please run:" + echo " docker-compose exec backup /usr/local/bin/init_repo.sh" + + while ! borg info &> /dev/null; do + sleep 10; + done + + echo " ... ready" +fi + +touch /var/log/borg +echo "${CRON_PATTERN} root /usr/local/bin/do_backup.sh 2>> /var/log/borg" >> /etc/crontab + +crond +exec "$@" + diff --git a/backup/init_repo.sh b/backup/init_repo.sh new file mode 100755 index 0000000000000000000000000000000000000000..cad1aed6ce65ff669729aed522ffa9b56155fd85 --- /dev/null +++ b/backup/init_repo.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set +e + +source /etc/borg/.env + +borg init -e repokey + diff --git a/ci/tag_latest.sh b/ci/tag_latest.sh index 3ca39e3cb5e251d64b42a5bccab55d2cd3596ffc..d0c89fdd4d6d886a1e254b4860c3ba1ec4b7cd2e 100755 --- a/ci/tag_latest.sh +++ b/ci/tag_latest.sh @@ -1,6 +1,6 @@ #!/bin/sh -for image in api webserver +for image in hub webserver backup do docker tag ${CI_REGISTRY_IMAGE}/${image}:${CI_COMMIT_SHA} ${CI_REGISTRY_IMAGE}/${image}:latest docker push ${CI_REGISTRY_IMAGE}/${image}:latest diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index a549aabdf4de16144bfa6f98456c31971aa53ba0..ebf7940c824523c09eb3ed58aa07ee9dd169b811 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,3 +5,6 @@ services: webserver: image: ${CI_REGISTRY_IMAGE}/webserver:${CI_COMMIT_SHA} + + backup: + image: ${CI_REGISTRY_IMAGE}/backup:${CI_COMMIT_SHA} diff --git a/docker-compose.yml b/docker-compose.yml index 14d1d68c491c12448d3749bebe95d51c663f5ae9..e3815a884954cb28b8fed9f8fccd8a04ce7362fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,23 +47,23 @@ services: volumes: - "database:/var/lib/postgresql/data" -# backup: -# restart: always -# build: backup -# environment: -# BORG_PASSPHRASE: ${BORG_PASSPHRASE} -# SSH_ID: ${SSH_ID} -# BORG_REPO: ${BORG_REPO} -# CRON_PATTERN: ${CRON_PATTERN} -# PRUNE_ARGS: ${PRUNE_ARGS} -# DB_USER: 'webapp' -# DB_PASSWORD: ${DB_PASSWORD} -# DB_NAME: 'vinogreets' -# DB_HOST: 'database' -# volumes: -# - "knownhosts:/etc/borg/persistent" + backup: + restart: always + build: backup + environment: + BORG_PASSPHRASE: ${BORG_PASSPHRASE} + SSH_ID: ${SSH_ID} + BORG_REPO: ${BORG_REPO} + CRON_PATTERN: ${CRON_PATTERN} + PRUNE_ARGS: ${PRUNE_ARGS} + DB_USER: 'webapp' + DB_PASSWORD: ${DB_PASSWORD} + DB_NAME: 'uhepp_hub' + DB_HOST: 'database' + volumes: + - "knownhosts:/etc/borg/persistent" volumes: static: database: - # knownhosts: + knownhosts: diff --git a/uhepp-js/package-lock.json b/uhepp-js/package-lock.json index a54299391f815ed02553f256836207d61b6a3fdb..7bd5c58e34de0150bd9d183ec194493238074ecb 100644 --- a/uhepp-js/package-lock.json +++ b/uhepp-js/package-lock.json @@ -1178,6 +1178,11 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz", "integrity": "sha512-OEdH7SyC1suTdhBGW91/zBfR6qaIhThbcN8PUXtXilY4GYnSBbVqOntdHbC1vXwsDnX0Qix2m2+DSU1J51ybOQ==" }, + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" + }, "@popperjs/core": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.6.0.tgz", @@ -5712,6 +5717,11 @@ "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "dev": true }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5984,6 +5994,11 @@ "object-visit": "^1.0.0" } }, + "material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "math-expression-evaluator": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.3.6.tgz", @@ -7596,6 +7611,20 @@ "warning": "^4.0.3" } }, + "react-color": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", + "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "requires": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + } + }, "react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -7682,6 +7711,14 @@ "debounce": "^1.2.0" } }, + "reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "requires": { + "lodash": "^4.0.1" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -9131,6 +9168,11 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, + "tinycolor2": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", + "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==" + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", diff --git a/uhepp-js/package.json b/uhepp-js/package.json index ff876500ef55e57dad1845dafbc8c98b3b9ddd26..2c03c5b4a8c7426f2d5943a325f7f4249ab6b52c 100644 --- a/uhepp-js/package.json +++ b/uhepp-js/package.json @@ -25,12 +25,14 @@ "core-js": "^3.6.5", "html-webpack-plugin": "^3.2.0", "jquery": "^3.4.1", + "js-cookie": "^2.2.1", "mathjax-react": "^1.0.6", "mocha": "^8.2.1", "popper.js": "^1.15.0", "postcss-loader": "^3.0.0", "react": "^16.8.6", "react-bootstrap": "^1.0.0-beta.12", + "react-color": "^2.19.3", "react-dom": "^16.8.6", "react-router-dom": "^5.2.0", "sass-loader": "^7.1.0", diff --git a/uhepp-js/src/common.scss b/uhepp-js/src/common.scss index 75bda6f46b5d02e798038af17cba06a7c493cd03..c44fad169cbd4b25973657613fc3bf9f535aae5c 100644 --- a/uhepp-js/src/common.scss +++ b/uhepp-js/src/common.scss @@ -54,21 +54,34 @@ dd { margin-left: 1rem; } +.badge-container { + line-break: anywhere; +} +.badge-container .badge-pair { + white-space: nowrap; +} + .badge-pair { .badge, .badge-pill { &:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; + border-left: none; padding-left: 0.3em; } &:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; padding-right: 0.3em; + border-right: none; } } } +.badge-outline-primary { + border: 2px solid theme-color("primary"); +} + .controls-head { display: flex; justify-content: space-between; @@ -102,6 +115,10 @@ body { } } + +.plot-background h2 { + font-size: 1.5rem; +} @include media-breakpoint-up(md) { .plot-background { background-image: url('/static/uhepp_vault/background.png'); @@ -116,5 +133,37 @@ body { background-repeat: no-repeat; background-position: 80% 100%; background-size: contain; + h2 { + font-size: 2rem; + } } } + +.alert { + border-bottom: none; + border-left: none; + border-right: none; + border-top-width: 0.25rem; +} + +.alert-danger { + border-color: theme-color("danger"); +} +.alert-success { + border-color: theme-color("success"); +} + + +.plot-caption { + cursor: pointer; + justify-content: space-between; + display: flex; +} + +.plot-caption .pen{ + visibility: hidden; +} + +.plot-caption:hover .pen{ + visibility: visible; +} diff --git a/uhepp-js/src/components/Caption.jsx b/uhepp-js/src/components/Caption.jsx new file mode 100644 index 0000000000000000000000000000000000000000..231da952c95808c0ddb430dd66462c6bd2b1d529 --- /dev/null +++ b/uhepp-js/src/components/Caption.jsx @@ -0,0 +1,53 @@ + +import React, { useState } from "react"; + +const Caption = ({editable, comment, handleUpdate}) => { + const [editMode, setEditMode] = useState(false) + const [cmt, setCmt] = useState(comment) + + const updateComment = (e) => { + if (editable) { + const comment = e.target.value; + setCmt(comment) + } + } + + const handleSubmit = (e) => { + e.preventDefault() + handleUpdate(cmt) + } + + const softLeave = () => { + if (comment == cmt) { + setEditMode(false) + } + } + + const handleClick = () => { + if (editable) { + setEditMode(true) + } + } + + console.log(style) + + if (editMode) { + return (<form method="post" className="my-2 d-flex" onSubmit={e => handleSubmite(e)}> + <textarea name="comment" cols="40" autoFocus className="textarea form-control" + onBlur={() => softLeave()} + id="id_comment" rows="2" value={cmt} onChange={e => updateComment(e)} /> + <div> + <button type="submit" className="btn btn-outline-secondary ml-1"> + <i className="fas fa-save"></i> + </button> + </div> + </form>) + } else { + return <div className="plot-caption mx-5" onClick={() => handleClick()}> + <p>{comment}</p> + <i className="fas fa-pen m-1 pen text-muted"></i> + </div> + } +} + +export default Caption diff --git a/uhepp-js/src/components/Uhepp.jsx b/uhepp-js/src/components/Uhepp.jsx index ea3ec3b4b344ccc170c33fb2d801a6c182e1e37a..9cb0c46f8bf66ca373d58b022f9e013356709132 100644 --- a/uhepp-js/src/components/Uhepp.jsx +++ b/uhepp-js/src/components/Uhepp.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import UheppHistUI from "./UheppHistUI.jsx"; const Error = (message) => ( @@ -12,7 +12,7 @@ const Error = (message) => ( </div>) -class Uhepp extends React.Component { +class UheppWithData extends React.Component { constructor(props) { super(props); this.state = { error: null }; @@ -34,7 +34,7 @@ class Uhepp extends React.Component { if (!uhepp.version) { return Error("Missing uhepp version") } - if (uhepp.version != "0.1") { + if (uhepp.version != "0.1" && uhepp.version != "0.2") { return Error(`Unsupported uhepp version: ${uhepp.version}`) } @@ -42,6 +42,8 @@ class Uhepp extends React.Component { return <UheppHistUI width={this.props.width} height={this.props.height} + uuid={this.props.uuid} + size={this.props.size} uhepp={uhepp} /> } @@ -50,4 +52,34 @@ class Uhepp extends React.Component { } + +const Uhepp = ({width, height, uuid}) => { + const [uhepp, setUhepp] = useState(null); + + const url = "/api/plots/" + uuid + useEffect(() => { + async function fetchData() { + const response = await fetch(url); + const size =response.headers.get("Content-Length") + const json = await response.json(); + setUhepp({uhepp: json.uhepp, size: size}) + } + fetchData(); + }, [url]); + + if (uhepp) { + return <UheppWithData width={width} height={height} size={uhepp.size} uhepp={uhepp.uhepp} uuid={uuid} /> + } else { + return (<> + <div className="d-flex justify-content-center my-5"> + <div className="spinner-border text-primary" role="status"> + <span className="sr-only">Loading...</span> + </div> + </div> + <div className="text-center">Loading data...</div> + </>) + } + +} + export default Uhepp; diff --git a/uhepp-js/src/components/UheppHist.jsx b/uhepp-js/src/components/UheppHist.jsx index 8790d520be2b09e9b063dafb98920828ece653d6..5593ac7d31320f49cf1f6fcc6ab9c7dd039e0ea4 100644 --- a/uhepp-js/src/components/UheppHist.jsx +++ b/uhepp-js/src/components/UheppHist.jsx @@ -1,8 +1,8 @@ -import React, { useState } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { AxisLeft, AxisBottom } from '@vx/axis'; import { AxisRight, AxisTop } from '@vx/axis'; import { AreaStack } from '@vx/shape'; -import { scaleLinear, scaleOrdinal } from '@vx/scale'; +import { scaleLinear, scaleLog } from '@vx/scale'; import { Group } from '@vx/group'; import { extent } from 'd3-array'; import { mathjax } from 'mathjax-full/js/mathjax'; @@ -127,6 +127,46 @@ const Bar = ({ onMouseOut={() => onMouseOut()} /> ) +const Graph = ({ + xScale, + yScale, + data, + style={}, +}) => { + const x = data.x + const y = data.y + const points = Array(x.length).fill(0).map( + (_, i) => [xScale(x[i]), yScale(y[i])] + ) + + return <g> + { Array(x.length - 1).fill(0).map( + (_, i) => <line x1={points[i][0]} y1={points[i][1]} + x2={points[i + 1][0]} y2={points[i + 1][1]} + strokeWidth="2" + fillOpacity="0" + stroke={style.color || "#000"} + key={i} + /> + )} + </g> +} + +const VLine = ({ + xScale, + yScale, + x, + range, + style={}, +}) => ( + <line y1={yScale(range[0])} + y2={yScale(range[1])} + strokeWidth="2" + stroke={style.color || "#000"} + x1={xScale(x)} + x2={xScale(x)} /> +) + const Point = ({ xScale, yScale, @@ -213,8 +253,7 @@ const Ratio = ({ const y_values = bin_indices.map(i => numerator[i] / denominator[i]) const [new_x, new_y] = histogramify(edges, y_values) - const stat = bin_indices.map(i => Math.sqrt(stat_num[i]**2 / denominator[i]**2 - + numerator[i]**2 / denominator[i]**4 * stat_den[i]**2)) + const stat = bin_indices.map(i => stat_num[i] / denominator[i]) objects.push(<Step x={new_x} y={new_y} @@ -227,6 +266,19 @@ const Ratio = ({ highlightedBin={highlightedBin} onMouseOver={() => onMouseOverBin(bin_i)} onMouseOut={() => onMouseOverBin(null)} />) + stat.forEach((s, bin_i) => { + if (isFinite(s)) { + objects.splice(0, 0, <rect + key={`rect-num-uncert-${ri_i}-${bin_i}`} + x={xScale(edges[bin_i])} + width={xScale(edges[bin_i + 1]) - xScale(edges[bin_i])} + y={ratioScale(y_values[bin_i] + s)} + height={ratioScale(y_values[bin_i] - s) - ratioScale(y_values[bin_i] + s)} + fill="url(#errorBand)" + onMouseOver={() => onMouseOverBin(bin_i)} + onMouseOut={() => onMouseOverBin(null)} />) + } + }) } else if (ratio_item.type == "points") { for (let bin_i=0; bin_i < n_bins; bin_i++) { @@ -237,8 +289,7 @@ const Ratio = ({ const stat_den = sumStat(uhepp.yields, ratio_item.denominator, bin_i + 1) const y_value = numerator / denominator - const stat = Math.sqrt(stat_num**2 / denominator**2 - + numerator**2 / denominator**4 * stat_den**2) + const stat = stat_num / denominator const bin_center = (edges[bin_i + 1] + edges[bin_i]) / 2 const width = edges[bin_i + 1] - edges[bin_i] @@ -327,7 +378,7 @@ const Histogram = ({ (uhepp.bins.density_width ? uhepp.bins.density_width / (edges[i + 1] - edges[i]) : 1) ) - const [new_x, new_y] = histogramify(edges, y_values) + const [new_x, new_y] = histogramify(edges, y_values.map((v, i) => v + bottom[i])) objects.push(<Step x={new_x} y={new_y} @@ -429,12 +480,6 @@ const tex_html = mathjax.document('', { } }); -const length_metrix = (str) => { - var short_count = (str.match(/[,.tli1: ]/g) || []).length; - var long_count = (str.match(/[MWXZ]/g) || []).length; - return str.length + 0.2 * long_count - 0.5 * short_count -} - const EmbeddedMathJax = ({src, posX, posY, align="left"}) => { let html = tex_html; let node = document.createElement("div"); @@ -487,7 +532,22 @@ const Legend = ({ const lineSkip = -16; const mathSkip = 120; let i = 0 - let legend = [] + let legend = []; + (post_uhepp.graphs || []).forEach((graph, graph_i) => { + legend.push(<> + <line x1={0} x2={20} + y1={(i + 1) * lineSkip - 5} + y2={(i + 1) * lineSkip - 5} + strokeWidth="2" + stroke={graph.style.color || "#000"} + key={`legend-${i}`} + /> + <MixedText x={30} y={(i + 1) * lineSkip} + key={`legend-label-${i}`}>{graph.label}</MixedText> + </>) + + i++ + }) post_uhepp.stacks.map((stack, stack_index) => { stack.content.map((stack_item, si_i) => { if (stack.type == "points") { @@ -576,8 +636,47 @@ const UheppHist = ({width, height, uhepp}) => { const UheppHistPost = ({width, height, uhepp}) => { const post_uhepp = uhepp // backwards compatibility - const [highlightedBin, setHighlightedBin] = useState(null); - const [showTotal, setShowTotal] = useState(false); + const [highlightedBin, setHighlightedBin] = useState(null) + const [showTotal, setShowTotal] = useState(false) + const [pngDownload, setPngDownload] = useState(false) + const [pngInProgress, setPngInProgress] = useState(false) + + const canvasHeight = height * 2 + const canvasWidth = width * 2 + const canvasRef = useRef(null) + const svgRef = useRef(null) + + useEffect(() => { + setPngDownload(false) + setPngInProgress(false) + }, [uhepp]) + + const generatePng = () => { + setPngInProgress(true) + const canvas = canvasRef.current + const ctx = canvas.getContext('2d') + const svg = svgRef.current.cloneNode(true) + svg.setAttribute("height", `${canvasHeight}px`) + svg.setAttribute("width", `${canvasWidth}px`) + const data = (new XMLSerializer()).serializeToString(svg) + + const svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'}) + const url = URL.createObjectURL(svgBlob) + + const img = new Image() + img.onload = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height) + ctx.drawImage(img, 0, 0) + URL.revokeObjectURL(url) + + canvas.toBlob(blob => { + const pngUrl = URL.createObjectURL(blob) + setPngDownload(pngUrl) + setPngInProgress(false) + }, "image/png") + } + img.src = url + } const defaultColorsCycle = new ColorCycle([ "#1f77b4", @@ -595,10 +694,10 @@ const UheppHistPost = ({width, height, uhepp}) => { const margin = { - top: 10, - bottom: 60, + top: 5, + bottom: 40, left: 80, - right: 80, + right: 10, sep: 3, } @@ -616,27 +715,45 @@ const UheppHistPost = ({width, height, uhepp}) => { range: [0, xMax], domain: extent(uhepp.bins.rebin || uhepp.bins.edges), }) - const yScale = scaleLinear({ - range: [yMax, 0], - domain: [0, getMaxBin(post_uhepp) * 1.5], - }) - const ratioScale = scaleLinear({ + const maxBinMain = getMaxBin(post_uhepp) + const isScaleLin = !(uhepp.y_axis && uhepp.y_axis.log) + const yScaleMin = (uhepp.y_axis && uhepp.y_axis.min) ?? (isScaleLin ? 0 : 1); + const yScaleMax = (uhepp.y_axis && uhepp.y_axis.max) ?? (isScaleLin ? maxBinMain * 1.5 : maxBinMain * 10**1.5 ); + + + const yScale = isScaleLin ? ( + scaleLinear({ + range: [yMax, 0], + domain: [yScaleMin, yScaleMax], + }) + ) : ( + scaleLog({ + range: [yMax, 0], + domain: [yScaleMin, yScaleMax], + clamp: true, + }) + ) + + const isRatioScaleLin = !(uhepp.ratio_axis && uhepp.ratio_axis.log) + const ratioScaleMaker = isRatioScaleLin ? scaleLinear : scaleLog + const ratioScale = ratioScaleMaker({ range: [ratioMax, 0], domain: [ - (uhepp.ratio_axis && uhepp.ratio_axis.min) || 0.5, - (uhepp.ratio_axis && uhepp.ratio_axis.max) || 1.5, + (uhepp.ratio_axis && uhepp.ratio_axis.min) ?? 0.5, + (uhepp.ratio_axis && uhepp.ratio_axis.max) ?? 1.5, ], + clamp: !isRatioScaleLin, }) - - const equidistent = computeEquidistent(uhepp.bins.rebin || uhepp.bins.edges, uhepp.bins.density_width) - return ( + return (<> <div className="uhepp-container"> <svg viewBox={`0 0 ${width} ${height}`} + ref={svgRef} onMouseOut={() => setHighlightedBin(null)} > <style> + svg * {'{font-family: sans-serif}'} rect {'{shape-rendering: crispEdges }'} .uhepp-brand {'{font-style: italic; font-weight: bold}'} .uhepp-brand, .uhepp-label {'{font-size: 20px}'} @@ -664,6 +781,26 @@ const UheppHistPost = ({width, height, uhepp}) => { xScale={xScale} yScale={yScale} /> + { (post_uhepp.v_lines || []).map((vline, i) => + <VLine + xScale={xScale} + yScale={yScale} + x={vline.x} + range={vline.range || [0, maxBinMain]} + key={i} + style={vline.style} /> + ) + } + { (post_uhepp.graphs || []).map((graph, i) => + <Graph + xScale={xScale} + yScale={yScale} + data={graph} + key={i} + style={graph.style} /> + ) + } + </g> <Group top={26} left={13}> @@ -807,7 +944,7 @@ const UheppHistPost = ({width, height, uhepp}) => { numTicks={5} tickTransform="translate(8, 0)" /> - { uhepp.ratio_axis.label && + { uhepp.ratio_axis && uhepp.ratio_axis.label && <g transform={`matrix(0 -1 1 0 ${-margin.left + 20} ${ratioMax / 2})`}> <MixedText y={0} x={0} align="center"> { uhepp.ratio_axis.label } @@ -863,6 +1000,27 @@ const UheppHistPost = ({width, height, uhepp}) => { </Group> } </svg> </div> - ) + + <div className="container text-right"> + <canvas className="d-none" ref={canvasRef} width={canvasWidth} height={canvasHeight}></canvas> + { !pngDownload && + <button className="btn btn-sm btn-outline-primary" + disabled={pngInProgress} + onClick={() => generatePng()}> + { !pngInProgress && <span> + <i className="fas fa-download"></i> Convert to .png + </span> } + { pngInProgress && <> + <span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"> + </span> Converting... + </> } + </button> } + { pngDownload && + <a href={pngDownload} download={uhepp.metadata.filename + ".png"} className="btn btn-sm btn-primary"> + <i className="fas fa-download"></i> Download .png + </a> } + </div> + </>) + } export default UheppHist; diff --git a/uhepp-js/src/components/UheppHistUI.jsx b/uhepp-js/src/components/UheppHistUI.jsx index 3eef63ed84ad417faee35c46453998671b2b90df..5d805ac350f207e3eae8ba62eca60d3d5227c224 100644 --- a/uhepp-js/src/components/UheppHistUI.jsx +++ b/uhepp-js/src/components/UheppHistUI.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import UheppHist from "./UheppHist.jsx"; import { HashRouter as Router, @@ -9,11 +9,13 @@ import { useParams } from "react-router-dom"; import { objfilter, objmap } from "../helpers/uhepp.js"; +import Cookies from 'js-cookie' +import { SketchPicker } from 'react-color'; const noVariationUsed = (uhepp) => { const main_names = uhepp.stacks.map(stack => stack.content.map(item => item.yields)).flat() - const num_names = uhepp.ratio.map(item => item.numerator).flat() - const den_names = uhepp.ratio.map(item => item.denominator).flat() + const num_names = uhepp.ratio ? uhepp.ratio.map(item => item.numerator).flat() : [] + const den_names = uhepp.ratio ? uhepp.ratio.map(item => item.denominator).flat() : [] const names = [main_names, den_names, num_names].flat() const varied_names = names.filter(name => (name && name.indexOf("/") != -1)) @@ -39,7 +41,10 @@ const varstyle = (updown) => { const makeVariationStacks = (uhepp, stackId, variation, updown) => { const stack = uhepp.stacks[stackId] - return stack ? [{ + if (!stack) { + return [] + } + return [{ type: "step", content: [{ style: varstyle(updown), @@ -48,12 +53,15 @@ const makeVariationStacks = (uhepp, stackId, variation, updown) => { item.yield.map(name => `${name}/${variation}/${updown}`) ).flat() }] - }] : [] + }] } const makeVariationRatio = (uhepp, stackId, variation, updown) => { const stack = uhepp.stacks[stackId] - return stack ? [{ + if (!stack) { + return [] + } + return [{ type: "step", style: varstyle(updown), numerator: stack.content.map(item => @@ -62,26 +70,186 @@ const makeVariationRatio = (uhepp, stackId, variation, updown) => { denominator: stack.content.map(item => item.yield.map(name => `${name}`) ).flat() - }] : [] + }] +} + +const SingleBadge = ({tag}) => (<> + <span className="badge badge-pill badge-primary mx-1">{tag}</span> + { ' ' } +</>) + +const BadgePair = ({tag, value}) => (<> + <span className="badge-pair mx-1"> + <span className="badge badge-pill badge-primary badge-outline-primary">{tag}</span> + <span className="badge badge-pill badge-outline-primary text-dark">{value}</span> + </span> + { ' ' } +</>) + +const BadgeList = ({tags}) => ( + Object.entries(tags).map(([key, value], i) => + value == null ? + <SingleBadge key={i} /> : <BadgePair key={i} tag={key} value={value} /> + ) +) + +const DelayedSelect = ({timeout=300, ...props}) => { + return <select {...props} /> +} + +const DelayedInput = ({value, onChange, onType=null, timeout=300, ...props}) => { + const [text, setText] = useState(value) + const [timer, setTimer] = useState(null) + + useEffect(() => { + setText(value); + }, [value]) + + const handleChange = (e) => { + const content = e.target.value + setText(content) + if (timer) { + clearTimeout(timer) + } + if (onType) { + onType(e) + } + setTimer(setTimeout(() => { + onChange({"target": {"value": content}}) + }, timeout)) + } + + return <input value={text} onChange={(e) => handleChange(e)} {...props} /> + } + +const FileSize = ({value}) => { + const unit = ["Byes", "KiB", "MiB", "GiB"] + const divider = 1024; + const threshold = 2; + + let i = 0; + for (; i < unit.length; i++) { + if (value >= threshold * divider) { + value /= divider + } else { + break + } + } + + const formatted_value = (i==0) ? value.toFixed(0) : value.toFixed(1) + + return <span>{formatted_value} {unit[i]}</span> +} + +const ColorPicker = ({value, onChange}) => { + const [modal, setModal] = useState(false) + const [current, setCurrent] = useState(value) + + useEffect(() => { + setCurrent(value); + }, [value]) + + const handleChangeComplete = (c) => { + setCurrent(c) + onChange({'target': {value: c}}) + } + + return (<> + <div className="input-group-prepend"> + <span className="input-group-text">Color</span> + </div> + <DelayedInput type="text" className="form-control" placeholder="color" + value={current} + onType={(e) => setCurrent(e.target.value)} + onChange={(e) => handleChangeComplete(e.target.value)} /> + + <div className="input-group-append" onClick={() => setModal(!modal)}> + <span className="input-group-text" style={{ + backgroundColor: current || "#fff", + cursor: "pointer", + width: "3rem" + }}> </span> + </div> + { modal && + <div className="p-3"> + <SketchPicker color={current} + presetColors={ + ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', + '#bfbd22', '#17beff', '#dddddd', '#222222', '#d0e1f2', '#94c4df', '#4a98c9', '#1764ab'] + } + onChange={c => setCurrent(c.hex)} + onChangeComplete={c => handleChangeComplete(c.hex)} /> + </div> + } + </>) + +} const UheppHistUIWithSyst = ({ width, height, uhepp, + uuid, + size, onEnvChange, onEnvStackChange, onReset, + onStackContentChange, + onStackItemRename, + onStackItemChangeColor, + onAddStackItem, + onDeleteStackItem, + onMoveUpStackItem, + onMoveDownStackItem, + onMoveUpStack, + onMoveDownStack, + onDeleteStack, + onAddStack, + onStackTypeChange, + onMoveUpRatioItem, + onMoveDownRatioItem, + onDeleteRatioItem, + onAddRatioItem, + onRatioItemTypeChange, + onRatioItemNumeratorChange, + onRatioItemDenominatorChange, + onRatioItemChangeColor, isVariationReady, variations, origStacks, + origRatio, + origYields, envName, envId, + setIsClean, + isClean }) => { const [include_underflow, setUnderflow] = useState(!!uhepp.bins.include_underflow) const [include_overflow, setOverflow] = useState(!!uhepp.bins.include_overflow) const [rebin, setRebin] = useState(uhepp.bins.rebin || uhepp.bins.edges) const [density_width, setDensity] = useState(uhepp.bins.density_width) + const [collections, setCollections] = useState(null); + const [destination, setDestination] = useState("") + const [saveStatus, setSaveStatus] = useState(null) + + const url = "/api/collections" + useEffect(() => { + async function fetchData() { + const response = await fetch(url); + const json = await response.json(); + setCollections(json) + if (json.length > 0) { + setDestination(json[0].url) + } + } + fetchData(); + }, [url]); + + const handleDestination = (e) => { + setDestination(e.target.value) + setSaveStatus(null) + } const reset = () => { setUnderflow(!!uhepp.bins.include_underflow) @@ -94,58 +262,140 @@ const UheppHistUIWithSyst = ({ const handleRebin = (e) => { let values = Array.from(e.target.selectedOptions, option => parseFloat(option.value)) setRebin(values) + setIsClean(false) } const handleUnderflow = (e) => { let value = e.target.checked setUnderflow(value) + setIsClean(false) } const handleOverflow = (e) => { let value = e.target.checked setOverflow(value) + setIsClean(false) } const handleDensityWidth = (e) => { let value = parseFloat(e.target.value) setDensity(value) + setIsClean(false) + } + + // Fallback to original rebin or original binning + const takeFirstIfSubset = (rebin, orig) => { + let isSubset = true + rebin.forEach(e => { + if (orig.indexOf(e) < 0) { + isSubset = false + } + }) + return isSubset ? rebin : orig } + const rebin_singleFallback = rebin.length > 1 ? rebin : (uhepp.bins.rebin) + const rebin_subsetFallback = takeFirstIfSubset(rebin_singleFallback, uhepp.bins.edges) const uhepp_derived = Object.assign({}, uhepp, {bins: Object.assign({}, uhepp.bins, { - rebin: rebin.length > 1 ? rebin : (uhepp.bins.rebin), + rebin: rebin_subsetFallback, include_underflow, include_overflow, density_width, })}) + const save = () => { + const csrftoken = Cookies.get('csrftoken'); + + async function asyncStringify(str) { + return JSON.stringify(str); + } + + const data = { + "collection": destination, + "uhepp": Object.assign({}, uhepp_derived, { + yields: origYields, + metadata: Object.assign({}, uhepp_derived.metadata, { + tags: Object.assign({}, uhepp_derived.metadata.tags, { + "forked": uuid + }) + }) + }) + } + + async function fetchData() { + const response = await fetch("/api/plots/", { + method: "POST", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'X-CSRFToken': csrftoken + }, + body: await asyncStringify(data) + }) + if (response.ok) { + setSaveStatus(null) + const json = await response.json() + window.location = "/p/" + json.uuid + } else { + setSaveStatus('error') + } + } + setSaveStatus('loading') + fetchData() + } + return <> - <div>{ Object.entries(uhepp.metadata.tags).map(([key, value]) => - value == null ? - <span className="badge badge-pill badge-primary mx-1">{key}</span> : - <span className="badge-pair mx-1"> - <span className="badge badge-pill badge-primary">{key}</span> - <span className="badge badge-pill badge-secondary">{value}</span> - </span> - ) }</div> + <div className="badge-container"><BadgeList tags={uhepp.metadata.tags} /></div> + <UheppHist width={width} height={height} uhepp={uhepp_derived} /> <ul className="nav nav-tabs" id="view-options" role="tablist"> <li className="nav-item"> - <a className="nav-link active" id="info-tab" data-toggle="tab" href="#info" role="tab" aria-controls="info" aria-selected="true">Info</a> + <a className="nav-link active" id="info-tab" data-toggle="tab" href="#info" role="tab" aria-controls="info" aria-selected="true"> + <i className="d-none d-lg-inline fas fa-info-circle mr-1"></i> + Info + </a> + </li> + <li className="nav-item"> + <a className="nav-link" id="binning-tab" data-toggle="tab" href="#binning" role="tab" aria-controls="binning" aria-selected="true"> + <i className="d-none d-lg-inline fas fa-chart-bar mr-1"></i> + Binning + </a> + </li> + <li className="nav-item"> + <a className="nav-link" id="stacks-tab" data-toggle="tab" href="#stacks" role="tab" aria-controls="stacks" aria-selected="false"> + <i className="d-none d-lg-inline fas fa-layer-group mr-1"></i> + Stacks + </a> + </li> + <li className="nav-item"> + <a className="nav-link" id="ratio-tab" data-toggle="tab" href="#ratio" role="tab" aria-controls="ratio" aria-selected="false"> + <i className="d-none d-lg-inline fas fa-percentage mr-1"></i> + Ratio + </a> + </li> + <li className="nav-item"> + <a className="nav-link" id="variations-tab" data-toggle="tab" href="#variations" role="tab" aria-controls="variations" aria-selected="false"> + <i className="d-none d-lg-inline fas fa-envelope mr-1"></i> + Variations + </a> </li> <li className="nav-item"> - <a className="nav-link" id="binning-tab" data-toggle="tab" href="#binning" role="tab" aria-controls="binning" aria-selected="true">Binning</a> + <a className="nav-link" id="reset-tab" data-toggle="tab" href="#reset" role="tab" aria-controls="reset" aria-selected="false"> + <i className="d-none d-lg-inline fas fa-trash-restore mr-1"></i> + Restore + </a> </li> - { <li className="nav-item"> - <a className="nav-link" id="variations-tab" data-toggle="tab" href="#variations" role="tab" aria-controls="variations" aria-selected="false">Variations</a> - </li> } <li className="nav-item"> - <a className="nav-link" id="reset-tab" data-toggle="tab" href="#reset" role="tab" aria-controls="reset" aria-selected="false">Reset</a> + <a className="nav-link" id="save-tab" data-toggle="tab" href="#save" role="tab" aria-controls="save" aria-selected="false"> + <i className="d-none d-lg-inline fas fa-code-branch mr-1"></i> + Fork + </a> </li> </ul> <div className="tab-content" id="view-options-content"> - <div className="tab-pane show active p-3" id="info" role="tabpanel" aria-labelledby="binning-tab"> + <div className="tab-pane show active p-3" id="info" role="tabpanel" aria-labelledby="info-tab"> <dl> <dt>Author</dt> <dd>{ uhepp.metadata.author || <i>None</i>}</dd> @@ -164,14 +414,15 @@ const UheppHistUIWithSyst = ({ <dd>{ uhepp.metadata.Ecm_TeV ? uhepp.metadata.Ecm_TeV + " TeV" : <i>None</i>}</dd> <dt>Tags</dt> - <dd>{ Object.entries(uhepp.metadata.tags).map(([key, value]) => - value == null ? - <span className="badge badge-pill badge-primary mx-1">{key}</span> : - <span className="badge-pair mx-1"> - <span className="badge badge-pill badge-primary">{key}</span> - <span className="badge badge-pill badge-secondary">{value}</span> - </span> - ) }</dd> + <dd className="badge-container"> + <BadgeList tags={uhepp.metadata.tags} /> + </dd> + { size && <> + <dt>File size</dt> + <dd> + <FileSize value={size} /> + </dd> + </>} </dl> </div> <div className="tab-pane p-3" id="binning" role="tabpanel" aria-labelledby="binning-tab"> @@ -219,6 +470,156 @@ const UheppHistUIWithSyst = ({ </div> </form> </div> + <div className="tab-pane p-3" id="stacks" role="tabpanel" aria-labelledby="stacks-tab"> + <form> + { origStacks.map((stack, i) => (<div key={i}> + <h3 className="controls-head mt-2"> + <span>Stack {i}</span> + <span> + <button disabled={i == 0} className="btn btn-outline-primary mx-1" onClick={(e) => onMoveUpStack(e, i)}> + <i className="fas fa-arrow-up"></i> + </button> + <button disabled={(i + 1) >= origStacks.length} className="btn btn-outline-primary mx-1" onClick={(e) => onMoveDownStack(e, i)}> + <i className="fas fa-arrow-down"></i> + </button> + <button className="btn btn-outline-danger ml-1" onClick={(e) => onDeleteStack(e, i)}> + <i className="fas fa-trash"></i> + </button> + </span> + </h3> + <div className="from-group"> + <label htmlFor={`t-${i}`}>Histogram type</label> + <select id={`t-${i}`} value={stack.type} className="form-control" onChange={(e) => onStackTypeChange(e, i)}> + <option value="step">step</option> + <option value="stepfilled">stepfilled</option> + <option value="points">points</option> + </select> + </div> + { stack.content.map((content, j) => ( + <div key={`${j}_${stack.content.length}`}> + <h5>Item {j}</h5> + <div className="form-row mb-2"> + <div className="col-md-4"> + + + <div className="input-group mb-1"> + <div className="input-group-prepend"> + <span className="input-group-text">Label</span> + </div> + <DelayedInput type="text" className="form-control" placeholder="label" + value={content.label} onChange={(e) => onStackItemRename(e, i, j)}/> + </div> + + <div className="input-group my-1"> + <ColorPicker value={(content.style) ? content.style.color || "" : ""} onChange={(e) => onStackItemChangeColor(e, i, j)} /> + </div> + + <div className="my-1"> + <button className="btn btn-sm btn-outline-danger mr-1" onClick={(e) => onDeleteStackItem(e, i, j)}> + <i className="fas fa-trash"></i> + </button> + <button disabled={j == 0} className="btn btn-sm btn-outline-primary mx-1" onClick={(e) => onMoveUpStackItem(e, i, j)}> + <i className="fas fa-arrow-up"></i> + </button> + <button disabled={(j + 1) >= stack.content.length} className="btn btn-sm btn-outline-primary mx-1" onClick={(e) => onMoveDownStackItem(e, i, j)}> + <i className="fas fa-arrow-down"></i> + </button> + </div> + + </div> + <div className="col-md-8"> + <DelayedSelect multiple size={8} value={content.yield} id={`p-${i}-${j}`} className="form-control" + onChange={(e) => onStackContentChange(e, i, j)}> + { Object.keys(uhepp.yields).map((name, i) => <option value={name} key={i}>{name}</option>) } + </DelayedSelect> + </div> + </div> + + </div> + ))} + <div className="my-1"> + <button className="btn btn-sm btn-outline-primary" onClick={(e) => onAddStackItem(e, i)}> + <i className="fas fa-plus mr-1"></i> + Add item {stack.content.length} to stack + </button> + </div> + + + </div>))} + + <div className="my-3"> + <button className="btn btn-outline-primary" onClick={(e) => onAddStack(e)}> + <i className="fas fa-plus mr-1"></i> + Add stack {origStacks.length} + </button> + </div> + </form> + </div> + + <div className="tab-pane p-3" id="ratio" role="tabpanel" aria-labelledby="ratio-tab"> + <form> + { origRatio.map((ratioitem, i) => (<div key={i}> + <h3 className="controls-head mt-2"> + <span>Ratio item {i}</span> + <span> + <button disabled={i == 0} className="btn btn-outline-primary mx-1" onClick={(e) => onMoveUpRatioItem(e, i)}> + <i className="fas fa-arrow-up"></i> + </button> + <button disabled={(i + 1) >= origRatio.length} className="btn btn-outline-primary mx-1" onClick={(e) => onMoveDownRatioItem(e, i)}> + <i className="fas fa-arrow-down"></i> + </button> + <button className="btn btn-outline-danger ml-1" onClick={(e) => onDeleteRatioItem(e, i)}> + <i className="fas fa-trash"></i> + </button> + </span> + </h3> + + <div className="form-row mb-2"> + <div className="col-md-4"> + <div className="form-group mb-1"> + <label htmlFor={`t-${i}`}>Histogram type</label> + <select id={`t-${i}`} value={ratioitem.type} className="form-control" onChange={(e) => onRatioItemTypeChange(e, i)}> + <option value="step">step</option> + <option value="points">points</option> + </select> + </div> + <div className="input-group my-1"> + <ColorPicker value={(ratioitem.style) ? ratioitem.style.color || "" : ""} onChange={(e) => onRatioItemChangeColor(e, i)} /> + </div> + </div> + + <div className="col-md-4"> + <div className="form-group"> + <label htmlFor={`p-${i}`}>Numerator</label> + <DelayedSelect multiple size={8} value={ratioitem.numerator} id={`p-${i}`} className="form-control" + onChange={(e) => onRatioItemNumeratorChange(e, i)}> + { Object.keys(uhepp.yields).map((name, i) => <option value={name} key={i}>{name}</option>) } + </DelayedSelect> + </div> + </div> + + <div className="col-md-4"> + <div className="form-group"> + <label htmlFor={`p-${i}}`}>Denominator</label> + <DelayedSelect multiple size={8} value={ratioitem.denominator} id={`p-${i}`} className="form-control" + onChange={(e) => onRatioItemDenominatorChange(e, i)}> + { Object.keys(uhepp.yields).map((name, i) => <option value={name} key={i}>{name}</option>) } + </DelayedSelect> + </div> + </div> + + </div> + </div>))} + + <div className="my-3"> + <button className="btn btn-outline-primary" onClick={(e) => onAddRatioItem(e)}> + <i className="fas fa-plus mr-1"></i> + Add ratio item {origRatio.length} + </button> + </div> + </form> + </div> + <div className="tab-pane p-3" id="variations" role="tabpanel" aria-labelledby="variations-tab"> { !isVariationReady && <p>To use the variation feature, you must add variation data to @@ -227,10 +628,10 @@ const UheppHistUIWithSyst = ({ { isVariationReady && <form> <div className="form-group"> - <label htmlFor="envelop">Add envelop of </label> + <label htmlFor="envelop">Add envelope of </label> <select value={envName} className="form-control" id="envelop" onChange={(e) => onEnvChange(e)}> { [ - <option value="NOMINAL" key="-1">Nominal</option>, + <option value="NOMINAL" key={"nonminal"}>Nominal</option>, ...variations.map((name, i) => <option value={name} key={i}>{name}</option>) ] } @@ -245,6 +646,7 @@ const UheppHistUIWithSyst = ({ </form> } </div> + <div className="tab-pane p-3" id="reset" role="tabpanel" aria-labelledby="reset-tab"> <p>Reset all view modifications. Pull the plot to make permanent changes.</p> @@ -252,37 +654,267 @@ const UheppHistUIWithSyst = ({ Reset view </button> </div> + + <div className="tab-pane p-3" id="save" role="tabpanel" aria-labelledby="save-tab"> + <form> + <p>Save the current view into another collection.</p> + <div className="form-group"> + <label htmlFor="env-stack">Destination collection</label> + { collections == null ? + <div className="d-flex justify-content-center"> + <div className="spinner-border text-primary" role="status"> + <span className="sr-only">Loading...</span> + </div> + </div> : + <select value={destination} className="form-control" onChange={(e) => handleDestination(e)}> + { collections.map((c, i) => <option value={c.url} key={i}>{c.title}</option>) } + </select> + } + </div> + + <p> + <button className="btn btn-secondary" type="button" onClick={() => save()} disabled={saveStatus == 'loading'}> + Save as new plot + { saveStatus == 'loading' && <> + <span className="ml-2 spinner-border spinner-border-sm" role="status"> </span> + <span className="sr-only">Loading...</span> + </>} + </button> + </p> + {saveStatus == 'loading' && + <small className="text-muted">Depending on the size of the data, this + might take some time{ size && <span> (file size <FileSize value={size} />)</span>}.</small> + } + {saveStatus == 'error' && + <p className="alert alert-danger"> + Something went wrong. Please try again and make sure you have permission to write to that collection. + </p> } + </form> + </div> + </div> </> } -const UheppHistUIRouted = ({width, height, uhepp}) => { +const patchList = (iter, i, repl) => iter.map((v, j) => (i == j) ? repl : v) +const UheppHistUIRouted = ({width, height, uhepp, uuid, size}) => { const {envName, envId} = Object.assign({envName: "NOMINAL", envId: 0}, useParams()) const history = useHistory() + const [stacks, setStacks] = useState(uhepp.stacks) + const [ratio, setRatio] = useState(uhepp.ratio || []) + const [isClean, setIsClean] = useState(true) + + const getClean = () => isClean + + window.onbeforeunload = () => { + if (!isClean) { + return "Really exit?" + } + } + const reset = () => { history.push("/") + setStacks(uhepp.stacks) + setRatio(uhepp.ratio || []) + setIsClean(true) } const handleEnvelop = (e) => { let variationName = e.target.value history.push(`/${variationName}/${envId}`) + setIsClean(false) } const handleEnvStack = (e) => { let stackId = parseInt(e.target.value) history.push(`/${envName}/${stackId}`) + setIsClean(false) } - const variations = variationList(uhepp) - const isVariationReady = noVariationUsed(uhepp) && (variations.length > 0) + const handleStackContentChange = (e, stackId, itemId) => { + const yields = Array.from(e.target.selectedOptions, option => option.value) + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": patchList(stacks[stackId].content, itemId, Object.assign({}, + stacks[stackId].content[itemId], {"yield": yields}))}))) + setIsClean(false) + } - const main_names = uhepp.stacks.map(stack => stack.content.map(item => item.yields)).flat() - const num_names = uhepp.ratio.map(item => item.numerator).flat() - const den_names = uhepp.ratio.map(item => item.denominator).flat() + const handleStackItemRename = (e, stackId, itemId) => { + const label = e.target.value + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": patchList(stacks[stackId].content, itemId, Object.assign({}, + stacks[stackId].content[itemId], {"label": label}))}))) + setIsClean(false) + } + + const handleStackItemChangeColor = (e, stackId, itemId) => { + const color = e.target.value + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": patchList(stacks[stackId].content, itemId, Object.assign({}, + stacks[stackId].content[itemId], {"style": + Object.assign({}, stacks[stackId].content[itemId].style || {}, {"color": + color})}))}))) + setIsClean(false) + } + + const handleAddStackItem = (e, stackId) => { + e.preventDefault() + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": [...stacks[stackId].content, {yield: [], label: "New item", + style: {}}]}))) + setIsClean(false) + } + + const handleMoveUpStackItem = (e, stackId, item) => { + e.preventDefault() + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": [ + ...stacks[stackId].content.filter((_, i) => i < (item-1)), + stacks[stackId].content[item], + stacks[stackId].content[item - 1], + ...stacks[stackId].content.filter((_, i) => i > item), + ]}))) + setIsClean(false) + } + + const handleMoveDownStackItem = (e, stackId, item) => { + e.preventDefault() + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": [ + ...stacks[stackId].content.filter((_, i) => i < item), + stacks[stackId].content[item + 1], + stacks[stackId].content[item ], + ...stacks[stackId].content.filter((_, i) => i > item + 1), + ]}))) + setIsClean(false) + } + + const handleDeleteStackItem = (e, stackId, itemId) => { + e.preventDefault() + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], + {"content": stacks[stackId].content.filter((_, j) => j != itemId)}))) + setIsClean(false) + } + + const handleDeleteStack = (e, stackId) => { + e.preventDefault() + setStacks(stacks.filter((_, i) => i != stackId)) + setIsClean(false) + } + + const handleAddStack = (e) => { + e.preventDefault() + setStacks([...stacks, {"type": "stepfilled", "error": "stat", content: []}]) + setIsClean(false) + } + + const handleStackTypeChange = (e, stackId) => { + const type = e.target.value + setStacks(patchList(stacks, stackId, Object.assign({}, stacks[stackId], {"type": type}))) + setIsClean(false) + } + + const handleMoveDownStack = (e, item) => { + e.preventDefault() + setStacks([ + ...stacks.filter((_, i) => i < item), + stacks[item + 1], + stacks[item ], + ...stacks.filter((_, i) => i > item + 1), + ]) + setIsClean(false) + } + + const handleMoveUpStack = (e, item) => { + e.preventDefault() + setStacks([ + ...stacks.filter((_, i) => i < item - 1), + stacks[item], + stacks[item - 1], + ...stacks.filter((_, i) => i > item), + ]) + setIsClean(false) + } + + + const handleDeleteRatioItem = (e, itemId) => { + e.preventDefault() + setRatio(ratio.filter((_, i) => i != itemId)) + setIsClean(false) + } + + const handleAddRatioItem = (e) => { + e.preventDefault() + setRatio([...ratio, { + "type": "step", + "error": "stat", + numerator: [], + denominator: [], + style: {} + }]) + setIsClean(false) + } + + const handleRatioItemTypeChange = (e, itemId) => { + const type = e.target.value + setRatio(patchList(ratio, itemId, Object.assign({}, ratio[itemId], {"type": type}))) + setIsClean(false) + } + + const handleMoveDownRatioItem = (e, item) => { + e.preventDefault() + setRatio([ + ...ratio.filter((_, i) => i < item), + ratio[item + 1], + ratio[item ], + ...ratio.filter((_, i) => i > item + 1), + ]) + setIsClean(false) + } + + const handleMoveUpRatioItem = (e, item) => { + e.preventDefault() + setRatio([ + ...ratio.filter((_, i) => i < item - 1), + ratio[item], + ratio[item - 1], + ...ratio.filter((_, i) => i > item), + ]) + setIsClean(false) + } + + const handleRatioItemChangeColor = (e, item) => { + const color = e.target.value + setRatio(patchList(ratio, item, Object.assign({}, ratio[item], + {"style": Object.assign({}, ratio[item].style, {color: color})}))) + setIsClean(false) + } + + const handleRatioItemNumeratorChange = (e, itemId) => { + const yields = Array.from(e.target.selectedOptions, option => option.value) + setRatio(patchList(ratio, itemId, Object.assign({}, ratio[itemId], {"numerator": yields}))) + setIsClean(false) + } + + const handleRatioItemDenominatorChange = (e, itemId) => { + const yields = Array.from(e.target.selectedOptions, option => option.value) + setRatio(patchList(ratio, itemId, Object.assign({}, ratio[itemId], {"denominator": yields}))) + setIsClean(false) + } + + const mod_uhepp = Object.assign({}, uhepp, {"stacks": stacks, "ratio": ratio}) + + const variations = variationList(mod_uhepp) + const isVariationReady = noVariationUsed(mod_uhepp) && (variations.length > 0) + + const main_names = mod_uhepp.stacks.map(stack => stack.content.map(item => item.yield).flat()).flat() + const num_names = mod_uhepp.ratio ? mod_uhepp.ratio.map(item => item.numerator).flat() : [] + const den_names = mod_uhepp.ratio ? mod_uhepp.ratio.map(item => item.denominator).flat() : [] const all_names = [...main_names, ...num_names, ...den_names] + const used_var_names = all_names.filter(n => n).map(n => n.split("/")).filter(t => t.length == 3).map(t => t[1]) - const filter_var = (var_updown) => objfilter(var_updown, (value, key) => ((all_names.indexOf(key) != -1) || key.indexOf(envName) != -1)) - const pruned = Object.assign({}, uhepp, + const filter_var = (var_updown) => objfilter(var_updown, (value, key) => ((used_var_names.indexOf(key) != -1) || key == envName)) + const pruned = Object.assign({}, mod_uhepp, {"yields": objmap(uhepp.yields, y => Object.assign({}, y, { "var_up": filter_var(y.var_up || {}), "var_down": filter_var(y.var_down || {}), @@ -291,9 +923,10 @@ const UheppHistUIRouted = ({width, height, uhepp}) => { const pruned_env = envName == "NOMINAL" ? pruned : Object.assign({}, pruned, {stacks: [ - ...pruned.stacks, + ...pruned.stacks.filter((_, i) => i <= envId), ...makeVariationStacks(pruned, envId, envName, "up"), ...makeVariationStacks(pruned, envId, envName, "down"), + ...pruned.stacks.filter((_, i) => i > envId), ], ratio: [ ...makeVariationRatio(pruned, envId, envName, "up"), @@ -304,19 +937,47 @@ const UheppHistUIRouted = ({width, height, uhepp}) => { return <UheppHistUIWithSyst width={width} + size={size} height={height} uhepp={pruned_env} onEnvChange={e => handleEnvelop(e)} onEnvStackChange={e => handleEnvStack(e)} + onStackContentChange={(e, i, j) => handleStackContentChange(e, i, j)} + onStackItemRename={(e, i, j) => handleStackItemRename(e, i, j)} + onStackItemChangeColor={(e, i, j) => handleStackItemChangeColor(e, i, j)} + onAddStackItem={(e, i) => handleAddStackItem(e, i)} + onDeleteStackItem={(e, i, j) => handleDeleteStackItem(e, i, j)} onReset={() => reset()} + onMoveUpStackItem={(e, i, j) => handleMoveUpStackItem(e, i, j)} + onMoveDownStackItem={(e, i, j) => handleMoveDownStackItem(e, i, j)} + onMoveUpStack={(e, i) => handleMoveUpStack(e, i)} + onMoveDownStack={(e, i) => handleMoveDownStack(e, i)} + onDeleteStack={(e, i) => handleDeleteStack(e, i)} + onAddStack={(e) => handleAddStack(e)} + onStackTypeChange={(e, i) => handleStackTypeChange(e, i)} + + onMoveUpRatioItem={(e, i) => handleMoveUpRatioItem(e, i)} + onMoveDownRatioItem={(e, i) => handleMoveDownRatioItem(e, i)} + onDeleteRatioItem={(e, i) => handleDeleteRatioItem(e, i)} + onAddRatioItem={(e) => handleAddRatioItem(e)} + onRatioItemTypeChange={(e, i) => handleRatioItemTypeChange(e, i)} + onRatioItemNumeratorChange={(e, i) => handleRatioItemNumeratorChange(e, i)} + onRatioItemDenominatorChange={(e, i) => handleRatioItemDenominatorChange(e, i)} + onRatioItemChangeColor={(e, i) => handleRatioItemChangeColor(e, i)} + + setIsClean={isClean => setIsClean(isClean)} + isClean={isClean} + envName={envName} envId={envId} + uuid={uuid} origStacks={pruned.stacks} + origRatio={pruned.ratio} + origYields={uhepp.yields} isVariationReady={isVariationReady} variations={variations} /> } - const UheppHistUI = (props) => { return <Router><Switch> <Route path="/:envName/:envStack" render={() => <UheppHistUIRouted {...props} />} /> diff --git a/uhepp_org/uhepp_api/fixtures/collections.json b/uhepp_org/uhepp_api/fixtures/collections.json index d2730ebf6d5884757a8733c1f1d22e384691b49d..69153ac4de5efdc71aecefca4a9a78eaf1862995 100644 --- a/uhepp_org/uhepp_api/fixtures/collections.json +++ b/uhepp_org/uhepp_api/fixtures/collections.json @@ -1 +1 @@ -[{"model": "uhepp_vault.collection", "pk": 1, "fields": {"title": "Frank at the zoo", "slug": "", "owner": 1, "description": null, "visibility": 30}}, {"model": "uhepp_vault.collection", "pk": 2, "fields": {"title": "Frank in the office", "slug": "", "owner": 1, "description": null, "visibility": 20}}, {"model": "uhepp_vault.collection", "pk": 3, "fields": {"title": "Frank at home", "slug": "", "owner": 1, "description": null, "visibility": 10}}, {"model": "uhepp_vault.collection", "pk": 4, "fields": {"title": "Karl at the zoo", "slug": "", "owner": 4, "description": null, "visibility": 30}}, {"model": "uhepp_vault.collection", "pk": 5, "fields": {"title": "Karl in the office", "slug": "", "owner": 4, "description": null, "visibility": 20}}, {"model": "uhepp_vault.collection", "pk": 6, "fields": {"title": "Karl at home", "slug": "", "owner": 4, "description": null, "visibility": 10}}, {"model": "uhepp_vault.collection", "pk": 7, "fields": {"title": "John at the zoo", "slug": "", "owner": 3, "description": null, "visibility": 30}}, {"model": "uhepp_vault.collection", "pk": 8, "fields": {"title": "John in the office", "slug": "", "owner": 3, "description": null, "visibility": 20}}, {"model": "uhepp_vault.collection", "pk": 9, "fields": {"title": "John at home", "slug": "", "owner": 3, "description": null, "visibility": 10}}] \ No newline at end of file +[{"model": "uhepp_vault.collection", "pk": 1, "fields": {"title": "Frank at the zoo", "slug": "", "owner": 1, "description": null, "visibility": 30, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 2, "fields": {"title": "Frank in the office", "slug": "", "owner": 1, "description": null, "visibility": 20, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 3, "fields": {"title": "Frank at home", "slug": "", "owner": 1, "description": null, "visibility": 10, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 4, "fields": {"title": "Karl at the zoo", "slug": "", "owner": 4, "description": null, "visibility": 30, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 5, "fields": {"title": "Karl in the office", "slug": "", "owner": 4, "description": null, "visibility": 20, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 6, "fields": {"title": "Karl at home", "slug": "", "owner": 4, "description": null, "visibility": 10, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 7, "fields": {"title": "John at the zoo", "slug": "", "owner": 3, "description": null, "visibility": 30, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 8, "fields": {"title": "John in the office", "slug": "", "owner": 3, "description": null, "visibility": 20, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}, {"model": "uhepp_vault.collection", "pk": 9, "fields": {"title": "John at home", "slug": "", "owner": 3, "description": null, "visibility": 10, "activity": "2021-02-23T00:00:00+01:00", "created": "2021-02-23T00:00:00+01:00"}}] diff --git a/uhepp_org/uhepp_api/serializers.py b/uhepp_org/uhepp_api/serializers.py index 4f9c1644a5e37f77331c55305339552ce1cb1543..b8e66a7ae4124c9f2c98885c6db3509bc3c6ac5d 100644 --- a/uhepp_org/uhepp_api/serializers.py +++ b/uhepp_org/uhepp_api/serializers.py @@ -93,4 +93,4 @@ class PlotSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Plot - fields = ["uuid", "collection", "url", "uhepp"] + fields = ["uuid", "collection", "url", "uhepp", "comment"] diff --git a/uhepp_org/uhepp_api/uhepp.schema.json b/uhepp_org/uhepp_api/uhepp.schema.json index ce4c5f4e8740e2803bc13db98298f9a44d4fb70c..703ece91479005da9916d46360bae05be2909562 100644 --- a/uhepp_org/uhepp_api/uhepp.schema.json +++ b/uhepp_org/uhepp_api/uhepp.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://uhepp.org/uhepp_v0_1.schema.json", + "$id": "https://uhepp.org/uhepp_v0_3.schema.json", "title": "UHepp", "description": "Universal high-energy physics plots", "type": "object", @@ -41,7 +41,7 @@ "properties": { "version": { "type": "string", - "enum": ["0.1"] + "enum": ["0.1", "0.2", "0.3"] }, "type": { "type": "string", @@ -81,13 +81,15 @@ "enum": ["step", "stepfilled", "points"] }, "error": {"$ref": "#/definitions/error"}, + "x_errorbar": {"type": "boolean"}, + "keep_zero": {"type": "boolean"}, "content": { "type": "array", "items": { "type": "object", "properties": { "yield": {"$ref": "#/definitions/yield_list"}, - "label": {"type": "string"}, + "label": {"type": ["string", "null"]}, "style": {"$ref": "#/definitions/style"} }, "required": ["yield", "label"], @@ -113,7 +115,10 @@ "type": "string", "enum": ["step", "stepfilled", "points"] }, - "error": {"$ref": "#/definitions/error"} + "error": {"$ref": "#/definitions/error"}, + "x_errorbar": {"type": "boolean"}, + "keep_zero": {"type": "boolean"}, + "den_error": {"$ref": "#/definitions/error"} }, "required": ["numerator"], "additionalProperties": false @@ -164,8 +169,8 @@ "symbol": {"type": "string"}, "unit": {"type": "string"}, "name": {"type": "string"}, - "log": {"type": "boolean"} - }, + "log": {"type": "boolean"}, + "unit_in_brackets": {"type": "boolean"} }, "required": ["symbol"], "additionalProperties": false }, @@ -178,7 +183,7 @@ "rebin": {"$ref": "#/definitions/edges"}, "include_underflow": {"type": "boolean"}, "include_overflow": {"type": "boolean"}, - "density_width": {"type": "boolean"} + "density_width": {"type": ["number", "null"]} }, "required": ["edges"], "additionalProperties": false diff --git a/uhepp_org/uhepp_api/views.py b/uhepp_org/uhepp_api/views.py index cd228103a4dc86a573814c5ace748da40ea63bdd..9e96349a2bc29ff279e31d0fb394f1beebe9a0a8 100644 --- a/uhepp_org/uhepp_api/views.py +++ b/uhepp_org/uhepp_api/views.py @@ -150,10 +150,15 @@ class PlotViewSet(viewsets.ModelViewSet): api_url = self.request.build_absolute_uri(api_url) location = {"Location": api_url} + expose = {} + if self.action == "retrieve": + expose = {"Access-Control-Expose-Headers": "Content-Length"} + return { "Link": f'<{ui_url}>; rel="ui"', "X-Resource-UUID": uuid, - **location + **location, + **expose } def retrieve(self, request, *args, **kwargs): diff --git a/uhepp_org/uhepp_org/settings.py b/uhepp_org/uhepp_org/settings.py index c90dbdd5bb781d1e509707edc82ff379a1fd4f9a..4675ed8c9adf2aa523c737b6fb8ef1f9f186d544 100644 --- a/uhepp_org/uhepp_org/settings.py +++ b/uhepp_org/uhepp_org/settings.py @@ -80,7 +80,10 @@ else: LOGOUT_REDIRECT_URL = "http://dev.uhepp.org:8000/" # Application definition -OIDC_RP_CLIENT_SECRET = os.environ['OIDC_RP_CLIENT_SECRET'] +if 'OIDC_RP_CLIENT_SECRET' not in os.environ: + print("Setting 'OIDC_RP_CLIENT_SECRET' to an empty string.") + print("SSO will not work!") +OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET', "") OIDC_RP_SIGN_ALGO = "RS256" OIDC_OP_JWKS_ENDPOINT = "https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/certs" @@ -109,6 +112,7 @@ INSTALLED_APPS = [ 'pygmentify', 'svg', 'crispy_forms', + 'django.contrib.humanize', ] CRISPY_TEMPLATE_PACK = 'bootstrap4' @@ -201,4 +205,6 @@ STATIC_URL = '/static/' DATETIME_INPUT_FORMATS = '%Y-%m-%d %H:%M' -SCHEMA_URL = "https://gitlab.cern.ch/fsauerbu/uhepp/-/raw/master/uhepp.schema.json" +SCHEMA_URL_1 = "https://gitlab.cern.ch/fsauerbu/uhepp/-/raw/0.1.6/uhepp.schema.json" +SCHEMA_URL_2 = "https://gitlab.cern.ch/fsauerbu/uhepp/-/raw/v0.2.0/uhepp.schema.json" +SCHEMA_URL_3 = "https://gitlab.cern.ch/fsauerbu/uhepp/-/raw/0cff18263ff936853e65a120cf9da02cb0deee93/uhepp.schema.json" diff --git a/uhepp_org/uhepp_vault/draft/icon.svg b/uhepp_org/uhepp_vault/draft/icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..026ce224246b8f5ce32c145a889bb0660f5c5234 --- /dev/null +++ b/uhepp_org/uhepp_vault/draft/icon.svg @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="30mm" + height="30mm" + viewBox="0 0 30 30" + version="1.1" + id="svg8" + inkscape:version="0.92.5 (0.92.5+68)" + sodipodi:docname="icon.svg" + inkscape:export-filename="/home/esel/repositories/uhepp.org/uhepp_org/uhepp_vault/static/uhepp_vault/favicon.png" + inkscape:export-xdpi="27.093334" + inkscape:export-ydpi="27.093334"> + <defs + id="defs2"> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath4812"> + <path + inkscape:connector-curvature="0" + id="path4814" + d="M 5.0000001,277.41073 H 25 v 3.95636 H 5.0000001 Z" + style="opacity:0.43700005;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.29999998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + </clipPath> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="4.6478472" + inkscape:cx="48.904119" + inkscape:cy="21.289944" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1855" + inkscape:window-height="1176" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="1" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-267)"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 3.0113853,275.56753 26.851993,275.11212" + id="path4753" + inkscape:connector-curvature="0" /> + <rect + style="opacity:1;fill:#e9ecef;fill-opacity:1;stroke:none;stroke-width:0.30000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4747" + width="30" + height="30" + x="0" + y="267" + rx="3" + ry="3" /> + <g + id="g4809" + transform="matrix(1.2,0,0,1,-3,5.821)" + style="stroke-width:0.91287094"> + <rect + y="282" + x="5" + height="15" + width="20" + id="rect4749" + style="opacity:1;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.27386126;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + style="opacity:1;fill:#1f8aba;fill-opacity:1;stroke:none;stroke-width:0.27386126;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4751" + width="20" + height="6.9848194" + x="5" + y="279" /> + <path + transform="translate(0,-0.52916663)" + clip-path="url(#clipPath4812)" + inkscape:connector-curvature="0" + d="m 28.11165,276.79127 -3.220261,5.57765 m 0.574426,-5.57765 -3.220261,5.57765 m 0.574426,-5.57765 -3.220261,5.57765 m 0.574426,-5.57765 -3.220261,5.57765 m 0.574426,-5.57765 -3.220261,5.57765 m 0.574426,-5.57765 -3.220261,5.57765 m 0.574426,-5.57765 -3.2202607,5.57765 m 0.5744256,-5.57765 -3.2202591,5.57765 m 0.5744256,-5.57765 -3.2202591,5.57765" + style="fill:#b3b3b3;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:0.51577204;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4777" /> + </g> + <g + id="g4804" + transform="matrix(1.2,0,0,1,-3,5.821)" + style="stroke-width:0.91287094"> + <path + inkscape:connector-curvature="0" + id="path4755" + d="M 5.5755688,274.03044 H 24.424431" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.06349456;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.06349456;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 15,264.89255 v 18.27578" + id="path4757" + inkscape:connector-curvature="0" /> + <ellipse + cy="274.03043" + cx="15.019766" + id="path4759" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06349456;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + rx="2.0833333" + ry="2.5" /> + </g> + </g> +</svg> diff --git a/uhepp_org/uhepp_vault/forms.py b/uhepp_org/uhepp_vault/forms.py index 1022de619ddaf18c0daeb6ab2e9aa56dae0c73c2..e892c0a0307551b02008d2ccc9123c3c3a149774 100644 --- a/uhepp_org/uhepp_vault/forms.py +++ b/uhepp_org/uhepp_vault/forms.py @@ -1,5 +1,6 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from . import models class TokenForm(forms.Form): description = forms.CharField(label=_('Description'), max_length=100) @@ -22,3 +23,9 @@ class CollectionPermForm(forms.BaseForm): self.declared_fields = self.base_fields super().__init__(*args, **kwds) + +class PlotForm(forms.ModelForm): + class Meta: + model = models.Plot + fields = ["comment"] + diff --git a/uhepp_org/uhepp_vault/management/commands/move_api_count_to_ui.py b/uhepp_org/uhepp_vault/management/commands/move_api_count_to_ui.py new file mode 100644 index 0000000000000000000000000000000000000000..90543d36d910f17270674e5a3809f06f13d184c7 --- /dev/null +++ b/uhepp_org/uhepp_vault/management/commands/move_api_count_to_ui.py @@ -0,0 +1,12 @@ + +from django.core.management.base import BaseCommand +from uhepp_vault.models import Plot +from django.db.models import F + +class Command(BaseCommand): + def handle(self, *args, **options): + print("Setting adding ui views to api") + Plot.objects.all().update( + api_view_count=F('ui_view_count') + F('api_view_count') + ) + print("Done") diff --git a/uhepp_org/uhepp_vault/migrations/0007_auto_20210201_1005.py b/uhepp_org/uhepp_vault/migrations/0007_auto_20210201_1005.py new file mode 100644 index 0000000000000000000000000000000000000000..d11118eab32add3180625b7c6b601df0efec5402 --- /dev/null +++ b/uhepp_org/uhepp_vault/migrations/0007_auto_20210201_1005.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.3 on 2021-02-01 10:05 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('uhepp_vault', '0006_plot_title'), + ] + + operations = [ + migrations.AddField( + model_name='plot', + name='uploaded', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AlterField( + model_name='plot', + name='title', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/uhepp_org/uhepp_vault/migrations/0008_auto_20210223_1620.py b/uhepp_org/uhepp_vault/migrations/0008_auto_20210223_1620.py new file mode 100644 index 0000000000000000000000000000000000000000..96c1926d24af9887cd2abff4b8d295e73fe37482 --- /dev/null +++ b/uhepp_org/uhepp_vault/migrations/0008_auto_20210223_1620.py @@ -0,0 +1,26 @@ +# Generated by Django 3.1.3 on 2021-02-23 16:20 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('uhepp_vault', '0007_auto_20210201_1005'), + ] + + operations = [ + migrations.AddField( + model_name='collection', + name='activity', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='collection', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + ] diff --git a/uhepp_org/uhepp_vault/migrations/0009_auto_20210409_1516.py b/uhepp_org/uhepp_vault/migrations/0009_auto_20210409_1516.py new file mode 100644 index 0000000000000000000000000000000000000000..0e9b150780812a49ed341bf74fead01de6db9dd4 --- /dev/null +++ b/uhepp_org/uhepp_vault/migrations/0009_auto_20210409_1516.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1.7 on 2021-04-09 15:16 + +from django.db import migrations, models +import django.db.models.functions.text + + +class Migration(migrations.Migration): + + dependencies = [ + ('uhepp_vault', '0008_auto_20210223_1620'), + ] + + operations = [ + migrations.AlterModelOptions( + name='collection', + options={'ordering': ['-activity', django.db.models.functions.text.Lower('title')]}, + ), + migrations.AddField( + model_name='plot', + name='comment', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/uhepp_org/uhepp_vault/models.py b/uhepp_org/uhepp_vault/models.py index 7c78ba3c8fff19a3ec4b46ae8ef7fd2b2943819e..61404e3454d4eb5022448a9d76231205431e2e68 100644 --- a/uhepp_org/uhepp_vault/models.py +++ b/uhepp_org/uhepp_vault/models.py @@ -6,6 +6,8 @@ from django.db import models from django.db.models import F from django.contrib.auth.models import User, Group from django.utils.translation import gettext_lazy as _ +from django.utils import timezone +from django.db.models.functions import Lower from uhepp_api.models import Token @@ -36,6 +38,11 @@ class Collection(models.Model): choices=VISIBILITY_LEVELS, default=PRIVATE_LEVEL, ) + created = models.DateTimeField(auto_now_add=True) + activity = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ['-activity', Lower('title')] def __str__(self): return f"{self.owner}: {self.title}" @@ -46,7 +53,8 @@ class LazyPlotManger(models.Manager): def get_queryset(self, *args, **kwargs): return super() \ .get_queryset(*args, **kwargs) \ - .defer("uhepp") + .defer("uhepp") \ + .order_by('-uploaded', 'title') class Plot(models.Model): """Database record of a uhepp compliant plot""" @@ -54,7 +62,9 @@ class Plot(models.Model): uhepp = models.JSONField() ui_view_count = models.IntegerField(default=0) api_view_count = models.IntegerField(default=0) - title = models.TextField(null=True, blank=True) + title = models.CharField(max_length=255, null=True, blank=True) + uploaded = models.DateTimeField(auto_now_add=True) + comment = models.TextField(null=True, blank=True) collection = models.ForeignKey( Collection, @@ -66,7 +76,7 @@ class Plot(models.Model): def view_count(self): # Plus 1 since increments are not updated - return self.api_view_count + self.ui_view_count + 1 + return self.api_view_count + 1 def increment_api_view_count(self): """Increment the view counter without a race condition""" @@ -80,6 +90,16 @@ class Plot(models.Model): ui_view_count=F('ui_view_count') + 1 ) + def save(self, *args, **kwds): + if self.title is None: + self.title = str(self) + + if self.collection: + self.collection.activity = timezone.now() + self.collection.save() + + return super().save(*args, **kwds) + def __str__(self): if self.title: return self.title diff --git a/uhepp_org/uhepp_vault/static/svg/logo.svg b/uhepp_org/uhepp_vault/static/svg/logo.svg index 13ee7d0825ed4c46342c61e46e0cc81d5aa8a1d5..36c734e324ad50a239c4181d3892ed7c96b50eed 100644 --- a/uhepp_org/uhepp_vault/static/svg/logo.svg +++ b/uhepp_org/uhepp_vault/static/svg/logo.svg @@ -1 +1 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" id="svg8" version="1.1" viewBox="0 0 60 19" height="19mm" width="60mm"> <g transform="translate(0,-278)" id="layer1"> <g id="text4749" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;line-height:125%;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-0.02910417px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" aria-label="uhepp hub"> <g id="text4782" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.05555534px;line-height:125%;font-family:arial;-inkscape-font-specification:'arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-0.02910417px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" aria-label="uhepphub"> <g transform="translate(-0.02517295,-1.443995)" id="g4809"> <path d="m 7.6599449,289.39653 v 2.667 c 0,0.62089 -0.635,1.08656 -1.411112,1.08656 -0.649111,0 -0.987777,-0.635 -0.987777,-1.31233 v -2.74505 c 0,-0.26812 0,-0.381 -0.169334,-0.381 -0.141111,0 -0.733777,0.18344 -1.298222,0.28222 -0.197555,0.0282 -0.381,0.14111 -0.381,0.29633 0,0.15522 0.141111,0.26811 0.296334,0.32456 0.338666,0.127 0.381,0.46566 0.381,0.70555 v 1.98305 c 0,0.81845 0.578555,1.72156 1.396999,1.72156 0.818445,0 1.27,-0.127 1.905,-0.635 0.183445,-0.15522 0.225778,-0.23989 0.282223,0.0141 0.04233,0.19755 0.04233,0.39511 0.08467,0.53622 0.01411,0.0706 0.08467,0.19756 0.197556,0.19756 0.211666,0 1.3687777,-0.33867 1.6227774,-0.46567 0.112888,-0.0564 0.254,-0.16933 0.254,-0.35278 0,-0.0988 -0.112889,-0.22578 -0.183445,-0.22578 -0.112889,0 -0.4092217,0.0847 -0.5926667,0.0847 -0.1411107,0 -0.2257767,-0.0423 -0.2257767,-0.18344 v -4.71311 c 0,-0.26812 0,-0.381 -0.169334,-0.381 -0.141111,0 -1.072444,0.15522 -1.580444,0.22577 -0.197556,0.0282 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.155222,0.23989 0.324555,0.26811 0.395111,0.0564 0.663223,0.254 0.663223,0.64911 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4784" /> <path d="m 16.275438,292.41631 v -2.55411 c 0,-1.03011 -0.776111,-1.96144 -1.763889,-1.96144 -0.733777,0 -1.284111,0.16933 -1.749777,0.55033 -0.112889,0.0988 -0.282223,0.31044 -0.282223,-0.0565 v -3.65477 c 0,-0.26811 0,-0.381 -0.169333,-0.381 -0.141111,0 -0.903111,0.23989 -1.467555,0.33866 -0.197556,0.0282 -0.409223,0.14111 -0.409223,0.29634 0,0.15522 0.169334,0.26811 0.324556,0.32455 0.338667,0.127 0.550333,0.40922 0.550333,0.64911 v 6.477 c 0,0.35278 -0.155222,0.79023 -0.536222,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05644,0.28223 0.423333,0.28223 0.310444,0 0.973667,-0.0847 1.255889,-0.0847 0.225778,0 0.719667,0.0847 1.030111,0.0847 0.366889,0 0.409222,-0.14112 0.409222,-0.28223 0,-0.14111 -0.155222,-0.254 -0.536222,-0.36689 -0.268111,-0.0847 -0.324556,-0.55033 -0.324556,-0.90311 l 0.01411,-2.25777 c 0,-1.07245 0.564444,-1.55223 1.382888,-1.55223 0.733778,0 1.227667,0.81845 1.227667,1.651 v 2.159 c 0,0.35278 -0.07056,0.83256 -0.324556,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05645,0.28223 0.423333,0.28223 0.310445,0 0.762,-0.0847 1.044223,-0.0847 0.225777,0 0.931333,0.0847 1.241777,0.0847 0.366889,0 0.381,-0.14112 0.381,-0.28223 0,-0.14111 -0.127,-0.254 -0.508,-0.36689 -0.381,-0.11288 -0.522111,-0.56444 -0.536222,-0.93133 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4786" /> <path d="m 19.322115,289.52353 c 0,-0.28222 0.578555,-0.97366 1.425222,-0.97366 0.409222,0 0.832556,0.28222 0.832556,0.69144 0,0.19756 -0.07056,0.43745 -0.211667,0.43745 h -1.862667 c -0.169333,0 -0.183444,-0.0564 -0.183444,-0.15523 z m 3.485444,-0.15522 c 0,-0.71967 -0.903111,-1.46755 -1.862666,-1.46755 -1.721556,0 -3.048,1.32644 -3.048,3.06211 0,1.59455 0.874889,3.11855 2.441222,3.11855 1.382889,0 2.201333,-0.67733 2.201333,-1.08655 0,-0.14111 -0.07055,-0.19756 -0.211667,-0.19756 -0.141111,0 -0.761999,0.32456 -1.467555,0.32456 -1.114778,0 -1.778,-1.15711 -1.778,-2.22956 0,-0.23989 0,-0.26811 0.05644,-0.42333 0.04233,-0.11289 0.07056,-0.14111 0.268112,-0.14111 h 2.977444 c 0.366889,0 0.423333,-0.57856 0.423333,-0.95956 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4788" /> <path d="m 25.143831,289.63642 c 0,-0.508 0.423333,-1.00189 1.058333,-1.00189 1.227667,0 1.552222,1.18534 1.552222,2.35656 0,1.15711 -0.493888,2.34244 -1.594555,2.34244 -0.522111,0 -1.016,-0.22577 -1.016,-0.74789 z m -1.171222,6.604 c 0,0.35278 -0.07056,0.69145 -0.254,0.74789 -0.381,0.11289 -0.620889,0.22578 -0.620889,0.36689 0,0.14111 0.127,0.28222 0.493889,0.28222 1.312333,0 2.159,0.18345 2.638777,0.18345 0.366889,0 0.479778,-0.14111 0.479778,-0.28223 0,-0.14111 -0.225778,-0.254 -0.606778,-0.36688 -0.761999,-0.21167 -0.959555,-0.32456 -0.959555,-0.81845 v -2.27189 c 0,-0.127 0.01411,-0.19755 0.141111,-0.19755 0.05645,0 0.268111,0.16933 0.550333,0.16933 1.876778,0 3.217334,-1.79211 3.217334,-3.71122 0,-1.18534 -0.945445,-2.44122 -2.116667,-2.44122 -0.635,0 -1.368778,0.33866 -1.594555,0.56444 -0.112889,0.11289 -0.183445,0.0423 -0.197556,-0.0282 v -0.254 c 0,-0.26811 0,-0.39511 -0.169333,-0.39511 -0.141111,0 -1.312334,0.46566 -1.467556,0.55033 -0.183444,0.0847 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.141111,0.23989 0.296333,0.29633 0.338667,0.127 0.578556,0.47978 0.578556,0.71967 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4790" /> <path d="m 31.426141,289.63642 c 0,-0.508 0.423334,-1.00189 1.058334,-1.00189 1.227666,0 1.552222,1.18534 1.552222,2.35656 0,1.15711 -0.493889,2.34244 -1.594556,2.34244 -0.522111,0 -1.016,-0.22577 -1.016,-0.74789 z m -1.171222,6.604 c 0,0.35278 -0.07055,0.69145 -0.254,0.74789 -0.381,0.11289 -0.620889,0.22578 -0.620889,0.36689 0,0.14111 0.127,0.28222 0.493889,0.28222 1.312334,0 2.159,0.18345 2.638778,0.18345 0.366889,0 0.479778,-0.14111 0.479778,-0.28223 0,-0.14111 -0.225778,-0.254 -0.606778,-0.36688 -0.762,-0.21167 -0.959556,-0.32456 -0.959556,-0.81845 v -2.27189 c 0,-0.127 0.01411,-0.19755 0.141112,-0.19755 0.05644,0 0.268111,0.16933 0.550333,0.16933 1.876778,0 3.217333,-1.79211 3.217333,-3.71122 0,-1.18534 -0.945444,-2.44122 -2.116667,-2.44122 -0.634999,0 -1.368777,0.33866 -1.594555,0.56444 -0.112889,0.11289 -0.183444,0.0423 -0.197556,-0.0282 v -0.254 c 0,-0.26811 0,-0.39511 -0.169333,-0.39511 -0.141111,0 -1.312333,0.46566 -1.467555,0.55033 -0.183445,0.0847 -0.409223,0.19756 -0.409223,0.35278 0,0.15522 0.141111,0.23989 0.296334,0.29633 0.338666,0.127 0.578555,0.47978 0.578555,0.71967 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4792" /> <path d="m 41.955894,292.41631 v -2.55411 c 0,-1.03011 -0.776111,-1.96144 -1.763889,-1.96144 -0.733777,0 -1.284111,0.16933 -1.749777,0.55033 -0.112889,0.0988 -0.282222,0.31044 -0.282222,-0.0565 v -3.65477 c 0,-0.26811 0,-0.381 -0.169334,-0.381 -0.141111,0 -0.903111,0.23989 -1.467555,0.33866 -0.197556,0.0282 -0.409222,0.14111 -0.409222,0.29634 0,0.15522 0.169333,0.26811 0.324555,0.32455 0.338667,0.127 0.550333,0.40922 0.550333,0.64911 v 6.477 c 0,0.35278 -0.155222,0.79023 -0.536222,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05644,0.28223 0.423333,0.28223 0.310445,0 0.973667,-0.0847 1.255889,-0.0847 0.225778,0 0.719667,0.0847 1.030111,0.0847 0.366889,0 0.409222,-0.14112 0.409222,-0.28223 0,-0.14111 -0.155222,-0.254 -0.536222,-0.36689 -0.268111,-0.0847 -0.324555,-0.55033 -0.324555,-0.90311 l 0.01411,-2.25777 c 0,-1.07245 0.564444,-1.55223 1.382889,-1.55223 0.733777,0 1.227666,0.81845 1.227666,1.651 v 2.159 c 0,0.35278 -0.07055,0.83256 -0.324555,0.90311 -0.381,0.11289 -0.550334,0.22578 -0.550334,0.36689 0,0.14111 0.05644,0.28223 0.423334,0.28223 0.310444,0 0.762,-0.0847 1.044222,-0.0847 0.225778,0 0.931333,0.0847 1.241778,0.0847 0.366888,0 0.381,-0.14112 0.381,-0.28223 0,-0.14111 -0.127,-0.254 -0.508,-0.36689 -0.381,-0.11288 -0.522112,-0.56444 -0.536223,-0.93133 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" id="path4794" /> <path d="m 47.641351,289.39653 v 2.667 c 0,0.62089 -0.635,1.08656 -1.411111,1.08656 -0.649111,0 -0.987778,-0.635 -0.987778,-1.31233 v -3.556 c 0,-0.26812 0,-0.381 -0.169333,-0.381 -0.141112,0 -0.733778,0.18344 -1.298223,0.28222 -0.197555,0.0282 -0.381,0.14111 -0.381,0.29633 0,0.15522 0.141111,0.26811 0.296334,0.32456 0.338666,0.127 0.381,0.46566 0.381,0.70555 v 2.794 c 0,0.81845 0.578555,1.72156 1.397,1.72156 0.818444,0 1.27,-0.127 1.905,-0.635 0.183444,-0.15522 0.225777,-0.23989 0.282222,0.0141 0.04233,0.19755 0.04233,0.39511 0.08466,0.53622 0.0141,0.0706 0.0847,0.19756 0.19756,0.19756 0.21166,0 1.36878,-0.33867 1.62278,-0.46567 0.11288,-0.0564 0.254,-0.16933 0.254,-0.35278 0,-0.0988 -0.11289,-0.22578 -0.18345,-0.22578 -0.11289,0 -0.40922,0.0847 -0.59267,0.0847 -0.14111,0 -0.22577,-0.0423 -0.22577,-0.18344 v -4.71311 c 0,-0.26812 0,-0.381 -0.16934,-0.381 -0.14111,0 -1.072581,0.15522 -1.580581,0.22577 -0.197555,0.0282 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.155222,0.23989 0.324555,0.26811 0.395112,0.0564 0.663223,0.254 0.663223,0.64911 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" id="path4796" /> <path d="m 52.221066,289.28364 c 0,-0.0988 0.35278,-0.64911 1.17122,-0.64911 1.28411,0 1.94734,1.34056 1.94734,2.62467 0,1.143 -0.46567,2.14489 -1.49578,2.14489 -1.08656,0 -1.62278,-0.49389 -1.62278,-1.18533 z m -1.17122,-3.302 v 7.28134 c 0,0.21166 0.94544,0.81844 2.35655,0.81844 1.778,0 3.23145,-1.50989 3.23145,-3.27378 0,-1.41111 -1.05834,-2.90688 -2.49767,-2.90688 -0.77611,0 -1.38289,0.26811 -1.62278,0.43744 -0.21166,0.15522 -0.29633,0.127 -0.29633,-0.0706 v -3.51366 c 0,-0.28222 -0.0141,-0.39511 -0.18344,-0.39511 -0.14112,0 -0.90312,0.23989 -1.46756,0.33866 -0.19756,0.0282 -0.40922,0.14111 -0.40922,0.29634 0,0.15522 0.16933,0.26811 0.32455,0.32455 0.33867,0.127 0.56445,0.39511 0.56445,0.66322 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" id="path4798" /> </g> </g> </g> </g> </svg> +<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" id="svg8" version="1.1" viewBox="0 0 60 19" height="19mm" width="60mm"> <g transform="translate(0,-278)" id="layer1"> <g id="text4749" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;line-height:125%;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-0.02910417px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" aria-label="uhepp hub"> <g id="text4782" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.05555534px;line-height:125%;font-family:arial;-inkscape-font-specification:'arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-0.02910417px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" aria-label="uhepphub"> <g transform="translate(-0.02517295,-1.443995)" id="g4809"> <path d="m 7.6599449,289.39653 v 2.667 c 0,0.62089 -0.635,1.08656 -1.411112,1.08656 -0.649111,0 -0.987777,-0.635 -0.987777,-1.31233 v -2.74505 c 0,-0.26812 0,-0.381 -0.169334,-0.381 -0.141111,0 -0.733777,0.18344 -1.298222,0.28222 -0.197555,0.0282 -0.381,0.14111 -0.381,0.29633 0,0.15522 0.141111,0.26811 0.296334,0.32456 0.338666,0.127 0.381,0.46566 0.381,0.70555 v 1.98305 c 0,0.81845 0.578555,1.72156 1.396999,1.72156 0.818445,0 1.27,-0.127 1.905,-0.635 0.183445,-0.15522 0.225778,-0.23989 0.282223,0.0141 0.04233,0.19755 0.04233,0.39511 0.08467,0.53622 0.01411,0.0706 0.08467,0.19756 0.197556,0.19756 0.211666,0 1.3687777,-0.33867 1.6227774,-0.46567 0.112888,-0.0564 0.254,-0.16933 0.254,-0.35278 0,-0.0988 -0.112889,-0.22578 -0.183445,-0.22578 -0.112889,0 -0.4092217,0.0847 -0.5926667,0.0847 -0.1411107,0 -0.2257767,-0.0423 -0.2257767,-0.18344 v -4.71311 c 0,-0.26812 0,-0.381 -0.169334,-0.381 -0.141111,0 -1.072444,0.15522 -1.580444,0.22577 -0.197556,0.0282 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.155222,0.23989 0.324555,0.26811 0.395111,0.0564 0.663223,0.254 0.663223,0.64911 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4784" /> <path d="m 16.275438,292.41631 v -2.55411 c 0,-1.03011 -0.776111,-1.96144 -1.763889,-1.96144 -0.733777,0 -1.284111,0.16933 -1.749777,0.55033 -0.112889,0.0988 -0.282223,0.31044 -0.282223,-0.0565 v -3.65477 c 0,-0.26811 0,-0.381 -0.169333,-0.381 -0.141111,0 -0.903111,0.23989 -1.467555,0.33866 -0.197556,0.0282 -0.409223,0.14111 -0.409223,0.29634 0,0.15522 0.169334,0.26811 0.324556,0.32455 0.338667,0.127 0.550333,0.40922 0.550333,0.64911 v 6.477 c 0,0.35278 -0.155222,0.79023 -0.536222,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05644,0.28223 0.423333,0.28223 0.310444,0 0.973667,-0.0847 1.255889,-0.0847 0.225778,0 0.719667,0.0847 1.030111,0.0847 0.366889,0 0.409222,-0.14112 0.409222,-0.28223 0,-0.14111 -0.155222,-0.254 -0.536222,-0.36689 -0.268111,-0.0847 -0.324556,-0.55033 -0.324556,-0.90311 l 0.01411,-2.25777 c 0,-1.07245 0.564444,-1.55223 1.382888,-1.55223 0.733778,0 1.227667,0.81845 1.227667,1.651 v 2.159 c 0,0.35278 -0.07056,0.83256 -0.324556,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05645,0.28223 0.423333,0.28223 0.310445,0 0.762,-0.0847 1.044223,-0.0847 0.225777,0 0.931333,0.0847 1.241777,0.0847 0.366889,0 0.381,-0.14112 0.381,-0.28223 0,-0.14111 -0.127,-0.254 -0.508,-0.36689 -0.381,-0.11288 -0.522111,-0.56444 -0.536222,-0.93133 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4786" /> <path d="m 19.322115,289.52353 c 0,-0.28222 0.578555,-0.97366 1.425222,-0.97366 0.409222,0 0.832556,0.28222 0.832556,0.69144 0,0.19756 -0.07056,0.43745 -0.211667,0.43745 h -1.862667 c -0.169333,0 -0.183444,-0.0564 -0.183444,-0.15523 z m 3.485444,-0.15522 c 0,-0.71967 -0.903111,-1.46755 -1.862666,-1.46755 -1.721556,0 -3.048,1.32644 -3.048,3.06211 0,1.59455 0.874889,3.11855 2.441222,3.11855 1.382889,0 2.201333,-0.67733 2.201333,-1.08655 0,-0.14111 -0.07055,-0.19756 -0.211667,-0.19756 -0.141111,0 -0.761999,0.32456 -1.467555,0.32456 -1.114778,0 -1.778,-1.15711 -1.778,-2.22956 0,-0.23989 0,-0.26811 0.05644,-0.42333 0.04233,-0.11289 0.07056,-0.14111 0.268112,-0.14111 h 2.977444 c 0.366889,0 0.423333,-0.57856 0.423333,-0.95956 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4788" /> <path d="m 25.143831,289.63642 c 0,-0.508 0.423333,-1.00189 1.058333,-1.00189 1.227667,0 1.552222,1.18534 1.552222,2.35656 0,1.15711 -0.493888,2.34244 -1.594555,2.34244 -0.522111,0 -1.016,-0.22577 -1.016,-0.74789 z m -1.171222,6.604 c 0,0.35278 -0.07056,0.69145 -0.254,0.74789 -0.381,0.11289 -0.620889,0.22578 -0.620889,0.36689 0,0.14111 0.127,0.28222 0.493889,0.28222 1.312333,0 2.159,0.18345 2.638777,0.18345 0.366889,0 0.479778,-0.14111 0.479778,-0.28223 0,-0.14111 -0.225778,-0.254 -0.606778,-0.36688 -0.761999,-0.21167 -0.959555,-0.32456 -0.959555,-0.81845 v -2.27189 c 0,-0.127 0.01411,-0.19755 0.141111,-0.19755 0.05645,0 0.268111,0.16933 0.550333,0.16933 1.876778,0 3.217334,-1.79211 3.217334,-3.71122 0,-1.18534 -0.945445,-2.44122 -2.116667,-2.44122 -0.635,0 -1.368778,0.33866 -1.594555,0.56444 -0.112889,0.11289 -0.183445,0.0423 -0.197556,-0.0282 v -0.254 c 0,-0.26811 0,-0.39511 -0.169333,-0.39511 -0.141111,0 -1.312334,0.46566 -1.467556,0.55033 -0.183444,0.0847 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.141111,0.23989 0.296333,0.29633 0.338667,0.127 0.578556,0.47978 0.578556,0.71967 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4790" /> <path d="m 31.426141,289.63642 c 0,-0.508 0.423334,-1.00189 1.058334,-1.00189 1.227666,0 1.552222,1.18534 1.552222,2.35656 0,1.15711 -0.493889,2.34244 -1.594556,2.34244 -0.522111,0 -1.016,-0.22577 -1.016,-0.74789 z m -1.171222,6.604 c 0,0.35278 -0.07055,0.69145 -0.254,0.74789 -0.381,0.11289 -0.620889,0.22578 -0.620889,0.36689 0,0.14111 0.127,0.28222 0.493889,0.28222 1.312334,0 2.159,0.18345 2.638778,0.18345 0.366889,0 0.479778,-0.14111 0.479778,-0.28223 0,-0.14111 -0.225778,-0.254 -0.606778,-0.36688 -0.762,-0.21167 -0.959556,-0.32456 -0.959556,-0.81845 v -2.27189 c 0,-0.127 0.01411,-0.19755 0.141112,-0.19755 0.05644,0 0.268111,0.16933 0.550333,0.16933 1.876778,0 3.217333,-1.79211 3.217333,-3.71122 0,-1.18534 -0.945444,-2.44122 -2.116667,-2.44122 -0.634999,0 -1.368777,0.33866 -1.594555,0.56444 -0.112889,0.11289 -0.183444,0.0423 -0.197556,-0.0282 v -0.254 c 0,-0.26811 0,-0.39511 -0.169333,-0.39511 -0.141111,0 -1.312333,0.46566 -1.467555,0.55033 -0.183445,0.0847 -0.409223,0.19756 -0.409223,0.35278 0,0.15522 0.141111,0.23989 0.296334,0.29633 0.338666,0.127 0.578555,0.47978 0.578555,0.71967 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#e6e6e6;stroke-width:0.26458332px" id="path4792" /> <path d="m 41.955894,292.41631 v -2.55411 c 0,-1.03011 -0.776111,-1.96144 -1.763889,-1.96144 -0.733777,0 -1.284111,0.16933 -1.749777,0.55033 -0.112889,0.0988 -0.282222,0.31044 -0.282222,-0.0565 v -3.65477 c 0,-0.26811 0,-0.381 -0.169334,-0.381 -0.141111,0 -0.903111,0.23989 -1.467555,0.33866 -0.197556,0.0282 -0.409222,0.14111 -0.409222,0.29634 0,0.15522 0.169333,0.26811 0.324555,0.32455 0.338667,0.127 0.550333,0.40922 0.550333,0.64911 v 6.477 c 0,0.35278 -0.155222,0.79023 -0.536222,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05644,0.28223 0.423333,0.28223 0.310445,0 0.973667,-0.0847 1.255889,-0.0847 0.225778,0 0.719667,0.0847 1.030111,0.0847 0.366889,0 0.409222,-0.14112 0.409222,-0.28223 0,-0.14111 -0.155222,-0.254 -0.536222,-0.36689 -0.268111,-0.0847 -0.324555,-0.55033 -0.324555,-0.90311 l 0.01411,-2.25777 c 0,-1.07245 0.564444,-1.55223 1.382889,-1.55223 0.733777,0 1.227666,0.81845 1.227666,1.651 v 2.159 c 0,0.35278 -0.07055,0.83256 -0.324555,0.90311 -0.381,0.11289 -0.550334,0.22578 -0.550334,0.36689 0,0.14111 0.05644,0.28223 0.423334,0.28223 0.310444,0 0.762,-0.0847 1.044222,-0.0847 0.225778,0 0.931333,0.0847 1.241778,0.0847 0.366888,0 0.381,-0.14112 0.381,-0.28223 0,-0.14111 -0.127,-0.254 -0.508,-0.36689 -0.381,-0.11288 -0.522112,-0.56444 -0.536223,-0.93133 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" id="path4794" /> <path d="m 47.641351,289.39653 v 2.667 c 0,0.62089 -0.635,1.08656 -1.411111,1.08656 -0.649111,0 -0.987778,-0.635 -0.987778,-1.31233 v -3.556 c 0,-0.26812 0,-0.381 -0.169333,-0.381 -0.141112,0 -0.733778,0.18344 -1.298223,0.28222 -0.197555,0.0282 -0.381,0.14111 -0.381,0.29633 0,0.15522 0.141111,0.26811 0.296334,0.32456 0.338666,0.127 0.381,0.46566 0.381,0.70555 v 2.794 c 0,0.81845 0.578555,1.72156 1.397,1.72156 0.818444,0 1.27,-0.127 1.905,-0.635 0.183444,-0.15522 0.225777,-0.23989 0.282222,0.0141 0.04233,0.19755 0.04233,0.39511 0.08466,0.53622 0.0141,0.0706 0.0847,0.19756 0.19756,0.19756 0.21166,0 1.36878,-0.33867 1.62278,-0.46567 0.11288,-0.0564 0.254,-0.16933 0.254,-0.35278 0,-0.0988 -0.11289,-0.22578 -0.18345,-0.22578 -0.11289,0 -0.40922,0.0847 -0.59267,0.0847 -0.14111,0 -0.22577,-0.0423 -0.22577,-0.18344 v -4.71311 c 0,-0.26812 0,-0.381 -0.16934,-0.381 -0.14111,0 -1.072581,0.15522 -1.580581,0.22577 -0.197555,0.0282 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.155222,0.23989 0.324555,0.26811 0.395112,0.0564 0.663223,0.254 0.663223,0.64911 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" id="path4796" /> <path d="m 52.221066,289.28364 c 0,-0.0988 0.35278,-0.64911 1.17122,-0.64911 1.28411,0 1.94734,1.34056 1.94734,2.62467 0,1.143 -0.46567,2.14489 -1.49578,2.14489 -1.08656,0 -1.62278,-0.49389 -1.62278,-1.18533 z m -1.17122,-3.302 v 7.28134 c 0,0.21166 0.94544,0.81844 2.35655,0.81844 1.778,0 3.23145,-1.50989 3.23145,-3.27378 0,-1.41111 -1.05834,-2.90688 -2.49767,-2.90688 -0.77611,0 -1.38289,0.26811 -1.62278,0.43744 -0.21166,0.15522 -0.29633,0.127 -0.29633,-0.0706 v -3.51366 c 0,-0.28222 -0.0141,-0.39511 -0.18344,-0.39511 -0.14112,0 -0.90312,0.23989 -1.46756,0.33866 -0.19756,0.0282 -0.40922,0.14111 -0.40922,0.29634 0,0.15522 0.16933,0.26811 0.32455,0.32455 0.33867,0.127 0.56445,0.39511 0.56445,0.66322 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" id="path4798" /> </g> </g> </g> </g> </svg> diff --git a/uhepp_org/uhepp_vault/static/svg/logo_dark.svg b/uhepp_org/uhepp_vault/static/svg/logo_dark.svg new file mode 100644 index 0000000000000000000000000000000000000000..82561647808eba039bb73832bc2fc53ef7ff700c --- /dev/null +++ b/uhepp_org/uhepp_vault/static/svg/logo_dark.svg @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="svg8" + version="1.1" + viewBox="0 0 60 19" + height="19mm" + width="60mm" + sodipodi:docname="logo_dark.svg" + inkscape:version="0.92.5 (0.92.5+68)"> + <metadata + id="metadata19"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs17" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1855" + inkscape:window-height="1176" + id="namedview15" + showgrid="false" + inkscape:zoom="1.3978819" + inkscape:cx="21.46104" + inkscape:cy="35.905512" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="1" + inkscape:current-layer="g4809" /> + <g + transform="translate(0,-278)" + id="layer1"> + <g + id="text4749" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;line-height:125%;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-0.02910417px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + aria-label="uhepp hub"> + <g + id="text4782" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.05555534px;line-height:125%;font-family:arial;-inkscape-font-specification:'arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-0.02910417px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + aria-label="uhepphub"> + <g + transform="translate(-0.02517295,-1.443995)" + id="g4809"> + <path + d="m 7.6599449,289.39653 v 2.667 c 0,0.62089 -0.635,1.08656 -1.411112,1.08656 -0.649111,0 -0.987777,-0.635 -0.987777,-1.31233 v -2.74505 c 0,-0.26812 0,-0.381 -0.169334,-0.381 -0.141111,0 -0.733777,0.18344 -1.298222,0.28222 -0.197555,0.0282 -0.381,0.14111 -0.381,0.29633 0,0.15522 0.141111,0.26811 0.296334,0.32456 0.338666,0.127 0.381,0.46566 0.381,0.70555 v 1.98305 c 0,0.81845 0.578555,1.72156 1.396999,1.72156 0.818445,0 1.27,-0.127 1.905,-0.635 0.183445,-0.15522 0.225778,-0.23989 0.282223,0.0141 0.04233,0.19755 0.04233,0.39511 0.08467,0.53622 0.01411,0.0706 0.08467,0.19756 0.197556,0.19756 0.211666,0 1.3687777,-0.33867 1.6227774,-0.46567 0.112888,-0.0564 0.254,-0.16933 0.254,-0.35278 0,-0.0988 -0.112889,-0.22578 -0.183445,-0.22578 -0.112889,0 -0.4092217,0.0847 -0.5926667,0.0847 -0.1411107,0 -0.2257767,-0.0423 -0.2257767,-0.18344 v -4.71311 c 0,-0.26812 0,-0.381 -0.169334,-0.381 -0.141111,0 -1.072444,0.15522 -1.580444,0.22577 -0.197556,0.0282 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.155222,0.23989 0.324555,0.26811 0.395111,0.0564 0.663223,0.254 0.663223,0.64911 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#666666;stroke-width:0.26458332px" + id="path4784" + inkscape:connector-curvature="0" /> + <path + d="m 16.275438,292.41631 v -2.55411 c 0,-1.03011 -0.776111,-1.96144 -1.763889,-1.96144 -0.733777,0 -1.284111,0.16933 -1.749777,0.55033 -0.112889,0.0988 -0.282223,0.31044 -0.282223,-0.0565 v -3.65477 c 0,-0.26811 0,-0.381 -0.169333,-0.381 -0.141111,0 -0.903111,0.23989 -1.467555,0.33866 -0.197556,0.0282 -0.409223,0.14111 -0.409223,0.29634 0,0.15522 0.169334,0.26811 0.324556,0.32455 0.338667,0.127 0.550333,0.40922 0.550333,0.64911 v 6.477 c 0,0.35278 -0.155222,0.79023 -0.536222,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05644,0.28223 0.423333,0.28223 0.310444,0 0.973667,-0.0847 1.255889,-0.0847 0.225778,0 0.719667,0.0847 1.030111,0.0847 0.366889,0 0.409222,-0.14112 0.409222,-0.28223 0,-0.14111 -0.155222,-0.254 -0.536222,-0.36689 -0.268111,-0.0847 -0.324556,-0.55033 -0.324556,-0.90311 l 0.01411,-2.25777 c 0,-1.07245 0.564444,-1.55223 1.382888,-1.55223 0.733778,0 1.227667,0.81845 1.227667,1.651 v 2.159 c 0,0.35278 -0.07056,0.83256 -0.324556,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05645,0.28223 0.423333,0.28223 0.310445,0 0.762,-0.0847 1.044223,-0.0847 0.225777,0 0.931333,0.0847 1.241777,0.0847 0.366889,0 0.381,-0.14112 0.381,-0.28223 0,-0.14111 -0.127,-0.254 -0.508,-0.36689 -0.381,-0.11288 -0.522111,-0.56444 -0.536222,-0.93133 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#666666;stroke-width:0.26458332px" + id="path4786" + inkscape:connector-curvature="0" /> + <path + d="m 19.322115,289.52353 c 0,-0.28222 0.578555,-0.97366 1.425222,-0.97366 0.409222,0 0.832556,0.28222 0.832556,0.69144 0,0.19756 -0.07056,0.43745 -0.211667,0.43745 h -1.862667 c -0.169333,0 -0.183444,-0.0564 -0.183444,-0.15523 z m 3.485444,-0.15522 c 0,-0.71967 -0.903111,-1.46755 -1.862666,-1.46755 -1.721556,0 -3.048,1.32644 -3.048,3.06211 0,1.59455 0.874889,3.11855 2.441222,3.11855 1.382889,0 2.201333,-0.67733 2.201333,-1.08655 0,-0.14111 -0.07055,-0.19756 -0.211667,-0.19756 -0.141111,0 -0.761999,0.32456 -1.467555,0.32456 -1.114778,0 -1.778,-1.15711 -1.778,-2.22956 0,-0.23989 0,-0.26811 0.05644,-0.42333 0.04233,-0.11289 0.07056,-0.14111 0.268112,-0.14111 h 2.977444 c 0.366889,0 0.423333,-0.57856 0.423333,-0.95956 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#666666;stroke-width:0.26458332px" + id="path4788" + inkscape:connector-curvature="0" /> + <path + d="m 25.143831,289.63642 c 0,-0.508 0.423333,-1.00189 1.058333,-1.00189 1.227667,0 1.552222,1.18534 1.552222,2.35656 0,1.15711 -0.493888,2.34244 -1.594555,2.34244 -0.522111,0 -1.016,-0.22577 -1.016,-0.74789 z m -1.171222,6.604 c 0,0.35278 -0.07056,0.69145 -0.254,0.74789 -0.381,0.11289 -0.620889,0.22578 -0.620889,0.36689 0,0.14111 0.127,0.28222 0.493889,0.28222 1.312333,0 2.159,0.18345 2.638777,0.18345 0.366889,0 0.479778,-0.14111 0.479778,-0.28223 0,-0.14111 -0.225778,-0.254 -0.606778,-0.36688 -0.761999,-0.21167 -0.959555,-0.32456 -0.959555,-0.81845 v -2.27189 c 0,-0.127 0.01411,-0.19755 0.141111,-0.19755 0.05645,0 0.268111,0.16933 0.550333,0.16933 1.876778,0 3.217334,-1.79211 3.217334,-3.71122 0,-1.18534 -0.945445,-2.44122 -2.116667,-2.44122 -0.635,0 -1.368778,0.33866 -1.594555,0.56444 -0.112889,0.11289 -0.183445,0.0423 -0.197556,-0.0282 v -0.254 c 0,-0.26811 0,-0.39511 -0.169333,-0.39511 -0.141111,0 -1.312334,0.46566 -1.467556,0.55033 -0.183444,0.0847 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.141111,0.23989 0.296333,0.29633 0.338667,0.127 0.578556,0.47978 0.578556,0.71967 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#666666;stroke-width:0.26458332px" + id="path4790" + inkscape:connector-curvature="0" /> + <path + d="m 31.426141,289.63642 c 0,-0.508 0.423334,-1.00189 1.058334,-1.00189 1.227666,0 1.552222,1.18534 1.552222,2.35656 0,1.15711 -0.493889,2.34244 -1.594556,2.34244 -0.522111,0 -1.016,-0.22577 -1.016,-0.74789 z m -1.171222,6.604 c 0,0.35278 -0.07055,0.69145 -0.254,0.74789 -0.381,0.11289 -0.620889,0.22578 -0.620889,0.36689 0,0.14111 0.127,0.28222 0.493889,0.28222 1.312334,0 2.159,0.18345 2.638778,0.18345 0.366889,0 0.479778,-0.14111 0.479778,-0.28223 0,-0.14111 -0.225778,-0.254 -0.606778,-0.36688 -0.762,-0.21167 -0.959556,-0.32456 -0.959556,-0.81845 v -2.27189 c 0,-0.127 0.01411,-0.19755 0.141112,-0.19755 0.05644,0 0.268111,0.16933 0.550333,0.16933 1.876778,0 3.217333,-1.79211 3.217333,-3.71122 0,-1.18534 -0.945444,-2.44122 -2.116667,-2.44122 -0.634999,0 -1.368777,0.33866 -1.594555,0.56444 -0.112889,0.11289 -0.183444,0.0423 -0.197556,-0.0282 v -0.254 c 0,-0.26811 0,-0.39511 -0.169333,-0.39511 -0.141111,0 -1.312333,0.46566 -1.467555,0.55033 -0.183445,0.0847 -0.409223,0.19756 -0.409223,0.35278 0,0.15522 0.141111,0.23989 0.296334,0.29633 0.338666,0.127 0.578555,0.47978 0.578555,0.71967 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#666666;stroke-width:0.26458332px" + id="path4792" + inkscape:connector-curvature="0" /> + <path + d="m 41.955894,292.41631 v -2.55411 c 0,-1.03011 -0.776111,-1.96144 -1.763889,-1.96144 -0.733777,0 -1.284111,0.16933 -1.749777,0.55033 -0.112889,0.0988 -0.282222,0.31044 -0.282222,-0.0565 v -3.65477 c 0,-0.26811 0,-0.381 -0.169334,-0.381 -0.141111,0 -0.903111,0.23989 -1.467555,0.33866 -0.197556,0.0282 -0.409222,0.14111 -0.409222,0.29634 0,0.15522 0.169333,0.26811 0.324555,0.32455 0.338667,0.127 0.550333,0.40922 0.550333,0.64911 v 6.477 c 0,0.35278 -0.155222,0.79023 -0.536222,0.90311 -0.381,0.11289 -0.550333,0.22578 -0.550333,0.36689 0,0.14111 0.05644,0.28223 0.423333,0.28223 0.310445,0 0.973667,-0.0847 1.255889,-0.0847 0.225778,0 0.719667,0.0847 1.030111,0.0847 0.366889,0 0.409222,-0.14112 0.409222,-0.28223 0,-0.14111 -0.155222,-0.254 -0.536222,-0.36689 -0.268111,-0.0847 -0.324555,-0.55033 -0.324555,-0.90311 l 0.01411,-2.25777 c 0,-1.07245 0.564444,-1.55223 1.382889,-1.55223 0.733777,0 1.227666,0.81845 1.227666,1.651 v 2.159 c 0,0.35278 -0.07055,0.83256 -0.324555,0.90311 -0.381,0.11289 -0.550334,0.22578 -0.550334,0.36689 0,0.14111 0.05644,0.28223 0.423334,0.28223 0.310444,0 0.762,-0.0847 1.044222,-0.0847 0.225778,0 0.931333,0.0847 1.241778,0.0847 0.366888,0 0.381,-0.14112 0.381,-0.28223 0,-0.14111 -0.127,-0.254 -0.508,-0.36689 -0.381,-0.11288 -0.522112,-0.56444 -0.536223,-0.93133 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" + id="path4794" + inkscape:connector-curvature="0" /> + <path + d="m 47.641351,289.39653 v 2.667 c 0,0.62089 -0.635,1.08656 -1.411111,1.08656 -0.649111,0 -0.987778,-0.635 -0.987778,-1.31233 v -3.556 c 0,-0.26812 0,-0.381 -0.169333,-0.381 -0.141112,0 -0.733778,0.18344 -1.298223,0.28222 -0.197555,0.0282 -0.381,0.14111 -0.381,0.29633 0,0.15522 0.141111,0.26811 0.296334,0.32456 0.338666,0.127 0.381,0.46566 0.381,0.70555 v 2.794 c 0,0.81845 0.578555,1.72156 1.397,1.72156 0.818444,0 1.27,-0.127 1.905,-0.635 0.183444,-0.15522 0.225777,-0.23989 0.282222,0.0141 0.04233,0.19755 0.04233,0.39511 0.08466,0.53622 0.0141,0.0706 0.0847,0.19756 0.19756,0.19756 0.21166,0 1.36878,-0.33867 1.62278,-0.46567 0.11288,-0.0564 0.254,-0.16933 0.254,-0.35278 0,-0.0988 -0.11289,-0.22578 -0.18345,-0.22578 -0.11289,0 -0.40922,0.0847 -0.59267,0.0847 -0.14111,0 -0.22577,-0.0423 -0.22577,-0.18344 v -4.71311 c 0,-0.26812 0,-0.381 -0.16934,-0.381 -0.14111,0 -1.072581,0.15522 -1.580581,0.22577 -0.197555,0.0282 -0.409222,0.19756 -0.409222,0.35278 0,0.15522 0.155222,0.23989 0.324555,0.26811 0.395112,0.0564 0.663223,0.254 0.663223,0.64911 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" + id="path4796" + inkscape:connector-curvature="0" /> + <path + d="m 52.221066,289.28364 c 0,-0.0988 0.35278,-0.64911 1.17122,-0.64911 1.28411,0 1.94734,1.34056 1.94734,2.62467 0,1.143 -0.46567,2.14489 -1.49578,2.14489 -1.08656,0 -1.62278,-0.49389 -1.62278,-1.18533 z m -1.17122,-3.302 v 7.28134 c 0,0.21166 0.94544,0.81844 2.35655,0.81844 1.778,0 3.23145,-1.50989 3.23145,-3.27378 0,-1.41111 -1.05834,-2.90688 -2.49767,-2.90688 -0.77611,0 -1.38289,0.26811 -1.62278,0.43744 -0.21166,0.15522 -0.29633,0.127 -0.29633,-0.0706 v -3.51366 c 0,-0.28222 -0.0141,-0.39511 -0.18344,-0.39511 -0.14112,0 -0.90312,0.23989 -1.46756,0.33866 -0.19756,0.0282 -0.40922,0.14111 -0.40922,0.29634 0,0.15522 0.16933,0.26811 0.32455,0.32455 0.33867,0.127 0.56445,0.39511 0.56445,0.66322 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.11111069px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';fill:#61b3d8;fill-opacity:1;stroke-width:0.26458332px" + id="path4798" + inkscape:connector-curvature="0" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/favicon.png b/uhepp_org/uhepp_vault/static/uhepp_vault/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..a55b83cc30ba19f97cb3c5897867b3bce0a9eb85 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/favicon.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-100.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-100.png new file mode 100644 index 0000000000000000000000000000000000000000..2b45530a0551a78587e07b1b0073b8b111989faf Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-100.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-128.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..fd34e382d5032860bb5c8baf8e9cfa9cdf8120c4 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-128.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-152.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-152.png new file mode 100644 index 0000000000000000000000000000000000000000..9ba96ee0b8ca339f1acf579fa0292ff17d906417 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-152.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-167.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-167.png new file mode 100644 index 0000000000000000000000000000000000000000..8da3235466ffa34fb2fcc5b38cccfa7de38eb445 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-167.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-180.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-180.png new file mode 100644 index 0000000000000000000000000000000000000000..e5b49ac800527dc8e47620febd4482654dcf15c2 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-180.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-192.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..34c5213ab56a05d9aca37ee5994ee13a12715f76 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-192.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-57.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-57.png new file mode 100644 index 0000000000000000000000000000000000000000..d418b8b7fa8f5c6952720cfaa09b6a0de37e9ef6 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-57.png differ diff --git a/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-76.png b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-76.png new file mode 100644 index 0000000000000000000000000000000000000000..be8849e56f004f7f97e2f6061af0ccac9738c214 Binary files /dev/null and b/uhepp_org/uhepp_vault/static/uhepp_vault/touch-icon-76.png differ diff --git a/uhepp_org/uhepp_vault/templates/404.html b/uhepp_org/uhepp_vault/templates/404.html new file mode 100644 index 0000000000000000000000000000000000000000..d9260fe17dea82e72012f6c97d694f52fe356b5d --- /dev/null +++ b/uhepp_org/uhepp_vault/templates/404.html @@ -0,0 +1,28 @@ + +{% extends 'uhepp_vault/base.html' %} + +{% block teaser %} +<div class="jumbotron jumbo-fluid plot-background shadow"> +<div class="container"> + <div class="text-danger display-4"> + <i class="fas fa-exclamation-triangle"></i> 404 + </div> + <h2>Not found!</h2> +</div> +</div> +{% endblock %} + +{% block content %} + +<div class="alert alert-danger"> +<h2 class="mt-2">Oops, something went wrong</h2> +<p> +We couldn't find the resource you were looking for. +Please make sure that</p> +<ul> + <li>the requested resource exists and</li> + <li>you have permission to view the resource.</li> +</ul> +The owner of the resource might have not made it visible to you. +</div> +{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/500.html b/uhepp_org/uhepp_vault/templates/500.html new file mode 100644 index 0000000000000000000000000000000000000000..e519871c195161801c00e3d5a78ef4682194106d --- /dev/null +++ b/uhepp_org/uhepp_vault/templates/500.html @@ -0,0 +1,22 @@ +{% extends 'uhepp_vault/base.html' %} + +{% block teaser %} +<div class="jumbotron jumbo-fluid plot-background shadow"> +<div class="container"> + <div class="text-danger display-4"> + <i class="fas fa-exclamation-triangle"></i> 500 + </div> + <h2>Internal error!</h2> +</div> +</div> +{% endblock %} + +{% block content %} + +<div class="alert alert-danger"> +<h2 class="mt-2">Oops, this is embarrassing</h2> +<p> +Something went wrong at our end. This might be a temporary issue. Please try +again. If the issue persists, please contact an administrator. +</div> +{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/registration/login.html b/uhepp_org/uhepp_vault/templates/registration/login.html index 2f3d86f3f689cc84d870db1d81d44666f5760cb1..11df7cc2c0bd01d80bc04a95ba54f3c57e0ebe77 100644 --- a/uhepp_org/uhepp_vault/templates/registration/login.html +++ b/uhepp_org/uhepp_vault/templates/registration/login.html @@ -13,14 +13,17 @@ <h1>Login</h1> <div class="row"> - <div class="col-md-6 p-4 card bg-light border-primary"> + <div class="col-md-6 d-flex"><div class="card p-4 bg-light border-primary"> <h2 class="mt-2"> CERN Single Sign-On <small class="text-muted">preferred</small> </h2> <p>Use your CERN account to sign in or create a new account.</p> <p><a tabindex="0" class="btn btn-primary" href="{% url 'oidc_authentication_init' %}{% if request.GET.next %}?next={{ request.GET.next }}{% endif %}">Sign in with CERN SSO</a></p> - </div> + <small class="text-multed">Please note, using the SSO creates an + account on uhepp.org, see our <a href="{% url 'uhepp_vault:privacy-policy' %}">Privacy Policy</a> for details. + </small> + </div></div> <div class="col-md-6 px-4"> <h2 class="mt-2">Uhepp hub account</h2> <p>You can only use this option if you have received a special @@ -28,6 +31,8 @@ <form method="post">{% csrf_token %} {{ form|crispy }} <button type="submit" class="btn btn-outline-primary">Sign in</button> + + </div> </div> {% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/base.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/base.html index e648b720acab344b7b24639e75aeb061c203cb7a..bb0e002f7b43e07f8770bb3179421ceca1665837 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/base.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/base.html @@ -7,11 +7,24 @@ <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="author" content="Frank Sauerburger" /> - <meta name="description" content="Universal HEP Plot storage -- uhepp" /> + <meta name="description" content="Universal HEP Plot storage - uhepp" /> + <link rel="canonical" href="https://uhepp.org/" /> + <link rel="icon" type="image/png" href="{% static 'uhepp_vault/favicon.png' %}"> + <link rel="apple-touch-icon" href="{% static 'uhepp_vault/touch-icon-120.png' %}"> + <link rel="apple-touch-icon" sizes="57x57" href="{% static 'uhepp_vault/touch-icon-57.png' %}"> + <link rel="apple-touch-icon" sizes="76x76" href="{% static 'uhepp_vault/touch-icon-76.png' %}"> + <link rel="apple-touch-icon" sizes="100x100" href="{% static 'uhepp_vault/touch-icon-100.png' %}"> + <link rel="apple-touch-icon" sizes="152x152" href="{% static 'uhepp_vault/touch-icon-152.png' %}"> + <link rel="apple-touch-icon" sizes="180x180" href="{% static 'uhepp_vault/touch-icon-180.png' %}"> + <link rel="apple-touch-icon" sizes="167x167" href="{% static 'uhepp_vault/touch-icon-167.png' %}"> + <link rel="icon" sizes="192x192" href="{% static 'uhepp_vault/touch-icon-192.png' %}"> <link rel="canonical" href="https://uhepp.org/" /> <link rel="stylesheet" href="{% static 'react/main.css' %}"> <link rel="stylesheet" href="{% pygmentify_css %}"> - <title>uhepp hub</title> + <script src="{% static 'react/runtime.js' %}"></script> + <script src="{% static 'react/vendors.js' %}"></script> + <script src="{% static 'react/main.js' %}"></script> + <title>{% block title %}uhepp hub{% endblock %}</title> <style> .navbar-brand svg { height: 39px; @@ -45,7 +58,7 @@ <a class="dropdown-item" href="{% url 'uhepp_vault:plot-list' %}">Plots</a> </div> </li> - {% if request.user.is_anonymous %} + {% if not request.user or request.user.is_anonymous %} <li class="nav-item"> <a class="nav-link" href="/login">Sign in</a> </li> @@ -78,9 +91,6 @@ {% endblock %} </div> -<script src="{% static 'react/runtime.js' %}"></script> -<script src="{% static 'react/vendors.js' %}"></script> -<script src="{% static 'react/main.js' %}"></script> {% block loadscript %} {% endblock %} @@ -88,7 +98,11 @@ <div class="container"> <div class="row"> <div class="col-md-5" style="text-align: left;"> - <p>Copyright 2020-{% now "Y" %}, Frank Sauerburger<p> + <ul class="pl-0"> + <li class="mb-3">Copyright 2020-{% now "Y" %}, Frank Sauerburger</li> + <li><a href="https://howlargeisthelhc.com/">howlargeisthelhc.com</a></li> + <li><a href="https://lhc-xsecs.org/">lhc-xsecs.org</a></li> + </ul> </div> <div class="col-md-2" style="text-align: center;"> <i title="Made with Poisson" class="fas fa-wave-square"></i> @@ -96,8 +110,9 @@ <div class="col-md-5" style="text-align: right"> <ul> <li><a href="mailto:f.sauerburger@cern.ch">Contact</a></li> - <li><a href="/legal">Legal notice</a></li> - <li><a href="/legal">Privacy policy</a></li> + <li><a href="{% url 'uhepp_vault:legal-notice' %}">Legal notice</a></li> + <li><a href="{% url 'uhepp_vault:terms-of-service' %}">Terms of service</a></li> + <li><a href="{% url 'uhepp_vault:privacy-policy' %}">Privacy policy</a></li> </ul> </div> </div> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection-index.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection-index.html deleted file mode 100644 index d57836f6f83a3808f141e2c762a4f472b7d7012d..0000000000000000000000000000000000000000 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection-index.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends 'uhepp_vault/base.html' %} - -{% block content %} -{% if plots %} - <ul> - {% for plot in plots %} - <li><a href="{% url 'uhepp_vault:plot-detail-json' plot.uuid %}">{{ plot }}: {{ plot.uuid }}<a></li> - {% endfor %} - </ul> -{% else %} - <p>No plots in this collection.</p> -{% endif %} -{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection.html deleted file mode 100644 index 47b6bd8fdd7898d031def482e8efb6c6785d6109..0000000000000000000000000000000000000000 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends 'uhepp_vault/base.html' %} - -{% block content %} -<h1> {{ collection.title }}</h1> -<p>Owner: <a href="{% url 'uhepp_vault:user-detail' owner %}">{{ owner.first_name }} {{ owner.last_name }}</a></p> - -{% if collection.description %} - <p> {{ collection.description }}</p> -{% endif %} - -{% if user == owner %} -<p>This is your collection</p> -<h3>Permission</h3> - -<h4>Access</h4> -<form method="post">{% csrf_token %} - <p>This collection and its plots can be <b>accessed</b> by: - {{ access_perm_form }} - - <p>Everyone in the above list can access the collection or its plots via their - unique ids or a link. Share this collection by link: - <a href="{% url 'uhepp_vault:collection-detail' collection.uuid %}"> - {% url 'uhepp_vault:collection-detail' collection.uuid %}</a></p> - - <h4>Discovery</h4> - <p>This collection is <b>listed and searchable</b> for: - {{ discover_perm_form }} - - <p>Everyone in the above list sees the collection on your profile but might not - be able to access the collection and its plots.</p> - <button type="submit">Update permissions</button> -</form> - -{% endif %} - -<h2>Plots</h2> -{% if plots %} - <ul> - {% for plot in plots %} - <li><a href="{% url 'uhepp_vault:plot-detail-json' plot.uuid %}">{{ plot }}: {{ plot.uuid }}<a></li> - {% endfor %} - </ul> -{% else %} - <p>No plots in this collection.</p> -{% endif %} -{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_confirm_delete.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_confirm_delete.html index f61bea56ec569e01b8ce6d3f0c5a082e278cefcd..5598fe2d3091c40efeb7bacdb8b711b0574cd234 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_confirm_delete.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_confirm_delete.html @@ -1,5 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% block title %}{{ collection.title }} - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_create.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_create.html deleted file mode 100644 index c8bc35d18697359d3c0e414c45c887312e36a26a..0000000000000000000000000000000000000000 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_create.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends 'uhepp_vault/base.html' %} - -{% block content %} -<form method="post">{% csrf_token %} - {{ form }} - <button type="submit">Create</button> -</form> -{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_detail.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_detail.html index 4e6789cd4edddac206dbab6007119e41c53055f3..22019fb5413e8f4f6021b417a9060e70e269561c 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_detail.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_detail.html @@ -1,5 +1,8 @@ {% extends 'uhepp_vault/base.html' %} {% load pygmentify_tags %} +{% load humanize %} + +{% block title %}{{ collection.title }} - uhepp hub{% endblock %} {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> @@ -75,7 +78,7 @@ hist.push({{ collection.pk }}) <h5>Pull collection</h5> {% pygmentify %} <pre class="bash"> -uhepp cpull {{ colleciton.pk }} DESTINATION +uhepp cpull {{ collection.pk }} DESTINATION </pre> {% endpygmentify %} @@ -98,6 +101,8 @@ uhepp push {{ collection.pk }} local_file.json <li> <a href="{% url 'uhepp_vault:plot-detail' plot.uuid %}"> {{ plot }} + <small title="{{ plot.uploaded|date:"Y-m-d H:m" }}" class="text-muted"> + ({{ plot.uploaded|naturaltime }})</small> </a> </li> {% endfor %}</ul> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_form.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_form.html index 0eda2f50de9fac66506076d6661c1f8e41b135a5..f81fefd9012952ca3cdf0ca45a3d9b5bd7e98d63 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_form.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_form.html @@ -1,6 +1,8 @@ {% extends 'uhepp_vault/base.html' %} {% load crispy_forms_tags %} +{% block title %}{% if view.edit %}Edit {{ collection.title }}{% else %}New Collection{% endif %} - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_list.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_list.html index fad959e48a3c6fab59548488101b989b323048f2..30c1479dedfff4a8f9415f3a084c2b3f5c6e62d1 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_list.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/collection_list.html @@ -1,4 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% load humanize %} + +{% block title %}Collections - uhepp hub{% endblock %} {% block content %} <nav aria-label="breadcrumb"> @@ -31,14 +34,19 @@ <span class="badge badge-primary badge-pill">{{ collection.plots.count }}</span> {% endif %} </div> - <small class="text-body"> - {{ collection.description }} - </small> + <div class="d-flex justify-content-between align-items-start"> + <small class="text-body"> + {{ collection.description }} + </small> + <small title="{{ collection.activity|date:"Y-m-d H:m" }}" class="text-muted"> + {{ collection.activity|naturaltime }} + </small> + </div> </a> {% endfor %} </div> {% else %} -<p>{{ user.first_name }} has no collections shared with you</p> +<p>There are no collections shared with you</p> {% endif %} {% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/getting_started.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/getting_started.html deleted file mode 100644 index 338eb99590fe7952b91e48b8c8a4fc750cf4eff5..0000000000000000000000000000000000000000 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/getting_started.html +++ /dev/null @@ -1,204 +0,0 @@ -{% extends 'uhepp_vault/base.html' %} -{% load pygmentify_tags %} - -{% block content %} - -<h1>Getting started</h1> -<p>This getting started guide will walk you through</p> -<ul> - <li>the creation of your first plot in uhepp format,</li> - <li>how to save and load plots locally and,</li> - <li>how to interact with the API of <a href="/">uhepp.org</a>.</li> -</ul> - -<p>The guide expects that you have some basic experience in Python. You need to -be familiar with functions and objects in Python, however, you don't need to be a library developer. The -whole uhepp eco-system does not rely on ROOT, therefore it's not a disadvantage, -if you are not familiar with ROOT. You should have a working installation of -Python 3 on your machine. The guide will install additional Python package. The -guide expect that you can install packages, either in your home directory, -system-wide or in a virtual environment</p> - -<p>This guide will not show you how to process HEP data. It is assumes that -you have an analysis framework in place that iterates over individual events and whose output is a collection of -histograms. These might be ROOT TH1 histograms or numpy arrays produced with -Numpy, Pandas, Dask, PySpark or Caffea. For this guide, we will read sample -histograms including statistical uncertainty from a JSON file.</p> - -<h2>Install requirements</h2> -<p>In principle, you only need a text editor to write and edit JSON or YAML files -and a simple tool like curl to interact with the API. However, this -would be extremely cumbersome. It is much more convenient to use the uhepp -Python package for both tasks. The package provides an interface to create and -manipulate plots in uhepp format stored locally or remotely via the API.</p> - -<p>For this guide we will need the uhepp package to work with the toy data. Make sure the -following package is installed. If you use pip, simply run:</p> - -{% pygmentify %} -<pre class="bash"> -pip install uhepp -</pre> -{% endpygmentify %} - -<p>To test that it is installed, open an interactive Python 3 shell and type</p> -{% pygmentify %} -<pre class="python"> -import uhepp -</pre> -{% endpygmentify %} - -<p>Keep the shell open, we will use it trough out this guide.</p> - -<h2>Create your first plot</h2> - -<h3>Raw data</h3> - -<p>The first step is to load the sample histograms from a prepared <a href="#">JSON -file</a>. -You can download the file using your browser, or if you prefer your terminal, -execute</p> - -{% pygmentify %} -<pre class="bash"> -curl -o toyhisto.json https://xxx/ -</pre> -{% endpygmentify %} - -<p>To load the sample data we can use Python's build-in json parser. If you have -downloaded the file to a different location, you need to adjust the path in the -following code snippet.</p> - -{% pygmentify %} -<pre class="python"> -import json -with open("toyhisto.json") toyhisto_file: - toydata = json.load(toyhisto_file) -</pre> -{% endpygmentify %} - -<p>The toydata variable is a dictionary, contains binned yields for three processes: -Simulated background (bkg), simulated signal (sig) and measured data. (Here in this example, -even the measure data is taken from a random generator.) Besides the yields, the -variable also contains the statistical uncertainty for each bin and process. -Additionally, the dictionary contains the key bin_edges. This variable, stores -the boundaries of the bins. Feel free, to explore the data. For example, run<p> - -{% pygmentify %} -<pre class="python"> -toydata["bkg"] -</pre> -{% endpygmentify %} - -<p>or</p> - -{% pygmentify %} -<pre class="python"> -toydata["sig_stat"] -</pre> -{% endpygmentify %} - -<p>in your interactive Python shell.</p> - -<p>You might have notices, that there are 41 bin edges. This means you would expect -40 bins. However, the binned data lists have 42 entries. The additional two -values correspond to the under- and overflow bins. By convention, these two bins -are always included in the raw data of a uhepp histogram. It is up to you, to -tell uhepp to include these events in the visual plot or not.</p> - - -<p>A uhepp histogram stores the raw data separated from the visual -specification. Let's start by creating a UHeppHist object and adding the raw -data to the histogram. When you create a histogram object, the first argument is -the mathematical symbol of the quantity of the x-axis. In our case, the data -represents a mass distribution, so we use the letter m. The second argument, are -the bin boundaries. We take them directly from the toydata dictionary.</p> - -{% pygmentify %} -<pre class="python"> -hist = uhepp.UHeppHist("m", toydata["bin_edges"]) -</pre> -{% endpygmentify %} - -<p>The raw data is stored in the yields attribute. It is another dictionary -mapping arbitrary internal names to the binned data. The binned data is stored -as Yield objects. The yields objects couple the value with its uncertainties. A -yield object comes close to a ROOT TH1 object (with some key distinctions). Yield -objects can be added, scales, etc. while propagating uncertainties. Let's first -create the yield objects from the sample data.</p> - -{% pygmentify %} -<pre class="python"> -signal_yield = uhepp.Yield(toydata["sig"], toydata["sig_stat"]) -background_yield = uhepp.Yield(toydata["bkg"], toydata["bkg_stat"]) -data_yield = uhepp.Yield(toydata["data"], toydata["data_stat"]) -</pre> -{% endpygmentify %} - -<p>Finally, let's add the yields to our histogram.</p> - -{% pygmentify %} -<pre class="python"> -hist.yields = { - "signal": signal_yield, - "background": background_yield, - "data": data_yield -} -</pre> -{% endpygmentify %} - -<p>The names that we use here as dictionary keys, can be arbitrary strings. You are encouraged to -use descriptive names, which makes editing the histogram much easier. We will -use these names later to refer to the yields when we specify the content of the -main plot or the ratio plot. In this example we've created a single background -entry. In a real-world histogram, you would have several different physics -proccesses with one entry per proccess. You are encouraged to use a fine-grained -process list here. Merging two or more yield entries in the visual specification is easy.</p> - -<h3>Visual settings</h3> - -{% pygmentify %} -<pre class="python"> -signal_si = uhepp.StackItem(["signal"], "Signal") -background_si = uhepp.StackItem(["background"], "Background") - -mc_stack = uhepp.Stack([background_si, signal_si]) -hist.stacks.append(mc_stack) -</pre> -{% endpygmentify %} - -{% pygmentify %} -<pre class="python"> -data_si = uhepp.StackItem(["data"], "Data") - -data_stack = uhepp.Stack([data_si], bartype="points") -hist.stacks.append(data_stack) -</pre> -{% endpygmentify %} - -{% pygmentify %} -<pre class="python"> -hist.variable = "Mass" -hist.unit = "GeV" -hist.filename = "higgs_mass_dist" -hist.author = "Your name" -</pre> -{% endpygmentify %} - -{% pygmentify %} -<pre class="python"> -ratio_si = uhepp.RatioItem(["data"], ["signal", "background"], bartype="points") -hist.ratio.append(ratio_si) -</pre> -{% endpygmentify %} - -{% pygmentify %} -<pre class="python"> -hist.show() -</pre> -{% endpygmentify %} -<h2>Local storage</h2> - -<h2>Online storage</h2> - -{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/home.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/home.html index 253cc6d751c64ce1fac4a4e977bb3dc9eb406823..4a164410d8fbccbe3e0240d995be46e2740ab0f2 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/home.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/home.html @@ -5,7 +5,13 @@ <div class="jumbotron jumbo-fluid plot-background shadow"> <div class="container"> <h1 class="display-4">uhepp hub</h1> - <h2>Home of {{ plot_count }} plots</h2> + <h2> + {% if plot_count < 10 %} + Home of plots + {% else %} + Home of over {{ plot_count }} plots + {% endif %} + </h2> </div> </div> {% endblock %} @@ -23,7 +29,7 @@ clear from the context which component is meant. The three parts are:</p> <p>The <a href="https://gitlab.cern.ch/fsauerbu/uhepp">Universal HEP plot</a> format defines a way to store the raw data and the visualization style in a - single. This allows for easy changes to the appearance, including colors, + single file. This allows for easy changes to the appearance, including colors, labels, or binning.</p> <p>The specification does not force a particular syntax. You can store the data in your favorite format that supports lists and maps, for example, YAML or diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/legal_notice.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/legal_notice.html new file mode 100644 index 0000000000000000000000000000000000000000..f8a8e01fb0c3392184a845372bb19696176f3c71 --- /dev/null +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/legal_notice.html @@ -0,0 +1,20 @@ +{% extends 'uhepp_vault/base.html' %} + +{% block title %}Legal notice - uhepp hub{% endblock %} + +{% block content %} +<nav aria-label="breadcrumb" class="d-none d-sm-block"> + <ol class="breadcrumb my-2"> + <li class="breadcrumb-item"><a href="/">Home</a></li> + <li class="breadcrumb-item"><a href="{% url 'uhepp_vault:legal-notice' %}">Legal notice</a></li> + </ol> +</nav> + +<h1 id="legal-notice">Legal notice</h1> +<p>Frank Sauerburger<br /> +Lameystr. 1<br /> +79108 Freiburg</p> + +<p>Phone: +49 7668 3192560<br /> +Email: info@sauerburger.com</p> +{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/logout.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/logout.html index d2cd33c250add5e6df923cb347de98748fd234c8..2dabd50a52c75702b2df203df3d2288473bc69f1 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/logout.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/logout.html @@ -1,6 +1,8 @@ {% extends 'uhepp_vault/base.html' %} {% load pygmentify_tags %} +{% block title %}Logout - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot.html deleted file mode 100644 index e3061379b4c3acca2d479033bf6e2a730d36d44d..0000000000000000000000000000000000000000 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot.html +++ /dev/null @@ -1 +0,0 @@ -{{ plot.data }} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_confirm_delete.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_confirm_delete.html index 4ba497f91be7d69c7a763dddb15b36a7faf6bf04..8d69119a1b8c043036f638e8928546b809bb7322 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_confirm_delete.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_confirm_delete.html @@ -1,5 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% block title %}{{ plot.title }} - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html index 9d8567f5a5d023d550bf30fa53670be325197e0a..9745177ac863ab2a0bf801f87acae07b8a7315e5 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html @@ -1,5 +1,9 @@ {% extends 'uhepp_vault/base.html' %} {% load pygmentify_tags %} +{% load humanize %} +{% load crispy_forms_tags %} + +{% block title %}{{ plot.title }} - uhepp hub{% endblock %} {% block content %} <nav aria-label="breadcrumb" class="d-none d-lg-block"> @@ -26,11 +30,18 @@ </a> </span> {% endif %}</h1> -<div class="d-none d-lg-inline text-muted"><i class="fas fa-eye"></i> - {{ plot.view_count }} view{{ plot.view_count|pluralize }} + +<div class="d-none d-lg-inline text-muted"> + <i class="fas fa-eye"></i> + {{ plot.view_count }} view{{ plot.view_count|pluralize }}, + <span title="{{ plot.uploaded|date:"Y-m-d H:m" }}"> + <i class="fas fa-clock"></i> + {{ plot.uploaded|naturaltime }} + </span> </div> -<div class="d-flex"> + +<div class="d-flex align-items-center"> <span class="d-none d-md-inline">UUID: {{ plot.uuid }}</span> <div class="ml-auto btn-group btn-group-sm access-menu"> <div class="btn-group btn-group-sm dropdown"> @@ -41,7 +52,9 @@ <form> <p>Download the uhepp data directly as a JSON file.</p> <p> - <a class="btn btn-primary" href="{% url 'uhepp_vault:plot-download' plot.uuid %}">Download JSON</a> + <a class="btn btn-primary" href="{% url 'uhepp_vault:plot-download' plot.uuid %}"> + <i class="fas fa-download mr-1"></i> + Download JSON</a> </p> <p>Copy the download URL</p> <input type="text" value="{{ request.scheme }}://{{ request.get_host }}{% url 'uhepp_vault:plot-download' plot.uuid %}" /> @@ -112,18 +125,14 @@ uhepp push {{ plot.collection.pk }} local_file.json</pre> </div> </div> - <div id="app-root"> <div class="d-flex justify-content-center my-5"> <div class="spinner-border text-primary" role="status"> <span class="sr-only">Loading...</span> </div> </div> + <div class="text-center">Loading interface...</div> </div> -{{ plot.uhepp|json_script:"plot-data" }} - - - {% endblock %} @@ -133,7 +142,7 @@ uhepp.fe.ReactDOM.render( uhepp.fe.React.createElement(uhepp.fe.Uhepp, { width: "555", height: "400", - uhepp: JSON.parse(document.getElementById('plot-data').textContent) + uuid: "{{plot.uuid}}" }), document.getElementById('app-root') ); diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_list.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_list.html index 4b05942c5bdd24444a5624424c449a8cf0443493..478b04a971edf954996ac82276b3846495fec509 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_list.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_list.html @@ -1,4 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% load humanize %} + +{% block title %}Plots - uhepp hub{% endblock %} {% block content %} <nav aria-label="breadcrumb"> @@ -15,6 +18,8 @@ <a href="{% url 'uhepp_vault:plot-detail' plot.uuid %}"> {{ plot.collection.owner.username}} / {{ plot.collection.title }} / {{ plot }} + <small title="{{ plot.uploaded|date:"Y-m-d H:m" }}" class="text-muted"> + ({{ plot.uploaded|naturaltime }})</small> </a> </li> {% endfor %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/plots.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/plots.html deleted file mode 100644 index 7695c165f23840face3f7b984a1517b5dff80533..0000000000000000000000000000000000000000 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/plots.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends 'uhepp_vault/base.html' %} - -{% block content %} -{% if plots %} - <ul> - {% for plot in plots %} - <li><a href="{% url 'uhepp_vault:plot-detail-json' plot.uuid %}">{{ plot }}: {{ plot.uuid }}<a></li> - {% endfor %} - </ul> -{% else %} - <p>No plots are available.</p> -{% endif %} -{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/privacy_policy.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/privacy_policy.html new file mode 100644 index 0000000000000000000000000000000000000000..be3f68092e38833993868ffe0b3289c8441cc44d --- /dev/null +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/privacy_policy.html @@ -0,0 +1,415 @@ +{% extends 'uhepp_vault/base.html' %} + +{% block title %}Privacy policy - uhepp hub{% endblock %} + +{% block content %} +<nav aria-label="breadcrumb" class="d-none d-sm-block"> + <ol class="breadcrumb my-2"> + <li class="breadcrumb-item"><a href="/">Home</a></li> + <li class="breadcrumb-item"><a href="{% url 'uhepp_vault:privacy-policy' %}">Privacy policy</a></li> + </ol> +</nav> + +<h1 id="privacy-policy">Privacy Policy</h1> +<p> + This privacy policy ("Privacy Policy") applies to all visitors and + users ("you") of the universal high-energy physics plot hub at <a + href="https://uhepp.org">uhepp.org</a> (the "Website", which are + offered by <a href="mailto:frank@sauerburger.com">Frank Sauerburger</a> + ("we" or "us"). Please read this Privacy Policy carefully. + By accessing or using any part of the Websites, you acknowledge you have been + informed of and consent to our practices with regard to your personal + information and data. +</p> + +<div class="card m-3 p-2"> + <h2 id="the-short-version">The short version</h2> + <p> + We collect your information only with your consent; we only collect the + minimum amount of personal information that is necessary to fulfill the + purpose of your interaction with us; we don't sell it to third parties; and we + only use it as this Privacy Policy describes. The data are processed within + the EU. + </p> + <p> + Many portions of the Website, including information you voluntarily provide, + will be public-facing for the open sharing of innovative developments, ideas, + and information that makes our collaborative community so great. While we are + committed to open sharing, we strive to respect the privacy of individual + community members and will minimize the information we collect and share. If + you do not want to share your information, including personally identifiable + information, with other community members and the public, please be thoughtful + as to how you interact with the Website and what information you provide + through the the Website (for example, through creating a public profile, + contributions to collections, uploads, and forks). + </p> + <p> + Of course, the short version doesn't tell you everything, so please read on + for more details! + </p> +</div> + +<h2 id="what-information-we-collect-and-why">What information we collect and why</h2> + +<h3 id="information-from-website-browsers">Information from website browsers</h3> +<p> + If you're <strong>just browsing the website</strong>, we collect the same + basic information that most websites collect. We use common internet + technologies, such as cookies and web server logs. This is stuff we collect + from everybody, whether they have an account or not. +</p> +<p> + The information we + collect about all visitors to our website includes the visitor's browser type, + language preference, referring site, additional websites requested, and the + date and time of each visitor request. We also collect potentially + personally-identifying information like Internet Protocol (IP) addresses. +</p> + +<div class="ml-4"> + <h4 id="why-do-we-collect-this">Why do we collect this?</h4> + <p> + We collect this information to monitor and protect the security of the + website. + </p> +</div> + +<h3 id="information-from-users-with-accounts">Information from users with accounts</h3> +<p> + If you use the CERN single-sign on, you create a user account and profile on + our site. We require some basic information at the time of account creation. + We receive the information in from the single-sign on interaction in an + automatied way. We store the your CERN user principle name, your home + institute, the CERN person ID, your email address and your given and last + names. +</p> +<p> + "User Personal Information" is any information about one of our users which + could, alone or together with other information, personally identify him or her. + Information such as a username and password, an email address, a real name, and + a photograph are examples of "User Personal Information." +</p> +<p> + User + Personal Information does not include aggregated, non-personally identifying + information. We may use aggregated, non-personally identifying information to + operate, improve, and optimize our website and service. +</p> + +<div class="ml-4"> + <h4 id="why-do-we-collect-this-1">Why do we collect this?</h4> + <ul> + <li> + We need your + User Personal Information to create your account, and to provide the services + you request. We use your User Personal Information, specifically your username, + to identify you the Website. + </li> + <li> + We use it to fill out your profile and + share that profile with other users if you ask us to. + </li> + <li> + We will use your + email address to communicate with you if you've said that's okay, <strong>and + only for the reasons you've said that's okay</strong>. Please see our section on + email communication for more information. + </li> + <li> + We use personally identifying information to contact in case of abuse of + the service; to block you from the abuseing the service. + </li> + <li> + We limit our use of your User + Personal Information to the purposes listed in this Privacy Policy. If we need + to use your User Personal Information for other purposes, we will ask your + permission first. You can always see what information we have, how we're using + it, and what permissions you have given us in your + <a href="{% url 'uhepp_vault:account-detail' %}">account settings</a>. + </li> + </ul> +</div> + +<h3 id="bases-for-processing-your-information">Bases for Processing Your Information</h3> +<p> + <b>Performance of a contract.</b> The use of your information may + be necessary to perform the contract that you have with us. For example, if you + use the Website to contribute to a collection, create a profile, post and comment + through the Website, or request information through the Website, we will use + your information to carry out our obligation to complete and administer that + request. +</p> +<p> + <b>Legitimate interests.</b> We use your information for our legitimate + interests, such as to provide you with the best content through the Website and + communications with users and the public, to improve our products and services, + and for administrative, security, fraud prevention and legal purposes. +</p> + +<h2 id="what-information-the-website-does-not-collect">What information the Website does not collect</h2> +<p> + We do not intentionally collect <strong>sensitive + personal information</strong>, such as social security numbers, genetic data, + health information, or religious information. Although the Website does not + request or intentionally collect any sensitive personal information, we realize + that you might store this kind of information in your account, such as in + a collection. If you store any sensitive personal information on our servers, you + are consenting to our storage of that information on our servers, which are in + the EU. +</p> +<p> + We do not intentionally collect information that is + <strong>stored in your collections</strong> or other free-form content inputs. + Information in your collections belongs to you, and you are responsible for it, + as well as for making sure that your content complies with our + <a href="{% url 'uhepp_vault:terms-of-service' %}">Terms of Service</a>. + We do not access private + collections unless required to for security or maintenance, or for support + reasons, with the consent of the collections owner. +</p> +<p> + If your collections is + public, anyone (including us) may view its contents. If you have included + private or sensitive information in your public collection, such as email + addresses, that information may be indexed by search engines or used by third + parties. In addition, we do not generally search for content in your + collections. +</p> +<p> + If you're a <strong>child under the age of 16</strong>, you + may not have an account on the Website. The Website does not knowingly collect + information from or direct any of our content specifically to children under 16. + If we learn or have reason to suspect that you are a user who is under the age + of 16, we will, unfortunately, have to close the child's account. Please see our + <a href="{% url 'uhepp_vault:terms-of-service' %}">Terms of Service</a> for + information about account termination. +</p> + +<h2 id="how-we-share-the-information-we-collect">How we share the information we collect</h2> +<p> + We only disclose potentially + personally-identifying and personally-identifying information to those of our + employees, contractors, and affiliated organizations that (i) need to know that + information in order to process it on our behalf or to provide services + available on the Website, and (ii) that have agreed not to disclose it to + others. +</p> +<p> + We will <strong>not rent or sell</strong> potentially + personally-identifying and personally-identifying information to anyone. Other + than to our employees, contractors, and affiliated organizations, as described + above, we disclose potentially personally-identifying and personally-identifying + information only when required to do so by law, or when we believe in good faith + that disclosure is reasonably necessary to protect the property or rights of us, + third parties, or the public at large. +</p> +<p> + We take measures reasonably + necessary to protect against the unauthorized access, use, alteration, or + destruction of potentially personally-identifying and personally-identifying + information. +</p> + +<h2 id="public-information-on-our-website">Public Information on our Website</h2> +<p> + Much of the Website is public-facing. If your content is + public-facing, third parties may access and use it in compliance with our + <a href="{% url 'uhepp_vault:terms-of-service' %}">Terms of Service</a>. + We do not sell that content; it + is yours. However, we do allow third parties, such as research organizations or + archives, to compile public-facing information. +</p> + +<p> + Your Personal + Information, associated with your content, may be gathered by third parties in + these compilations of data. If you do not want your Personal Information to + appear in third parties' compilations of data, please do not make your Personal + Information publicly available and be sure to + <a href="{% url 'uhepp_vault:account-detail' %}">configure your profile address to be + private</a>. +</p> + +<h2 id="how-we-communicate-with-you">How we communicate with you</h2> +<p> + If you are a registered user of the Website and + have supplied your email address, we may occasionally send you an email to tell + you about security, system information, new features, solicit your feedback, or + just keep you up to date with what's going on at the Website. We will send + emails to you about activities on the Website, such as collections, comments + if you have subscribed to receive such notifications. There's + a link located at the bottom of each of the notification emails we + send you so you can stop receiving such emails at any time. +</p> + +<h2 id="cookies">Cookies</h2> +<p> + The Website uses cookies to make interactions with + our service easy and meaningful. We use cookies (and similar technologies, like + HTML5 localStorage) to keep you logged in. +</p> +<p> + A + cookie is a small piece of text that our web server stores on your computer or + mobile device, which your browser sends to us when you return to our site. + Cookies do not necessarily identify you if you are merely visiting the Website; + however, a cookie may store a unique identifier for each logged in user. The + cookies the Website sets are essential for the operation of the website or are + used for performance or functionality. Visitors who do not wish to have cookies + placed on their computers may set their browsers to refuse cookies before using + the Websites. Disabling browser cookies may cause certain features of our + websites to not function properly. +</p> + +<h2 id="how-we-secure-your-information">How we secure your information</h2> +<p> + We + take all measures reasonably necessary to protect User Personal Information from + unauthorized access, alteration, or destruction; maintain data accuracy; and + help ensure the appropriate use of User Personal Information. We follow + generally accepted industry standards to protect the personal information + submitted to us, both during transmission and once we receive it. +</p> +<p> + No + method of transmission, or method of electronic storage, is 100% secure. + Therefore, we cannot guarantee its absolute security. +</p> +<h2 id="privacy-practices">Privacy Practices</h2> +<p> +Information we collect will be +stored and processed in the EU in accordance with this Privacy Policy. For all +Website visitors and users, we will: +</p> +<ul> + <li> + provide clear methods of unambiguous, informed consent when we do collect + your personal information; + </li> + <li> + only collect the minimum amount of personal data necessary for the purpose + it is collected for, unless you choose to provide us more; + </li> + <li> + offer you simple methods of accessing, correcting, or deleting your information that we + have collected, with the exception of information you voluntarily provide that + is necessary to retain as is for the integrity of our project code as described + further below; and + </li> + <li> + provide Website users notice, choice, + accountability, security, and access, and we limit the purpose for processing. + We also provide our users a method of recourse and enforcement. + </li> +</ul> +<p> +If +you are located in the European Union, you are entitled to the following rights +with regard to your personal information and data: +</p> +<ul> + <li> + Right of access + to your personal data, to know what information about you we hold + </li> + <li> + Right + to correct any incorrect or incomplete personal data about yourself that we + hold + </li> + <li> + Right to restrict/suspend our processing of your personal + data + </li> + <li> + Right to complain to a supervisory authority if you believe your + privacy rights are being violated + </li> +</ul> + +<p>Additional rights that may apply to you in certain instances:</p> +<ul> + <li>Right of data portability (if our processing is based on consent and + automated means)</li> + <li> Right to withdraw consent at any time (if processing is based on + consent)</li> + <li> Right to object to processing (if processing is based on legitimate + interests)</li> + <li> Right to object to processing of personal data for direct marketing + purposes</li> + <li> Right of erasure of your personal data from our system ("right to be forgotten") if certain grounds are + met</li> +</ul> + +<p> + To exercise your privacy rights, you can email us at the address + given below in the "Contacting us about your privacy" section of this + Privacy Policy. +</p> + +<h2 id="data-retention-and-deletion">Data Retention and Deletion</h2> +<p>If you already have an account on the Website, you may access, +update, alter, or delete your basic user profile information by logging into +your account and updating profile settings.</p> +<p>We will retain your +information for as long as your account is active or as needed to perform our +contractual obligations, provide you services through the Website, to comply +with legal obligations, resolve disputes, preserve legal rights, or enforce our +agreements.</p> +<p>We may retain certain User Personal Information indefinitely +unless you delete it or request its deletion. For example, we don’t +automatically delete inactive user accounts, so unless you choose to delete your +account, we will retain your account information indefinitely.</p> +<p>Please note that due to the open source nature of our products, services, and +community, we may retain limited +personally-identifiable information indefinitely. For example, if you provide +your information in connection with a comment, we may display that information +even if you have deleted your account as we do not automatically delete +community posts. Also, as described in our <a href="{% url 'uhepp_vault:terms-of-service' %}">Terms of +Service</a>, if you contribute to collections on the Website and provide your +personal information in connection with that contribution, that information +(including your name) will be embedded and publicly displayed with your +contribution and we will not be able to delete or erase it because doing so +would break the collection.</p> + +<h2 id="contacting-us-about-your-privacy">Contacting us about your privacy</h2> +<p>If you have questions or concerns about the way we are handling your +information or would like to exercise your privacy rights, please email us at +<a href="mailto:frank@sauerburger.com">frank@sauerburger.com</a> +or write a letter to</p> <blockquote> <p>Frank Sauerburger<br />Lameystr. 1<br +/> 79108 Freibrurg<br /> Germany</p> </blockquote> <p>We will respond +within 30 days of receiving your message at the latest but please note for +promptest response, we recommend emailing us.</p> + +<h2 id="privacy-policy-changes">Privacy Policy Changes</h2> +<p>Although most changes +are likely to be minor, we may change our privacy policy from time to time, and +in our sole discretion. We will provide notification to users who have provided +us email addresses of material changes to this Privacy Policy through our +website prior to the change taking effect by posting a notice on our home page +or sending email to the email address specified in your account. We encourage +visitors to frequently check this page for any minor changes to its Privacy +Policy. Your continued use of this site after any change in this Privacy Policy +will constitute your acceptance of such change.</p> + +<hr /> + +<div class="text-muted"> +<p>This privacy +statement is based on the <a href="https://about.gitlab.com/privacy/">privacy +statement of GitLab.com</a> which is published under a creative commons license +(<a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>) and +on the <a +href="https://help.github.com/articles/github-privacy-statement/">privacy +statement of GitHub.com</a> which is published under a creative commons license +(<a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a>). As a +result, this privacy statement is released under the <a +href="https://creativecommons.org/licenses/by-sa/4.0/">creative commons +Attribution-ShareAlike 4.0 International</a> license. Your use of this +information does not create an attorney-client relationship between you and us. +We are not a law firm. These policies and procedures may not suit your +organization's needs. Please consult a lawyer if you want to adopt these +policies for your own uses.</p> +</div> + +{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_detail.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_detail.html index fda74198967b61b32bdc3ee3f8d2b02315eea0d6..732151e85e2f28cde62f108a62fe4d3382efd1a7 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_detail.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_detail.html @@ -1,4 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% load humanize %} + +{% block title %}{{ user.first_name }} {{ user.last_name }} - uhepp hub{% endblock %} {% block content %} <nav aria-label="breadcrumb"> @@ -53,9 +56,14 @@ <span class="badge badge-primary badge-pill">{{ collection.plots.count }}</span> {% endif %} </div> - <small class="text-body"> - {{ collection.description }} - </small> + <div class="d-flex justify-content-between align-items-start"> + <small class="text-body"> + {{ collection.description }} + </small> + <small title="{{ collection.activity|date:"Y-m-d H:m" }}" class="text-muted"> + {{ collection.activity|naturaltime }} + </small> + </div> </a> {% endfor %} </div> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_form.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_form.html index 9fc3ab3ebd55f6601071b6bb263c7566f2284bde..285d3be2044cefa1e38e13b300cb946d48cb67bb 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_form.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_form.html @@ -1,6 +1,8 @@ {% extends 'uhepp_vault/base.html' %} {% load crispy_forms_tags %} +{% block title %}Account - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_list.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_list.html index 0c51013dcb70e725235a1d2253a797caf82a7f66..4b1a897d200df7268fc66570b6e8c4e4b9f910ef 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_list.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/profile_list.html @@ -1,5 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% block title %}Users - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/terms_of_service.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/terms_of_service.html new file mode 100644 index 0000000000000000000000000000000000000000..59367dc529ed7633102f293e8dc52608e6c86bcb --- /dev/null +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/terms_of_service.html @@ -0,0 +1,262 @@ +{% extends 'uhepp_vault/base.html' %} + +{% block title %}Terms of service - uhepp hub{% endblock %} + +{% block content %} +<nav aria-label="breadcrumb" class="d-none d-sm-block"> + <ol class="breadcrumb my-2"> + <li class="breadcrumb-item"><a href="/">Home</a></li> + <li class="breadcrumb-item"><a href="{% url 'uhepp_vault:terms-of-service' %}">Terms of service</a></li> + </ol> +</nav> + +<h1 id="terms-of-service">Terms of Service</h1> +<p> + The following terms and conditions govern all use of the universal + high-energy physics plot hub at <a href="https://uhepp.org">uhepp.org</a> (the + "Website") which are owned and operated by <a + href="mailto:frank@sauerburger.com">Frank Sauerburger</a> (“we†or “usâ€), + including all content, services and support packages provided via the Website. + The Website is offered subject to your acceptance without modification of all + of the terms and conditions contained herein and all other operating rules, + policies (including, without limitation, procedures that may be published from + time to time on this Website by us (collectively, the "Agreement"). +</p> + +<p> + Please read this Agreement carefully before accessing or using the Website. + By accessing or using any part of the Website, you agree to be bound by the + terms and conditions of this Agreement. If you do not agree to all the terms + and conditions of this Agreement, then you may not access the Website or use + any of the services. If these terms and conditions are considered an offer + by us, acceptance is expressly limited to these terms. The Website is + available only to individuals who are at least 16 years old. +</p> + +<h2 id="ownership">1. Ownership</h2> +<p> + Ownership, copyright, and title of any software that is developed by Frank + Sauerburger + shall at all times remain with Frank Sauerburger. Ownership, copyright, and title of + any software that is developed by us shall at all times remain with us. You + shall not acquire directly, indirectly or by implication any title, + copyright or ownership in the software or any parts thereof. We do not claim + any ownership rights to the information that you submit to the Website + itself, your code is yours. +</p> + +<h2 id="your-account-and-website">2. Your Account and Website</h2> +<p> + If you create an account on the Website, you are responsible for maintaining + the security of your account, and you are fully responsible for all + activities that occur under the account and any other actions taken in + connection with the account. You must immediately notify us of any + unauthorized use of your account or any other breaches of security. We will + not be liable for any acts or omissions by You, including any damages of any + kind incurred as a result of such acts or omissions. + +<h3 id="acceptable-user-of-your-account-and-the-website">Acceptable Use of +Your Account and the Website</h3> +<p> + By accepting this Agreement, you agree not to use, encourage, promote, or + facilitate others to use, the Website or your account in a way that is + harmful to others ("Acceptable Use"). Examples of harmful use + include, but are not limited to, engaging in illegal or fraudulent + activities, infringing upon others' intellectual property rights, + distributing harmful or offensive content that is defamatory, obscene, + abusive, an invasion of privacy, or harassing, violating the security or + integrity of any computer, network or communications system, and taxing + resources with activities such as cryptocurrency mining. +</p> + +<h2 id="responsibility-of-website-visitors">3. Responsibility of Website Visitors</h2> +<p> + We have not reviewed, and cannot review, all of the material, including + data, posted to the Website, and cannot therefore be + responsible for that material’s content, use or effects. By operating the + Website, we do not represent or imply that it endorses the material there + posted, or that it believes such material to be accurate, useful or + non-harmful. You are responsible for taking precautions as necessary to + protect yourself and your computer systems from viruses, worms, Trojan + horses, and other harmful or destructive content. The Website may contain + content that is offensive, indecent, or otherwise objectionable, as well as + content containing technical inaccuracies, typographical mistakes, and other + errors. The Website may also contain material that violates the privacy or + publicity rights, or infringes the intellectual property and other + proprietary rights, of third parties, or the downloading, copying or use of + which is subject to additional terms and conditions, stated or unstated. We + disclaim any responsibility for any harm resulting from the use by visitors + of the Website, or from any downloading by those visitors of content there + posted. You are encouraged to report any violations of our Acceptable Use + requirements to us. +</p> +<h2 id="content-posted-on-other-websites">4. Content Posted on Other Websites</h2> +<p> + We have not reviewed, and cannot review, all of the material, including + computer software, made available through the websites and webpages to which + this website links, and that link to this website. We do not have any + control over those external websites and webpages, and are not responsible + for their contents or their use. If links are set by us, we ensure, to an + extent possible to us as a third party, that the linked content doesn't + violate applicable law at the time of setting the link. By linking to an + external website or webpage, we do not represent or imply that we + endorse such website or webpage. You are responsible for taking + precautions as necessary to protect yourself and your computer systems + from viruses, worms, Trojan horses, and other harmful or destructive + content. We disclaim any responsibility for any harm resulting from your + use of external websites and webpages. +</p> + +<h2 id="copyright-infringement">5. Copyright Infringement</h2> +<p> + As we ask others to respect its intellectual property rights, we respects + the intellectual property rights of others. If you believe that material + located on or linked to by us violates your copyright, you are encouraged to + notify us. +</p> + +<h2 id="data-privacy">6. Data Privacy</h2> +<p> + You shall ensure that any and all information or data, including without + limitation, personal data, used by you in connection with the Agreement + ("User Data") is collected, processed, transferred and used in full + compliance with Applicable Data Protection Laws (as defined below) and that + it has all obtained all necessary authorizations and consents from any data + subjects to process User Data. If applicable, you shall adopt and maintain + appropriate organizational, technical and security measures prior to any + such collection, processing or transfer in order to protect against + unauthorized access to or use of User Data. You shall immediately inform use + upon becoming aware of any breach within the meaning of Applicable Data + Protection Law relating to User Data (a "Security Incident") and to + cooperate with us in any investigation thereof and in the implementation of + any measures reasonably required to be taken in response thereto. If + required by Applicable Data Protection Laws, the parties will enter into + standard contractual clauses under GDPR (as defined below) for the transfer + of any User Data outside of the European Union. For purposes hereof: (a) + "Applicable Data Protection Laws" means any applicable laws, + statutes or regulations as may be amended, extended or re-enacted from time + to time which relate to personal data including without limitation (i) prior + to 25 May 2018, the EU Data Protection Directive 95/46/EC as transposed into + EU Member State law; (ii) from and after 25 May 2018, GDPR and any EU Member + State laws implementing the GDPR; and (iii) the e-Privacy Directive + 2002/58/EC, as amended and as transposed into EU Member State law and any + legislation replacing the e-Privacy Directive and (b) "GDPR" means + the Regulation (EU) 2016/679 of the European Parliament and of the Counsel + of 27 April 2016 on the protection of natural persons with regard to the + processing of personal data and on the free movement of such data, and + repealing Directive 95/46/EC (General Data Protection Regulation). +</p> + +<p> + As part of your voluntary contribution to any collection, by agreeing to these + terms, you are acknowledging and agreeing that your name and email address + will become embedded and part of the collection, which may be publicly + available. You understand the removal of this information would be + impermissibly destructive to the collection and the interests of all those who + contribute, utilize, and benefit from it. Therefore, in consideration of + your participation in any project, you understand that retaining your name + and email address, as described above, does not require your consent and + that the right of erasure, as spelled out in the GDRP Article 17 (1) b does + not apply. The legal basis for our lawful processing of this personal data + is Article 6 (1) f ("processing is necessary for the purposes of the + legitimate interests pursued by the controller"). +</p> + +<p>See also our <a href="{% url 'uhepp_vault:privacy-policy' %}">privacy policy</a>.</p> + +<h2 id="changes">7. Changes</h2> +<p> + We reserve the right, at its sole discretion, to modify or replace any part + of this Agreement. It is your responsibility to check this Agreement + periodically for changes. Your continued use of or access to the Website + following the posting of any changes to this Agreement constitutes + acceptance of those changes. We may also, in the future, offer new services + and/or features through the Website (including, the release of new tools and + resources). Such new features and/or services shall be subject to the terms + and conditions of this Agreement. We may also, in the future, remove + features at any time without warning. +</p> + +<h2 id="general-representation">8. General Representation</h2> +<p> + You represent and warrant that (i) your use of the Website will be in strict + accordance with this Agreement and with all applicable laws and regulations + (including without limitation any local laws or regulations in your country, + state, city, or other governmental area, regarding online conduct and + acceptable content, and including all applicable laws regarding the + transmission of technical data exported from the EU or the country in which + you reside) and (ii) your use of the Website will not infringe or + misappropriate the intellectual property rights of any third party. +</p> + +<h2 id="termination">9. Termination</h2> +<p> + We may terminate your access to all or any part of the Website at any time, + with or without cause, with or without notice, effective immediately. If you + wish to terminate this Agreement or your account, you may simply discontinue + using the Website or delete user account. All provisions of this Agreement + which by their nature should survive termination shall survive termination, + including, without limitation, ownership provisions, warranty disclaimers, + indemnity and limitations of liability. +</p> + +<h2 id="limitation-of-liability">10. Limitation of Liability</h2> +<p> + In no event will we or any of our affiliates, our suppliers or licensors, be + liable with respect to any subject matter of this Agreement under any + contract, negligence, strict liability or other legal or equitable theory + for: (i) any special, incidental or consequential damages; (ii) the cost of + procurement for substitute products or services; (iii) for interruption + of use or loss or corruption of data; or (iv) for any damages + whatsoever. We shall have no liability for any failure or delay due to + matters beyond their reasonable control. The foregoing shall not apply + to the extent prohibited by applicable law. +</p> + +<h2 id="indemnification">11. Indemnification</h2> +<p> + You agree to indemnify and hold harmless us, its affiliates, contractors, + and its licensors, and their respective directors, officers, employees and + agents from and against any and all claims and expenses, including + attorneys' fees, arising out of your use of this Website, including but not + limited to your violation of this Agreement. +</p> + +<h2 id="disclaimer-of-warranty">12. Disclaimer of Warranty</h2> +<p> + The Website is provided "as is". We and our affiliates, suppliers + and licensors hereby disclaim all warranties of any kind, express or + implied, including, without limitation, the warranties of merchantability, + fitness for a particular purpose and non-infringement. Neither we nor our + suppliers and licensors, makes any warranty that the Website will be error + free or that access thereto will be continuous or uninterrupted. You + understand that you download from, or otherwise obtain content or services + through, the Website at your own discretion and risk. +</p> + +<h2 id="partial-invalidity">13. Partial Invalidity</h2> +<p> + If any provision of this document is held by a court of competent + jurisdiction to be invalid, void, or unenforceable, the remaining provisions + shall nevertheless continue in full force without being impaired or + invalidated in any way. +</p> + +<h2 id="failure-to-enforce">14. Failure to Enforce</h2> +<p> + The failure of either party to enforce at any time, or for any period of + time, the provisions hereof shall not be construed to be a waiver of such + provisions or of the right of such party to enforce each and every such + provision. +</p> + +<h2 id="governing-law">15. Governing law</h2> +<p>This Agreement shall be governed by and interpreted in accordance with the laws of the Federal Republic of Germany.</p> + +<hr /> + +<p class="text-muted"> +This agreement is based on the <a href="https://about.gitlab.com/terms/">terms of Use of GitLab.com</a> which is published under a creative commons license (<a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>). As a result, this privacy statement is released under the <a href="https://creativecommons.org/licenses/by-sa/4.0/">creative commons Attribution-ShareAlike 4.0 International</a> license. Your use of this information does not create an attorney-client relationship between you and us. We are not a law firm. These policies and procedures may not suit your organization's needs. Please consult a lawyer if you want to adopt these policies for your own uses. +</p> + +{% endblock %} diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/token_confirm_delete.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/token_confirm_delete.html index 5bc8fe1ec44c17f9cabfec5444a3875ed1a2f018..b40ec1485b8698ab609122dc3139c34dd8b9b239 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/token_confirm_delete.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/token_confirm_delete.html @@ -1,5 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% block title %}API tokens - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/token_form.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/token_form.html index c28317405a5183e2b44a1649d1a64442fefa0132..fe261485d95ad601136033c030c892157b82bef7 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/token_form.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/token_form.html @@ -2,6 +2,8 @@ {% load pygmentify_tags %} {% load crispy_forms_tags %} +{% block title %}API tokens - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/token_list.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/token_list.html index 5b840e1af9efa2ff27b14818b8e8a461c0bd1969..4e32c96d1a7ecd400f3085bbdb0e7dfe23256818 100644 --- a/uhepp_org/uhepp_vault/templates/uhepp_vault/token_list.html +++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/token_list.html @@ -1,5 +1,7 @@ {% extends 'uhepp_vault/base.html' %} +{% block title %}API tokens - uhepp hub{% endblock %} + {% block content %} <nav aria-label="breadcrumb" class="d-none d-sm-block"> <ol class="breadcrumb my-2"> diff --git a/uhepp_org/uhepp_vault/tests/test_quantize.py b/uhepp_org/uhepp_vault/tests/test_quantize.py new file mode 100644 index 0000000000000000000000000000000000000000..00062e7e30af78c513d150d2d9754d46f710a7b7 --- /dev/null +++ b/uhepp_org/uhepp_vault/tests/test_quantize.py @@ -0,0 +1,12 @@ + +import unittest +from uhepp_vault.views import quantize + +class QuantizeTest(unittest.TestCase): + + def testQuantize(self): + self.assertEqual(quantize(123, 0), 0) + self.assertEqual(quantize(123, 1), 100) + self.assertEqual(quantize(123, 2), 120) + self.assertEqual(quantize(123, 3), 123) + self.assertEqual(quantize(123, 4), 123) diff --git a/uhepp_org/uhepp_vault/urls.py b/uhepp_org/uhepp_vault/urls.py index e437ebf1c0a4e366c42eafcbbe277b9b9ed72eef..da40c3df6d375cd585fb51bb464cb9bbabbcc0f7 100644 --- a/uhepp_org/uhepp_vault/urls.py +++ b/uhepp_org/uhepp_vault/urls.py @@ -2,16 +2,22 @@ from django.urls import path, include from django.contrib.auth import views as auth_views from django.views.generic.base import RedirectView from django.conf import settings +from django.views.defaults import page_not_found, server_error from . import views app_name = 'uhepp_vault' urlpatterns = [ path('', views.home, name='home'), - path('uhepp_v0_1.schema.json', RedirectView.as_view(url=settings.SCHEMA_URL)), + path('uhepp_v0_1.schema.json', RedirectView.as_view(url=settings.SCHEMA_URL_1)), + path('uhepp_v0_2.schema.json', RedirectView.as_view(url=settings.SCHEMA_URL_2)), + path('uhepp_v0_3.schema.json', RedirectView.as_view(url=settings.SCHEMA_URL_3)), path('account/', views.AccountEditView.as_view(), name='account-detail'), path('login/', auth_views.LoginView.as_view()), path('logout/', views.logout, name='logout'), + path('legal-notice/', views.legal_notice, name='legal-notice'), + path('terms-of-service/', views.terms_of_service, name='terms-of-service'), + path('privacy-policy/', views.privacy_policy, name='privacy-policy'), path('tokens/', views.TokenListView.as_view(), name='token-list'), path('tokens/new', views.TokenCreateView.as_view(), name='token-create'), path('tokens/<int:pk>', views.TokenDeleteView.as_view(), name='token-delete'), @@ -26,4 +32,6 @@ urlpatterns = [ path('p/<str:uuid>', views.PlotDetail.as_view(), name='plot-detail'), path('p/<str:uuid>/delete', views.PlotDeleteView.as_view(), name='plot-delete'), path('p/<str:uuid>/download', views.plot_download, name='plot-download'), + path(r'404/', page_not_found, {'exception': Exception()}), + path(r'500/', server_error), ] diff --git a/uhepp_org/uhepp_vault/views.py b/uhepp_org/uhepp_vault/views.py index 1cce9da13bd274eb13299b3e3d1eea6f944b9b67..28761d806ad154b6d348bf724d466b2397160935 100644 --- a/uhepp_org/uhepp_vault/views.py +++ b/uhepp_org/uhepp_vault/views.py @@ -1,3 +1,4 @@ +import math from django.shortcuts import render from django.views import generic from django.db.models import Q @@ -18,13 +19,32 @@ from uhepp_api.masks import MaskRelatedColletions from .models import PUBLIC_LEVEL, INTERNAL_LEVEL, PUBLIC_LEVEL, \ Collection, Plot, Profile -from .forms import TokenForm +from .forms import TokenForm, PlotForm + +def quantize(value, sig_digits): + if value <= 100: + return value + + magnitude = math.floor(math.log10(value)) + scale = magnitude - sig_digits + 1 + return math.floor(value / 10**scale) * 10**scale + def home(request): plot_count = Plot.objects.count() + plot_count = quantize(plot_count, 2) context = dict(plot_count=plot_count) return render(request, "uhepp_vault/home.html", context=context) +def legal_notice(request): + return render(request, "uhepp_vault/legal_notice.html") + +def privacy_policy(request): + return render(request, "uhepp_vault/privacy_policy.html") + +def terms_of_service(request): + return render(request, "uhepp_vault/terms_of_service.html") + class TokenListView(LoginRequiredMixin, generic.ListView): model = Token template_name = 'uhepp_vault/token_list.html' @@ -156,7 +176,6 @@ class CollectionUpdateView(LoginRequiredMixin, generic.UpdateView): fields = ['title', 'description', 'visibility'] edit = True - def get_success_url(self): return reverse_lazy('uhepp_vault:collection-detail', args=[self.object.pk]) @@ -193,10 +212,12 @@ class PlotListView(generic.ListView): ) return queryset -class PlotDetail(generic.DetailView): +class PlotDetail(generic.UpdateView): model = Plot slug_field = 'uuid' slug_url_kwarg = 'uuid' + fields = ["comment"] + template_name = 'uhepp_vault/plot_detail.html' def get_queryset(self): if self.request.user.is_anonymous: