Skip to content

Commit 2309593

Browse files
authored
79 task write trails CONFIG functions load_scaled_circles (#83)
* added trails_points_scaled.json into utils * load_scaled_circles will be in config because it will only be run once per run rather than per participant. The json file will remain in utils * wrote unit tests * ruff fixes * fixed indentation * moved required_fields out of loop, rename trail_screen to trail_task_number * rename point to point_dict * remove comments * reformat test * small changes * ruff fixes
1 parent c68e01e commit 2309593

3 files changed

Lines changed: 634 additions & 0 deletions

File tree

src/graphomotor/core/config.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
"""Configuration module for graphomotor."""
22

33
import dataclasses
4+
import json
45
import logging
6+
import os
57
import typing
68
from importlib import metadata
9+
from typing import Dict
710

811
import numpy as np
912

13+
from graphomotor.core import models
14+
1015

1116
def get_version() -> str:
1217
"""Return graphomotor version."""
@@ -93,3 +98,76 @@ def add_custom_params(cls, config_dict: dict[str, float | int]) -> "SpiralConfig
9398
)
9499

95100
return cls(**filtered_params)
101+
102+
103+
def load_scaled_circles(filepath: str) -> Dict[str, Dict[str, models.CircleTarget]]:
104+
"""Load circle configurations from trails_points_scaled.json.
105+
106+
This function reads a JSON file containing circle target definitions for various
107+
trail types and constructs CircleTarget instances for each defined circle.
108+
This will be configured only once per run.
109+
110+
Args:
111+
filepath: Path to the JSON file containing circle configurations.
112+
113+
Returns:
114+
A dictionary mapping each trail type to dictionaries of CircleTarget instances.
115+
116+
Raises:
117+
FileNotFoundError: If the specified filepath does not exist.
118+
json.JSONDecodeError: If the file contains invalid JSON.
119+
KeyError: If required fields (x, y, label, radius) are missing from circle data.
120+
TypeError: If trails_data is not a dictionary or trail_points is not a list.
121+
"""
122+
if not os.path.exists(filepath):
123+
raise FileNotFoundError(
124+
f"The path '{filepath}' does not exist. "
125+
"Confirm that all necessary files are in place."
126+
)
127+
128+
with open(filepath, "r") as f:
129+
trails_data = json.load(f)
130+
131+
if not isinstance(trails_data, dict):
132+
raise TypeError(
133+
f"Expected trails_data to be a dictionary, got {type(trails_data).__name__}"
134+
)
135+
136+
circles = {}
137+
required_fields = ["x", "y", "label", "radius"]
138+
for trail_task_number, trail_points in trails_data.items():
139+
if not isinstance(trail_points, list):
140+
raise TypeError(
141+
f"Expected trail_points for '{trail_task_number}' to be a list, "
142+
f"got {type(trail_points).__name__}"
143+
)
144+
145+
trail_circles = {}
146+
for idx, point_dict in enumerate(trail_points):
147+
if not isinstance(point_dict, dict):
148+
raise TypeError(
149+
f"Expected point at index {idx} in '{trail_task_number}' to be a "
150+
f"dictionary, got {type(point_dict).__name__}"
151+
)
152+
153+
missing_fields = [
154+
field for field in required_fields if field not in point_dict
155+
]
156+
if missing_fields:
157+
raise KeyError(
158+
f"Missing required field(s) {missing_fields} at index {idx} "
159+
f"of trail '{trail_task_number}'"
160+
)
161+
162+
order = idx + 1
163+
circle = models.CircleTarget(
164+
order=order,
165+
center_x=point_dict["x"],
166+
center_y=point_dict["y"],
167+
label=str(point_dict["label"]),
168+
radius=point_dict["radius"],
169+
)
170+
trail_circles[circle.label] = circle
171+
172+
circles[trail_task_number] = trail_circles
173+
return circles

0 commit comments

Comments
 (0)