Greg Thompson commited on
Commit
38eb5cf
2 Parent(s): e6d0144 8d43cf4

Merge branch 'vlad' into 'feature-wormhole'

Browse files

Data-driven quiz

See merge request tangibleai/community/mathtext-fastapi!12

app.py CHANGED
@@ -1,12 +1,10 @@
1
  """FastAPI endpoint
2
  To run locally use 'uvicorn app:app --host localhost --port 7860'
 
 
3
  """
4
  import ast
5
- import scripts.quiz.generators as generators
6
- import scripts.quiz.hints as hints
7
- import scripts.quiz.questions as questions
8
- import scripts.quiz.utils as utils
9
-
10
  from fastapi import FastAPI, Request
11
  from fastapi.responses import JSONResponse
12
  from fastapi.staticfiles import StaticFiles
@@ -146,168 +144,186 @@ async def evaluate_user_message_with_nlu_api(request: Request):
146
  message_data = data_dict.get('message_data', '')
147
  nlu_response = evaluate_message_with_nlu(message_data)
148
  return JSONResponse(content=nlu_response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
 
151
- @app.post("/question")
152
  async def ask_math_question(request: Request):
153
- """Generate a question and return it as response along with question data
154
 
155
  Input
156
- request.body: json - amount of correct and incorrect answers in the account
157
  {
158
- 'number_correct': 0,
159
- 'number_incorrect': 0,
160
- 'level': 'easy'
161
  }
162
 
163
  Output
164
- context: dict - the information for the current state
165
  {
166
  'text': 'What is 1+2?',
167
- 'question_numbers': [1,2,3], #3 numbers - current number, ordinal number, times
168
- 'right_answer': 3,
169
- 'number_correct': 0,
170
- 'number_incorrect': 0,
171
- 'hints_used': 0
172
  }
173
  """
174
  data_dict = await request.json()
175
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
176
- right_answers = message_data['number_correct']
177
- wrong_answers = message_data['number_incorrect']
178
- level = message_data['level']
179
 
180
- return JSONResponse(generators.start_interactive_math(right_answers, wrong_answers, level))
181
 
182
 
183
  @app.post("/hint")
184
  async def get_hint(request: Request):
185
- """Generate a hint and return it as response along with hint data
186
 
187
  Input
188
- request.body:
189
  {
190
- 'question_numbers': [1,2,3], #3 numbers - current number, ordinal number, times
191
- 'right_answer': 3,
192
- 'number_correct': 0,
193
- 'number_incorrect': 0,
194
- 'level': 'easy',
195
- 'hints_used': 0
196
  }
197
 
198
  Output
199
- context: dict - the information for the current state
200
  {
201
- 'text': 'What is 1+2?',
202
- 'question_numbers': [1,2,3], #2 or 3 numbers
203
- 'right_answer': 3,
204
- 'number_correct': 0,
205
- 'number_incorrect': 0,
206
- 'level': 'easy',
207
- 'hints_used': 0
208
  }
209
  """
210
  data_dict = await request.json()
211
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
212
- question_numbers = message_data['question_numbers']
213
- right_answer = message_data['right_answer']
214
- number_correct = message_data['number_correct']
215
- number_incorrect = message_data['number_incorrect']
216
- level = message_data['level']
217
- hints_used = message_data['hints_used']
218
 
219
- return JSONResponse(hints.generate_hint(question_numbers, right_answer, number_correct, number_incorrect, level, hints_used))
220
 
221
 
222
- @app.post("/generate_question")
223
- async def generate_question(request: Request):
224
- """Generate a bare question and return it as response
225
 
226
  Input
227
- request.body: json - level
228
  {
229
- 'level': 'easy'
 
 
230
  }
231
 
232
  Output
233
- context: dict - the information for the current state
234
  {
235
- "question": "Let's count up by 2s. What number is next if we start from 10?
236
- 6 8 10 ..."
 
 
237
  }
238
  """
239
  data_dict = await request.json()
240
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
241
- level = message_data['level']
 
 
 
 
 
 
 
242
 
243
- return JSONResponse(questions.generate_question_data(level)['question'])
244
 
245
 
246
- @app.post("/numbers_by_level")
247
- async def get_numbers_by_level(request: Request):
248
- """Generate three numbers and return them as response
249
 
250
  Input
251
- request.body: json - level
252
  {
253
- 'level': 'easy'
 
254
  }
255
 
256
- Output
257
- context: dict - three generated numbers for specified level
258
- {
259
- "current_number": 10,
260
- "ordinal_number": 2,
261
- "times": 1
262
- }
263
  """
264
  data_dict = await request.json()
265
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
266
- level = message_data['level']
267
- return JSONResponse(questions.generate_numbers_by_level(level))
268
 
 
269
 
270
- @app.post("/number_sequence")
271
- async def get_number_sequence(request: Request):
272
- """Generate a number sequence
 
273
 
274
  Input
275
- request.body: json - level
276
  {
277
- "current_number": 10,
278
- "ordinal_number": 2,
279
- "times": 1
280
  }
281
 
282
- Output
283
- one of following strings with (numbers differ):
284
- ... 1 2 3
285
- 1 2 3 ...
286
  """
287
  data_dict = await request.json()
288
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
289
- cur_num = message_data['current_number']
290
- ord_num = message_data['ordinal_number']
291
- times = message_data['times']
292
- return JSONResponse(questions.generate_number_sequence(cur_num, ord_num, times))
 
 
 
 
 
293
 
294
 
295
- @app.post("/level")
296
- async def get_next_level(request: Request):
297
- """Depending on current level and desire to level up/down return next level
298
 
299
  Input
300
- request.body: json - level
301
  {
302
- "current_level": "easy",
303
- "level_up": True
 
304
  }
305
 
306
  Output
307
- Literal - "easy", "medium" or "hard"
308
  """
309
  data_dict = await request.json()
310
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
311
- cur_level = message_data['current_level']
312
- level_up = message_data['level_up']
313
- return JSONResponse(utils.get_next_level(cur_level, level_up))
 
 
 
 
 
 
 
 
1
  """FastAPI endpoint
2
  To run locally use 'uvicorn app:app --host localhost --port 7860'
3
+ or
4
+ `python -m uvicorn app:app --reload --host localhost --port 7860`
5
  """
6
  import ast
7
+ import mathactive.microlessons.num_one as num_one_quiz
 
 
 
 
8
  from fastapi import FastAPI, Request
9
  from fastapi.responses import JSONResponse
10
  from fastapi.staticfiles import StaticFiles
 
144
  message_data = data_dict.get('message_data', '')
145
  nlu_response = evaluate_message_with_nlu(message_data)
146
  return JSONResponse(content=nlu_response)
147
+
148
+
149
+ @app.post("/num_one")
150
+ async def num_one(request: Request):
151
+ """
152
+ Input:
153
+ {
154
+ "user_id": 1,
155
+ "message_text": 5,
156
+ }
157
+ Output:
158
+ {
159
+ 'messages':
160
+ ["Let's", 'practice', 'counting', '', '', '46...', '47...', '48...', '49', '', '', 'After', '49,', 'what', 'is', 'the', 'next', 'number', 'you', 'will', 'count?\n46,', '47,', '48,', '49'],
161
+ 'input_prompt': '50',
162
+ 'state': 'question'
163
+ }
164
+ """
165
+ data_dict = await request.json()
166
+ message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
167
+ user_id = message_data['user_id']
168
+ message_text = message_data['message_text']
169
+ return num_one_quiz.process_user_message(user_id, message_text)
170
 
171
 
172
+ @app.post("/start")
173
  async def ask_math_question(request: Request):
174
+ """Generate a question data
175
 
176
  Input
 
177
  {
178
+ 'difficulty': 0.1,
179
+ 'do_increase': True | False
 
180
  }
181
 
182
  Output
 
183
  {
184
  'text': 'What is 1+2?',
185
+ 'difficulty': 0.2,
186
+ 'question_numbers': [3, 1, 4]
 
 
 
187
  }
188
  """
189
  data_dict = await request.json()
190
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
191
+ difficulty = message_data['difficulty']
192
+ do_increase = message_data['do_increase']
 
193
 
194
+ return JSONResponse(generators.start_interactive_math(difficulty, do_increase))
195
 
196
 
197
  @app.post("/hint")
198
  async def get_hint(request: Request):
199
+ """Generate a hint data
200
 
201
  Input
 
202
  {
203
+ 'start': 5,
204
+ 'step': 1,
205
+ 'difficulty': 0.1
 
 
 
206
  }
207
 
208
  Output
 
209
  {
210
+ 'text': 'What number is greater than 4 and less than 6?',
211
+ 'difficulty': 0.1,
212
+ 'question_numbers': [5, 1, 6]
 
 
 
 
213
  }
214
  """
215
  data_dict = await request.json()
216
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
217
+ start = message_data['start']
218
+ step = message_data['step']
219
+ difficulty = message_data['difficulty']
 
 
 
220
 
221
+ return JSONResponse(hints.generate_hint(start, step, difficulty))
222
 
223
 
224
+ @app.post("/question")
225
+ async def ask_math_question(request: Request):
226
+ """Generate a question data
227
 
228
  Input
 
229
  {
230
+ 'start': 5,
231
+ 'step': 1,
232
+ 'question_num': 1 # optional
233
  }
234
 
235
  Output
 
236
  {
237
+ 'question': 'What is 1+2?',
238
+ 'start': 5,
239
+ 'step': 1,
240
+ 'answer': 6
241
  }
242
  """
243
  data_dict = await request.json()
244
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
245
+ start = message_data['start']
246
+ step = message_data['step']
247
+ arg_tuple = (start, step)
248
+ try:
249
+ question_num = message_data['question_num']
250
+ arg_tuple += (question_num,)
251
+ except KeyError:
252
+ pass
253
 
254
+ return JSONResponse(questions.generate_question_data(*arg_tuple))
255
 
256
 
257
+ @app.post("/difficulty")
258
+ async def get_hint(request: Request):
259
+ """Generate a number matching difficulty
260
 
261
  Input
 
262
  {
263
+ 'difficulty': 0.01,
264
+ 'do_increase': True
265
  }
266
 
267
+ Output - value from 0.01 to 0.99 inclusively:
268
+ 0.09
 
 
 
 
 
269
  """
270
  data_dict = await request.json()
271
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
272
+ difficulty = message_data['difficulty']
273
+ do_increase = message_data['do_increase']
274
 
275
+ return JSONResponse(utils.get_next_difficulty(difficulty, do_increase))
276
 
277
+
278
+ @app.post("/start_step")
279
+ async def get_hint(request: Request):
280
+ """Generate a start and step values
281
 
282
  Input
 
283
  {
284
+ 'difficulty': 0.01,
285
+ 'path_to_csv_file': 'scripts/quiz/data.csv' # optional
 
286
  }
287
 
288
+ Output - tuple (start, step):
289
+ (5, 1)
 
 
290
  """
291
  data_dict = await request.json()
292
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
293
+ difficulty = message_data['difficulty']
294
+ arg_tuple = (difficulty,)
295
+ try:
296
+ path_to_csv_file = message_data['path_to_csv_file']
297
+ arg_tuple += (path_to_csv_file,)
298
+ except KeyError:
299
+ pass
300
+
301
+ return JSONResponse(utils.get_next_difficulty(*arg_tuple))
302
 
303
 
304
+ @app.post("/sequence")
305
+ async def generate_question(request: Request):
306
+ """Generate a sequence from start, step and optional separator parameter
307
 
308
  Input
 
309
  {
310
+ 'start': 5,
311
+ 'step': 1,
312
+ 'sep': ', ' # optional
313
  }
314
 
315
  Output
316
+ 5, 6, 7
317
  """
318
  data_dict = await request.json()
319
  message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
320
+ start = message_data['start']
321
+ step = message_data['step']
322
+ arg_tuple = (start, step)
323
+ try:
324
+ sep = message_data['sep']
325
+ arg_tuple += (sep,)
326
+ except KeyError:
327
+ pass
328
+
329
+ return JSONResponse(utils.convert_sequence_to_string(*arg_tuple))
mathtext_fastapi/conversation_manager.py CHANGED
@@ -14,8 +14,8 @@ from mathtext_fastapi.math_subtraction_fsm import MathSubtractionFSM
14
  from supabase import create_client
15
  from transitions import Machine
16
 
17
- from scripts.quiz.generators import start_interactive_math
18
- from scripts.quiz.hints import generate_hint
19
 
20
  load_dotenv()
21
 
 
14
  from supabase import create_client
15
  from transitions import Machine
16
 
17
+ from mathactive.generators import start_interactive_math
18
+ from mathactive.hints import generate_hint
19
 
20
  load_dotenv()
21
 
mathtext_fastapi/v2_conversation_manager.py CHANGED
@@ -16,8 +16,8 @@ from mathtext_fastapi.math_subtraction_fsm import MathSubtractionFSM
16
  from supabase import create_client
17
  from transitions import Machine
18
 
19
- from scripts.quiz.generators import start_interactive_math
20
- from scripts.quiz.hints import generate_hint
21
 
22
  load_dotenv()
23
 
 
16
  from supabase import create_client
17
  from transitions import Machine
18
 
19
+ from mathactive.generators import start_interactive_math
20
+ from mathactive.hints import generate_hint
21
 
22
  load_dotenv()
23
 
pyproject.toml CHANGED
@@ -20,14 +20,17 @@ classifiers = [
20
 
21
 
22
  [tool.poetry.dependencies]
 
23
  mathtext = {git = "https://gitlab.com/tangibleai/community/mathtext", rev = "main"}
24
- fastapi = "0.74.*"
25
  pydantic = "*"
26
- python = "^3.8,<3.10"
27
- requests = "2.27.*"
28
  sentencepiece = "0.1.*"
29
  supabase = "*"
30
  uvicorn = "0.17.*"
 
 
31
 
32
  [tool.poetry.group.dev.dependencies]
33
  pytest = "^7.2"
 
20
 
21
 
22
  [tool.poetry.dependencies]
23
+ mathactive = {git = "git@gitlab.com:tangibleai/community/mathactive.git", rev = "vlad"}
24
  mathtext = {git = "https://gitlab.com/tangibleai/community/mathtext", rev = "main"}
25
+ fastapi = "^0.90.0"
26
  pydantic = "*"
27
+ python = "^3.8"
28
+ requests = "2.27.*"
29
  sentencepiece = "0.1.*"
30
  supabase = "*"
31
  uvicorn = "0.17.*"
32
+ pandas = "^1.5.3"
33
+ scipy = "^1.10.1"
34
 
35
  [tool.poetry.group.dev.dependencies]
36
  pytest = "^7.2"
requirements.txt CHANGED
@@ -2,15 +2,24 @@ dill
2
  en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1-py3-none-any.whl
3
  fuzzywuzzy
4
  jsonpickle
5
- mathtext @ git+https://gitlab.com/tangibleai/community/mathtext@main
6
- fastapi==0.74.*
 
 
 
 
7
  openpyxl
8
- pydantic==1.10.*
9
  python-Levenshtein
10
- requests==2.27.*
11
- sentencepiece==0.1.*
12
  sentence-transformers
13
  supabase
14
  transitions
15
- uvicorn==0.17.*
 
 
16
 
 
 
 
 
 
 
 
2
  en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1-py3-none-any.whl
3
  fuzzywuzzy
4
  jsonpickle
5
+ mathtext
6
+ mathactive
7
+ fastapi
8
+ pydantic
9
+ requests
10
+ sentencepiece
11
  openpyxl
 
12
  python-Levenshtein
 
 
13
  sentence-transformers
14
  supabase
15
  transitions
16
+ uvicorn
17
+ pandas
18
+ scipy
19
 
20
+ # Deprecated
21
+ # mathtext @ git+https://gitlab.com/tangibleai/community/mathtext@main
22
+ # fastapi==0.74.*
23
+ # pydantic==1.10.*
24
+ # requests==2.27.*
25
+ # sentencepiece==0.1.*
scripts/make_request.py CHANGED
@@ -84,34 +84,35 @@ run_simulated_request('v2/manager', '5')
84
  # run_simulated_request('manager', '')
85
  # run_simulated_request('manager', 'add')
86
  # run_simulated_request('manager', 'subtract')
87
- # run_simulated_request("question", {
88
- # 'number_correct': 0,
89
- # 'number_incorrect': 0,
90
- # 'level': 'easy'
91
  # })
92
  # run_simulated_request("hint", {
93
- # 'question_numbers': [1, 2, 3],
94
- # 'right_answer': 3,
95
- # 'number_correct': 0,
96
- # 'number_incorrect': 0,
97
- # 'level': 'easy',
98
- # 'hints_used': 0
99
- # })
100
- # run_simulated_request("generate_question", {
101
- # 'level': 'medium'
102
  # })
103
- # run_simulated_request("numbers_by_level", {
104
- # 'level': 'medium'
 
 
105
  # })
106
- # run_simulated_request("number_sequence", {
107
- # "current_number": 10,
108
- # "ordinal_number": 2,
109
- # "times": 1
110
  # })
111
- # run_simulated_request("level", {
112
- # "current_level": "hard",
113
- # "level_up": False
 
 
 
 
 
114
  # })
 
115
  # run_simulated_request('manager', 'exit')
116
 
117
 
 
84
  # run_simulated_request('manager', '')
85
  # run_simulated_request('manager', 'add')
86
  # run_simulated_request('manager', 'subtract')
87
+
88
+ # run_simulated_request("start", {
89
+ # 'difficulty': 0.04,
90
+ # 'do_increase': True
91
  # })
92
  # run_simulated_request("hint", {
93
+ # 'start': 5,
94
+ # 'step': 1,
95
+ # 'difficulty': 0.56 # optional
 
 
 
 
 
 
96
  # })
97
+ # run_simulated_request("question", {
98
+ # 'start': 2,
99
+ # 'step': 1,
100
+ # 'question_num': 2 # optional
101
  # })
102
+ # run_simulated_request("difficulty", {
103
+ # 'difficulty': 0.01,
104
+ # 'do_increase': False # True | False
 
105
  # })
106
+ run_simulated_request("num_one", {
107
+ "user_id": "1",
108
+ "message_text": "61",
109
+ })
110
+ # run_simulated_request("sequence", {
111
+ # 'start': 2,
112
+ # 'step': 1,
113
+ # 'sep': '... '
114
  # })
115
+
116
  # run_simulated_request('manager', 'exit')
117
 
118
 
scripts/quiz/__init__.py ADDED
File without changes
scripts/quiz/data.csv ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ difficulty,start
2
+ 0.01,1
3
+ 0.02,0
4
+ 0.05,5
5
+ 0.07,10
6
+ 0.08,14
7
+ 0.1,20
8
+ 0.11,22
9
+ 0.13,27
10
+ 0.14,28
11
+ 0.16,30
12
+ 0.17,32
13
+ 0.18,34
14
+ 0.2,37
15
+ 0.21,39
16
+ 0.23,42
17
+ 0.25,43
18
+ 0.27,46
19
+ 0.3,50
20
+ 0.34,57
21
+ 0.35,64
22
+ 0.37,78
23
+ 0.39,89
24
+ 0.41,100
25
+ 0.44,112
26
+ 0.45,130
27
+ 0.48,147
28
+ 0.5,164
29
+ 0.52,180
30
+ 0.55,195
31
+ 0.58,209
32
+ 0.6,223
33
+ 0.61,236
34
+ 0.63,248
35
+ 0.64,259
36
+ 0.65,271
37
+ 0.67,284
38
+ 0.69,296
39
+ 0.7,308
40
+ 0.72,321
41
+ 0.73,333
42
+ 0.75,346
43
+ 0.78,359
44
+ 0.8,370
45
+ 0.81,385
46
+ 0.83,399
47
+ 0.84,408
48
+ 0.87,420
49
+ 0.88,435
50
+ 0.89,447
51
+ 0.9,458
52
+ 0.93,469
53
+ 0.94,483
54
+ 0.96,494
55
+ 0.97,500
56
+ 0.99,513
scripts/quiz/generators.py DELETED
@@ -1,33 +0,0 @@
1
- from .questions import generate_question_data
2
- from .utils import get_next_level
3
-
4
-
5
- def start_interactive_math(right_answers=0, wrong_answers=0, level="easy"):
6
- if wrong_answers > 2:
7
- wrong_answers = 0
8
- right_answers = 0
9
- level = get_next_level(level, False)
10
- elif right_answers > 2:
11
- right_answers = 0
12
- wrong_answers = 0
13
- level = get_next_level(level)
14
-
15
- question_data = generate_question_data(level)
16
- question = question_data['question']
17
- right_answer = question_data['answer']
18
- cur_num = question_data['current_number']
19
- ord_num = question_data['ordinal_number']
20
- times = question_data['times']
21
-
22
- numbers_group = [cur_num, ord_num, times]
23
- output = {
24
- "text": question,
25
- "question_numbers": numbers_group,
26
- "right_answer": right_answer,
27
- 'number_correct': right_answers,
28
- 'number_incorrect': wrong_answers,
29
- 'level': level,
30
- "hints_used": 0
31
- }
32
- return output
33
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/quiz/hints.py DELETED
@@ -1,32 +0,0 @@
1
- import random
2
-
3
-
4
- def generate_hint(question_nums, right_answer, right_answers, wrong_answers, level, hints_used):
5
- ord_num = question_nums[1] # ordinal number
6
- equation = right_answer - 2 * ord_num - 1
7
- min_num = equation if equation > 0 else 0
8
- seq_before = " ".join(
9
- [str(num) for num in range(right_answer - ord_num, min_num, -ord_num)][::-1]
10
- ) # sequence before right answer
11
- seq_after = " ".join(
12
- [str(num) for num in range(right_answer + ord_num, right_answer + 2 * ord_num + 1, ord_num)]
13
- ) # sequence after right answer
14
- hints = [
15
- f"What number will fill the gap in a sequence {seq_before} ... {seq_after}?",
16
- f"What number is {ord_num} in the account after {right_answer - ord_num}?",
17
- f"What number is {ord_num} in the account before {right_answer + ord_num}?",
18
- f"What number is greater than {right_answer - 1} and less than {right_answer + 1}?"
19
- ]
20
- rand_hint = random.choice(hints)
21
- hints_used += 1
22
-
23
- output = {
24
- "text": rand_hint,
25
- "question_numbers": question_nums,
26
- "right_answer": right_answer,
27
- 'number_correct': right_answers,
28
- 'number_incorrect': wrong_answers,
29
- 'level': level,
30
- "hints_used": hints_used
31
- }
32
- return output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/quiz/questions.py DELETED
@@ -1,116 +0,0 @@
1
- import random
2
- from typing import Literal
3
-
4
-
5
- def generate_question_data(level: Literal["easy", "medium", "hard"] = "easy"):
6
- """generate question, its numbers and proper answer"""
7
-
8
- nums = generate_numbers_by_level(level)
9
- cur_num = nums['current_number'] # current number
10
- ord_num = nums['ordinal_number'] # ordinal number
11
- seq_up_by_one = generate_number_sequence(cur_num, ord_num=1, times=1) # sequence with ord_num = 1, times = 1
12
-
13
- count_up_by_one_questions = [
14
- {
15
- "question": f"Let's practice counting. After {cur_num}, what number is next?\n{seq_up_by_one}",
16
- "current_number": cur_num,
17
- "ordinal_number": 1,
18
- "times": 1,
19
- "answer": cur_num + 1
20
- }
21
- ]
22
- seq_up_by_ord = generate_number_sequence(cur_num, ord_num, times=1) # sequence with times = 1
23
- count_up_by_ord_questions = [
24
- {
25
- "question": f"What number comes {ord_num} number after {cur_num}?\n{seq_up_by_ord}",
26
- "current_number": cur_num,
27
- "ordinal_number": ord_num,
28
- "times": 1,
29
- "answer": cur_num + ord_num
30
- },
31
- {
32
- "question": f"If we count up {ord_num} from {cur_num}, what number is next?\n{seq_up_by_ord}",
33
- "current_number": cur_num,
34
- "ordinal_number": ord_num,
35
- "times": 1,
36
- "answer": cur_num + ord_num
37
- }
38
- ]
39
- times = 1 if level == "easy" else nums['times']
40
- times_ord_seq = generate_number_sequence(cur_num, ord_num, times)
41
- times_ord_questions = [
42
- {
43
- "question": f"We're counting up by {times}s. What number is {ord_num} after {cur_num}?\n{times_ord_seq}",
44
- "current_number": cur_num,
45
- "ordinal_number": ord_num,
46
- "times": times,
47
- "answer": cur_num + ord_num * times
48
- }
49
- ]
50
- times_only_seq = generate_number_sequence(cur_num, 1, times) # sequence with ordinal number = 1
51
- times_only_questions = [
52
- {
53
- "question": f"Let's count up by {times}s. What number is next if we start from {cur_num}?\n{times_only_seq}",
54
- "current_number": cur_num,
55
- "ordinal_number": 1,
56
- "times": times,
57
- "answer": cur_num + times
58
- }
59
- ]
60
- questions = [*count_up_by_one_questions, *count_up_by_ord_questions, *times_only_questions, *times_ord_questions]
61
- random_choice = random.choice(questions)
62
- return random_choice
63
-
64
-
65
- def generate_numbers_by_level(level: Literal["easy", "medium", "hard"] = "easy"):
66
- """generate current number, ordinal number and times parameter
67
-
68
- returns
69
- dict with params:
70
- :param current_number: current number
71
- :param ordinal numebr: the number we count up by
72
- :param times: the number of times we count up by ordinal number"""
73
-
74
- if level == "easy":
75
- cur_num = random.randint(1, 8)
76
- ord_num = random.randint(1, 2)
77
- times = 1
78
- elif level == "medium":
79
- cur_num = random.randint(1, 94)
80
- ord_num = random.randint(1, 3)
81
- times = random.randint(1, 2)
82
- elif level == "hard":
83
- cur_num = random.randint(1, 488)
84
- ord_num = random.randint(1, 4)
85
- times = random.randint(1, 2)
86
-
87
- return {
88
- "current_number": cur_num,
89
- "ordinal_number": ord_num,
90
- "times": times
91
- }
92
-
93
-
94
- def generate_number_sequence(cur_num, ord_num, times=1):
95
- """generate one of 2 sequences. For example we want 55 to be a right answer, then sequences can be:
96
- 52 53 54 ...
97
- ... 56 57 58
98
-
99
- parameters
100
- :cur_num: current number
101
- :ord_num: ordinal number
102
- :times: times"""
103
- max_num = cur_num + times * ord_num
104
-
105
- seq_before = [str(num) for num in range(max_num - times, 0, -times)][:3][::-1]
106
- seq_after = [str(num) for num in range(max_num + times, max_num + 4 * times, times)]
107
- seq_before.append("...")
108
- seq_after.insert(0, "...")
109
-
110
- seqs = []
111
- if len(seq_before) == 4:
112
- seqs.append(seq_before)
113
- if len(seq_after) == 4:
114
- seqs.append(seq_after)
115
- rand_seq = " ".join(random.choice(seqs))
116
- return rand_seq
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/quiz/utils.py DELETED
@@ -1,13 +0,0 @@
1
- from typing import Literal
2
-
3
- def get_next_level(cur_level, levep_up: Literal[True, False] = True):
4
- if levep_up:
5
- if cur_level == "easy":
6
- return "medium"
7
- else:
8
- return "hard"
9
- else:
10
- if cur_level == "medium":
11
- return "easy"
12
- else:
13
- return "medium"