Commit 3001233b authored by Yohan Boniface's avatar Yohan Boniface

Explorer: basic tag based navigation for scenarios

parent c7c06663
......@@ -4,9 +4,9 @@ from roll import HttpError, Roll
from roll.extensions import cors
from . import VERSION
from .config import FEATURES, FINANCEMENTS, GLOSSARY, NAF, RAW_RULES, SCHEMA
from .config import FINANCEMENTS, GLOSSARY, NAF, RAW_RULES, SCHEMA
from .core import simulate
from .debugging import data_from_lbf_url, make_scenario
from .debugging import data_from_lbf_url, make_scenario, SCENARIOS
from .loggers import log_simulate, logger
from .openapis import OPENAPI
from .routine import get_formation_xml
......@@ -89,9 +89,9 @@ async def explore_financements(request, response):
response.json = FINANCEMENTS
@app.route('/explore/features')
async def explore_features(request, response):
response.json = FEATURES
@app.route('/explore/scenarios')
async def explore_scenarios(request, response):
response.json = SCENARIOS
@app.route('/explore/catalog')
......
......@@ -412,7 +412,6 @@ Scénario: Formation de 12 mois
Et le plafond de prise en charge vaut 18000
Et la rémunération applicable vaut 0
@wip
Scénario: Sans code financeur
Soit un bénéficiaire et une formation
Et la rémunération du bénéficiaire vaut 1500
......
......@@ -3,11 +3,16 @@ import bz2
import os
import sys
from datetime import datetime
from pathlib import Path
from urllib.parse import parse_qs, urlparse
import phpserialize
from behave.runner_util import parse_features
from .config import SCHEMA
from .config import SCHEMA, FINANCEMENTS
SCENARIOS = []
def yellow(s):
......@@ -152,3 +157,59 @@ def make_scenario(data, financements, name='Donne-moi un nom'):
steps.append(f"Alors le financement «{financement['nom']}» n'est "
"pas proposé")
return header + '\n '.join(steps)
def load_scenarios():
paths = (Path(__file__).parent / 'config/features/').glob('*.feature')
features = parse_features([str(p) for p in paths], language='fr')
scenarios = []
tags = set([])
for feature in features:
for scenario in feature.scenarios:
scenarios.append(load_scenario(feature, scenario))
tags.update(set(scenario.tags))
return scenarios
def load_scenario(feature, scenario):
scenario.tags.append(feature.name.lower())
steps = [load_step(scenario, step) for step in scenario.steps]
return {
'name': scenario.name,
'raw': '\n'.join('{keyword} {name}'.format(**step) for step in steps),
'tags': set(scenario.tags),
}
def load_step(scenario, step):
scenario_tag_from_step(scenario, step)
return {
'keyword': step.keyword,
'name': step.name,
}
def scenario_tag_from_step(scenario, step):
if step.step_type == 'given':
if step.name == "c'est un bénéficiaire de droit privé":
scenario.tags.append('salarié')
prefixes = [
"c'est une formation éligible région",
"la région de l'établissement du bénéficiaire vaut",
"la région du bénéficiaire vaut",
"le type de contrat du bénéficiaire vaut",
]
for prefix in prefixes:
if step.name.startswith(prefix):
scenario.tags.append(step.name[len(prefix)+2:-1].lower())
elif step.step_type == 'when':
prefix = "je sélectionne le financement"
if step.name.startswith(prefix):
name = step.name[len(prefix)+2:-1]
for financement in FINANCEMENTS:
if financement['nom'] == name:
scenario.tags.extend(
[t.lower() for t in financement['tags']])
SCENARIOS = load_scenarios()
<features>
<ul>
<li each={ props, id in this.features }><a href="#features/{ id }" class='{ on: this.active && this.active.startsWith(id) }' title={ id }>{ props.name }</a></li>
</ul>
<div if={ this.feature }>
<h3>{ this.feature.path } <a href=https://framagit.org/ybon/trefle/tree/master/trefle/config/{ this.feature.path } target=_blank> <i class="icon">edit</i></a></h3>
<rule content={ this.feature.raw } class=with-lines></rule>
</div>
<div if={ !this.feature }>
<p>Sélectionner un fichier dans le menu.</p>
</div>
<script>
this.features = []
this.feature = null
this.active = null
this.on('mount', () => this.load())
this.load_data = (data) => {
this.features = data
if (this.opts.id) {
this.active = decodeURIComponent(this.opts.id)
this.feature = data[this.active.split('~')[0]]
}
this.update()
// Force element target after DOM has been rebuilt
// (eg. if we have a ~line in the URL, to highlight it).
window.location.hash = window.location.hash
}
this.load = () => {
fetch('/explore/features')
.then((response) => response.json())
.then(this.load_data)
}
this.mixin(View)
</script>
<style scoped>
:scope.tools {
min-width: 1200px;
grid-template-columns: 1fr 4fr;
}
h3 {
font-variant: small-caps;
}
</style>
</features>
......@@ -5,7 +5,7 @@
<a href="#rules">Règles de gestion</a>
<a href="#simulate">Simuler</a>
<a href="#financements">Financements</a>
<a href="#features">Scénarios</a>
<a href="#scenarios">Scénarios</a>
<a href="#tools">Outils</a>
<a href="#glossary">Glossaire</a>
</nav>
......
<scenarios>
<ul>
<li each={ filter in this.filters }><a href="#" onclick={ this.toggle } class='{ on: this.selected_filters && (this.selected_filters.indexOf(filter) != -1) }' title={ filter }>{ filter }</a></li>
</ul>
<div if={ this.selected_filters.length && this.active.length }>
<!-- <h3>{ this.feature.path } <a href=https://framagit.org/ybon/trefle/tree/master/trefle/config/{ this.feature.path } target=_blank> <i class="icon">edit</i></a></h3> -->
<ul>
<li each={ this.active }><h4>{ name }</h4>
<rule content={ raw }></rule>
<!-- <pre><div each={ steps }>{ keyword } { name }</div></pre> -->
</li>
</ul>
</div>
<div if={ !this.selected_filters.length }>
<p>Sélectionner au moins une catégorie dans le menu.</p>
</div>
<script>
this.data = []
this.active = []
this.selected_filters = []
this.filters = []
this.on('mount', () => this.load())
this.load_data = (data) => {
this.data = data
if (this.opts.filters) {
this.selected_filters = this.opts.filters
this.active = []
}
main: for (var i = 0, scenario; i < this.data.length; i++) {
scenario = this.data[i]
for (var j = 0; j < this.selected_filters.length; j++) {
if (scenario.tags.indexOf(this.selected_filters[j]) === -1) continue main;
}
for (var j = 0; j < scenario.tags.length; j++) {
if (this.filters.indexOf(scenario.tags[j]) === -1) this.filters.push(scenario.tags[j])
}
this.active.push(scenario)
}
this.filters.sort((a, b) => a.localeCompare(b))
this.update()
}
this.toggle = (e) => {
var index = this.selected_filters.indexOf(e.item.filter)
if (index > -1) this.selected_filters.splice(index, 1)
else this.selected_filters.push(e.item.filter)
e.preventDefault()
route(`scenarios/${this.selected_filters.join(',')}`)
}
this.load = () => {
fetch('/explore/scenarios')
.then((response) => response.json())
.then(this.load_data)
}
this.mixin(View)
</script>
<style scoped>
:scope.tools {
min-width: 1200px;
grid-template-columns: 1fr 4fr;
}
h3 {
font-variant: small-caps;
}
</style>
</scenarios>
......@@ -11,7 +11,7 @@
<script src="components/glossary.tag.html" type="riot/tag"></script>
<script src="components/rule.tag.html" type="riot/tag"></script>
<script src="components/rules.tag.html" type="riot/tag"></script>
<script src="components/features.tag.html" type="riot/tag"></script>
<script src="components/scenarios.tag.html" type="riot/tag"></script>
<script src="components/simulate.tag.html" type="riot/tag"></script>
<script src="components/tools.tag.html" type="riot/tag"></script>
<script src="components/financements.tag.html" type="riot/tag"></script>
......@@ -26,7 +26,7 @@
<rules class=tools></rules>
<simulate class=tools></simulate>
<financements class=full></financements>
<features class=tools></features>
<scenarios class=tools></scenarios>
<tools></tools>
</main>
......@@ -36,8 +36,8 @@
route('/schema', () => riot.mount('schema'))
route('/rules/(.+)', (id) => riot.mount('rules', {id: id}))
route('/rules', () => riot.mount('rules'))
route('/features/(.+)', (id) => riot.mount('features', {id: id}))
route('/features', () => riot.mount('features'))
route('/scenarios/(.+)', (filters) => riot.mount('scenarios', {filters: decodeURIComponent(filters).split(',')}))
route('/scenarios', () => riot.mount('scenarios'))
route('/simulate', () => riot.mount('simulate'))
route('/simulate/(.+)', (url) => riot.mount('simulate', {url: url}))
route('/tools', () => riot.mount('tools'))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment