→ ces demandes, à l'exception de celle pour "un système externe", doivent être natives dans combo, et pas "blurps". (d'après moi aujourd'hui, Frédéric, 3 avril 2015)
Idées (Thomas) pour déclarer des COMBO_BLURPS dans le settings :
# settings.py COMBO_BLURPS = [ { 'slug': 'slug', 'template': 'slug.html', 'title': 'Blurp Title', 'aggregator': methode sources -> context, # 'list', 'dict', 'chain', ... 'sources': [ { 'slug': 'slug1', 'title': 'Source Title' 'url': 'https://wcs/myspace?NameId={{ user.username }}', 'initial_data': { }, # valeur initiale de la ressource si c'est un dict, pourrait être d'un autre type...? 'signature': '...?', 'lazy': true; # les données sont récupérées aussitôt (cf plus bas) }, { 'slug': 'slug2', ... } ], 'sources_choice': false; # possibiliter de limiter à un sous-ensemble des sources (false par défaut) }, { blurp2 ... }, ]
On prend chaque source sélectionnée (ou toutes sinon) → construction d'un contexte
# (tendance pseudo code) datas = {} for source in sources: data = Data(**source, context) # le contexte sera utilisé pour calculer l'url if not source['lazy']: data = data() context.update(data.as_dict()) source['data'] = data
Note : les sources "non lazy" sont récupérées aussitôt et permettent d'augmenter le contexte (ainsi, un premier webservice peut donnes des infos aux suivants)
A partir du sources qui contient donc des source['data'], on construit le contexte qui sera envoyé au template, selon le type d'aggregator défini dans le blurp:
data
directement, pas d'agrégation, bien sûrcontext = [data1, data2, data3]
context = {'slug1': data1, 'slug2': data2, 'slug3': data3}
context = [] + data1 + data2 + data3 # non lazy
Point à éclaircir : le fonctionnement des initial_data
(si besoin de plus de paramétres => une classe fille)
On construit le settings.COMBO_BLURPS à partir du hobo.json, c'est donc un TenantSettingsBlurps à écrire.
Exemple de blurps automatisables sur un combo lié à "n" wcs:openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem openssl ec -in vapid_private.pem -pubout -out vapid_public.pem openssl ec -in vapid_private.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' > vapid_private_key.txt openssl ec -in vapid_private.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' > vapid_public_key.txt
Dans var/lib/combo/tenant/nom-du-tenant/settings.json
, ajouter :
"PWA_VAPID_PUBLIK_KEY": "contenu de vapid_public_key.txt", "PWA_VAPID_PRIVATE_KEY": "contenu de vapid_private_key.txt", "PWA_VAPID_CLAIMS": { "sub": "mailto:admin+whatever@yourdomain.com" }
https://<combo>/manage/
en allant dans le menu hamburger, lien "Paiement en ligne"{
"numcli": "112233",
"saisie": "T"
}
"numcli"
le numéro client fourni par la DGFIP"saisie"
:
T
pour les tests,X
pour les tests d'activation (obligatoires avec mise en prod),W
pour la mise en production.Il est possible d'ajouter de nouveaux types de cellules pour afficher des données disponibles au format JSON, ces cellules apparaitront ensuite dans la liste des cellules disponibles, sous un sous-titre "Extra".
Dans les settings (soit le settings.py
du projet, soit le settings.json
du domaine), la définition se fait dans une clé JSON_CELL_TYPES
.
Par exemple, pour la création d'une cellule affichant la qualité de l'air, tirant ses données du site strasmap.eu, la cellule serait annoncée de la sorte :
"JSON_CELL_TYPES": { "info-atmo": { "name": "Info Atmo", "url": "http://strasmap.eu/remote.amf.json/Atmo.status" } }
La suite du travail consiste à fournir un gabarit de rendu pour ces données, ceux-ci se placent dans le répertoire templates/combo/json/ du site (e.g. /var/lib/combo/tenants/www.example.net/templates/combo/json/), avec comme nom l'identifiant de la cellule + ".html", ex: info-atmo.html
.
Il s'agit d'un gabarit Django, ils sont documentés en détails à cette adresse : https://docs.djangoproject.com/fr/1.11/ref/templates/
Les données qui auront été téléchargées sont mises à disposition dans la variable json
; ainsi, avec le fichier téléchargé ayant cette structure :
{ "cdate" : "mer. 06 déc. 2017", "calltime" : 10000, "datatime" : "mer. 06 déc. 2017 à 09:49", "pdate" : "jeu. 07 déc. 2017", "ctext" : "Pour ce mercredi, le maintien d'un temps calme accompagné de brumes matinales favorisera une nouvelle augmentation des niveaux de particules. La qualité de l'air devrait être bonne à moyenne en plaine avec des indices 4 à 5 sur 10 en particules alors qu'elle restera très bonne sur les reliefs avec des indices 2 en ozone.", "ptext" : "Jeudi, sous un temps sec et lumineux, les émissions de polluants seront bien dispersées par le vent. En plaine, les particules devraient se contenir à des indices 4 sur 10 maximum et sur les reliefs l'ozone dominera à des indices 2-3. C'est donc une bonne voire très bonne qualité de l'air qui sera attendue sur le territoire alsacien.", "s" : [ {"lc" : "bonne", "cp" : "#9AEB00", "cc" : "#9AEB00", "sp" : "status_4", "lp" : "bonne", "sc" : "status_4", "id" : 67043}, {"lc" : "bonne", "cp" : "#1AD100", "cc" : "#1AD100", "sp" : "status_3", "lp" : "bonne", "sc" : "status_3", "id" : 67049}, […]
La cellule doit trouver la ligne correspondant au code INSEE de la commune pour en afficher la qualité de l'air puis afficher l'information textuelle générale.
{% for city in json.s %} {% if city.id == 67482 %}<h2>Qualité de l'air : {{city.lc}}</h2>{% endif %} {% endfor %} <p>{{json.ctext}}</p> <p><small>{{json.datatime}}</small></p>
Plutôt qu'avoir le code INSEE en dur dans le gabarit, il est possible de rendre la cellule paramétrable en faisant évoluer sa déclaration vers la suivante :
"JSON_CELL_TYPES": { "info-atmo": { "name": "Info Atmo", "url": "http://strasmap.eu/remote.amf.json/Atmo.status", "form": [ { "varname": "commune", "type": "string", "label": "Commune" } ] } }
Les paramètres sont accessibles dans la variable parameters
, le gabarit devient donc :
{% for city in json.s %} {% ifequal parameters.commune city.id|stringformat:"s" %}<h2>Qualité de l'air : {{city.lc}}</h2>{% endif %} {% endfor %} <p>{{json.ctext}}</p> <p><small>{{json.datatime}}</small></p>
Dans cet exemple précis, le code INSEE de la commune se trouve sous forme d'entier et non de chaine de caractères, la condition utilise donc {% ifequal %}
et city.id
est converti en chaine par le filtre stringformat:"s"
.
La suite, c'est se dire qu'il est plus facile d'écrire Oberschaeffolsheim que 67350 et permettre le paramétrage de la cellule via un nom de commune plutôt que le code INSEE. Pour réaliser ça la cellule va faire appel à une deuxième source de données, qui contient la correspondance entre les codes et les noms.
La déclaration de la cellule ajoute donc cette seconde URL :
"JSON_CELL_TYPES": { "info-atmo": { "name": "Info Atmo", "url": "http://strasmap.eu/remote.amf.json/Atmo.status", "additional-data": [ {"key": "communes", "url": "http://strasmap.eu/remote.amf.json/Atmo.geometry" }, ], "form": [ { "varname": "commune", "type": "string", "label": "Commune" } ] } }
Le contenu qui en sera récupéré sera mis à disposition dans la variable communes
(spécifiées dans l'attribut key
). Il s'agit donc de boucler pour trouver le code de la commune selon son nom puis de reprendre comme précédemment sur la recherche de la qualité associée au code :
{% for commune in communes.s %} {% if commune.ln == parameters.commune %} {% for q in json.s %} {% ifequal commune.id q.id|stringformat:"s" %} <h2>Qualité de l'air : {{q.lc}}</h2> {% endifequal %} {% endfor %} {% endif %} {% endfor %} <p> {{json.ctext}} </p> <p><small>{{json.datatime}}</small></p>
Par défaut les données téléchargées sont conservées en cache 60 secondes, on peut augmenter cette durée en accompagnant les URL d'une clé cache_duration
:
"JSON_CELL_TYPES": { "info-atmo": { "name": "Info Atmo", "url": "http://strasmap.eu/remote.amf.json/Atmo.status", "cache_duration": 3600, "additional-data": [ {"key": "communes", "url": "http://strasmap.eu/remote.amf.json/Atmo.geometry", "cache_duration": 86400 }, ], "form": [ { "varname": "commune", "type": "string", "label": "Commune" } ] } }
Pour revenir au contenu, plutôt qu'un paramétrage de la commune, on pourrait vouloir la récupérer depuis le profil de l'usager, on passerait alors par un appel supplémentaire, vers l'API du fournisseur d'identités :
"JSON_CELL_TYPES": { "info-atmo": { "name": "Info Atmo", "url": "http://strasmap.eu/remote.amf.json/Atmo.status", "cache_duration": 3600, "additional-data": [ {"key": "communes", "url": "http://strasmap.eu/remote.amf.json/Atmo.geometry", "cache_duration": 86400 }, {"key": "user", "url": "{{ idp_url }}api/users/{{ user_nameid }}/" } ] } }
On voit ici la possibilité d'utiliser des variables dans les URL, {{ idp_url }}
reprendra l'adresse du fournisseur d'identités et {{ user_nameid }}
contiendra l'identifiant unique de l'usager.
Plutôt que comparer avec le paramètre de la cellule, la comparaison peut donc se faire par rapport au profil de l'usager, pour lequel on suppose qu'il contiendra sa commune dans le champ city
:
{% for commune in communes.s %} {% if commune.ln == user.city %} {% for q in json.s %} {% ifequal commune.id q.id|stringformat:"s" %} <h2>Qualité de l'air : {{q.lc}}</h2> {% endifequal %} {% endfor %} {% endif %} {% endfor %} <p> {{json.ctext}} </p> <p><small>{{json.datatime}}</small></p>
Dans le paramétrage d'une régie on peut renseigner l'adresse du webservice en charge du calcul des frais supplémentaires.
Imaginons un service où on peut commander un document et où on peut préciser le nombre d'exemplaires souhaités, il faut que cette information supplémentaire "nombre d'exemplaires" puisse d'abord être transmise de w.c.s. au panier, pour ensuite être transmise au webservice de calcul.
Dans le paramétrage côté w.c.s., on aurait ainsi :
Ces données seront ainsi jointes au panier et le webservice de calcul des frais additionnels les recevra dans un POST sous cette forme :
{ "data": [ { "amount": "10.00", "subject": "commande document normal n°84-10", "details": "", "request_data": { "amount": "10", "nb_exemplaires": "2" }, "source_url": "https://www.example.net/document-normal/10/" }, { "amount": "15.00", "subject": "commande document rare n°34-19", "details": "", "request_data": { "amount": "15", "nb_exemplaires": "1" }, "source_url": "https://www.exemple.net/document-rare/19/" } ] }
Sa place est peut-être plus dans le projet Authentic, à voir plus tard.
Les échanges avec l'usager se font via une pop-up, ça n'est peut-être pas possible mais je me suis fais plaisir et on en discute pour ceux qui ont envie.
Pierre : Je verrais plutôt, sur la page d'inscription, le séparateur entre les champs obligatoires et les autres.
Victor : à vrai dire je l'avais fait au début. c'est pas un problème.
L'usager valide son inscription en remplissant les champs de son espace personnel (remarque de Pierre a prendre en compte sur cette page, le séparateur pourrait être entre les champs obligatoire et les autres)
Texte : Pour vous créer un compte citoyen et ainsi bénéficier de ses avantages (démarches pré-remplies, suivi des démarches, liaison avec différents services en ligne), complétez le formulaire ci-dessous.
Ici je prévois que le process de bienvenue soit un popup au-dessus de la page d'accueil. Si ce process à lieu dans Authentic et pas Combo, se sera peut-être pas possible. Alors le process de bienvenue aura lieu dans des pages "normales".
Une première page de texte qui explique le compte citoyen et ce qui va suivre.
EDIT Pierre : le texte du mockup est bien pour décrire ce qui se passe et doit servir au développeur mais je pense qu'on peut faire un peu plus court pour le citoyen. Je propose (en gardant bcp des choses faites par Vic :
Bienvenue sur le compte citoyen !
C'est votre première connexion sur le compte nous allons finaliser votre inscription.
Votre compte citoyen est le point d'accès privilégié vers l'ensemble des services proposés par la ville. Il vous permet de pré-remplir les démarches en ligne, de suivre leur traitement et de centraliser l'information des différents services offerts par votre collectivité (médiathèque, piscine, conservatoire...).
Pour cela vous devez relier votre compte citoyen à ces services. Une fois la liaison effectuée vous verrez automatiquement dans votre compte les informations relatives au service concerné.
On affiche la liste des services pouvant être raccordés. Chaque service fait l'objet de la saisie d'infos de la part de l'usager. On peut imaginer que pour des services provisionnés, il suffit juste d'activer le curseur.
Là encore je propose d'élaguer les textes :
Si vous ne pouvez/voulez pas activer les services maintenant, vous pourrez le faire ultérieurement depuis votre espace personnel [on rebaptise la page page profil en espace personnel ? pourquoi pas].
L'accès à certains services nécessite la création préalable d'un compte sur ces derniers - lorsque vous n'en avez pas.
Une seconde popup permet permet à l'usager de saisir ses infos en fonction du service lié. Pour les services provisionnés, pas de saisie juste un consentement et le retour ok ou ko. Peut-être prévoir l'affichage d'infos spécifiques au service ? "le raccordement de votre compte famille vous permet de payer vos factures en ligne....".
EDIT Pierre : carrément pour l'ajout d'infos spécifiques au service et pour une simplification du titre "Informations nécessaires à la liaison".
Pendant la tentative de lien au service mise en attente
En cas d'erreur on permet à l'usager de ressaisir. Peut-être des comportements à distinguer en fonction du type d'erreur ? service injoignable...
Ici erreur de l'usager qui est invité à réessayer. Peut-
Le service est raccordé ! le curseur passe au vert.
Le popup de raccordement est fermé, l'usager revient sur la liste le curseur est vert pour ce service.
Une fois les services raccordés. L'usager clic sur suivant. Une dernière page lui explique que la procédure est terminée.
Vocabulaire : on a pas tranché pour savoir si on utilisait, mon compte, profil ou espace personnel. Je vais proposer dans #6068
Cette page aurait peut-être plus sa place dans la partie w.c.s. Je la pose là pour le moment.
Ce que j'imagine pour la "navigation ticket de suivi"
Une étape a prévoir ensuite c'est comment un usager rattache un ticket de suivi à son compte citoyen (pour une demande faite au guichet par exemple).
En mode connecté ou non, on affiche une zone permettant de saisir le numéro de suivi. Idéalement, la "zone suivi" est positionnée partout au même endroit quelque soit la page du portail. En haut à droite me parait pas mal.
Le formulaire n'est pas enregistré, l'action est déclenchée par le passage à la page suivante. L'usager valide le fonctionnement en cliquant sur ce même bouton (cgu).
Dans le bloc : un lien d'aide pour savoir ce qu'est ce numéro. Il devrait y en avoir au moins un second "Envoyer le ticket par mail". Il est dans les mockups suivants.
Prévoir une étape de suppression manuelle du brouillon ? soit un bouton annuler en bas de page, soit un lien dans le bloc du numéro de suivi. Sous-entendu, "je quitte le formulaire et je ne veux pas conserver de brouillon."
On peut imaginer mettre en avant ce mode brouillon pour les personnes qui n'ont pas de compte et ne pas les laisser passer à côté.
On ouvrirai alors un popup d'aide et explication sur le ticket au moment de son enregistrement.
Le module de recherche peut être étendu à d'autres systèmes dès qu'ils proposent un webservice compatible. Ce document détaille le format du webservice attendu.
Le système de recherche de Combo va lancer une requête HTTP GET sur une URL, en lui envoyant en paramètre (query-string) la chaîne de caractère saisie par l'utilisateur :
GET https://example.net/search/?q=foobar
Le nom du paramètre "q" est en fait libre, dans les exemples qui suivent nous continuerons à l'appeler "q". "foobar" est la chaîne de caractère saisie par l'utilisateur.
Combo peut également envoyer un paramètre contenant l'email de l'utilisateur effectuant la recherche. Cela permet au système tiers de ne renvoyer que les résultats pertinents pour cet utilisateur. Exemple de requête :
GET https://example.net/search/?email=login@example.org&q=foobar
Ici encore le paramètre "email" peut être nommé autrement.
Dans des intégrations plus fortes, si le système tiers est déjà lié à Publik par un SSO, il est possible d'envoyer dans la requête l'identifiant de fédération (sub ou NameID).
La requête GET doit recevoir une réponse en JSON, sous la forme suivante :
GET https://example.net/search/?q=foobar 200 OK Content-type: application/json { "err": 0, "data": [ { "url": "https://files.example.net/dossier/3/", "text": "Dossier foobar", "description": "Dossier numéro <b>3</b>, en date du <i>10/12/2019</i>" }, { ...objet2... }, ... ] }
err
est un entier, 0 indique que tout s'est bien passé. Toute autre valeur est interprétée comme une erreurdata
est une liste des objets trouvés, dans l'ordre de pertinencetext
: nom de l'objet, sur une seule ligne, qui sera affiché à l'utilisateur effectuant la rechercheurl
: l'URL de l'objet, typiquement un lien vers la page du système tiers qui va afficher l'objetdescription
(optionnel) : une description plus longue, autorisant un peu de formatage HTML si besoinUn objet peut contenir d'autres clés dont les valeurs sont jugées utiles à présenter à la personne qui fait une recherche. Par exemple sur une recherche d'usager, on pourrait avoir :
"data": [ { "url": "https://users.example.net/user/3/", "text": "Ferdinand Poitevin", "first_name": "Ferdinand", "last_name": "Poitevin", "dob": "1860-10-09", "phone": "+33123456789", "mobile": "+33612345678", "address": "169 rue du Château, Paris" }, ...
Ces valeurs peuvent être utilisées pour personnaliser le rendu des résultats. Un gabarit sera programmé dans Combo pour afficher le résultat du moteur.
Combo peut envoyer un paramètre « email=... » lors de la recherche. Dans ce cas, les résultats peuvent être modulés en fonction de cet email, qui représente utilisateur qui effectue la recherche.
Il peut être décidé :Développement d'une cellule alimentée par JSON