Spaces:
Build error
Build error
Merge branch 'vlad' into 'feature-wormhole'
Browse filesData-driven quiz
See merge request tangibleai/community/mathtext-fastapi!12
- app.py +109 -93
- mathtext_fastapi/conversation_manager.py +2 -2
- mathtext_fastapi/v2_conversation_manager.py +2 -2
- pyproject.toml +6 -3
- requirements.txt +15 -6
- scripts/make_request.py +23 -22
- scripts/quiz/__init__.py +0 -0
- scripts/quiz/data.csv +56 -0
- scripts/quiz/generators.py +0 -33
- scripts/quiz/hints.py +0 -32
- scripts/quiz/questions.py +0 -116
- scripts/quiz/utils.py +0 -13
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
|
| 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("/
|
| 152 |
async def ask_math_question(request: Request):
|
| 153 |
-
"""Generate a question
|
| 154 |
|
| 155 |
Input
|
| 156 |
-
request.body: json - amount of correct and incorrect answers in the account
|
| 157 |
{
|
| 158 |
-
'
|
| 159 |
-
'
|
| 160 |
-
'level': 'easy'
|
| 161 |
}
|
| 162 |
|
| 163 |
Output
|
| 164 |
-
context: dict - the information for the current state
|
| 165 |
{
|
| 166 |
'text': 'What is 1+2?',
|
| 167 |
-
'
|
| 168 |
-
'
|
| 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 |
-
|
| 177 |
-
|
| 178 |
-
level = message_data['level']
|
| 179 |
|
| 180 |
-
return JSONResponse(generators.start_interactive_math(
|
| 181 |
|
| 182 |
|
| 183 |
@app.post("/hint")
|
| 184 |
async def get_hint(request: Request):
|
| 185 |
-
"""Generate a hint
|
| 186 |
|
| 187 |
Input
|
| 188 |
-
request.body:
|
| 189 |
{
|
| 190 |
-
'
|
| 191 |
-
'
|
| 192 |
-
'
|
| 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
|
| 202 |
-
'
|
| 203 |
-
'
|
| 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 |
-
|
| 213 |
-
|
| 214 |
-
|
| 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(
|
| 220 |
|
| 221 |
|
| 222 |
-
@app.post("/
|
| 223 |
-
async def
|
| 224 |
-
"""Generate a
|
| 225 |
|
| 226 |
Input
|
| 227 |
-
request.body: json - level
|
| 228 |
{
|
| 229 |
-
'
|
|
|
|
|
|
|
| 230 |
}
|
| 231 |
|
| 232 |
Output
|
| 233 |
-
context: dict - the information for the current state
|
| 234 |
{
|
| 235 |
-
|
| 236 |
-
|
|
|
|
|
|
|
| 237 |
}
|
| 238 |
"""
|
| 239 |
data_dict = await request.json()
|
| 240 |
message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
|
| 241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
|
| 243 |
-
return JSONResponse(questions.generate_question_data(
|
| 244 |
|
| 245 |
|
| 246 |
-
@app.post("/
|
| 247 |
-
async def
|
| 248 |
-
"""Generate
|
| 249 |
|
| 250 |
Input
|
| 251 |
-
request.body: json - level
|
| 252 |
{
|
| 253 |
-
'
|
|
|
|
| 254 |
}
|
| 255 |
|
| 256 |
-
Output
|
| 257 |
-
|
| 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 |
-
|
| 267 |
-
|
| 268 |
|
|
|
|
| 269 |
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
|
|
|
| 273 |
|
| 274 |
Input
|
| 275 |
-
request.body: json - level
|
| 276 |
{
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
"times": 1
|
| 280 |
}
|
| 281 |
|
| 282 |
-
Output
|
| 283 |
-
|
| 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 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
|
| 294 |
|
| 295 |
-
@app.post("/
|
| 296 |
-
async def
|
| 297 |
-
"""
|
| 298 |
|
| 299 |
Input
|
| 300 |
-
request.body: json - level
|
| 301 |
{
|
| 302 |
-
|
| 303 |
-
|
|
|
|
| 304 |
}
|
| 305 |
|
| 306 |
Output
|
| 307 |
-
|
| 308 |
"""
|
| 309 |
data_dict = await request.json()
|
| 310 |
message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 18 |
-
from
|
| 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
|
| 20 |
-
from
|
| 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.
|
| 25 |
pydantic = "*"
|
| 26 |
-
python = "^3.8
|
| 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 = "[email protected]: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
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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 |
-
|
| 88 |
-
#
|
| 89 |
-
# '
|
| 90 |
-
# '
|
| 91 |
# })
|
| 92 |
# run_simulated_request("hint", {
|
| 93 |
-
# '
|
| 94 |
-
# '
|
| 95 |
-
# '
|
| 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("
|
| 104 |
-
# '
|
|
|
|
|
|
|
| 105 |
# })
|
| 106 |
-
# run_simulated_request("
|
| 107 |
-
#
|
| 108 |
-
#
|
| 109 |
-
# "times": 1
|
| 110 |
# })
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|