Skip to content

Commit caa14c6

Browse files
committed
Refactor Paul’s changes
Create a practice component to hold common code for the practice feature
1 parent f08c3a4 commit caa14c6

File tree

7 files changed

+143
-94
lines changed

7 files changed

+143
-94
lines changed

bases/rsptx/book_server_api/routers/rslogging.py

+32-54
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
TimezoneRequest,
7575
)
7676
from rsptx.auth.session import auth_manager
77+
from rsptx.practice.core import potentially_change_flashcard
7778

7879
# Routing
7980
# =======
@@ -360,76 +361,53 @@ async def updatelastpage(
360361
tz_offset = float(values.get("tz_offset", 0))
361362
else:
362363
tz_offset = 0
363-
364+
364365
if practice_settings:
365366
if request_data.markingComplete:
366367
if practice_settings.flashcard_creation_method == 0:
367368
# self-paced flashcard creation based on marking a page as complete
368-
rslogger.debug(f"self-paced flashcard creation based on marking a page as complete\n{request_data=}")
369+
rslogger.debug(
370+
f"self-paced flashcard creation based on marking a page as complete\n{request_data=}"
371+
)
369372
course = await fetch_course(user.course_name)
370-
await potentially_change_flashcard(course.base_course, lpd["last_page_chapter"], lpd["last_page_subchapter"], user, tz_offset, add=True)
373+
await potentially_change_flashcard(
374+
course.base_course,
375+
lpd["last_page_chapter"],
376+
lpd["last_page_subchapter"],
377+
user,
378+
tz_offset,
379+
add=True,
380+
)
371381

372382
elif request_data.markingIncomplete:
373383
if practice_settings.flashcard_creation_method == 0:
374384
course = await fetch_course(user.course_name)
375-
rslogger.debug(f"self-paced flashcard deletion based on marking a page as incomplete\n{request_data=}")
376-
await potentially_change_flashcard(course.base_course, lpd["last_page_chapter"], lpd["last_page_subchapter"], user, tz_offset, remove=True)
385+
rslogger.debug(
386+
f"self-paced flashcard deletion based on marking a page as incomplete\n{request_data=}"
387+
)
388+
await potentially_change_flashcard(
389+
course.base_course,
390+
lpd["last_page_chapter"],
391+
lpd["last_page_subchapter"],
392+
user,
393+
tz_offset,
394+
remove=True,
395+
)
377396
elif request_data.pageLoad and practice_settings.flashcard_creation_method == 3:
378397
# self-paced flashcard creation based on loading a page
379398
course = await fetch_course(user.course_name)
380-
await potentially_change_flashcard(course.base_course, lpd["last_page_chapter"], lpd["last_page_subchapter"], user, tz_offset, add=True)
399+
await potentially_change_flashcard(
400+
course.base_course,
401+
lpd["last_page_chapter"],
402+
lpd["last_page_subchapter"],
403+
user,
404+
tz_offset,
405+
add=True,
406+
)
381407

382408
return make_json_response(detail="Success")
383409

384410

385-
async def potentially_change_flashcard(
386-
base_course_name:str,
387-
chapter,
388-
subcchapter,
389-
user: AuthUserValidator,
390-
tz_offset: float,
391-
add=False,
392-
remove=False,
393-
) -> None:
394-
395-
# check if already have a card for this subchapter
396-
existing_flashcard = await fetch_one_user_topic_practice(
397-
user,
398-
chapter,
399-
subcchapter
400-
)
401-
402-
if add:
403-
if not existing_flashcard:
404-
# See if this subchapter has any questions marked for use in the practice tool.
405-
questions = await fetch_qualified_questions(
406-
base_course_name, chapter, subcchapter
407-
)
408-
if len(questions) > 0: # There is at least one qualified question in this subchapter
409-
rslogger.debug(f"Adding flashcard for {chapter=}, {subcchapter=}, {questions[0].name=}")
410-
now = datetime.utcnow()
411-
now_local = now - timedelta(hours=tz_offset)
412-
await create_user_topic_practice(
413-
user,
414-
chapter,
415-
subcchapter,
416-
questions[0].name,
417-
now_local,
418-
now,
419-
tz_offset,
420-
)
421-
else:
422-
rslogger.debug("no questions found for this subchapter")
423-
else:
424-
rslogger.debug("already have a flashcard for this subchapter")
425-
elif remove:
426-
if existing_flashcard:
427-
rslogger.debug(f"Removing flashcard for {chapter=}, {subcchapter=}, {existing_flashcard.question_name=}")
428-
await delete_one_user_topic_practice(existing_flashcard.id)
429-
else:
430-
rslogger.debug("no flashcard found to delete for this subchapter")
431-
432-
433411
# _getCompletionStatus
434412
# --------------------
435413
@router.get("/getCompletionStatus")

components/rsptx/practice/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from rsptx.practice import core
2+
3+
__all__ = ["core"]
4+

components/rsptx/practice/core.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from rsptx.db.crud import (
2+
fetch_one_user_topic_practice,
3+
create_user_topic_practice,
4+
fetch_qualified_questions,
5+
delete_one_user_topic_practice,
6+
)
7+
8+
from rsptx.db.models import AuthUserValidator
9+
from rsptx.logging import rslogger
10+
from datetime import datetime, timedelta
11+
12+
13+
async def potentially_change_flashcard(
14+
base_course_name: str,
15+
chapter,
16+
subcchapter,
17+
user: AuthUserValidator,
18+
tz_offset: float,
19+
add=False,
20+
remove=False,
21+
) -> None:
22+
23+
# check if already have a card for this subchapter
24+
existing_flashcard = await fetch_one_user_topic_practice(user, chapter, subcchapter)
25+
26+
if add:
27+
if not existing_flashcard:
28+
# See if this subchapter has any questions marked for use in the practice tool.
29+
questions = await fetch_qualified_questions(
30+
base_course_name, chapter, subcchapter
31+
)
32+
if (
33+
len(questions) > 0
34+
): # There is at least one qualified question in this subchapter
35+
rslogger.debug(
36+
f"Adding flashcard for {chapter=}, {subcchapter=}, {questions[0].name=}"
37+
)
38+
now = datetime.utcnow()
39+
now_local = now - timedelta(hours=tz_offset)
40+
await create_user_topic_practice(
41+
user,
42+
chapter,
43+
subcchapter,
44+
questions[0].name,
45+
now_local,
46+
now,
47+
tz_offset,
48+
)
49+
else:
50+
rslogger.debug("no questions found for this subchapter")
51+
else:
52+
rslogger.debug("already have a flashcard for this subchapter")
53+
elif remove:
54+
if existing_flashcard:
55+
rslogger.debug(
56+
f"Removing flashcard for {chapter=}, {subcchapter=}, {existing_flashcard.question_name=}"
57+
)
58+
await delete_one_user_topic_practice(existing_flashcard.id)
59+
else:
60+
rslogger.debug("no flashcard found to delete for this subchapter")

projects/book_server/pyproject.toml

+15-14
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,30 @@ authors = ['Brad Miller <[email protected]>']
66
license = ""
77

88
packages = [
9-
{include = "rsptx/auth", from = "../../components"},
10-
{include = "rsptx/logging", from = "../../components"},
11-
{include = "rsptx/db", from = "../../components"},
12-
{include = "rsptx/validation", from = "../../components"},
13-
{include = "rsptx/configuration", from = "../../components"},
14-
{include = "rsptx/response_helpers", from = "../../components"},
15-
{include = "rsptx/lp_sim_builder", from = "../../components"},
16-
{include = "rsptx/templates", from = "../../components"},
17-
{include = "rsptx/exceptions", from = "../../components"},
18-
{include = "rsptx/book_server_api", from = "../../bases"},
9+
{ include = "rsptx/auth", from = "../../components" },
10+
{ include = "rsptx/logging", from = "../../components" },
11+
{ include = "rsptx/db", from = "../../components" },
12+
{ include = "rsptx/validation", from = "../../components" },
13+
{ include = "rsptx/configuration", from = "../../components" },
14+
{ include = "rsptx/response_helpers", from = "../../components" },
15+
{ include = "rsptx/lp_sim_builder", from = "../../components" },
16+
{ include = "rsptx/templates", from = "../../components" },
17+
{ include = "rsptx/exceptions", from = "../../components" },
18+
{ include = "rsptx/book_server_api", from = "../../bases" },
19+
{ include = "rsptx/practice", from = "../../components" },
1920
]
2021

2122

2223
[tool.poetry.dependencies]
2324
python = "^3.10"
2425
aioredis = "^2.0.0"
2526
async-timeout = "^3.0.0"
26-
celery = {extras = ["redis"], version = "^5.0.0"}
27+
celery = { extras = ["redis"], version = "^5.0.0" }
2728
fastapi = "0.95.2"
2829
# Per the `uvicorn docs <https://www.uvicorn.org/#quickstart>`_, install the standard (as opposed to minimal) uvicorn dependencies.
29-
uvicorn = {extras = ["standard"], version = "^0.17.0"}
30+
uvicorn = { extras = ["standard"], version = "^0.17.0" }
3031
# See the `poetry docs <https://python-poetry.org/docs/dependency-specification/#using-environment-markers>`_.
31-
gunicorn = {version = "^20.0.0", markers = "sys.platform != 'win32'"}
32+
gunicorn = { version = "^20.0.0", markers = "sys.platform != 'win32'" }
3233
Jinja2 = "<3.1.0"
3334
aiofiles = "^0.8.0"
3435
alembic = "^1.0.0"
@@ -68,7 +69,7 @@ pytest-env = "^0.6.0"
6869
psycopg2-binary = "^2.0.0"
6970
# This is used by VSCode for Python refactoring
7071
rope = "^0.21.0"
71-
runestone = {path = "../interactives", develop=true}
72+
runestone = { path = "../interactives", develop = true }
7273

7374
# Scripts
7475
# =======

pyproject.toml

+27-26
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,29 @@ readme = "README.rst"
1313

1414
packages = [
1515
# These are the components for the author server
16-
{include = "development"},
17-
{include = "rsptx/data_extract", from = "components"},
18-
{include = "rsptx/visualization", from = "components"},
19-
{include = "rsptx/db", from = "components"},
20-
{include = "rsptx/forms", from = "components"},
21-
{include = "rsptx/auth", from = "components"},
22-
{include = "rsptx/logging", from = "components"},
23-
{include = "rsptx/response_helpers", from = "components"},
24-
{include = "rsptx/validation", from = "components"},
25-
{include = "rsptx/templates", from = "components"},
26-
{include = "rsptx/cl_utils", from = "components"},
27-
{include = "runestone", from = "bases/rsptx/interactives"},
28-
{include = "rsptx/author_server_api", from = "bases"},
29-
{include = "rsptx/book_server_api", from = "bases"},
30-
{include = "rsptx/web2py_server", from = "bases"},
31-
{include = "rsptx/rsmanage", from = "bases"},
32-
{include = "rsptx/assignment_server_api",from = "bases"},
33-
{include = "rsptx/dash_server_api",from = "bases"},
34-
{include = "rsptx/lp_sim_builder",from = "components"},
35-
{include = "rsptx/configuration",from = "components"},
36-
{include = "rsptx/exceptions",from = "components"},
37-
{include = "rsptx/build_tools",from = "components"},
16+
{ include = "development" },
17+
{ include = "rsptx/data_extract", from = "components" },
18+
{ include = "rsptx/visualization", from = "components" },
19+
{ include = "rsptx/db", from = "components" },
20+
{ include = "rsptx/forms", from = "components" },
21+
{ include = "rsptx/auth", from = "components" },
22+
{ include = "rsptx/logging", from = "components" },
23+
{ include = "rsptx/response_helpers", from = "components" },
24+
{ include = "rsptx/validation", from = "components" },
25+
{ include = "rsptx/templates", from = "components" },
26+
{ include = "rsptx/cl_utils", from = "components" },
27+
{ include = "runestone", from = "bases/rsptx/interactives" },
28+
{ include = "rsptx/author_server_api", from = "bases" },
29+
{ include = "rsptx/book_server_api", from = "bases" },
30+
{ include = "rsptx/web2py_server", from = "bases" },
31+
{ include = "rsptx/rsmanage", from = "bases" },
32+
{ include = "rsptx/assignment_server_api", from = "bases" },
33+
{ include = "rsptx/dash_server_api", from = "bases" },
34+
{ include = "rsptx/lp_sim_builder", from = "components" },
35+
{ include = "rsptx/configuration", from = "components" },
36+
{ include = "rsptx/exceptions", from = "components" },
37+
{ include = "rsptx/build_tools", from = "components" },
38+
{ include = "rsptx/practice", from = "components" },
3839
]
3940

4041
[tool.poetry.dependencies]
@@ -52,7 +53,7 @@ botocore = "^1.29.87"
5253
celery = "^5.2.7"
5354
cryptography = "^3.0.0"
5455
cssselect = ">= 1.0"
55-
dash = {extras = ["celery", "diskcache"], version = "^2.7.0"}
56+
dash = { extras = ["celery", "diskcache"], version = "^2.7.0" }
5657
dash-bootstrap-components = "^1.2.1"
5758
diff-match-patch = ">= 20110725.1"
5859
fastapi = "^0.95.0"
@@ -111,8 +112,8 @@ pytest = "^7.0.0"
111112
pyvirtualdisplay = "^3.0.0"
112113
pywin32 = { version = ">= 301", markers = "sys.platform == 'win32'" }
113114
selenium = "^3.0.0"
114-
runestone = {path = "./projects/interactives", develop=true}
115-
rsmanage = {path = "./projects/rsmanage", develop=true}
115+
runestone = { path = "./projects/interactives", develop = true }
116+
rsmanage = { path = "./projects/rsmanage", develop = true }
116117
sphinx-click = "^4.4.0"
117118
fawltydeps = "^0.9.0"
118119
json2xml = "^3.21.0"
@@ -127,4 +128,4 @@ build-backend = "poetry.core.masonry.api"
127128
# -------------------
128129
# See the [docs](https://pycqa.github.io/isort/docs/configuration/black_compatibility.html).
129130
[tool.isort]
130-
profile = "black"
131+
profile = "black"

test/components/rsptx/practice/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from rsptx.practice import core
2+
3+
4+
def test_sample():
5+
assert core is not None

0 commit comments

Comments
 (0)