timeback-powerpath 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/.gitignore +3 -0
  2. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/PKG-INFO +1 -1
  3. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/pyproject.toml +1 -1
  4. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/__init__.py +14 -0
  5. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/client.py +4 -0
  6. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/lib/testing.py +69 -0
  7. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/__init__.py +4 -0
  8. timeback_powerpath-0.2.1/src/timeback_powerpath/resources/render_config.py +84 -0
  9. timeback_powerpath-0.2.1/src/timeback_powerpath/resources/test_out.py +73 -0
  10. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/types/inputs.py +34 -0
  11. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/types/responses.py +54 -0
  12. timeback_powerpath-0.2.1/tests/fixtures/test_render_config.py +23 -0
  13. timeback_powerpath-0.2.1/tests/fixtures/test_test_out.py +23 -0
  14. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/README.md +0 -0
  15. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/constants.py +0 -0
  16. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/exceptions.py +0 -0
  17. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/lib/__init__.py +0 -0
  18. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/lib/transport.py +0 -0
  19. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/py.typed +0 -0
  20. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/assessment.py +0 -0
  21. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/lesson_plans.py +0 -0
  22. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/placement.py +0 -0
  23. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/screening.py +0 -0
  24. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/syllabus.py +0 -0
  25. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/resources/test_assignments.py +0 -0
  26. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/types/__init__.py +0 -0
  27. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/src/timeback_powerpath/types/base.py +0 -0
  28. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/fixtures/test_assessment.py +0 -0
  29. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/fixtures/test_lesson_plans.py +0 -0
  30. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/fixtures/test_placement.py +0 -0
  31. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/fixtures/test_screening.py +0 -0
  32. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/fixtures/test_syllabus.py +0 -0
  33. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/fixtures/test_test_assignments.py +0 -0
  34. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/native/test_client.py +0 -0
  35. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/native/test_models.py +0 -0
  36. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/native/test_pagination.py +0 -0
  37. {timeback_powerpath-0.2.0 → timeback_powerpath-0.2.1}/tests/native/test_validation.py +0 -0
@@ -1,3 +1,6 @@
1
+ # ai
2
+ .claude
3
+
1
4
  # Python
2
5
  __pycache__/
3
6
  *.py[cod]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timeback-powerpath
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Timeback PowerPath client for adaptive learning operations
5
5
  Project-URL: Homepage, https://developer.timeback.com
6
6
  Project-URL: Documentation, https://docs.timeback.com
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "timeback-powerpath"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Timeback PowerPath client for adaptive learning operations"
5
5
  readme = { file = "README.md", content-type = "text/markdown" }
6
6
  requires-python = ">=3.12"
@@ -82,6 +82,7 @@ from .types import (
82
82
  GetNextQuestionParams,
83
83
  GetNextQuestionResponse,
84
84
  GetSubjectProgressResponse,
85
+ GetTestOutEligibilityResponse,
85
86
  ImportExternalResultsResponse,
86
87
  ImportExternalTestAssignmentResultsParams,
87
88
  InternalTestCreateResponse,
@@ -100,6 +101,8 @@ from .types import (
100
101
  LessonPlanSyncResult,
101
102
  LessonPlanTarget,
102
103
  LessonPlanUpdateStudentItemResponseInput,
104
+ MakeExternalStudentTestOutAssignmentInput,
105
+ MakeExternalStudentTestOutAssignmentResponse,
103
106
  MakeExternalTestAssignmentInput,
104
107
  MakeExternalTestAssignmentResponse,
105
108
  MoveItemAfterOperation,
@@ -119,6 +122,10 @@ from .types import (
119
122
  QtiTestConfig,
120
123
  QuizLikeLessonType,
121
124
  RemainingQuestionsPerDifficulty,
125
+ RenderConfigDeleteResponse,
126
+ RenderConfigGetResponse,
127
+ RenderConfigUpsertInput,
128
+ RenderConfigUpsertResponse,
122
129
  ResetAttemptInput,
123
130
  ResetAttemptResponse,
124
131
  ResetPlacementResponse,
@@ -192,6 +199,7 @@ __all__ = [
192
199
  "GetNextQuestionParams",
193
200
  "GetNextQuestionResponse",
194
201
  "GetSubjectProgressResponse",
202
+ "GetTestOutEligibilityResponse",
195
203
  "ImportExternalResultsResponse",
196
204
  "ImportExternalTestAssignmentResultsParams",
197
205
  "InputValidationError",
@@ -211,6 +219,8 @@ __all__ = [
211
219
  "LessonPlanTarget",
212
220
  "LessonPlanUpdateStudentItemResponseInput",
213
221
  "LessonPlansCreateInput",
222
+ "MakeExternalStudentTestOutAssignmentInput",
223
+ "MakeExternalStudentTestOutAssignmentResponse",
214
224
  "MakeExternalTestAssignmentInput",
215
225
  "MakeExternalTestAssignmentResponse",
216
226
  "MoveItemAfterOperation",
@@ -234,6 +244,10 @@ __all__ = [
234
244
  "QuizLikeLessonType",
235
245
  "RateLimitError",
236
246
  "RemainingQuestionsPerDifficulty",
247
+ "RenderConfigDeleteResponse",
248
+ "RenderConfigGetResponse",
249
+ "RenderConfigUpsertInput",
250
+ "RenderConfigUpsertResponse",
237
251
  "ResetAttemptInput",
238
252
  "ResetAttemptResponse",
239
253
  "ResetPlacementResponse",
@@ -12,9 +12,11 @@ from .lib.transport import Transport
12
12
  from .resources.assessment import AssessmentResource
13
13
  from .resources.lesson_plans import LessonPlansResource
14
14
  from .resources.placement import PlacementResource
15
+ from .resources.render_config import RenderConfigResource
15
16
  from .resources.screening import ScreeningResource
16
17
  from .resources.syllabus import SyllabusResource
17
18
  from .resources.test_assignments import TestAssignmentsResource
19
+ from .resources.test_out import TestOutResource
18
20
 
19
21
  if TYPE_CHECKING:
20
22
  from timeback_common import AuthCheckResult, Environment, PowerPathPaths, TimebackProvider
@@ -171,9 +173,11 @@ class PowerPathClient:
171
173
  self.assessments = AssessmentResource(_transport)
172
174
  self.lesson_plans = LessonPlansResource(_transport)
173
175
  self.placement = PlacementResource(_transport)
176
+ self.render_config = RenderConfigResource(_transport)
174
177
  self.screening = ScreeningResource(_transport)
175
178
  self.syllabus = SyllabusResource(_transport)
176
179
  self.test_assignments = TestAssignmentsResource(_transport)
180
+ self.test_out = TestOutResource(_transport)
177
181
 
178
182
  def get_transport(self) -> Transport | PowerPathTransportLike:
179
183
  """
@@ -21,6 +21,7 @@ from ..types.responses import (
21
21
  GetNextPlacementTestResponse,
22
22
  GetNextQuestionResponse,
23
23
  GetSubjectProgressResponse,
24
+ GetTestOutEligibilityResponse,
24
25
  ImportExternalResultsResponse,
25
26
  InternalTestCreateResponse,
26
27
  LessonPlanCourseSyncResult,
@@ -30,8 +31,12 @@ from ..types.responses import (
30
31
  LessonPlanOperationsResponse,
31
32
  LessonPlanResponse,
32
33
  LessonPlanSyncResult,
34
+ MakeExternalStudentTestOutAssignmentResponse,
33
35
  MakeExternalTestAssignmentResponse,
34
36
  PowerPathTestQuestion,
37
+ RenderConfigDeleteResponse,
38
+ RenderConfigGetResponse,
39
+ RenderConfigUpsertResponse,
35
40
  ResetAttemptResponse,
36
41
  ResetPlacementResponse,
37
42
  ScreeningAssignTestResponse,
@@ -73,6 +78,8 @@ class PowerPathRecordingTransport(BaseRecordingTransport):
73
78
  _placement_get_stub,
74
79
  _lesson_plans_get_stub,
75
80
  _assessment_get_stub,
81
+ _render_config_get_stub,
82
+ _test_out_get_stub,
76
83
  ):
77
84
  response = resolver(path)
78
85
  if response is not None:
@@ -86,6 +93,7 @@ class PowerPathRecordingTransport(BaseRecordingTransport):
86
93
  _placement_post_stub,
87
94
  _lesson_plans_post_stub,
88
95
  _assessment_post_stub,
96
+ _test_out_post_stub,
89
97
  ):
90
98
  response = resolver(path)
91
99
  if response is not None:
@@ -96,12 +104,20 @@ class PowerPathRecordingTransport(BaseRecordingTransport):
96
104
  for resolver in (
97
105
  _assignments_put_stub,
98
106
  _assessment_put_stub,
107
+ _render_config_put_stub,
99
108
  ):
100
109
  response = resolver(path)
101
110
  if response is not None:
102
111
  return response
103
112
  return {}
104
113
 
114
+ def _default_delete_stub(self, path: str) -> dict[str, Any]:
115
+ for resolver in (_render_config_delete_stub,):
116
+ response = resolver(path)
117
+ if response is not None:
118
+ return response
119
+ return {}
120
+
105
121
 
106
122
  def _assessment_standard_progress_stub() -> StandardProgressResponse:
107
123
  return StandardProgressResponse.model_validate(
@@ -462,4 +478,57 @@ def _syllabus_get_stub(path: str) -> dict[str, Any] | None:
462
478
  return None
463
479
 
464
480
 
481
+ def _render_config_get_stub(path: str) -> dict[str, Any] | None:
482
+ if "/render-config/" in path:
483
+ return dump_model(
484
+ RenderConfigGetResponse.model_validate(
485
+ {
486
+ "courseId": "course-001",
487
+ "rendererId": "renderer-001",
488
+ "rendererUrl": "https://renderer.example.com",
489
+ "suppressFeedback": False,
490
+ "suppressCorrectResponse": False,
491
+ }
492
+ ),
493
+ exclude_none=False,
494
+ )
495
+ return None
496
+
497
+
498
+ def _render_config_put_stub(path: str) -> dict[str, Any] | None:
499
+ if "/render-config/" in path:
500
+ return dump_model(
501
+ RenderConfigUpsertResponse(updated=["course-001"]),
502
+ exclude_none=False,
503
+ )
504
+ return None
505
+
506
+
507
+ def _render_config_delete_stub(path: str) -> dict[str, Any] | None:
508
+ if "/render-config/" in path:
509
+ return dump_model(
510
+ RenderConfigDeleteResponse(deleted="course-001"),
511
+ exclude_none=False,
512
+ )
513
+ return None
514
+
515
+
516
+ def _test_out_get_stub(path: str) -> dict[str, Any] | None:
517
+ if "/test-out/getTestOutEligibility/" in path:
518
+ return dump_model(
519
+ GetTestOutEligibilityResponse(eligible=True, reason=None),
520
+ exclude_none=False,
521
+ )
522
+ return None
523
+
524
+
525
+ def _test_out_post_stub(path: str) -> dict[str, Any] | None:
526
+ if "/test-out/makeExternalStudentTestOutAssignment" in path:
527
+ return dump_model(
528
+ MakeExternalStudentTestOutAssignmentResponse(success=True, data=None),
529
+ exclude_none=False,
530
+ )
531
+ return None
532
+
533
+
465
534
  __all__ = ["PowerPathRecordingTransport", "create_recording_transport"]
@@ -7,15 +7,19 @@ Resource classes for PowerPath API operations.
7
7
  from .assessment import AssessmentResource
8
8
  from .lesson_plans import LessonPlansResource
9
9
  from .placement import PlacementResource
10
+ from .render_config import RenderConfigResource
10
11
  from .screening import ScreeningResource
11
12
  from .syllabus import SyllabusResource
12
13
  from .test_assignments import TestAssignmentsResource
14
+ from .test_out import TestOutResource
13
15
 
14
16
  __all__ = [
15
17
  "AssessmentResource",
16
18
  "LessonPlansResource",
17
19
  "PlacementResource",
20
+ "RenderConfigResource",
18
21
  "ScreeningResource",
19
22
  "SyllabusResource",
20
23
  "TestAssignmentsResource",
24
+ "TestOutResource",
21
25
  ]
@@ -0,0 +1,84 @@
1
+ """
2
+ Render Config Resource
3
+
4
+ PowerPath custom renderer configuration operations.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, Any
10
+ from urllib.parse import quote
11
+
12
+ from timeback_common import serialize_input, validate_non_empty_string, validate_with_schema
13
+
14
+ from ..types.inputs import RenderConfigUpsertInput
15
+ from ..types.responses import (
16
+ RenderConfigDeleteResponse,
17
+ RenderConfigGetResponse,
18
+ RenderConfigUpsertResponse,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from ..lib.transport import Transport
23
+
24
+
25
+ class RenderConfigResource:
26
+ """Render config resource for managing custom renderer configurations on courses."""
27
+
28
+ def __init__(self, transport: Transport) -> None:
29
+ self._transport = transport
30
+
31
+ @property
32
+ def _base_path(self) -> str:
33
+ return self._transport.paths.base
34
+
35
+ async def upsert(
36
+ self, input: RenderConfigUpsertInput | dict[str, Any]
37
+ ) -> RenderConfigUpsertResponse:
38
+ """
39
+ Upsert render config for one or more courses.
40
+
41
+ Args:
42
+ input: Render config data including course_ids, renderer_id, and renderer_url
43
+
44
+ Returns:
45
+ List of updated course IDs
46
+ """
47
+ validate_with_schema(RenderConfigUpsertInput, input, "render config upsert")
48
+ body = await self._transport.put(
49
+ f"{self._base_path}/render-config/",
50
+ serialize_input(input, RenderConfigUpsertInput),
51
+ )
52
+ return RenderConfigUpsertResponse.model_validate(body)
53
+
54
+ async def get(self, course_id: str) -> RenderConfigGetResponse:
55
+ """
56
+ Get render config for a course.
57
+
58
+ Args:
59
+ course_id: The course ID to look up
60
+
61
+ Returns:
62
+ The render config for the course
63
+ """
64
+ validate_non_empty_string(course_id, "course_id")
65
+ body = await self._transport.get(
66
+ f"{self._base_path}/render-config/{quote(course_id, safe='')}"
67
+ )
68
+ return RenderConfigGetResponse.model_validate(body)
69
+
70
+ async def delete(self, course_id: str) -> RenderConfigDeleteResponse:
71
+ """
72
+ Delete render config for a course.
73
+
74
+ Args:
75
+ course_id: The course ID whose render config to delete
76
+
77
+ Returns:
78
+ Confirmation with the deleted course ID
79
+ """
80
+ validate_non_empty_string(course_id, "course_id")
81
+ body = await self._transport.delete(
82
+ f"{self._base_path}/render-config/{quote(course_id, safe='')}"
83
+ )
84
+ return RenderConfigDeleteResponse.model_validate(body)
@@ -0,0 +1,73 @@
1
+ """
2
+ Test Out Resource
3
+
4
+ PowerPath self-elected test-out operations.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, Any
10
+ from urllib.parse import quote
11
+
12
+ from timeback_common import serialize_input, validate_non_empty_string, validate_with_schema
13
+
14
+ from ..types.inputs import MakeExternalStudentTestOutAssignmentInput
15
+ from ..types.responses import (
16
+ GetTestOutEligibilityResponse,
17
+ MakeExternalStudentTestOutAssignmentResponse,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from ..lib.transport import Transport
22
+
23
+
24
+ class TestOutResource:
25
+ """Test out resource for self-elected test-out eligibility and assignment creation."""
26
+
27
+ def __init__(self, transport: Transport) -> None:
28
+ self._transport = transport
29
+
30
+ @property
31
+ def _base_path(self) -> str:
32
+ return self._transport.paths.base
33
+
34
+ async def get_eligibility(self, student_id: str, subject: str) -> GetTestOutEligibilityResponse:
35
+ """
36
+ Check if a student is eligible to self-request a test-out for a subject.
37
+
38
+ Args:
39
+ student_id: The student's OneRoster sourcedId
40
+ subject: The subject to check eligibility for
41
+
42
+ Returns:
43
+ Eligibility status and optional reason
44
+ """
45
+ validate_non_empty_string(student_id, "student_id")
46
+ validate_non_empty_string(subject, "subject")
47
+ body = await self._transport.get(
48
+ f"{self._base_path}/test-out/getTestOutEligibility/{quote(student_id, safe='')}/{quote(subject, safe='')}"
49
+ )
50
+ return GetTestOutEligibilityResponse.model_validate(body)
51
+
52
+ async def create_assignment(
53
+ self, input: MakeExternalStudentTestOutAssignmentInput | dict[str, Any]
54
+ ) -> MakeExternalStudentTestOutAssignmentResponse:
55
+ """
56
+ Create a self-elected test-out assignment.
57
+
58
+ Args:
59
+ input: Assignment data including one_roster_sourced_id and subject
60
+
61
+ Returns:
62
+ Success status and optional data
63
+ """
64
+ validate_with_schema(
65
+ MakeExternalStudentTestOutAssignmentInput,
66
+ input,
67
+ "make external student test out assignment",
68
+ )
69
+ body = await self._transport.post(
70
+ f"{self._base_path}/test-out/makeExternalStudentTestOutAssignment",
71
+ serialize_input(input, MakeExternalStudentTestOutAssignmentInput),
72
+ )
73
+ return MakeExternalStudentTestOutAssignmentResponse.model_validate(body)
@@ -511,6 +511,38 @@ class PlacementResetUserPlacementInput(BaseModel):
511
511
  model_config = {"populate_by_name": True}
512
512
 
513
513
 
514
+ # ═══════════════════════════════════════════════════════════════════════════════
515
+ # SELF-ELECTED TEST-OUT INPUTS
516
+ # ═══════════════════════════════════════════════════════════════════════════════
517
+
518
+
519
+ class MakeExternalStudentTestOutAssignmentInput(BaseModel):
520
+ """Input for creating a self-elected test-out assignment."""
521
+
522
+ one_roster_sourced_id: NonEmptyString = Field(alias="oneRosterSourcedId")
523
+ subject: NonEmptyString
524
+
525
+ model_config = {"populate_by_name": True}
526
+
527
+
528
+ # ═══════════════════════════════════════════════════════════════════════════════
529
+ # RENDER CONFIG INPUTS
530
+ # ═══════════════════════════════════════════════════════════════════════════════
531
+
532
+
533
+ class RenderConfigUpsertInput(BaseModel):
534
+ """Input for upserting render config for one or more courses."""
535
+
536
+ course_ids: list[NonEmptyString] = Field(alias="courseIds", min_length=1)
537
+ renderer_id: NonEmptyString = Field(alias="rendererId")
538
+ renderer_url: HttpUrl = Field(alias="rendererUrl")
539
+ renderer_version: NonEmptyString | None = Field(default=None, alias="rendererVersion")
540
+ suppress_feedback: bool | None = Field(default=None, alias="suppressFeedback")
541
+ suppress_correct_response: bool | None = Field(default=None, alias="suppressCorrectResponse")
542
+
543
+ model_config = {"populate_by_name": True}
544
+
545
+
514
546
  # ═══════════════════════════════════════════════════════════════════════════════
515
547
  # SCREENING INPUTS
516
548
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -567,6 +599,7 @@ __all__ = [
567
599
  "LessonPlanTarget",
568
600
  "LessonPlanUpdateStudentItemResponseInput",
569
601
  "LessonPlansCreateInput",
602
+ "MakeExternalStudentTestOutAssignmentInput",
570
603
  "MakeExternalTestAssignmentInput",
571
604
  "MoveItemAfterOperation",
572
605
  "MoveItemBeforeOperation",
@@ -575,6 +608,7 @@ __all__ = [
575
608
  "PlacementQueryParams",
576
609
  "PlacementResetUserPlacementInput",
577
610
  "QtiTestConfig",
611
+ "RenderConfigUpsertInput",
578
612
  "ResetAttemptInput",
579
613
  "ScreeningAssignTestInput",
580
614
  "ScreeningResetSessionInput",
@@ -607,6 +607,55 @@ class UpdateStudentItemResponseResult(BaseModel):
607
607
  model_config = {"extra": "allow"}
608
608
 
609
609
 
610
+ # ═══════════════════════════════════════════════════════════════════════════════
611
+ # TEST OUT (self-elected)
612
+ # ═══════════════════════════════════════════════════════════════════════════════
613
+
614
+
615
+ class GetTestOutEligibilityResponse(BaseModel):
616
+ """Result from checking test-out eligibility for a student/subject."""
617
+
618
+ eligible: bool
619
+ reason: str | None = None
620
+
621
+
622
+ class MakeExternalStudentTestOutAssignmentResponse(BaseModel):
623
+ """Result from creating a self-elected test-out assignment."""
624
+
625
+ success: bool
626
+ data: dict[str, Any] | None = None
627
+
628
+
629
+ # ═══════════════════════════════════════════════════════════════════════════════
630
+ # RENDER CONFIG
631
+ # ═══════════════════════════════════════════════════════════════════════════════
632
+
633
+
634
+ class RenderConfigUpsertResponse(BaseModel):
635
+ """Result from upserting render configs for one or more courses."""
636
+
637
+ updated: list[str]
638
+
639
+
640
+ class RenderConfigGetResponse(BaseModel):
641
+ """Render config for a single course."""
642
+
643
+ course_id: str = Field(alias="courseId")
644
+ renderer_id: str = Field(alias="rendererId")
645
+ renderer_url: str = Field(alias="rendererUrl")
646
+ renderer_version: str | None = Field(default=None, alias="rendererVersion")
647
+ suppress_feedback: bool = Field(alias="suppressFeedback")
648
+ suppress_correct_response: bool = Field(alias="suppressCorrectResponse")
649
+
650
+ model_config = {"populate_by_name": True}
651
+
652
+
653
+ class RenderConfigDeleteResponse(BaseModel):
654
+ """Result from deleting a render config."""
655
+
656
+ deleted: str
657
+
658
+
610
659
  # ═══════════════════════════════════════════════════════════════════════════════
611
660
  # SYLLABUS
612
661
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -634,6 +683,7 @@ __all__ = [
634
683
  "GetNextPlacementTestResponse",
635
684
  "GetNextQuestionResponse",
636
685
  "GetSubjectProgressResponse",
686
+ "GetTestOutEligibilityResponse",
637
687
  "ImportExternalResultsResponse",
638
688
  "InternalTestCreateResponse",
639
689
  "LessonPlanCourseSyncResult",
@@ -643,6 +693,7 @@ __all__ = [
643
693
  "LessonPlanOperationsResponse",
644
694
  "LessonPlanResponse",
645
695
  "LessonPlanSyncResult",
696
+ "MakeExternalStudentTestOutAssignmentResponse",
646
697
  "MakeExternalTestAssignmentResponse",
647
698
  "PaginationMeta",
648
699
  "PowerPath100ProgressResponse",
@@ -651,6 +702,9 @@ __all__ = [
651
702
  "PowerPathQuestionResult",
652
703
  "PowerPathTestQuestion",
653
704
  "RemainingQuestionsPerDifficulty",
705
+ "RenderConfigDeleteResponse",
706
+ "RenderConfigGetResponse",
707
+ "RenderConfigUpsertResponse",
654
708
  "ResetAttemptResponse",
655
709
  "ResetPlacementResponse",
656
710
  "ResponseResult",
@@ -0,0 +1,23 @@
1
+ """Fixture-driven tests for PowerPath render config resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from test_support.fixtures import fixtures_dir, make_resource_fixture_test
6
+ from timeback_powerpath.lib.testing import create_recording_transport
7
+ from timeback_powerpath.resources.render_config import RenderConfigResource
8
+
9
+ FIXTURES_DIR = fixtures_dir("powerpath", "render-config")
10
+
11
+
12
+ def _setup(
13
+ fail_request: dict[str, object] | None, transport_config: dict[str, object] | None
14
+ ) -> dict[str, object]:
15
+ transport = create_recording_transport(fail_request, transport_config)
16
+ return {"resource": RenderConfigResource(transport), "calls": transport.calls}
17
+
18
+
19
+ test_powerpath_render_config_fixtures = make_resource_fixture_test(
20
+ name="powerpath > render-config",
21
+ fixtures_dir=FIXTURES_DIR,
22
+ setup=_setup,
23
+ )
@@ -0,0 +1,23 @@
1
+ """Fixture-driven tests for PowerPath test out resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from test_support.fixtures import fixtures_dir, make_resource_fixture_test
6
+ from timeback_powerpath.lib.testing import create_recording_transport
7
+ from timeback_powerpath.resources.test_out import TestOutResource as PowerPathTestOutResource
8
+
9
+ FIXTURES_DIR = fixtures_dir("powerpath", "test-out")
10
+
11
+
12
+ def _setup(
13
+ fail_request: dict[str, object] | None, transport_config: dict[str, object] | None
14
+ ) -> dict[str, object]:
15
+ transport = create_recording_transport(fail_request, transport_config)
16
+ return {"resource": PowerPathTestOutResource(transport), "calls": transport.calls}
17
+
18
+
19
+ test_powerpath_test_out_fixtures = make_resource_fixture_test(
20
+ name="powerpath > test-out",
21
+ fixtures_dir=FIXTURES_DIR,
22
+ setup=_setup,
23
+ )