Skip to content

Commit 9648759

Browse files
committed
complete questionnaire.md and add link to go back to README.md
1 parent 61c966e commit 9648759

8 files changed

Lines changed: 186 additions & 75 deletions

‎README.md‎

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1+
[README.md](./README.md)
2+
13
# Unit Tests Practice
24

3-
This repository has been prepared to practice the automated testing framework unittest through different angles.
5+
### This repository has been prepared to practice the 'unittest' automated testing framework through different angles
6+
### It is based on the use of simple quizzes importer and viewer.
7+
8+
---
9+
10+
>python ./questionnaire_import.py
11+
12+
Loads some quizzes from a sample list in the program and converts them into a specific json [format](./questionnaire.md#expected-json-schema) before to save them
13+
into the [json_questionnaires](./json_questionnaires) folder
14+
15+
More info : [questionnaire_import.md](./questionnaire_import.md)
16+
17+
---
18+
19+
>python ./questionnaire.py <questionnaire.json>
20+
21+
Displays the quizz in argument and counts the number of good answers
22+
23+
More info : [questionnaire.md](./questionnaire.md)
24+
25+
---
426

5-
./questionnaire_import.py : Allows to load some questionnaires into json format
6-
These ones arrives
7-
# These questionnaires have been loaded from openquizzdb on https://www.kiwime.com/oqdb/files/
8-
# The API which allows to directly load them have been disconnected and is not available anymore
9-
# Some questionnaires have thus been transfered on www.codeavecjonathan.com for allowing
10-
# to be manipulated through this exercise
1127

‎empty.json‎

Whitespace-only changes.

‎json_file_without_extension‎

Lines changed: 0 additions & 1 deletion
This file was deleted.

‎questionnaire.md‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
[README.md](./README.md) > [questionnaire.md](./questionnaire.md)
2+
3+
# Questionnaire.py
4+
5+
Displays the quizz in argument and counts the number of good answers
6+
7+
```commandline
8+
python questionnaire.py <questionnaire.json>
9+
```
10+
11+
## Sample :
12+
13+
```commandline
14+
python questionnaire.py .\json_questionnaires\animaux_leschats_confirme.json
15+
```
16+
```commandline
17+
Catégorie : Animaux
18+
Difficulté : confirmé
19+
Titre : Les chats
20+
21+
QUESTION 1
22+
À quelle classe d'animaux vertébrés regroupant près de 5.400 espèces appartient le chat ?
23+
1 - Mammifères
24+
2 - Reptiles
25+
3 - Oiseaux
26+
4 - Poissons
27+
Votre réponse (entre 1 et 4) :
28+
```
29+
```commandline
30+
Bonne réponse
31+
32+
Score final : 14 sur 20
33+
```
34+
---
35+
36+
Questionnaire.py can accept a json file from :
37+
* the current folder
38+
* a different folder
39+
* an URL
40+
41+
Questionnaire.py tries to load the json file in argument or print :
42+
- [```Error in argument```] if no one or more than one argument is passed
43+
- [```Incorrect extension```] if the file as no json extension
44+
- [```Invalid Path```] if the path to the file can not be reached
45+
- [```No File```] if the file can not be find into the path folder
46+
- [```Invalid URL```] if the path to the server can not be reached
47+
- [```File not found```] if the file can not be reached on the distant server
48+
- [```Incompatible or no data in JSON file```] if the json file cannot be loaded and decoded
49+
- [```Incompatible Json schema```] if the loaded json file does not match the
50+
expected [schema](./questionnaire.md#expected-json-schema)
51+
- [```The title of the quizz is missing```] if the titre is an empty string
52+
53+
These messages may also be printed but are <u>not critical</u> to the quiz
54+
- [```Skipped question : nothing to ask in question```] if the question is an empty string
55+
- [```Skipped question : no one or more than one good answer in question```] if a question has no one or more than
56+
one ```boolean``` at ```True``` in its answers
57+
58+
At least, if all questions have been skipped, [```No compatible question in this quizz```] will be printed.
59+
60+
---
61+
## Expected Json Schema
62+
63+
```categorie``` and ```difficulte``` properties are not required but ```titre``` and ```questions``` are mandatory
64+
65+
66+
Each questions must have at least 2 possible answers and each answers must be defined by a ```string``` and
67+
a ```boolean``` only
68+
69+
70+
```json lines
71+
schema = {
72+
"$schema": "http://json-schema.org/draft/2020-12/schema",
73+
"type": "object",
74+
"required": [ "titre", "questions" ],
75+
"properties": {
76+
"categorie": {"type": "string"},
77+
"titre": {"type": "string"},
78+
"questions": {
79+
"type": "array",
80+
"default": [],
81+
"items": {
82+
"type": "object",
83+
"properties": {
84+
"titre": {"type": "string"},
85+
"choix": {
86+
"type": "array",
87+
"minItems": 2,
88+
"items": {
89+
"type": "array",
90+
"minItems": 2,
91+
"prefixItems": [
92+
{"type": "string"},
93+
{"type": "boolean"}
94+
],
95+
"items": false
96+
}
97+
}
98+
}
99+
}
100+
},
101+
"difficulte": {"type": "string"}
102+
}
103+
}
104+
```
105+

‎questionnaire.py‎

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class bcolors:
4040
UNDERLINE = '\033[4m'
4141

4242
def printerr(msg):
43-
return print(f"{bcolors.FAIL}{msg}{bcolors.ENDC}")
43+
return print(f"{bcolors.FAIL}Error : {msg}{bcolors.ENDC}")
4444

4545

4646
class Question:
@@ -118,38 +118,54 @@ class Questionnaire:
118118
}
119119
}
120120

121-
def __init__(self, json_questionnaire):
121+
def __init__(self, json_questionnaire, filename):
122122
'''Test the expected json schema before to initiate'''
123123

124124
try:
125125
validate(json_questionnaire, self.schema)
126126
except jsonschema.exceptions.ValidationError:
127127
printerr(f"Incompatible Json schema in file : {filename}")
128128
else:
129-
# 'categorie', 'difficulte' : non-blocking properties, but better to be completed as unknown if absent
129+
# 'categorie', 'difficulte' : non-critical properties, but better to be completed as unknown if absent
130130
self.categorie = json_questionnaire['categorie'] if json_questionnaire.get('categorie') else 'inconnue'
131131
self.difficulte = json_questionnaire['difficulte'] if json_questionnaire.get('difficulte') else 'inconnue'
132132

133-
# 'titre' or 'questions' : required properties already checked into schema
134-
self.titre = json_questionnaire['titre']
135-
self.questions = []
136-
for json_question in json_questionnaire['questions']:
137-
self.questions.append( Question.FromJSON(json_question) )
133+
# 'titre' or 'questions' are required properties
134+
# all the types are already verified into the schema validator but the len=0 may also identify an error
135+
if len(json_questionnaire['titre']) <= 0 :
136+
printerr(f"The title of the quizz is missing in file : {filename}")
137+
else:
138+
self.titre = json_questionnaire['titre']
139+
self.questions = []
140+
for idx, json_question in enumerate(json_questionnaire['questions']):
141+
if len(json_question['titre']) <= 0: # This is not critical but the question won't be added
142+
printerr(f"Skipped question : nothing to ask in question {idx} file : {filename}")
143+
else:
144+
good_answers = [ choix[0] for choix in json_question['choix'] if choix[1]==True]
145+
if len(good_answers)!=1: # This is not critical but the question won't be added
146+
printerr(f"Skipped question : no one or more than one good answer "
147+
f"in question {idx} file : {filename}")
148+
else:
149+
self.questions.append(Question.FromJSON(json_question))
138150

139151
def run(self):
140152
# Launch of the questionnaire
153+
print(f"=== QUESTIONNAIRE ===")
141154
print(f"Catégorie : {self.categorie}")
142155
print(f"Difficulté : {self.difficulte}")
143156
print(f"Titre : {self.titre}\n")
144157

145158
score = 0
146159
index = 0
147-
for question in self.questions:
148-
index+=1
149-
if question.ask(index):
150-
score += 1
160+
if len(self.questions) <= 0 :
161+
print("No compatible question in this quizz")
162+
else:
163+
for question in self.questions:
164+
index+=1
165+
if question.ask(index):
166+
score += 1
151167

152-
print("Score final :", score, "sur", len(self.questions))
168+
print("Score final :", score, "sur", len(self.questions))
153169
return score
154170

155171

@@ -189,7 +205,7 @@ def load_json_argv( sysargv ):
189205
try: # Opening of the JSON file
190206
json_file = open(filename, "r")
191207
except OSError:
192-
printerr(f"Invalid File : {filename}")
208+
printerr(f"Invalid Path to : {filename}")
193209
return None
194210
except FileNotFoundError:
195211
printerr(f"No File : {filename}")
@@ -202,16 +218,16 @@ def load_json_argv( sysargv ):
202218
printerr(f"Incompatible or no data in JSON file : {filename}")
203219
return None
204220

205-
return json_data
221+
return json_data, filename
206222

207223

208224

209225
def main():
210226

211-
json_data = load_json_argv( sys.argv )
227+
json_data, filename = load_json_argv( sys.argv )
212228

213229
if json_data:
214-
Questionnaire(json_data).run()
230+
Questionnaire(json_data, filename).run()
215231

216232
if __name__ == "__main__":
217233
main()

‎questionnaire_import.md‎

Lines changed: 25 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1+
[README.md](./README.md) > [questionnaire_import.md](./questionnaire_import.md)
2+
13
# Questionnaire_import.py
24

3-
These quizzes were uploaded from http://www.openquizzdb.org/ or https://www.kiwime.com/oqdb/files/
5+
Loads some quizzes from a sample list in the program :
6+
* openquizzdb_50.json
7+
* openquizzdb_86.json
8+
* openquizzdb_241.json
9+
* openquizzdb_90.json
10+
11+
and converts them into a specific json [format](./questionnaire.md#expected-json-schema) before to save them
12+
into the [json_questionnaires](./json_questionnaires) folder
413

5-
The API that allowed them to be uploaded for free has been disconnected and is no longer available. Some quizzes have
6-
thus been transferred to www.codeavecjonathan.com to simulate a download and be manipulated through this exercise
14+
Those quizzes were initially downloaded from http://www.openquizzdb.org/ or https://www.kiwime.com/oqdb/files/ but the
15+
API that allowed to do it for free has been disconnected and is no longer available. Those quizzes have
16+
thus been transferred on www.codeavecjonathan.com to be manipulated through this exercise
717

818

9-
### Initial json quizz format :
19+
### Sample of the initial json quiz format :
1020
```json
1121
{
1222
"fournisseur" : "OpenQuizzDB - Fournisseur de contenu libre (https://www.openquizzdb.org)",
@@ -31,7 +41,7 @@ thus been transferred to www.codeavecjonathan.com to simulate a download and be
3141
{
3242
"fr" :
3343
{
34-
"débutant" : [
44+
"débutant" : [10 items
3545
0 : {
3646
"id": 1,
3747
"question": "À quelle classe d'animaux vertébrés appartient le chat ?",
@@ -46,7 +56,8 @@ thus been transferred to www.codeavecjonathan.com to simulate a download and be
4656
},
4757
1: {},
4858
...
49-
10: {},
59+
10: {}
60+
],
5061
"confirmé" : [10 items],
5162
"expert" : [10 items],
5263
},
@@ -66,11 +77,11 @@ Each category comprises 10 questions with 4 possible answers to be proposed to t
6677
The good answer is saved under ```réponse```
6778
___
6879

69-
> questionnaire_import.py
80+
> python questionnaire_import.py
7081
71-
loads json quiz files and converts them to a new format by creating one file per difficulty category
82+
loads json quiz files and converts them to a new json format by creating one file per difficulty category
7283

73-
### json quizz format after import :
84+
### Sample of the json quizz format after import :
7485
```json
7586
{
7687
"categorie" : "Animaux",
@@ -93,47 +104,11 @@ loads json quiz files and converts them to a new format by creating one file per
93104
}
94105
```
95106
This file represents the second ```confirmé``` difficulty and therefore contains the first 20 questions
96-
The ```débutant``` quiz contains only the first 10 questions and the ```expert``` quiz contains all the questions
107+
The ```débutant``` quiz contains only the first 10 questions and the ```expert``` quiz contains the whole 30 questions
97108

98-
The only rubrics kept are: ```catégorie```, ```nom```, of ```catégorie-nom-slogan``` in French (fr) and the items of the
99-
```quizz``` in French(fr). These last ones are compiled to have 4 propositions and a boolean parameter indicating if the
100-
answer is the good one.
109+
The only keys kept are: ```catégorie```, ```nom```, of ```catégorie-nom-slogan``` in French (```fr```) and the items of
110+
the ```quizz``` in French(```fr```). These last ones are compiled to have 4 propositions and a boolean parameter
111+
indicating if the answer is the good one.
101112

102113
---
103-
### The schema must so match with the one tested in ./questionnaire.py
104-
```json
105-
schema = {
106-
"$schema": "http://json-schema.org/draft/2020-12/schema",
107-
"type": "object",
108-
"required": [ "titre", "questions" ],
109-
"properties": {
110-
"categorie": {"type": "string"},
111-
"titre": {"type": "string"},
112-
"questions": {
113-
"type": "array",
114-
"default": [],
115-
"items": {
116-
"type": "object",
117-
"properties": {
118-
"titre": {"type": "string"},
119-
"choix": {
120-
"type": "array",
121-
"minItems": 2,
122-
"items": {
123-
"type": "array",
124-
"minItems": 2,
125-
"prefixItems": [
126-
{"type": "string"},
127-
{"type": "boolean"}
128-
],
129-
"items": false
130-
}
131-
}
132-
}
133-
}
134-
},
135-
"difficulte": {"type": "string"}
136-
}
137-
}
138-
```
139-
114+
### The schema must so match with the one tested in [./questionnaire.py](./questionnaire.md#expected-json-schema)

0 commit comments

Comments
 (0)