Commit 6ced8784 authored by Yohan Boniface's avatar Yohan Boniface

Prototype of an explorer UI

parent 69e328d0
recursive-include trefle/config *
recursive-include trefle/explorer *
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
ssh_keys_urls:
ybon: http://nuage.yohanboniface.me/id_rsa.pub
version: master
version: explorer
trefle:
version: 0.1.3
......@@ -8,8 +8,8 @@ server {
charset utf-8;
client_max_body_size 25M;
location /static/ {
alias /srv/trefle/venv/lib/python3.6/site-packages/trefle/static/;
location /explorer/ {
alias /srv/trefle/venv/lib/python3.6/site-packages/trefle/explorer/;
}
location / {
......
import logging
import pkg_resources
from http import HTTPStatus
from pathlib import Path
from roll import Roll, HttpError
from roll.extensions import cors
from .core import simulate
from .openapis import OPENAPI
from .config import SCHEMA, RAW_RULES
logger = logging.getLogger('trefle')
logger.setLevel(logging.DEBUG)
......@@ -53,3 +55,13 @@ async def simulate_(request, response):
@app.route('/schema')
async def schema(request, response):
response.json = OPENAPI
@app.route('/explore/schema')
async def explore_schema(request, response):
response.json = SCHEMA
@app.route('/explore/rules')
async def explore_rules(request, response):
response.json = RAW_RULES
import time
from pathlib import Path
import ujson as json
from minicli import cli, run
from roll.extensions import simple_server
from roll.extensions import simple_server, static
from .api import app
from .config import ELIGIBILITE, MODALITES, SCHEMA
......@@ -128,6 +129,8 @@ def render_trace_rules():
@cli
def serve():
"""Run a web server (for development only)."""
# Debug only.
static(app, '/explorer/', Path(__file__).parent / 'explorer')
simple_server(app)
......
......@@ -14,6 +14,7 @@ FINANCEMENTS = {}
ORGANISMES = {}
ROOT = Path(__file__).parent / 'config'
IDCC = {}
RAW_RULES = {}
INTERCARIF_URL = 'https://labonneformation.pole-emploi.fr/ws_intercarif'
ELIGIBILITE_URL = 'http://www.intercariforef.org/serviceweb2/eligibilite/?filtre=branche&'
......@@ -88,7 +89,9 @@ def load_financements(data, output=None, properties=None, namespace=None):
def load_rules(path):
with path.open() as rules_file:
return Rule.load(rules_file.readlines())
data = rules_file.read()
RAW_RULES[path.stem] = data
return Rule.load(data.split('\n'))
# TODO: move in utils?
......@@ -100,10 +103,10 @@ def init():
print('Initializing config')
with (ROOT / 'schema.yml').open() as f:
SCHEMA.update(load_schema(yaml.safe_load(f.read())))
ELIGIBILITE.extend(load_rules(ROOT / 'eligibilite.rules'))
paths = (ROOT / 'modalites').glob('*.rules')
for path in sorted(paths, key=lambda p: p.name.lower()):
MODALITES.extend(load_rules(path))
ELIGIBILITE.extend(load_rules(ROOT / 'eligibilite.rules'))
with (ROOT / 'financements.yml').open() as f:
load_financements(yaml.safe_load(f.read()), FINANCEMENTS)
with (ROOT / 'organismes.yml').open() as f:
......
@font-face {
font-family: 'sans_italic';
src: url('./font/sourcesanspro-it-webfont.woff2') format('woff2'),
url('./font/sourcesanspro-it-webfont.woff') format('woff');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'sans_light';
src: url('./font/sourcesanspro-light-webfont.woff2') format('woff2'),
url('./font/sourcesanspro-light-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'sans_regular';
src: url('./font/sourcesanspro-regular-webfont.woff2') format('woff2'),
url('./font/sourcesanspro-regular-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'sans_bold';
src: url('./font/sourcesanspro-bold-webfont.woff2') format('woff2'),
url('./font/sourcesanspro-bold-webfont.woff') format('woff');
font-weight: normal;
font-style: bold;
}
@font-face {
font-family: 'mono_regular';
src: url('./font/sourcecodepro-regular-webfont.woff2') format('woff2'),
url('./font/sourcecodepro-regular-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'mono_bold';
src: url('./font/sourcecodepro-bold-webfont.woff2') format('woff2'),
url('./font/sourcecodepro-bold-webfont.woff') format('woff');
font-weight: normal;
font-style: bold;
}
@font-face {
font-family: 'mono_semibold';
src: url('./font/sourcecodepro-semibold-webfont.woff2') format('woff2'),
url('./font/sourcecodepro-semibold-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'mono_medium';
src: url('./font/sourcecodepro-medium-webfont.woff2') format('woff2'),
url('./font/sourcecodepro-medium-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'mono_italic';
src: url('./font/sourcecodepro-it-webfont.woff2') format('woff2'),
url('./font/sourcecodepro-it-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
html, body, a, p, div, li, dl {
margin: 0;
padding: 0;
color: #222;
font-family: 'sans_regular';
/* font-size: 1.1em;
line-height: 1.4em;
*/}
.main {
max-width: 1280px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 2fr 2fr;
grid-gap: 10px;
}
a {
text-decoration: none;
}
/************** Tool Tip Styles ********************************/
.tooltip {
display: inline-block;
position: relative;
cursor: help;
}
.tooltip:hover {
text-decoration: underline dotted;
}
.tooltip .tooltip-content {
display: none;
background: #eee;
border: 1px solid #68c3a3;
font-size: 0.875em;
padding: 1em;
position: absolute;
left: 0;
bottom: calc(100% + 20px);
min-width: 150px;
z-index: 10;
}
.tooltip .tooltip-content:after {
content: "";
position: absolute;
left: 0;
bottom: -27px;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #68c3a3;
}
.tooltip:hover .tooltip-content {
display: inline-block;
}
<navbar>
<h1><a href="."></a></h1>
<section>
<a href="#schema" class="button button-link">Schéma</a>
<a href="#rules" class="button button-link">Règles de gestion</a>
</section>
<style>
:scope {
display: block;
padding: 2em;
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr 5fr;
background-color: #68c3a3;
}
:scope h1 {
display: inline-block;
font-size: 3em;
line-height: 1em;
}
:scope section a {
font-size: 1.4em;
font-variant: small-caps;
}
:scope section a:hover {
text-decoration: underline dotted;
}
:scope a + a {
margin-left: 1em;
}
:scope * {
display: flex;
align-items: center;
}
</style>
<script>
</script>
</navbar>
<rules>
<div each={ raw, name in items }>
<h3>{name}</h3>
<pre data-is=raw content="{ raw }"/>
</div>
<script>
this.items = []
this.on('mount', () => this.load())
load () {
fetch('/explore/rules')
.then((response) => response.json())
.then((data) => {
this.items = data
this.update()
})
}
this.mixin(View)
</script>
<style>
:scope pre {
padding: 5px;
border: solid 1px #68c3a3;
background-color: #eee;
max-width: 1000px;
/*overflow: auto;*/
font-family: 'mono_regular';
}
:scope pre strong {
font-family: 'mono_bold';
font-weight: normal;
}
:scope pre .comment {
font-family: 'mono_italic';
color: #666;
}
:scope pre .keyword {
font-family: 'mono_semibold';
}
:scope pre .constant {
font-family: 'mono_semibold';
text-decoration: underline dotted;
cursor: help;
}
:scope pre .string,
:scope pre .number {
font-family: 'mono_semibold';
}
:scope h3 {
font-variant: small-caps;
}
</style>
</rules>
<raw>
raw = opts.content
raw = raw.replace(/Si /g, '<strong>Si </strong>')
raw = raw.replace(/Ou /g, '<strong>Ou </strong>')
raw = raw.replace(/, ou /g, '<strong>, ou </strong>')
raw = raw.replace(/Et /g, '<strong>Et </strong>')
raw = raw.replace(/, et /g, '<strong>, et </strong>')
raw = raw.replace(/Alors /g, '<strong>Alors </strong>')
raw = raw.replace(/(#.+)/g, "<em class=comment>$1</em>")
raw = raw.replace(/( \d+)/g, "<span class=number>$1</span>")
raw = raw.replace(/(«.+»)/g, "<span class=string>$1</span>")
for (const [key, schema] of Object.entries(SCHEMA)) {
if (schema.label) {
let replace = `<span class='keyword tooltip'>$1<span class=tooltip-content>`
replace += `<strong>Description</strong> ${schema.description || schema.label}<br>`
if (schema.value) replace += `<strong>Value</strong> ${schema.value}<br>`
if (schema.source) replace += `<strong>Source</strong> ${schema.source}`
replace += `</span></span>`
raw = raw.replace(new RegExp('(' + schema.label + ')', 'g'), replace)
}
}
this.root.innerHTML = raw
</raw>
<schema>
<dl each={ props, name in items }>
<dt>{props.label || props.description} <em>({props.type})</em></dt>
<dd if={props.value}><strong>Valeur</strong> {props.value}</dd>
<dd if={props.description}><strong>Description</strong> {props.description}</dd>
<dd if={props.source}><strong>Source</strong> {props.source}</dd>
<dd if={props.xpath}><strong>LHEO</strong> {props.xpath}</dd>
</dl>
<script>
this.items = SCHEMA
this.mixin(View)
</script>
<style>
:scope dt {
font-variant: small-caps;
}
:scope dt em {
font-variant: normal;
}
:scope dl {
margin-top: 10px;
}
</style>
</schema>
<!DOCTYPE html>
<meta charset=utf-8>
<title>Trèfle feuille à feuille</title>
<script src="vendor/riot/riot%2Bcompiler.js"></script>
<script src="vendor/riot/route%2Btag.min.js"></script>
<link rel="stylesheet" type="text/css" href="app.css">
<script type="text/javascript" src="mixin/view.mixin.js"></script>
<script src="components/navbar.tag.html" type="riot/tag"></script>
<script src="components/schema.tag.html" type="riot/tag"></script>
<script src="components/rules.tag.html" type="riot/tag"></script>
<app>
<header data-is=navbar class=header></header>
<section class=main>
<schema></schema>