InseeFr / Pogues

Questionnaire designer
MIT License
20 stars 17 forks source link

Roundabout component - DDI model #649

Open romaintailhurat opened 1 year ago

romaintailhurat commented 1 year ago

WIP

Describe what this component is about

This could be modelled as a DDI Sequence with a TypeOfSequence with value "roundabout".

<d:Sequence>
    <r:Agency>fr.insee</r:Agency>
    <r:ID>abcd1234</r:ID>
    <r:Version>1</r:Version>
    <d:ConstructName>
        <r:String xml:lang="fr-FR">ROUNDABOUT</r:String>
    </d:ConstructName>
    <r:Label>
        <r:Content xml:lang="fr-FR">Le rond-point</r:Content>
    </r:Label>
    <!-- Typing the sequence -->
    <d:TypeOfSequence controlledVocabularyID="INSEE-TOS-CL-1">roundabout</d:TypeOfSequence>
    <d:ControlConstructReference>
        [...]
    </d:ControlConstructReference>
</d:Sequence>
BulotF commented 1 year ago

Pour le rond-point, je ne sais pas s'il faut en faire une séquence ou un attribut de la boucle qu'il introduit. Je ne sais pas ce que l'on souhaite mettre exactement sur la page de rond-point à part :

Si l'on ne souhaite pas afficher davantage, le label de la boucle (qui n'est a priori pas utilisé) peut suffire comme label pour la page (ou du tableau des occurrences de boucle) et le reste peut s'en déduire...

Pour identifier la meilleure modélisation, il faudrait un exemple de ce que l'on souhaite obtenir avec l'orchestrateur. Le but est moins d'identifier ce à quoi cette page devra ressembler (l'UX design pourra la faire évoluer) que d'identifier quelles informations devront être fournies par Pogues, donc quelles informations devront figurer dans les modèles Pogues, DDI et Lunatic.

romaintailhurat commented 1 year ago

Pour avancer :

En terme d'usage du rond-point, deux éléments de configuration au moins :

  1. les expressions permettant de préciser comment déterminer l'état des sous-questionnaires ;
    "expressions": {
    "unnecessary": {
        "value": "AGE < 13",
        "type": "VTL"
    },
    "complete": {
        "value": "not(isnull(KNOWREC)) and not(isnull(SEXE)) and not(isnull(SOMETHING))",
        "type": "VTL"
    },
    "partial": {
        "value": "not(isnull(KNOWREC)) or not(isnull(SEXE)) or not(isnull(SOMETHING))",
        "type": "VTL"
    },
    "label": {
        "value": "\"Série de question pour \" || PRENOMS",
        "type": "VTL"
    }
    }
  2. la liste des composants pour chaque sous-questionnaire.

    "components": [
    {
        "id": "radio",
        "componentType": "Radio",
        "mandatory": false,
        "page": "4.1",
        "label": {
            "value": "\"Connaissez-vous le recensement de la population ?\"",
            "type": "VTL|MD"
        },
    
        "conditionFilter": { "value": "true", "type": "VTL" },
    
        "options": [
            { "value": "1", "label": { "value": "\"oui\"", "type": "VTL|MD" } },
    
            { "value": "2", "label": { "value": "\"non\"", "type": "VTL|MD" } }
        ],
        "response": { "name": "KNOWREC" }
    },
    {
        "id": "jsygk7m7",
        "componentType": "Subsequence",
        "page": "4.2",
        "label": {
            "value": "\"Deuxième page de questions pour \"|| PRENOMS",
            "type": "VTL|MD"
        },
        "conditionFilter": { "value": "true", "type": "VTL" }
    },
    ...
    ]

    A priori, on inclut uniquement des sous-séquences et des questions comme enfants du rond-point.

romaintailhurat commented 1 year ago

Asking @renaud23 if subsequence is the higher component level or not.

UPDATE : same constraint as for paged loop ("boucles paginées"), so any component seems good.

BulotF commented 1 year ago

De ce que je vois au niveau du modèle Lunatic présent en lien, on est assez près de la boucle liée :

Il manque :

Dans le cas d'une boucle liée avec filtre sur les occurrences, on était parti sur une Loop référençant un filtre, référençant lui-même une séquence -> ce sont les 3 éléments que l'on peut enrichir pour avoir les informations manquantes (mais il faudrait pour Eno indiquer dans la Loop qu'il s'agit d'un rond-point)

romaintailhurat commented 11 months ago

Would the following be a good design ?

<d:Loop>
    <r:Agency>fr.insee</r:Agency>
    <r:ID>lmomdlun</r:ID>
    <r:Version>1</r:Version>
    <d:ConstructName>
        <r:String xml:lang="fr-FR">B2</r:String>
    </d:ConstructName>
    <d:ControlConstructReference>
        <r:Agency>fr.insee</r:Agency>
        <r:ID>lmommfe7</r:ID>
        <r:Version>1</r:Version>
        <r:TypeOfObject>Sequence</r:TypeOfObject>
    </d:ControlConstructReference>
    <r:UserAttributePair>
        <r:AttributeKey>Navigation</r:AttributeKey>
        <r:AttributeValue>Roundabout</r:AttributeValue>
    </r:UserAttributePair>
    <r:UserAttributePair>
        <r:AttributeKey>RoundaboutConfiguration</r:AttributeKey>
        <r:AttributeValue><![CDATA[
                  "expressions": {
                     "unnecessary": {
                         "value": "AGE < 13",
                         "type": "VTL"
                     },
                     "complete": {
                         "value": "not(isnull(KNOWREC)) and not(isnull(SEXE)) and not(isnull(SOMETHING))",
                         "type": "VTL"
                     },
                     "partial": {
                         "value": "not(isnull(KNOWREC)) or not(isnull(SEXE)) or not(isnull(SOMETHING))",
                         "type": "VTL"
                     },
                     "label": {
                         "value": "\"Série de question pour \" || PRENOMS",
                         "type": "VTL"
                     }
                  }
                  ]]></r:AttributeValue>
    </r:UserAttributePair>
</d:Loop>

As suggested by @BulotF we are reusing here the DDI design for linked loops, adding two <r:UserAttributePair>:

Asking for validation @BulotF and @laurentC35 🤗

BulotF commented 11 months ago

There are sevreal problem with the second element :

romaintailhurat commented 11 months ago

VTL code is written "as is" as it is an example provided by a Lunatic roundabout developer.

So if i understand correctly, the configuration part of the Roundabout would be produced by scanning part of the DDI? Or all?

BulotF commented 11 months ago

Alternative design : A round-about is a mono-paged loop or a QuestionGrid with a roster. Still with something indicating that that component is a roundabout.

That component may be inside a Sequence with label "Libellé du rondpoint".

If it is a Loop, its content should be a unique ControlConstruct, but I don't know which kind of element inside : a StatementItem ?

If it is a QuestionGrid, probably a noDataByDefinition in each cell with a label containing the complete and partial formulas. Maybe with several columns if there are several linked loops.

romaintailhurat commented 11 months ago

It seems to me that if really makes sense to use a Loop as the base construct for the Roundabout 😃

romaintailhurat commented 11 months ago

The main idea is to design the roundabout as a Loop that contains a Sequence which itself contains a StatementItem and three ComputationItems, one for each Roundabout parameter:

Loop
    Sequence
        StatementItem
        ComputationItem[unnecessary]
        ComputationItem[partial]
        ComputationItem[complete]

The Loop

<d:Loop>
    <r:Agency>fr.insee</r:Agency>
    <r:ID>lnbp643q</r:ID>
    <r:Version>1</r:Version>
    <r:UserAttributePair>
        <r:AttributeKey>ROUNDABOUT_OBJECT</r:AttributeKey>
        <r:AttributeValue>ROUNDABOUT_LOOP</r:AttributeValue>
    </r:UserAttributePair>
    <d:ConstructName>
        <r:String xml:lang="fr-FR">B_RONDPOINT</r:String>
    </d:ConstructName>
    <d:ControlConstructReference>
        <r:Agency>fr.insee</r:Agency>
        <r:ID>lnbpayrw</r:ID>
        <r:Version>1</r:Version>
        <r:TypeOfObject>Sequence</r:TypeOfObject>
    </d:ControlConstructReference>
</d:Loop>

The Sequence

<d:Sequence>
    <r:Agency>fr.insee</r:Agency>
    <r:ID>lnbpayrw</r:ID>
    <r:Version>1</r:Version>
    <d:ConstructName>
        <r:String xml:lang="fr-FR">RONDPOINT</r:String>
    </d:ConstructName>
    <d:ControlConstructReference>[StatementItem]</d:ControlConstructReference>
    <d:ControlConstructReference>[ComputationItem]</d:ControlConstructReference>
    <d:ControlConstructReference>[ComputationItem]</d:ControlConstructReference>
    <d:ControlConstructReference>[ComputationItem]</d:ControlConstructReference>
</d:Sequence>

The StatementItem

<d:StatementItem>
    <r:Agency>fr.insee</r:Agency>
    <r:ID>LABEL_ET_BOUTON</r:ID>
    <r:Version>1</r:Version>
    <r:UserAttributePair>
        <r:AttributeKey>ROUNDABOUT_OBJECT</r:AttributeKey>
        <r:AttributeValue>LABEL_AND_BUTTON</r:AttributeValue>
    </r:UserAttributePair>
    <r:Label>
        <r:Content>Le libellé au-dessus du bouton</r:Content>
    </r:Label>
</d:StatementItem>

The ComputationItems

In order to describe completion parameters we use <d:ComputationItem>. For example here, for the unnecessary Lunatic Parameter:

<d:ComputationItem>
    <r:Agency>fr.insee</r:Agency>
    <r:ID>lahvkp0z-CI-0</r:ID>
    <r:Version>1</r:Version>
    <d:ConstructName>
        <r:String xml:lang="fr-FR">ROUNDABOUT_UNNECESSARY</r:String>
    </d:ConstructName>
    <r:Description>
        <r:Content xml:lang="fr-FR">Rond-point - Les itérations à masquer.</r:Content>
    </r:Description>
    <d:InterviewerInstructionReference>
        <r:Agency>fr.insee</r:Agency>
        <r:ID>lahvkp0z-CI-0-II-0</r:ID>
        <r:Version>1</r:Version>
        <r:TypeOfObject>Instruction</r:TypeOfObject>
    </d:InterviewerInstructionReference>
    <d:TypeOfComputationItem controlledVocabularyID="INSEE-LUNATIC">roundabout-unnecessary</d:TypeOfComputationItem>
    <r:CommandCode>
        <r:Command>
            <r:ProgramLanguage>vtl</r:ProgramLanguage>
            <r:CommandContent>AGE ;&lt; 13</r:CommandContent>
        </r:Command>
    </r:CommandCode>
</d:ComputationItem>
BulotF commented 11 months ago

1) To specify the type of loop, I would prefer :

<r:UserAttributePair>
   <r:AttributeKey>loopType</r:AttributeKey>
   <r:AttributeValue>roundabout</r:AttributeValue>
</r:UserAttributePair>

2) For the StatementItem :

<r:UserAttributePair>
   <r:AttributeKey>button</r:AttributeKey>
   <r:AttributeValue>roundabout-link</r:AttributeValue>
</r:UserAttributePair>

3) The controlledVocabulary for TypeOfComputationItem would be : roundabout-unnecessary roundabout-complete roundabout-partial

4) ComputationItem have no InterviewerInstructionReference

4) For at least dynamic arrays (QuestionGrid with 2 dimensions for which the first dimension is a roster and the second one is a codeList), I'd use a Variable to indicate where the buttons should be displayed. It would be mandatory when there is at least 2 buttons, to assign correctly the computation items to each. Alternative in the component that displays the link, I'd like to have a reference (basedOn ?) to the 3 parameters ComputationItem...

romaintailhurat commented 11 months ago

ComputationItem have no InterviewerInstructionReference

Yes, i just forgot to delete this part 😉

dzkwsk commented 10 months ago

Maybe it could be important to fill a controlled vocabulary for attributevaluekeys too I don't see how the choice is made between the partial label and complet label , when all the fields are completed I change my previous comment. two possibilities. the basedOnObject cardinality is 0 1. it can contain several basedOnReferences we could type with RserAttributPair. Why not using inparameter instead to get computation item value ?

BulotF commented 10 months ago

I thought that controlled vocabulary's AttributeKeys was specified in the DDI profile (and only in it)

InParameter is a better alternative. Nice.

dzkwsk commented 10 months ago

I agree in the DDI profile it's fine ! not the same status as a value

romaintailhurat commented 10 months ago

So it seems that we have an agreement on the design, i propose to add that to a proper and versionned document.

The implementation can start.

romaintailhurat commented 10 months ago

I wrapped up our comments in this document 👇

DDI - Roundabout design.pdf

I don't think we need an alternative QuestionGrid based design with this. But:

dzkwsk commented 9 months ago

a suggestion of identifying the used suggester also by a version number , if it evolves over time

romaintailhurat commented 9 months ago

A last review by @BulotF is in progress.

BulotF commented 5 months ago

Reprise des réflexions sur le rond-point... J'écris en français pour permettre aux participations à la discussion de ne pas alourdir la compréhension de mes explications avec la traduction.

Résumé de ma vision du rond-point du point de vue métier : Lorsqu'on est sur un questionnaire sans rond-point, l'avancement dans le questionnaire ne peut être que linéaire : Au niveau des boucles, on répond aux occurrences dans l'ordre et on attend d'en finir une avant d'entamer la suivante.

Avec le rond-point, on se donne la possibilité de basculer d'une occurrence à une autre, dans un ordre déterminé par le(s) répondant(s) et sans forcément avoir terminé une occurrence avant de basculer à une autre.

Pour faire un parallèle : le questionnaire global est le processus principal. On le met en pause pour lancer plusieurs petits processus parallèles. On le reprend lorsque tous ces petits processus sont terminés.

Pour avoir une expérience utilisateur la plus claire possible, on a besoin des éléments suivants :

Le rond-point peut être vu comme un conteneur de boucle(s), plutôt que comme un type de boucle particulier. Le définir comme une séquence de typeOfSequence = "roundabout" offre plusieurs possibilités intéressantes :

Par ailleurs, je pense que le filtre d'occurrence que l'on indiquait sous la forme d'un ComputationItem "unnecessary" mérite de redevenir un filtre (ce qu'il est déjà avec les boucles hors rond-point). Je conserverais les ComputationItem "complete" et "partial" tels quels.

Au niveau StatementItem, outre le libellé pour désigner l'occurrence, j'utiliserais des StatementItem pour les boutons "Ajouter une occurrence" (actuellement, on utiliser le label de la Loop et c'est assez moyen) et "supprimer une occurrence". Si on choisit de les indiquer au sein de la Sequence qui dépend directement de la Loop, on peut même choisir d'en faire des d:Instruction et donc de leur donner des InstructionName correspondant à leur statut : Loop.InstanceLabel ; Loop.Add ; Loop.Remove

Si on souhaite modéliser l'exemple suivant : rounabout

N.B. Modélisation en mode déréférencé (je remplace la référence par l'objet) qui n'indiquerait que les éléments utiles...

<d:Sequence>
    <r:Label>Liens vers les différentes personnes du ménage :</r:Label>
    <d:Instruction>
        <d:InstructionName>help</d:InstructionName>
        <d:InstructionText>Cette page permet d’accéder aux questionnaires des différentes personnes du ménage.
            Vous pouvez revenir à cette page en appuyant sur le bouton « revenir au rond-point »</d:InstructionText>
    </d:Instruction>
    <d:TypeOfSequence controlledVocabularyID="INSEE-TOS-CL-1">roundabout</d:TypeOfSequence>
    <d:Loop>
        <r:Label>Questions individuelles</r:Label>
        <d:Sequence>
            <d:Instruction>
                <d:InstructionName>Loop.InstanceLabel</d:InstructionName>
                <d:InstructionText>¤PRENOM¤</d:InstructionText>
            </d:Instruction>
            <d:Instruction>
                <d:InstructionName>Loop.Add</d:InstructionName>
                <d:InstructionText>Ajouter un individu</d:InstructionText>
            </d:Instruction>
            <d:Instruction>
                <d:InstructionName>Loop.Remove</d:InstructionName>
                <d:InstructionText>Supprimer un individu</d:InstructionText>
            </d:Instruction>
            <d:TypeOfSequence controlledVocabularyID="INSEE-TOS-CL-1">loopContent</d:TypeOfSequence>
            <d:ComputationItem>
                <d:TypeOfComputationItem controlledVocabularyID="INSEE-TOCI-CL-1">loop.complete</d:TypeOfComputationItem>
                <r:CommandCode>Tout est renseigné</r:CommandCode>
            </d:ComputationItem>
            <d:ComputationItem>
                <d:TypeOfComputationItem controlledVocabularyID="INSEE-TOCI-CL-1">loop.partial</d:TypeOfComputationItem>
                <r:CommandCode>Au moins une variable est renseignés</r:CommandCode>
            </d:ComputationItem>
            ... Contenu de la boucle questions individuelles ...
        </d:Sequence>
    </d:Loop>
    <d:Loop>
        <r:Label>Questions sur les majeurs</r:Label>
        <d:IfThenElse>
            <d:IfCondition>AGE >= 18</d:IfCondition>
            <d:Sequence>
                <d:TypeOfSequence controlledVocabularyID="INSEE-TOS-CL-1">filteredLoopContent</d:TypeOfSequence>
                <d:ComputationItem>
                    <d:TypeOfComputationItem controlledVocabularyID="INSEE-TOCI-CL-1">loop.complete</d:TypeOfComputationItem>
                    <r:CommandCode>Tout est renseigné</r:CommandCode>
                </d:ComputationItem>
                <d:ComputationItem>
                    <d:TypeOfComputationItem controlledVocabularyID="INSEE-TOCI-CL-1">loop.partial</d:TypeOfComputationItem>
                    <r:CommandCode>Au moins une variable est renseignés</r:CommandCode>
                </d:ComputationItem>
                ... Contenu de la boucle questions sur les majeurs ...
            </d:Sequence>
        </d:IfThenElse>
    </d:Loop>
    <d:Loop>
        <r:Label>Cœur de l’enquête</r:Label>
        <d:IfThenElse>
            <d:IfCondition>Indiv = Kish</d:IfCondition>
            <d:Sequence>
                <d:TypeOfSequence controlledVocabularyID="INSEE-TOS-CL-1">filteredLoopContent</d:TypeOfSequence>
                <d:ComputationItem>
                    <d:TypeOfComputationItem controlledVocabularyID="INSEE-TOCI-CL-1">loop.complete</d:TypeOfComputationItem>
                    <r:CommandCode>Tout est renseigné</r:CommandCode>
                </d:ComputationItem>
                <d:ComputationItem>
                    <d:TypeOfComputationItem controlledVocabularyID="INSEE-TOCI-CL-1">loop.partial</d:TypeOfComputationItem>
                    <r:CommandCode>Au moins une variable est renseignés</r:CommandCode>
                </d:ComputationItem>
                ... Contenu de la boucle cœur de l’enquête ...
            </d:Sequence>
        </d:IfThenElse>
    </d:Loop>
</d:Sequence>

Eléments de vocabulaire qui n'existent pas encore, mais auraient de l'intérêt pour les séquences : ajouter des valeurs à typeOfSequence : filterContent ; loopContent et filteredLoopContent (contenu d'une boucle dont les occurrences sont filtrées)

Cette description a l'avantage de ne pas utiliser r:UserAttributePair. L'enrichissement de la sémantique est porté par les modalités de TypeOfSequence ; InstructionName ; TypeOfComputationItem

laurentC35 commented 5 months ago

à documenter ailleurs qu'ici Faire une issue Pogues-Model + Eno

BulotF commented 2 months ago

Modification des besoins Lunatic :

dzkwsk commented 2 months ago

quelques remarques -je suis d'accord sur le fait que dans le DDI il n'est pas nécessaire de mettre tous les objets techniques pour gérer l'affichage, l'essentiel c'est d'avoir l'information logique/métier de la passation -je suis ok sur la présentation du ddi de l'exemple du 13 mars, il faudra voir avec les imports dans Colectica que les instructions des séquences sont bien importées (actuellement non décrits dans Designer), on est plutot sur interviewerinstruction il me semble -sur les exemples ce serait pratique de mentionner la version de DDI utilisé, ddi3_2 ou ddi3_3 et par la suite ddi3_4