|
| 1 | +# Sorting hat |
| 2 | + |
| 3 | +This repository hosts a questionnaire/survey/quiz-like application, which consist of a backend that is agnostic of the type of survey the user is taking, and a frontend that is specific to the topic of the survey. |
| 4 | + |
| 5 | +It is using **Angular** as it's frontend, including **apollo** and **graphql** both on the front and the back. **NestJS** for the backend and **prisma** as a Node.js/TypeScript ORM, with an **sqlite** database. |
| 6 | + |
| 7 | +In this README I use questionnaire/survey/quiz interchangeably, but I really mean of an application that gives you possible questions and answers, and with each answer on a question you score points towards a final result. |
| 8 | + |
| 9 | + |
| 10 | +_view in bigger resolution [here](https://raw.githubusercontent.com/lubeskih/sorting-hat/main/docs/assets/decision-tree.png?token=ALSATGCRDIGJHBLZOVEBIX3BTIVPM)_ |
| 11 | + |
| 12 | +**Table of Contents** |
| 13 | + |
| 14 | +- [How to build and run](#How-to-build-and-run) |
| 15 | +- [How it works](#How-it-works) |
| 16 | + - [Matrix](#Matrix) |
| 17 | + - [Survey](#Survey) |
| 18 | + - [User](#User) |
| 19 | +- [Scoring and decisions](#Decision) |
| 20 | +- [Database models and relations diagram](#Database-models-and-relations-diagram) |
| 21 | + |
| 22 | + |
| 23 | +## How to build and run |
| 24 | + |
| 25 | +```bash |
| 26 | +$ git clone https://github.com/lubeskih/sorting-hat.git |
| 27 | +$ cd sorting-hat |
| 28 | +$ docker-compose up # -d (optionally) |
| 29 | +``` |
| 30 | + |
| 31 | +Docker will try to start the applications on ports `4200` and `3000`, so make sure those are open and not used on your machine. Otherwise you'll have to change the Docker configuration to your desired ports. |
| 32 | + |
| 33 | +After the images are built and containers are running, visit `localhost:4200`. The `POTTERMORE` survey is following this JSON structure: [hp_survey.json](api/src/parser/service/hp_survey.json) and is seeded automatically by the `ParserService`, which parses and validates the file, and seeds the database if it is not already seeded. |
| 34 | + |
| 35 | +## How it works |
| 36 | + |
| 37 | +The full application is divided in two parts: a `backend` and a `frontend`. The `backend` survey engine is independent of the topic of the survey on the frontend. For example, this frontend hosted here is a survey for Harry Potter fans. But it can really be any kind of survey as long as it involves scoring points per given answer. |
| 38 | + |
| 39 | +The backend understands three concepts: |
| 40 | + |
| 41 | +* **Matrix** |
| 42 | +* **Survey** |
| 43 | +* **User** |
| 44 | + |
| 45 | +### Matrix |
| 46 | + |
| 47 | +I use the word **matrix** to describe an array of objects, where each object keeps a unique answer and an array of numbers which represent the score that the answer brings towards the final outcomes. |
| 48 | + |
| 49 | +The "source of truth" comes from a JSON file (find the application's JSON [here](api/src/parser/service/hp_survey.json)), which holds a structure that the backend parses/validates and then seeds the database. In a real-world situation you would have a nice frontend that would let the user make their own survey structure in a more user-friendly way, instead of writing JSON in a file. For example, the JSON structure for a matrix looks like this: |
| 50 | + |
| 51 | +```JSON |
| 52 | +{ |
| 53 | + "matrix": [ |
| 54 | + { |
| 55 | + "surveyTitle": "pottermore", |
| 56 | + "bias": [3, 2, 1, 0], |
| 57 | + "surveyMatrix": [ |
| 58 | + { |
| 59 | + "answerId": 1, |
| 60 | + "matrixId": 1, |
| 61 | + "score": [100, 100, 0, 0] |
| 62 | + }, |
| 63 | + { |
| 64 | + "answerId": 2, |
| 65 | + "matrixId": 1, |
| 66 | + "score": [0, 0, 100, 100] |
| 67 | + } |
| 68 | + ] |
| 69 | + } |
| 70 | + ] |
| 71 | +} |
| 72 | + |
| 73 | +``` |
| 74 | + |
| 75 | +### Survey |
| 76 | + |
| 77 | +A **Survey** is a structure that consists of: |
| 78 | +* A name of the survey |
| 79 | +* An ID of the matrix it's answers are being weighted |
| 80 | +* Array of questions |
| 81 | + |
| 82 | +A **Question** is an object that consists of: |
| 83 | +* A **value**, or the question itself |
| 84 | +* **answerChoice**, which is later send to the frontend to decide how the user should answer it's answers (text input, radio button, normal button). |
| 85 | +* **parentSurveyId** the survey id that the question is a child of. |
| 86 | +* **lastQuestion** which is a boolean that specifies if that question is the last question of the survey. |
| 87 | +* Array of answers |
| 88 | + |
| 89 | +An **Answer** is an object that has: |
| 90 | +* A value, or the answer itself |
| 91 | +* **parentQuestionId** which is the ID of it's parent |
| 92 | +* **nextQuestionId** which is the ID of the next question that should be asked if that answer is chosen. This property gives the survey a tree-structure navigation. |
| 93 | + |
| 94 | +A JSON structure for a survey looks like this: |
| 95 | + |
| 96 | +```JSON |
| 97 | +{ |
| 98 | + "survey": [ |
| 99 | + { |
| 100 | + "surveyTitle": "pottermore", |
| 101 | + "scoreMatrixId": 1, |
| 102 | + "questions": [ |
| 103 | + { |
| 104 | + "value": "Dawn or dusk?", |
| 105 | + "answerChoice": "radio_button", |
| 106 | + "parentSurveyId": 1, |
| 107 | + "lastQuestion": false, |
| 108 | + "answers": [ |
| 109 | + { |
| 110 | + "value": "Dawn", |
| 111 | + "parentQuestionId": 1, |
| 112 | + "nextQuestionId": 2 |
| 113 | + }, |
| 114 | + { |
| 115 | + "value": "Dusk", |
| 116 | + "parentQuestionId": 1, |
| 117 | + "nextQuestionId": 2 |
| 118 | + } |
| 119 | + ] |
| 120 | + }, |
| 121 | + ] |
| 122 | + } |
| 123 | + ] |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +### User |
| 128 | +A user is just an entity in database that has an unique session token as soon as they start a survey. The session token is kept is session storage, and is being sent upon each answer, so it can be recorded, and later linked against the score matrix to calculate the score. Each user can have a `user answer`, which is just a record of which user selected which answer on which question. |
| 129 | + |
| 130 | + |
| 131 | +## Scoring and decisions |
| 132 | + |
| 133 | +When a user chooses and answers and continues to the next question, the `DecisionsService` is recalculating it's score towards the possible final outcomes. |
| 134 | + |
| 135 | +This diagram shows how the `DecisionsService` is doing the calculations: |
| 136 | + |
| 137 | + |
| 138 | +_view in bigger resolution [here]()_ |
| 139 | + |
| 140 | +What we see here is, each column of the answer matrix represents points given to each house. For each column, the values are added, which represent the total score of one house. After we find the total score of each house, those scores are added to find the total score of all houses together. |
| 141 | + |
| 142 | +The **bias** is a preference towards a final outcome, it is used in case two houses have the same score. (you'd say it's unfair, and I'd agree, but then it's a _bias_). |
| 143 | + |
| 144 | +Then, we find percentage with: |
| 145 | + |
| 146 | +``` |
| 147 | +score = 100 * (score_house / score_total) |
| 148 | +``` |
| 149 | + |
| 150 | +## Database models and relations diagram |
| 151 | +<br> |
| 152 | + |
| 153 | + |
| 154 | +_view in bigger resolution [here](https://raw.githubusercontent.com/lubeskih/sorting-hat/main/docs/assets/relations.png?token=ALSATGCI3CL5GJTPYJIMWH3BTIVVU)_ |
| 155 | + |
| 156 | +## License |
| 157 | + |
| 158 | +This software is licensed under [MIT](LICENSE.md). |
0 commit comments