Skip to content

Commit cedf1aa

Browse files
committed
Format package sources for CI
1 parent 5efddfb commit cedf1aa

6 files changed

Lines changed: 133 additions & 63 deletions

File tree

‎src/carwatch/compliance.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,10 @@ def _clear_removed_wide_samples(
576576
return result
577577
removed_index = removed.reset_index()[["participant", "day", "scheduled_sample"]]
578578
if drop_entire_day:
579-
for participant, day in removed_index[["participant", "day"]].drop_duplicates().itertuples(
580-
index=False
579+
for participant, day in (
580+
removed_index[["participant", "day"]]
581+
.drop_duplicates()
582+
.itertuples(index=False)
581583
):
582584
columns = [column for column in result.columns if column[0] == day]
583585
_clear_wide_values(result, participant, columns)

‎src/carwatch/example_data.py‎

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ def _participants(n_participants: int, *, prefix: str) -> tuple[str, ...]:
257257
if not isinstance(prefix, str) or not prefix:
258258
raise ValueError("'participant_prefix' must be a non-empty string.")
259259
width = max(2, len(str(n_participants)))
260-
return tuple(f"{prefix}{number:0{width}d}" for number in range(1, n_participants + 1))
260+
return tuple(
261+
f"{prefix}{number:0{width}d}" for number in range(1, n_participants + 1)
262+
)
261263

262264

263265
def _config_value(
@@ -312,16 +314,16 @@ def _parse_study_config(
312314
"study_name",
313315
)
314316
participant_prefix = _as_non_empty_string(
315-
_config_value(
316-
config, "participant_prefix", "participantPrefix", default="VP_"
317-
),
317+
_config_value(config, "participant_prefix", "participantPrefix", default="VP_"),
318318
"participant_prefix",
319319
)
320320
sample_prefix = _as_non_empty_string(
321321
_config_value(config, "sample_prefix", "samplePrefix", default="S"),
322322
"sample_prefix",
323323
)
324-
start_sample = _config_value(config, "start_sample_from_zero", "startSampleFromZero")
324+
start_sample = _config_value(
325+
config, "start_sample_from_zero", "startSampleFromZero"
326+
)
325327
qr_start_sample = _config_value(config, "SS")
326328
if qr_start_sample is not None:
327329
qr_prefix, qr_zero = _parse_qr_start_sample(qr_start_sample)
@@ -363,16 +365,24 @@ def _parse_study_config(
363365
)
364366
has_evening_sample = _as_bool(
365367
_config_value(
366-
config, "has_evening_sample", "hasEveningSample", "E", default=bool(absolute_times)
368+
config,
369+
"has_evening_sample",
370+
"hasEveningSample",
371+
"E",
372+
default=bool(absolute_times),
367373
),
368374
"has_evening_sample",
369375
)
370376
check_duplicates = _as_bool(
371-
_config_value(config, "check_duplicates", "checkDuplicates", "FD", default=False),
377+
_config_value(
378+
config, "check_duplicates", "checkDuplicates", "FD", default=False
379+
),
372380
"check_duplicates",
373381
)
374382
enable_manual_scan = _as_bool(
375-
_config_value(config, "enable_manual_scan", "enableManualScan", "FM", default=False),
383+
_config_value(
384+
config, "enable_manual_scan", "enableManualScan", "FM", default=False
385+
),
376386
"enable_manual_scan",
377387
)
378388
configured_participants = _config_value(
@@ -455,7 +465,11 @@ def _decode_qr_config(payload: str) -> dict[str, Any]:
455465
except ValueError as error:
456466
raise ValueError(f"QR field {key!r} must be 0 or 1.") from error
457467
if "T" in decoded:
458-
decoded["T"] = [] if not decoded["T"] else [int(value) for value in decoded["T"].split(",")]
468+
decoded["T"] = (
469+
[]
470+
if not decoded["T"]
471+
else [int(value) for value in decoded["T"].split(",")]
472+
)
459473
if "A" in decoded:
460474
decoded["A"] = [] if not decoded["A"] else decoded["A"].split(",")
461475
return decoded
@@ -475,7 +489,9 @@ def _parse_registrations(
475489
"registration_date": "2026-01-04",
476490
}
477491
]
478-
if isinstance(registrations, (str, bytes)) or not isinstance(registrations, Sequence):
492+
if isinstance(registrations, (str, bytes)) or not isinstance(
493+
registrations, Sequence
494+
):
479495
raise TypeError("'registrations' must be a sequence of mappings.")
480496
if not registrations:
481497
raise ValueError("'registrations' must contain at least one registration.")
@@ -509,18 +525,32 @@ def _parse_registrations(
509525
_config_value(
510526
raw_registration,
511527
"registration_date",
512-
default=(pd.Timestamp("2026-01-04") + pd.Timedelta(days=7 * (number - 1))).date().isoformat(),
528+
default=(
529+
pd.Timestamp("2026-01-04") + pd.Timedelta(days=7 * (number - 1))
530+
)
531+
.date()
532+
.isoformat(),
513533
),
514534
f"registrations[{number - 1}].registration_date",
515535
)
516536
raw_dates = _config_value(raw_registration, "collection_dates", default=None)
517537
if raw_dates is None:
518538
start = pd.Timestamp(date) + pd.Timedelta(days=1)
519-
dates = tuple((start + pd.Timedelta(days=day)).date().isoformat() for day in range(study_days))
539+
dates = tuple(
540+
(start + pd.Timedelta(days=day)).date().isoformat()
541+
for day in range(study_days)
542+
)
520543
else:
521-
if isinstance(raw_dates, (str, bytes)) or not isinstance(raw_dates, Sequence):
522-
raise TypeError(f"registrations[{number - 1}].collection_dates must be a sequence.")
523-
dates = tuple(_as_date(value, f"registrations[{number - 1}].collection_dates") for value in raw_dates)
544+
if isinstance(raw_dates, (str, bytes)) or not isinstance(
545+
raw_dates, Sequence
546+
):
547+
raise TypeError(
548+
f"registrations[{number - 1}].collection_dates must be a sequence."
549+
)
550+
dates = tuple(
551+
_as_date(value, f"registrations[{number - 1}].collection_dates")
552+
for value in raw_dates
553+
)
524554
if len(dates) != study_days:
525555
raise ValueError(
526556
f"registrations[{number - 1}].collection_dates has {len(dates)} dates, "
@@ -531,18 +561,28 @@ def _parse_registrations(
531561
saliva_ids = None
532562
else:
533563
if isinstance(raw_ids, (str, bytes)) or not isinstance(raw_ids, Sequence):
534-
raise TypeError(f"registrations[{number - 1}].saliva_ids must be a sequence.")
535-
saliva_ids = tuple(_as_non_empty_string(value, "saliva_ids") for value in raw_ids)
536-
if len(saliva_ids) != sample_count or len(set(saliva_ids)) != len(saliva_ids):
564+
raise TypeError(
565+
f"registrations[{number - 1}].saliva_ids must be a sequence."
566+
)
567+
saliva_ids = tuple(
568+
_as_non_empty_string(value, "saliva_ids") for value in raw_ids
569+
)
570+
if len(saliva_ids) != sample_count or len(set(saliva_ids)) != len(
571+
saliva_ids
572+
):
537573
raise ValueError(
538574
f"registrations[{number - 1}].saliva_ids must contain {sample_count} unique identifiers."
539575
)
540576
condition = _config_value(raw_registration, "condition", default=None)
541577
if condition is not None:
542578
condition = _as_non_empty_string(condition, "condition")
543-
parsed.append(_Registration(name, study_days, date, dates, saliva_ids, condition))
579+
parsed.append(
580+
_Registration(name, study_days, date, dates, saliva_ids, condition)
581+
)
544582
if len({registration.study_name for registration in parsed}) != len(parsed):
545-
raise ValueError("Registration study_name values must be unique within a synthetic study.")
583+
raise ValueError(
584+
"Registration study_name values must be unique within a synthetic study."
585+
)
546586
return tuple(parsed)
547587

548588

@@ -644,7 +684,9 @@ def _as_absolute_schedule(value: Any) -> tuple[str, ...]:
644684
if isinstance(item, (int, np.integer)):
645685
item = f"{int(item):04d}"
646686
if not isinstance(item, str):
647-
raise TypeError("'saliva_alarm_times' entries must be strings or HHMM integers.")
687+
raise TypeError(
688+
"'saliva_alarm_times' entries must be strings or HHMM integers."
689+
)
648690
compact = item.replace(":", "")
649691
if len(compact) != 4 or not compact.isdigit():
650692
raise ValueError(f"Invalid fixed saliva alarm time: {item!r}.")
@@ -681,7 +723,12 @@ def _parse_qr_start_sample(value: Any) -> tuple[str, bool]:
681723

682724
def _filename_token(value: Any) -> str:
683725
text = _as_non_empty_string(value, "filename_token")
684-
return "".join(character if character.isalnum() else "-" for character in text).strip("-") or "study"
726+
return (
727+
"".join(character if character.isalnum() else "-" for character in text).strip(
728+
"-"
729+
)
730+
or "study"
731+
)
685732

686733

687734
def _timestamp(value: str) -> pd.Timestamp:
@@ -690,7 +737,9 @@ def _timestamp(value: str) -> pd.Timestamp:
690737

691738
def _event(value: pd.Timestamp, action: str, payload: dict[str, object]) -> str:
692739
milliseconds = int(value.timestamp() * 1_000)
693-
return f"{milliseconds};local;{action};{json.dumps(payload, separators=(',', ':'))}\n"
740+
return (
741+
f"{milliseconds};local;{action};{json.dumps(payload, separators=(',', ':'))}\n"
742+
)
694743

695744

696745
def _sample_ids(study: _SyntheticStudy, registration: _Registration) -> tuple[str, ...]:
@@ -793,14 +842,16 @@ def _deviation_minutes(
793842
tolerance = 15.0 if absolute else 5.0
794843
if absolute:
795844
lower, upper = (
796-
(tolerance + 2, tolerance + 20)
797-
if non_compliant
798-
else (0, tolerance - 1.0)
845+
(tolerance + 2, tolerance + 20) if non_compliant else (0, tolerance - 1.0)
799846
)
800847
magnitude = rng.uniform(lower, upper)
801848
return magnitude if rng.random() >= 0.5 else -magnitude
802849
if first_relative:
803-
return rng.uniform(tolerance + 2, tolerance + 20) if non_compliant else rng.uniform(0, tolerance - 1.0)
850+
return (
851+
rng.uniform(tolerance + 2, tolerance + 20)
852+
if non_compliant
853+
else rng.uniform(0, tolerance - 1.0)
854+
)
804855
if relative_interval is None:
805856
raise RuntimeError("Relative sample deviations require their interval.")
806857
lower, upper = (
@@ -875,7 +926,9 @@ def _write_study(
875926
participant_root = logs_root / participant
876927
participant_root.mkdir()
877928
canonical_day = 0
878-
for registration_number, configuration in enumerate(study.registrations, start=1):
929+
for registration_number, configuration in enumerate(
930+
study.registrations, start=1
931+
):
879932
metadata_timestamp = _timestamp(f"{configuration.registration_date} 18:00")
880933
metadata_file = participant_root / _source_filename(
881934
study, participant, metadata_timestamp
@@ -905,7 +958,9 @@ def _write_study(
905958
events: list[tuple[pd.Timestamp, str, dict[str, object]]] = []
906959
day_key = (participant, registration_number, registration_day)
907960
if day_key not in missing_awakenings:
908-
action = "alarm_stop" if rng.random() < 0.35 else "spontaneous_awakening"
961+
action = (
962+
"alarm_stop" if rng.random() < 0.35 else "spontaneous_awakening"
963+
)
909964
events.append((awakening, action, {"id": -1}))
910965
for position, (sample_id, sampling_time) in enumerate(
911966
zip(sample_ids, sample_times, strict=True), start=1
@@ -948,9 +1003,7 @@ def _write_study(
9481003
"date": collection_date,
9491004
"awakening_time": awakening.strftime("%H:%M"),
9501005
**{
951-
f"sampling_time_{position}": sampling_time.strftime(
952-
"%H:%M"
953-
)
1006+
f"sampling_time_{position}": sampling_time.strftime("%H:%M")
9541007
for position, sampling_time in enumerate(
9551008
sample_times, start=1
9561009
)
@@ -1003,9 +1056,7 @@ def _write_cortisol_data(
10031056
for participant in participants:
10041057
for position, multiplier in enumerate(profile, start=1):
10051058
condition_multiplier = (
1006-
1.12
1007-
if condition.casefold() == "challenge"
1008-
else 1.0
1059+
1.12 if condition.casefold() == "challenge" else 1.0
10091060
)
10101061
value = (
10111062
participant_baselines[participant]
@@ -1128,7 +1179,9 @@ def _write_issue_decisions(
11281179
create_cortisol_data: bool,
11291180
validate: bool,
11301181
) -> None:
1131-
folders = {participant: study_root / "logs" / participant for participant in participants}
1182+
folders = {
1183+
participant: study_root / "logs" / participant for participant in participants
1184+
}
11321185
raw_logs, source_audit = io.load_raw_logs_from_participant_folders(
11331186
folders, create_report=True
11341187
)
@@ -1177,7 +1230,9 @@ def _write_issue_decisions(
11771230
if final_report["issues"]["resolution_status"].eq("unresolved").any():
11781231
raise RuntimeError("Final conversion still contains unresolved issues.")
11791232
samples = logs.extract_sample_events_from_summary(final)
1180-
expected_samples = len(participants) * study.canonical_day_count * study.sample_count
1233+
expected_samples = (
1234+
len(participants) * study.canonical_day_count * study.sample_count
1235+
)
11811236
if len(samples) != expected_samples or samples["sampling_time"].isna().any():
11821237
raise RuntimeError("Final conversion does not contain every expected sample.")
11831238
actual_non_compliant = samples["sample_compliant"].eq(False).sum()
@@ -1189,10 +1244,10 @@ def _write_issue_decisions(
11891244
source_values = set(samples["sampling_time_source"].dropna())
11901245
if not source_values.issubset({"app", "manual_diary"}):
11911246
raise RuntimeError(f"Unexpected sampling-time sources: {source_values}.")
1192-
if samples["sampling_time_source"].eq("manual_diary").sum() != len(
1193-
missing_samples
1194-
):
1195-
raise RuntimeError("Manual diary patch count does not match missing raw samples.")
1247+
if samples["sampling_time_source"].eq("manual_diary").sum() != len(missing_samples):
1248+
raise RuntimeError(
1249+
"Manual diary patch count does not match missing raw samples."
1250+
)
11961251
if create_cortisol_data:
11971252
cortisol = pd.read_csv(study_root / "cortisol.csv").set_index(
11981253
["participant", "day", "sample_position", "condition"]

‎src/carwatch/io/_raw_logs.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def load_raw_logs_from_participant_folders(
242242
na_position="last",
243243
).reset_index(drop=True)
244244
audit["raw_event_count"] = pd.array(audit["raw_event_count"], dtype="Int64")
245-
#for column in _SOURCE_AUDIT_COLUMNS[:-1]:
245+
# for column in _SOURCE_AUDIT_COLUMNS[:-1]:
246246
# audit[column] = pd.array(audit[column], dtype="string")
247247
audit = audit[_SOURCE_AUDIT_COLUMNS]
248248
return result, audit

‎src/carwatch/io/_study_manager.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,10 +424,10 @@ def _validate_saved_study_results(data: pd.DataFrame) -> None:
424424
continue
425425
day_level = str(sample) == _DAY_SAMPLE
426426
sample_variable = str(variable) in {
427-
*_SAMPLE_VARIABLES,
428-
*_COMPLIANCE_SAMPLE_VARIABLES,
429-
"sampling_time_source",
430-
"sample_position",
427+
*_SAMPLE_VARIABLES,
428+
*_COMPLIANCE_SAMPLE_VARIABLES,
429+
"sampling_time_source",
430+
"sample_position",
431431
"day_expected",
432432
"day_scanned",
433433
}

‎src/carwatch/merge.py‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ def _merge_events_into_summary(
231231
) -> pd.DataFrame:
232232
"""Place merged long fields back into the canonical wide result schema."""
233233
events = merged.reset_index()
234-
missing_context = events[["participant", "day", "scheduled_sample"]].isna().any(
235-
axis=1
234+
missing_context = (
235+
events[["participant", "day", "scheduled_sample"]].isna().any(axis=1)
236236
)
237237
if missing_context.any():
238238
values = events.loc[
@@ -249,7 +249,10 @@ def _merge_events_into_summary(
249249
variable for variable in dict.fromkeys(added_variables) if variable in events
250250
]
251251
for variable in available:
252-
if variable in {"recorded_sample_in_schedule"} and events[variable].isna().all():
252+
if (
253+
variable in {"recorded_sample_in_schedule"}
254+
and events[variable].isna().all()
255+
):
253256
continue
254257
if _is_day_metadata(events, variable):
255258
_add_day_variable(result, events, variable)
@@ -278,7 +281,9 @@ def _is_day_metadata(events: pd.DataFrame, variable: str) -> bool:
278281
return bool(counts.le(1).all())
279282

280283

281-
def _add_day_variable(result: pd.DataFrame, events: pd.DataFrame, variable: str) -> None:
284+
def _add_day_variable(
285+
result: pd.DataFrame, events: pd.DataFrame, variable: str
286+
) -> None:
282287
values = (
283288
events[["participant", "day", variable]]
284289
.groupby(["participant", "day"], as_index=False, sort=False)[variable]
@@ -290,9 +295,11 @@ def _add_day_variable(result: pd.DataFrame, events: pd.DataFrame, variable: str)
290295
result.at[row["participant"], (row["day"], "day", variable)] = row[variable]
291296

292297

293-
def _add_sample_variable(result: pd.DataFrame, events: pd.DataFrame, variable: str) -> None:
294-
for day, sample in events[["day", "scheduled_sample"]].drop_duplicates().itertuples(
295-
index=False
298+
def _add_sample_variable(
299+
result: pd.DataFrame, events: pd.DataFrame, variable: str
300+
) -> None:
301+
for day, sample in (
302+
events[["day", "scheduled_sample"]].drop_duplicates().itertuples(index=False)
296303
):
297304
result[(day, sample, variable)] = pd.NA
298305
for row in events.to_dict(orient="records"):

0 commit comments

Comments
 (0)