---
tags: D2b
---
# D2b Pseudocode Approach
_I tried to write pseudocode for `MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped()` and based on that experience, I came up with this approach for tackling D2b using pseudocode as a starting point_
---
### Plan
- [x] We complete the detailed D2b GUI diagrams
- [x] We write down all functions from the D2a class diagrams as well as the D2b detailed GUI diagrams under "List of Interface functions"
- [ ] We define very basic subcomponents for the components of the Mobile App and Web App
- (We'll basically just be sorting the "interface functions" into different categories)
- [ ] We talk about the section 'Procedures and Guidelines' so we know exactly what to do when writing pseudocode
- [ ] We assign the definition of the pseudocode for functions from the `Mobile App` and `Web App` to eachother. (We don't assign the `Backend` functions though - For an explanation, see the section 'When encountering the need for an interface function call, remember to' under 'Procedures and Guidelines')
- [ ] We define the remaining `Backend` functions
- [ ] We create detailed class diagrams based on these functions
---
## Syntax
### Documentation of Pseudocode
#### Usage Tags
_Tags used on function calls / structure usages_
- `[Helper]`: The function call / structure usage tagged with this tag is a helper function or structure which is **not yet defined**
-`[xHelper]`: The function call / structure usage tagged with this tag is a helper function or structure wich has **already been defined**
_Tags used on function calls / structure usages_
- `[Cloud]`: The firebase function call tagged with this tag is **not yet defined**
- `[xCloud]`: The firebase function call tagged with this tag has **already been defined**
<!-- - ~~Tag `[Inter]` : This functions call is of an interface function which is not yet defined~~
- ~~Tag `[xInter]` : This function call is of an interface function wich has already been defined~~
- ~~(e.g when you define`MobileApp::Logic::AccountManagement::createAccount()`, and it calls `Firebasebackend::createAccount()`, and `Firebasebackend::createAccount()` was already defined (it definitely is, because it's predefined by firebase) - then the `createAccount()` call would be tagged like this: `[xInter]firebase.auth.createAccount(email, password)`)~~ -->
#### Definiton tags
_Tags used on function definitions / structure definitions_
- Defintion tag`[PUO]`: The helper function/structure defined here is `P`robably `U`seful for `O`ther functions.
- (e.g. when you define a general database-read function or something like that)
#### Other
- ??? 'Used by' list: a list above the definition of a helper function which specifies, which other functions this helper function is used by [This is probably unnecessary]
### Terminology
- 'Defining a function' == 'Writing the pseudocode for a function'
- `Interface function` A function provided by an interface of another component or class within our system (the ones from the D2a class diagrams), or a function which is triggered through the user interface (Like on...ButtonTapped) – These are the starting point for our pseudocode
## Procedures and Guidelines
<!-- _This specifies precisely how to document your work to allow for optimal collaboration. It's probably not necessary to read this if think you understood everything from "Syntax"_ -->
### __After defining a new Interface function, remember to__
1. **Mark as done**
- Mark the appropriate function under "List of interface functions" as done.
2. **Mark others as done**
- Check if there are other functions under "List of interface functions" which the function you just defined covers / makes obsolete.
- If you do find functions like that:
- Mark them as done as well
- Add a note describing which function they are covered by / why it's not necessary to write pseudocode for them.
- Examples which I encountered:
- At one point it made sense to implement communication with the backend directly in the GUI layer instead of going through the logic layer as specified in the D2a class diagrams. Thus the logic layer function became obsolete.
- It makes sense to skip duplicate function definitions between different components of the backend and the backend facades, because we don't actually have components and classes in the backend
- It makes sense to skip functions definitions which we won't implement our selves because they are already implemented by Firebase.
### __When encountering the need for an interface function call, remember to__
1. __Assign yourself__
- Go to 'List of Interface functions' and check if the interface function you need has already been defined
- If the interface function you want to use has **not been defined yet**, and no one else has been assigned to it, it is now your job to define it. You can assign yourself to the interface function by adding `TODO:Yourname` to the appropriate function under 'List of Interface functions'
- `REASON` If you defined a function A which depends on function B, you are in the best position to define function B as well. [Now that I think about it, this shouldn't be a big factor because we have interfaces, but I still think it'll work fine] This allows us to split up the work in a useful way by only assigning the definition of the Mobile App and Web App functions to people explicitly, and then having everyone implement the backend functions which are relevant to their Mobile App / Web App functions.
### __After defining a new helper function/structure or Firebase function, remember to__
<!-- - ??? ['Used by' list is probably unnecessary] Create a 'Used by' list above the definition and add the function(s) which you know are using it. -->
1. __Add a `[PUO]` tag if appropriate__
- Add a `PUO` tag, if you think it might be used by other functions which haven't been added to the 'Used by' list, yet.
- (e.g. when you define a general database-read function or something like that)
### __When encountering the need for a helper function/data structure or Firebase function remember to__
_Every thing we write about helper "functions" in this section also applies to other helper data structures like enums, etc._
1. __Check for existing helper / firebase functions__
- Check if there already is a helper / firebase function tagged with `[PUO]` which you can use instead of creating a new one.
2. __Tag your function call with `[Helper]` or `[xHelper]` (/ `[Cloud]` or `[xCloud]` in case of a Firebase function)__
- If you did find an existing function F suitable for your needs:
- Tag your use of F with `[xHelper]` (/ `[xCloud]`)
<!-- - ??? ['Used by' list is probably unnecessary] Go to the 'Used by' list above the definition of F, and add the function which now uses it. -->
- If you couldn't find a function suitable to your needs: (We'll call the undefined function G):
- Tag your use of G with `[Helper]` (/ `[Cloud]`)
3. __Define undefined helper functions__
- If you think that other functions might use this helper / cloud function as well, write down the function header right away and tag it with `[PUO]`.
- This is so that other teammates using the same helper function don't end up calling it something different than you do.
- It is your job to define the undefined helper functions which your interface functions depend on. But it's not important to define helper functions right away.
- `REASON` This serves the purpose of preventing duplicate implementations of helper functions and it lets us easily keep track of which helper functions still need a definition
---
# List of Interface functions
- _This includes functions found in the interfaces in the class diagrams of D2a as well as functions which are triggered through the user interface (Like `on...ButtonTapped`)_
- _This list can be used to guide the creation of Pseudocode_
## Overview
(...)
## Mobile App
### GUI
__Serverfacing__
+ [ ] refresh():void `TODO: XXX`
+ [ ] displayMoodSurvey(MoodSurvey):void `TODO: XXX`
+ [ ] displayCompanionRequest(request): Task<Response> `TODO: XXX`
### Logic
__AccountManagement__
+ [x] (Machen wir nicht weil das Firebase übernimmt) createAccount(email, nickname, password): ArrayList<ErrorCode> `DONE: Noah`
+ [x] (Machen wir nicht weil das Firebase übernimmt) logIn(email, password): ArrayList<ErrorCode> `TODO: Niko`
+ [x] (Machen wir nicht weil das Firebase übernimmt) logOut(): Void `TODO: Felix`
+ [x] (Machen wir nicht weil das Firebase übernimmt) deleteProfile(message): Void `TODO: Ela`
+ [x] (Machen wir nicht weil das Firebase übernimmt) resetPassword(email): Void `TODO: Tilman`
+ [x] (Machen wir nicht weil das Firebase übernimmt) changePassword(newPassword): ArrayList<ErrorCode> `TODO: Niko`
__CompanionManager__
+ [x] public void sendCompanionRequest(companionCode, relationship, errorHandler) `DONE: Noah`
+ [x] inviteCompanion(email, newRelation): ArrayList<ErrorCode> `TODO: Jason`
+ [x] changeCompanionRelationship(companion, newR): Void `TODO: Felix`
+ [x] removeCompanion(companion): Void `TODO: Ela `
+ [x] getCompanions(): ArrayList<Companion> `TODO: Tilman`
+ [x] getCompanion(CompanionId): Companion `TODO: Niko`
+ [x] confirmCompanionRequest(request): Void `DONE: Noah`
__ServerMessagePort_Out__
+ [x] getProfileData(): Dictionary `TODO: Jason`
+ [x] getExperimentSettings(): Dictionary `TODO: Felix`
__ServerMessagePort_In__ (This stuff is called by the backend)
+ [x] loadProfileData(): Void `TODO: Jason`
+ [ ] loadExperimentSettings(): Void `TODO: Felix`
+ [x] loadCompanions(): Void `TODO: Ela`
+ [x] getLocation(): Location -> firebase call `TODO: Tilman`
__MoodSurveyManager__
+ [x] displayMoodSurveyWithNotificationTrigger(): void `TODO: Ela`
+ [x] displayMoodSurveyWithUITrigger(): void `TODO: Tilman`
+ [x] commitMoodSurvey(MoodSurvey): Void `TODO: Niko`
__Utility__
- [x] getVisualisation(startDate, endDate, companionList, displayStress, displayMood, webViewRef): void `DONE: Noah`
## Web App
*(Alle Subcomponents entsprechen Screens aus dem Mockup)*
__Entry__
+ [x] (Machen wir nicht weil das Firebase übernimmt) logIn(password): ArrayList<ErrorCode> `TODO: Niko`
+ [x] (Machen wir nicht weil das Firebase übernimmt) logOut(userId): void `DONE: Noah`
+ [x] (Machen wir nicht weil das Firebase übernimmt) changePassword(oldPassword, newPassword, newPassword): ArrayList<ErrorCode> `TODO: Jason`
__Visualization__
+ [ ] getVisualisation(dataSet): void `TODO: Felix`
- N: Sollten wir vllt. in getVisualization umbenennen
__ExportData__
+ [ ] getDownloadLinkForUserDataCSV(from, to, omitRowsMarkedAsArchived, markExportedRowsAsArchived): HyperLink `TODO: Ela`
was ist omitRowsMarkedAsArchived???
<!-- Siehe hackmd kommentar (auf der rechten Seite) -->
<!-- // ^ Hab das hier geändert, damit es den Mockups besser entspricht
// "archiveUserData()" brauchen wir damit nicht mehr
// (auch in den Class Diagram source files auf GitLab (in WebApp_v3.drawio)) -->
<!-- + [ ] archiveUserData: ArrayList<ErrorCode> `TODO: Tilman` -->
__ExpSet_Notifications__
+ [x] getNotificationsSettings(UIRef): void `DONE: Noah`
+ [ ] setNotificationsSettings: ArrayList<ErrorCode> `TODO: Niko`
__ExpSet_ExperimentalCycle__
+ [x] getExperimentalCycleSettings(textFieldRef): void `DONE: Felix`
+ [x] setExperimentalCycleSettings: ArrayList<ErrorCode> `TODO: Jason`
__ExpSet_ConsentForm__
+ [x] getCurrentConsentForm(divRef): void `DONE: Ela`
+ [x] getDownloadLinkForConsentFormHistory(target): void `TODO: Tilman`
+ [x] replaceCurrentConsentForm: ArrayList<ErrorCode> `TODO: Niko`
__ExpSet_MoodSurvey__
+ [x] getMoodSurveyConfiguration(textFieldRef): void `DONE: Noah`
+ [x] setMoodSurveyConfiguration: ArrayList<ErrorCode> `TODO: Jason`
__ExpSet_Questionnaire__
+ [x] getQuestionnaire(textFieldRef): void `DONE: Felix`
+ [x] setQuestionnaire: ArrayList<ErrorCode> `TODO: Ela`
NOTE: vorläufig mal abgeändert zu setQuestionnaire(Questionnaire questionnaire), weil komisch ohne Parameter
+ [x] getUserAccountDeletionMessages(): ArrayList<String> `TODO: Tilman` // NOTE: Was macht das? War nicht in den D2a diagrammen oder?
// Noah: ^ Aller code aus der Web App ist glaube ich entweder Datenbank read/write oder GUI code. Eingentlich kann man da fast alles weglassen, weil es "trivial" ist oder? Vllt. können wir da nochmal Till fragen wenn ihr euch da auch unsicher seid
// Noah Ich hab die meisten `get...Settings()` funktionen direkt gemacht, weil die eigentlich alle gleich sind
## Backend
+ [x] (Machen wir nicht, weil das Firebase implementiert) createAccount(email, nickname, password): ArrayList<ErrorCode> `TODO: Niko`
+ [x] (Machen wir nicht, weil das Firebase implementiert) logIn(email, password): ArrayList<ErrorCode> `DONE: Noah`
+ [x] (Machen wir nicht, weil das Firebase implementiert) logOut(): Void `TODO: Jason`
+ [x] (Machen wir nicht, weil das Firebase implementiert) deleteProfile(message): Void `TODO: Felix`
+ [x] (Machen wir nicht, weil das Firebase implementiert) resetPassword(email): Void `TODO: Ela`
+ [x] (Machen wir nicht, weil das Firebase implementiert) changePassword(newPassword): ArrayList<ErrorCode> `TODO: Tilman`
+ [x] (Machen wir nicht, weil das Firebase implementiert) deleteProfile(reasonFromDeletion): ArrayList<ErrorCode> `TODO: Nikolai`
+ [x] changeCompanionRelationship(companion, newR): void `DONE: Noah`
+ [ ] removeCompanion(companion): void `TODO: Jason`
+ [x] requestCompanionLocationsForUser(User): Dictionary `TODO: Felix`
+ [x] setDataToDatabaseWithTransaction(transaction): ArrayList<ErrorCode> `TODO: Tilman`
+ [ ] getVisualisation(userList, parameters): WebView `TODO: Niko`
// Wir sollten denke ich eine Datenbankstruktur anlegen, und die Funktionen definieren, die ausgelöst werden wenn in der Datenbank an einer bestimmten Stelle was passiert. Sehr viel der essenziellen Server Logik sollte indirekt über solche Datenbank queries ausgelöst werden
---
public static enum Shared::Shared::MOOErrorCode {
nicknameTooShort,
invalidEmail,
passwordRepeatDoesntMatch,
passwordTooWeak,
UploadFailed
}
<!--
# Pseudocode
TODO: Add precondition / postcondition / other auxiliary stuff from the example document in here
## `MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped()`
This method is called when the register button on the register screen is tapped. It is responsible for registering a new User to firebase. Therefore first the entered fields are checked for validity and then the firebase API is called.
- Constrains:
- MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped() pre: Register Button Tapped
- MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped() post: valid nickname,email, psw1, psw2 -> user registered
- MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped() post: invalid nickname, email, psw1, psw2 -> user not registered
### Interface function
```java=
MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped() {
String nickname = findViewById(R.id.field_nickname).getText().toString();
String email = findViewById(R.id.field_email).getText().toString();
String password1 = findViewById(R.id.field_password1).getText().toString();
String password2 = findViewById(R.id.field_password2).getText().toString();
ArrayList<ErrorCode> errorList_localValidation = [xHelper]checkValidityLocal(nickname, email, password1, password2);
if (errorList_localValidation.length != 0) {
[xHelper]handleLocalErrorCodeList(errorList_localValidation);
}
//kommt hier ein else hin? oder ein return in der if clause davor // Ahh hast voll recht - Hab das return vergessen
//und es wird doch mAuth.createUser.. aufgerufen oder?
//bzw hast du die javascript authentifizierung genommen?
//glaube in android sieht das anderst aus:
/*
[xInter]mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
FirebaseUser user = mAuth.getCurrentUser();
} else {
Toast.makeText(this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
}
});
*/
[xInter]firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
[Helper]handleFirebaseErrorCode(errorCode);
});
}
```
## `MobileApp::GUI::RegisterScreenActivity::onLoginButtonTapped()`
- Constrains:
- MobileApp::GUI::RegisterScreenActivity::onLoginButtonTapped() pre: Login Button Tapped
- MobileApp::GUI::RegisterScreenActivity::onLoginButtonTapped() post: valid email, psw -> user logged in
- MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped() post: invalid email, psw -> user not logged in
### Interface function
```java=
MobileApp::GUI::RegisterScreenActivity::onLoginButtonTapped() {
String email = findViewById(R.id.field_email).getText().toString();
String password1 = findViewById(R.id.field_password).getText().toString();
[xInter]mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
FirebaseUser user = mAuth.getCurrentUser();
} else {
Toast.makeText(EmailPasswordActivity.this, "Authentication failed.",Toast.LENGTH_SHORT).show();
updateUI(null);
}
}
});
}
```
### Helper functions and structures
#### Helper structure
Used by
- `MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped()` and it's helper functions
- ...
`PUO`
```java=
public static enum Shared::Shared::MOOErrorCode {
nicknameTooShort,
invalidEmail,
passwordRepeatDoesntMatch,
passwordTooWeak,
UploadFailed
}
```
#### Helper function
Used by
- `MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped()`
- ...
`PUO`
```java=
private static MobileApp::GUI::SharedActivity::onCreate() {
private FirebaseAuth mAuth;
// ...
// Initialize Firebase Auth
mAuth = FirebaseAuth.getInstance();
}
```
#### Helper function
Used by
- `MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped()`
```java=
private static MobileApp::GUI::RegisterScreenActivity::checkValidityLocal(String nickname, String email, String password1, String password2) {
ArrayList<MOOErrorCode> errorList = new ArrayList<MOOErrorCode>();
if (nickname.length() < 2) {
errorList.add(MOOErrorCode.nicknameTooShort);
}
String emailRegex = ...
Pattern emailRegexPattern = Pattern.compile(emailRegex);
Matcher emailMatcher = emailRegexPattern.matcher(email);
if (!emailMatcher.find()) {
errorList.add(MOOErrorCode.invalidEmail);
}
if (!password1.equals(password2)) {
errorList.add(MOOErrorCode.passwordRepeatDoesntMatch);
}
if (password1.length < passwordMinLength || password2.length < passwordMinLength) {
errorList.add(MOOErrorCode.passwordTooWeak);
}
return errorList;
}
```
#### Helper function
Used by
- `MobileApp::GUI::RegisterScreenActivity::onRegisterButtonTapped()`
```java=
private static MobileApp::GUI::RegisterScreenActivity::handleLocalErrorCodeList(ArrayList<MOOErrorCode> errorList) {
for (MOOErroCode code : errorList) {
if (code == MOOErrorCode.nicknameTooShort) {
[Helper]comNicknameTooShortMessage();
} else if (code == MOOErrorCode.invalidEmail) {
[Helper]displayInvalidEmailMessage();
} else if (code == MOOErrorCode.passwordRepeatDoesntMatch) {
[Helper]displayPasswordRepeatDoesntMatchMessage();
} else if (code == MOOErrorCode.passwordTooWeak) {
[Helper]displayPasswordTooWeakMessage();
} else {
throw (new Error); // "Error: Unhandle-able MOOErrorCode encountered"
}
}
}
``` -->
## Diskussion
- [ ] __Asynchrone Datenbankleseoperationen__
Die Datenbank Leseoperationen sind wie ich gesehen habe zwangsweise asynchronous (`addListenerForSingleValueEvent` haben wir im Pseudocode verwendet). Davon sind wir beim groben Entwurf in D2a nicht ausgegangen. Das macht einige Sachen ein bisschen verwirrend. Wir können *nicht* wie wir dachten einfach von der GUI Komponente aus eine Funktion `Logic::getValueX() : ValueXType` auf der Logic Komponente aufrufen wenn wir den Value X brauchen.
Ich hab gesehen, dass im Pseudocode an ein paar Stellen folgendermassen umgestzt wurde, was wir aber nicht so umsetzten können: Der Wert, der aus der Datenbank gelesen wurde, wurde aus dem asynchronen Block returned - der asynchrone Block wird aber von Firebase selber aufgerufen, wenn die Datenbank-Leseoperation beendet wurde, und nicht von unserer eigenen Funktion - deshalb macht das returnen hier keinen Sinn.
Stattdessen würde ich folgendes Vorschlagen: Wir geben der Funktion `getValueX()` eine Referenz zum ValueX mit -> `getValueX(valueXRef)`, dann können wir im asynchronen Block auf den Pointer valueXRef zugreifen dort direkt den Wert den wir aus der Datenbank gelesen haben reinschreiben.
Wenn valueX im Rahmen einer Datenstruktur ds dem User dargestellt wird (ds könnte z.B. ein ArrayAdapter sein, wenn valueX innerhalb einer Liste dargestellt wird), dann müssten wir eventuell dazu/stattdessen eine Referenz zu ds als parameter mitgeben `getValueX(valueXRef, dsRef)` sodass wir dann, nachdem die Datenbank leseoperation abgeschlossen wurde im asynchronen Block dsRef.refreshContent() oder so aufrufen können, sodass der neue value X dem user auch dargestellt wird.
-> Auf die Art und Weise können wir die Grundstruktur aus D2a beibehalten - Wir behalten so ziemlich die trennung zwischen Logic und GUI und müssen nicht noch extra funktionen einführen.
-> I reworked "ShareLatex > 2b > 2b-3_classinterfaces > `getCompanions() : ArrayList$<$Companion$>$`" to implement this pattern
## Fragen
- [x] Benutzen wir Firestore oder Realtime Database? Der Pseudocode den ich gesehen habe sah eher nach Realtime Database aus
- Jason: Ich würde sagen firestore währe einfacher weil die interne struktur ein bisschen einfacher zu verwalten ist.
- [ ] Sollen bei den gespeicherten mood surveys auch die Fragen gespeichert werden ? Ansonsten verwirrend wenn die Fragen verändert werden
- Voll krass dass dir sowas auffällt da wür ich nie drauf gekommen! Würde wahrscheinlich am meisten Sinn machen, wenn wir eine historie an MoodSurveys / MoodSurvey Questions und entsprechenden ids speichern und die ids dann in den MoodSurvey Datenpunkten von den Usern referenzieren... Habs in Datenbank Struktur Version 2 (siehe unten) so umgesetzt
- [ ] Wie sollen Consent Forms gespeichert werden? PDF files weil der admin muss diese ja auch als file downloaden können.
- A1: WÜRDE JSON/TEXT SAGEN: {content: ..., agreed: ...}?
- A2: Würde Vorschlagen: Tupel aus (ID, html) - siehe Datenbank Struktur Version 2
- [x] Wir müssen Firebase Storage noch nutzen um die Files zu speichern richtig?
- Ja
- [ ] ReplaceCurrentConsentForm können wir doch umnennen zu addConsentForm. Bei dem register Vorgang kann doch dann einfach immer das aktuellste herausgezogen werden? somit hätten wir alle an einem platz.
- Ja könnte man auch so nennen ist beides okay imo. Aber denk dran die Sachen dann auch in den Diagrammen umzubennen
- [x] Ela: (siehe ServerMessagePort_In ): macht es überhaupt Sinn, diese Funktionen zu implementieren?
- N: Denke schon, wieso nicht?
- Ela: nvm kam mir erst komisch vor aber ich habs jetzt halbwegs gelöst
- [x] Ela: Ist die Datenbankstruktur unten eine Collection und die Unterpunkte Dokumente oder ist das alles in einem Dokument?
- Noah: Ich hätte gesagt, alle Zeilen, die mit - anfangen sind collections und alles was mit |- anfängt ist ein Dokument / Teil von einem Dokument
## Datenbank Struktur
Version 2:
-- users
|----- userId
|-------- firstName
|-------- lastName
|-------- nickname
|-------- experimentalCycleStartDate
|--------- moodSurveyResponses (?subcollection)
|------------- responseID
|----------------- metadata
|-------------------- triggerType
|-------------------- responseDateAndTime
|-------------------- moodSurveyVersionID
|----------------- question1AnswerString
|----------------- question2AnswerString
|----------------- ...
-- relationships
|----- relationshipID
|--------- userAID
|--------- userBID
|--------- userARelationship
|--------- userBRelationship
-- relationshipCreationSessions
|---- sessionID
|-------- requestingUserID
|-------- requestingUserRelationship
|-------- receivingUserCompanionCode
-- experimentSettings
|---- experimentalCycleLength
|---- notificationSettings
|-------- uniqueNotForUsers
|-------- randomSendRanges
|------------ sendRange1
|--------------- startTime
|--------------- endTime
|--------------- numberOfNot
|------------ sendRange2
|------------ ...
|---- moodSurveys
|-------- current
|------------ versionID
|---------------- descriptor
|-------------------- question1
|------------------------ title
|------------------------ conditions
|------------------------ type
|------------------------ parameterA
|------------------------ parameterB
|------------------------ ...
|-------- history
|------------ versionID
|---------------- descriptor
|------------ versionID
|---------------- descriptor
|------------ ...
|---- Questionnaire
|-------- current
|------------ versionID
|---------------- descriptor
|-------- history
|------------ versionID
|---------------- descriptor
|------------ versionID
|---------------- descriptor
|---- ConsentForms
|-------- current
|------------ versionID
|---------------- html
|-------- history
|------------ versionID
|---------------- html
|------------ versionID
|---------------- html
|------------ ...
Version 1:
-- users
|----- userId
|-------- name
|-------- nickname
|-------- companions
|------------- userId5
|------------- userId67
|-------- moodsurvey
|------------- survey1
|----------------- q1
|----------------- q2
-- moodsurvey
|----- survey1
|----- survey2
|----- survey3