diff --git a/docs/ecolyo/functionalities/challenge.md b/docs/ecolyo/functionalities/challenge.md index 4e5dc2a3d6ceb844cf01715f9daf68d3418830ef..3545ddb5d240126807b448741156ff0598ce8361 100644 --- a/docs/ecolyo/functionalities/challenge.md +++ b/docs/ecolyo/functionalities/challenge.md @@ -8,12 +8,12 @@ A quiz includes 4 basic questions and one custom question. All basics Questions are created in the quizEntity.json. We have to add : -| Field | Description | -| ------- | --------- | -| questionLabel | Label of the question | -| answers | Array of 3 answers (answerLabel, isTrue) | -| description | Explains the question | -| source | Source of the explanation | +| Field | Description | +| ------------- | ---------------------------------------- | +| questionLabel | Label of the question | +| answers | Array of 3 answers (answerLabel, isTrue) | +| description | Explains the question | +| source | Source of the explanation | This questions and answers are in random order. @@ -26,59 +26,58 @@ If it is a right answer, the quiz result is incremented by one. ### Custom Question -At the end of every quiz, we're creating a custom question. +At the end of every quiz, we're creating a custom question. Here are the fields used to create a custom question -| Field | Type | Description | -| ---------- | ------------------ | ---------------------------------------------------------------- | -| type | CustomQuestionType | Type of custom qusetion: DATA or CALCULATION. | -| timeStep | TimeStep | Time step of the result value (DAY / WEEK / MONTH / YEAR). For CALCULATION type it's represent daily / weekly / monthly / yearly average. | -| interval | TimeStep | Interval in which the data will be searched (DAY / WEEK / MONTH / YEAR) | -| period | CustomPeriod | Use to define a specific period for the interval { day? / month? / year? }. If not assigned the period will be the last interval (example: last week) | -| result | UserQuizState | Indicate the state of the custom question (UNLOCKED, CORRECT, UNCORRECT) | +| Field | Type | Description | +| -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| type | CustomQuestionType | Type of custom qusetion: DATA or CALCULATION. | +| timeStep | TimeStep | Time step of the result value (DAY / WEEK / MONTH / YEAR). For CALCULATION type it's represent daily / weekly / monthly / yearly average. | +| interval | TimeStep | Interval in which the data will be searched (DAY / WEEK / MONTH / YEAR) | +| period | CustomPeriod | Use to define a specific period for the interval { day? / month? / year? }. If not assigned the period will be the last interval (example: last week) | +| result | UserQuizState | Indicate the state of the custom question (UNLOCKED, CORRECT, UNCORRECT) | Calculation of custom question is done by conbinaison of all parameters: #### Type DATA - - If *period* is empty. +- If _period_ is empty. - Retrieve the **max load** of day/month/year (define by *timeStep*) from the last week, month, year (define by *interval*). - => example: What day did I consumme the most on the last week ? - type = CustomQuestionType.DATA - timeStep = TimeStep.DAY - interval = TimeStep.WEEK - period = {} +Retrieve the **max load** of day/month/year (define by _timeStep_) from the last week, month, year (define by _interval_). +=> example: What day did I consumme the most on the last week ? +type = CustomQuestionType.DATA +timeStep = TimeStep.DAY +interval = TimeStep.WEEK +period = {} - - If *period* is not empty +- If _period_ is not empty - Retrieve the **max load** of day/month/year (define by *timeStep*) from week, month, year (define by *interval*) of *period* - => example: What month did I consumme the most on year 2020 ? - type = CustomQuestionType.DATA - timeStep = TimeStep.MONTH - interval = TimeStep.YEAR - period = {year: 2020} +Retrieve the **max load** of day/month/year (define by _timeStep_) from week, month, year (define by _interval_) of _period_ +=> example: What month did I consumme the most on year 2020 ? +type = CustomQuestionType.DATA +timeStep = TimeStep.MONTH +interval = TimeStep.YEAR +period = {year: 2020} #### Type CALCULATION - - If *period* is empty +- If _period_ is empty - Retrieve the **average** of day/month/year (define by *timeStep*) from the last week, month, year (define by *interval*). - => example: Which is your daily average consumption on last week ? - type = CustomQuestionType.CALCULATION - timeStep = TimeStep.DAY - interval = TimeStep.WEEK - period = {} +Retrieve the **average** of day/month/year (define by _timeStep_) from the last week, month, year (define by _interval_). +=> example: Which is your daily average consumption on last week ? +type = CustomQuestionType.CALCULATION +timeStep = TimeStep.DAY +interval = TimeStep.WEEK +period = {} - - If *period* is not empty - - Retrieve the **average** of day/month/year (define by *timeStep*) from week, month, year (define by *interval*) of *period* - => example: Which is your daily average consumption on january ? - type = CustomQuestionType.DATA - timeStep = TimeStep.DAY - interval = TimeStep.MONTH - period = {month: 1} +- If _period_ is not empty +Retrieve the **average** of day/month/year (define by _timeStep_) from week, month, year (define by _interval_) of _period_ +=> example: Which is your daily average consumption on january ? +type = CustomQuestionType.DATA +timeStep = TimeStep.DAY +interval = TimeStep.MONTH +period = {month: 1} Then, two randoms answers are generated after defining the right answer. @@ -145,10 +144,10 @@ The accomplishment of an action is only conditionned by the duration. Once the u ### On launch -In order to find a valid reference period we search for a period which is defined by the duel duration. +In order to find a valid reference period we search for a period which is defined by the duel duration. We check the most recent period first if it's complete and then we go farther and farther in time if the ones before got missing values. -We also define a threshold for a maximum old period +We also define a threshold for a maximum old period !!! note "6 months for the moment - hardcoded." If the thresold is reached and no valid period was found, we alert the user that he can't launch the duel and have to wait before he can retry this process. @@ -160,7 +159,6 @@ if we have retrieved all data to calculate the user consumption on the duel dura !!! info "In order to not block the user if a value is mising, we are also setting the duel as done when we reach the startDate + delay in day to retrieve the data (based on the fluid) + 1 day. In this case the user consumption is done on known values." - ### On finish Once the state of the duel is set to DONE, we save the user result and determine if he wins (userComsumption < threshold of the reference period) or if he loses. Then the user sees his earned badge. @@ -169,37 +167,4 @@ Once the state of the duel is set to DONE, we save the user result and determine We can find the file challengeEntity.json in the /db folder. This file contains an array of challenges, and each of them includes relationships to duels, quiz, missions and actions. -### UserData creation - -Once a user launch a challenge in the Ecolyo app, we create a userChallenge and store it in the couchDB under the doctype '**com.grandlyon.ecolyo.userchallenge'.** During this process, the objects related to the challenge (quiz, duel, mission, action) will be be converted to an user version which contains informations about the user progress, the fluids connected and so on. So we have now a userChallenge that contains a userQuiz, a userDuel, etc. instead of relations. - -To illustrate this, let's show the conversion of quizEntity to userQuiz : - -```jsx -DuelEntity { - id: string - title: string - description: string - duration: Duration -} -``` - -Becomes : - -```jsx -UserQuiz { - id: string - title: string - description: string - duration: Duration - threshold: number - state: UserDuelState - startDate: string | null - fluidTypes: FluidType[] - userConsumption: number -} -``` - -### Data managment schema - - +For more precision about the data managment system you can refer the picture in the [Initialization section](./initialization.md) diff --git a/docs/ecolyo/functionalities/initialization.md b/docs/ecolyo/functionalities/initialization.md index f305104199fd2f949d6600423aa54a5ca38bce4c..8c8bbb0db13e1a3dd6f20070e5604eef22f53125 100644 --- a/docs/ecolyo/functionalities/initialization.md +++ b/docs/ecolyo/functionalities/initialization.md @@ -2,7 +2,7 @@ This section explains how to handle data storage. We handle data storage according to the following process : -The folder ```/src/db``` contains all the JSON entities that are directly stored in the couchDB during the initialization process that is executed in the splash screen. They are stored under their related doctype. +The folder `/src/db` contains all the JSON entities that are directly stored in the couchDB during the initialization process that is executed in the splash screen. They are stored under their related doctype. ## Initialization and Updating @@ -13,6 +13,8 @@ During the initialization process, we store a hash for each dataEntity that is l { "ecogestureHash": "", "challengeHash": "", + "quizHash": "", + "explorationHash": "", "duelHash": "", "isFirstConnection": true, "haveSeenFavoriteModal": false, @@ -26,6 +28,24 @@ During the initialization process, we store a hash for each dataEntity that is l This way, once the initialization is launched, we compare the hash we have in the current userProfile with the one generated from the entity located in /db folder, and if they are different we update the couchDB with the new data. +In order to handle this process, each entity that is likely to change has a function that takes the current hash in entry, and execute the following process : + +- creates a hash from the related entity file (for example **EcogestureData.json**) +- get the entities from the database +- if there is no entity stored, populates the related doctype (for this example **ECOGESTURE_DOCTYPE**) for each item stored in the entity file +- compare the number of entries created in the doctype with the number of item in the entity file +- if the previous hash and the new hash are different, delete all entries in the doctype and populates with the items stored in the entity file + +Functions used to init or update the hash : + +| Function | +| ----------------------------------- | +| initEcogesture(hash: string) | +| initChallengeEntity(hash: string) | +| initDuelEntity(hash: string) | +| initQuizEntity(hash: string) | +| initExplorationEntity(hash: string) | + ### dataEntity vs userData In the project, you'll see two versions for the same data. The dataEntity (quizEntity, challengeEntity, ...) is the data stored in the db folder and in the couchDB. These data are only edited in the couchDB when we update a challenge/quiz/duel or add a new one. @@ -38,7 +58,9 @@ Using this 2 structures allows us to keep user's datas when we'll update the app Relationships is a functionality made by cozy, they are built with the name of the relation, containing a "data" object, itself containing the id of the related item and its doctype "\_type". Using this allows us to reduce the size of the stored items and increase readability. -You can see more on [cozy documentation](https://docs.cozy.io/en/cozy-doctypes/docs/io.cozy.apps.suggestions/#relationships). +Relations are set as _HasOne_ relationships for **Quiz** and **Duel** because there is only one quiz and one duel for Challenge. Thus, we use a _HasMany_ relation for the **Exploration**, because one challenge can have several exploration in order the handle the specific cases of fluid-based explorations. + +You can see more about relationships on [cozy documentation](https://docs.cozy.io/en/cozy-doctypes/docs/io.cozy.apps.suggestions/#relationships). ```json "relationships": { @@ -47,6 +69,14 @@ You can see more on [cozy documentation](https://docs.cozy.io/en/cozy-doctypes/d }, "duel": { "data": { "_id": "DUEL001", "_type": "com.grandlyon.ecolyo.duel" } + }, + "exploration": { + "data": [ + { + "_id": "EXPLORATION001", + "_type": "com.grandlyon.ecolyo.exploration" + } + ] } } ``` @@ -55,7 +85,7 @@ You can see more on [cozy documentation](https://docs.cozy.io/en/cozy-doctypes/d We create a userData and store it in the couchDB under the doctype userData name '**com.grandlyon.ecolyo.userDataName'.** During this process, the objects related to the dataEntity will be be converted to an user version which contains informations about the user(example: user progress, the fluids connected and so on). -To illustrate this, let's show an example of the conversion of quizEntity to userQuiz : +To illustrate this, let's show an example of the conversion of duelEntity to UserDuel : ```jsx DuelEntity { @@ -69,7 +99,25 @@ DuelEntity { Becomes : ```jsx -UserQuiz { +UserDuel { + id: string + title: string + description: string + duration: Duration + threshold: number + state: UserDuelState + startDate: DateTime | null + fluidTypes: FluidType[] + userConsumption: number +} +``` + +Since we use the Luxon library for DateTimes, we need to have an intermediate interface for the UserDuel. Indeed, the database doesn't know the DateTime type, so when we query a UserDuel, its **startDate** property is a _string_ and not a _DateTime_. + +The solve this and avoid Typescript errors, we created the following interface : + +```jsx +UserDuelEntity { id: string title: string description: string @@ -81,3 +129,7 @@ UserQuiz { userConsumption: number } ``` + +### Data managment schema + +