pearmut 0.2.6__py3-none-any.whl → 0.2.8__py3-none-any.whl

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.
pearmut/app.py CHANGED
@@ -30,7 +30,8 @@ app.add_middleware(
30
30
 
31
31
  tasks_data = {}
32
32
  progress_data = load_progress_data(
33
- warn="No progress.json found. Running, but no campaign will be available.")
33
+ warn="No progress.json found. Running, but no campaign will be available."
34
+ )
34
35
 
35
36
  # load all tasks into data_all
36
37
  for campaign_id in progress_data.keys():
@@ -60,30 +61,31 @@ async def _log_response(request: LogResponseRequest):
60
61
 
61
62
  # append response to the output log
62
63
  save_db_payload(
63
- campaign_id, request.payload | {"user_id": user_id, "item_i": item_i})
64
+ campaign_id, request.payload | {"user_id": user_id, "item_i": item_i}
65
+ )
64
66
 
65
67
  # if actions were submitted, we can log time data
66
68
  if "actions" in request.payload:
67
- times = [
68
- x["time"] for x in request.payload["actions"]
69
- ]
69
+ times = [x["time"] for x in request.payload["actions"]]
70
70
  if progress_data[campaign_id][user_id]["time_start"] is None:
71
71
  progress_data[campaign_id][user_id]["time_start"] = min(times)
72
72
  progress_data[campaign_id][user_id]["time_end"] = max(times)
73
- progress_data[campaign_id][user_id]["time"] += sum([
74
- min(b - a, 60)
75
- for a, b in zip(times, times[1:])
76
- ])
73
+ progress_data[campaign_id][user_id]["time"] += sum(
74
+ [min(b - a, 60) for a, b in zip(times, times[1:])]
75
+ )
77
76
 
78
77
  # Initialize validation_checks if it doesn't exist
79
78
  if "validations" in request.payload:
80
79
  if "validations" not in progress_data[campaign_id][user_id]:
81
80
  progress_data[campaign_id][user_id]["validations"] = {}
82
81
 
83
- progress_data[campaign_id][user_id]["validations"][request.item_i] = request.payload["validations"]
82
+ progress_data[campaign_id][user_id]["validations"][request.item_i] = (
83
+ request.payload["validations"]
84
+ )
84
85
 
85
- update_progress(campaign_id, user_id, tasks_data,
86
- progress_data, request.item_i, request.payload)
86
+ update_progress(
87
+ campaign_id, user_id, tasks_data, progress_data, request.item_i, request.payload
88
+ )
87
89
  save_progress_data(progress_data)
88
90
 
89
91
  return JSONResponse(content="ok", status_code=200)
@@ -149,13 +151,15 @@ async def _dashboard_data(request: DashboardDataRequest):
149
151
 
150
152
  if campaign_id not in progress_data:
151
153
  return JSONResponse(content="Unknown campaign ID", status_code=400)
152
-
153
- is_privileged = (request.token == tasks_data[campaign_id]["token"])
154
+
155
+ is_privileged = request.token == tasks_data[campaign_id]["token"]
154
156
 
155
157
  progress_new = {}
156
158
  assignment = tasks_data[campaign_id]["info"]["assignment"]
157
159
  if assignment not in ["task-based", "single-stream"]:
158
- return JSONResponse(content="Unsupported campaign assignment type", status_code=400)
160
+ return JSONResponse(
161
+ content="Unsupported campaign assignment type", status_code=400
162
+ )
159
163
 
160
164
  # Get threshold info for the campaign
161
165
  validation_threshold = tasks_data[campaign_id]["info"].get("validation_threshold")
@@ -164,10 +168,9 @@ async def _dashboard_data(request: DashboardDataRequest):
164
168
  # shallow copy
165
169
  entry = dict(user_val)
166
170
  entry["validations"] = [
167
- all(v)
168
- for v in list(entry.get("validations", {}).values())
171
+ all(v) for v in list(entry.get("validations", {}).values())
169
172
  ]
170
-
173
+
171
174
  # Add threshold pass/fail status (only when user is complete)
172
175
  if all(entry["progress"]):
173
176
  entry["threshold_passed"] = check_validation_threshold(
@@ -183,11 +186,8 @@ async def _dashboard_data(request: DashboardDataRequest):
183
186
  progress_new[user_id] = entry
184
187
 
185
188
  return JSONResponse(
186
- content={
187
- "data": progress_new,
188
- "validation_threshold": validation_threshold
189
- },
190
- status_code=200
189
+ content={"data": progress_new, "validation_threshold": validation_threshold},
190
+ status_code=200,
191
191
  )
192
192
 
193
193
 
@@ -227,7 +227,9 @@ async def _download_annotations(
227
227
  for campaign_id in campaign_id:
228
228
  output_path = f"{ROOT}/data/outputs/{campaign_id}.jsonl"
229
229
  if campaign_id not in progress_data:
230
- return JSONResponse(content=f"Unknown campaign ID {campaign_id}", status_code=400)
230
+ return JSONResponse(
231
+ content=f"Unknown campaign ID {campaign_id}", status_code=400
232
+ )
231
233
  if not os.path.exists(output_path):
232
234
  output[campaign_id] = []
233
235
  else:
@@ -239,28 +241,33 @@ async def _download_annotations(
239
241
 
240
242
  @app.get("/download-progress")
241
243
  async def _download_progress(
242
- campaign_id: list[str] = Query(),
243
- token: list[str] = Query()
244
+ campaign_id: list[str] = Query(), token: list[str] = Query()
244
245
  ):
245
246
 
246
247
  if len(campaign_id) != len(token):
247
- return JSONResponse(content="Mismatched campaign_id and token count", status_code=400)
248
+ return JSONResponse(
249
+ content="Mismatched campaign_id and token count", status_code=400
250
+ )
248
251
 
249
252
  output = {}
250
253
  for i, cid in enumerate(campaign_id):
251
254
  if cid not in progress_data:
252
255
  return JSONResponse(content=f"Unknown campaign ID {cid}", status_code=400)
253
256
  if token[i] != tasks_data[cid]["token"]:
254
- return JSONResponse(content=f"Invalid token for campaign ID {cid}", status_code=400)
257
+ return JSONResponse(
258
+ content=f"Invalid token for campaign ID {cid}", status_code=400
259
+ )
255
260
 
256
261
  output[cid] = progress_data[cid]
257
262
 
258
263
  return JSONResponse(content=output, status_code=200)
259
264
 
265
+
260
266
  static_dir = f"{os.path.dirname(os.path.abspath(__file__))}/static/"
261
267
  if not os.path.exists(static_dir + "index.html"):
262
268
  raise FileNotFoundError(
263
- "Static directory not found. Please build the frontend first.")
269
+ "Static directory not found. Please build the frontend first."
270
+ )
264
271
 
265
272
  app.mount(
266
273
  "/",
pearmut/cli.py CHANGED
@@ -278,26 +278,24 @@ def _add_single_campaign(data_file, overwrite, server):
278
278
  )
279
279
 
280
280
  # Symlink path is based on the destination, stripping the 'assets/' prefix
281
- symlink_path = f"{STATIC_DIR}/{assets_destination}"
281
+ symlink_path = f"{STATIC_DIR}/{assets_destination}".rstrip("/")
282
282
 
283
283
  # Remove existing symlink if present and we are overriding the same campaign
284
284
  if os.path.lexists(symlink_path):
285
285
  # Check if any other campaign is using this destination
286
286
  current_campaign_id = campaign_data['campaign_id']
287
- tasks_dir = f"{ROOT}/data/tasks"
288
- if os.path.exists(tasks_dir):
289
- for task_file in os.listdir(tasks_dir):
290
- if task_file.endswith('.json'):
291
- other_campaign_id = task_file[:-5]
292
- if other_campaign_id != current_campaign_id:
293
- with open(f"{tasks_dir}/{task_file}", "r") as f:
294
- other_campaign = json.load(f)
295
- other_assets = other_campaign.get("info", {}).get("assets")
296
- if other_assets and isinstance(other_assets, dict):
297
- if other_assets.get("destination") == assets_destination:
298
- raise ValueError(
299
- f"Assets destination '{assets_destination}' is already used by campaign '{other_campaign_id}'."
300
- )
287
+
288
+ for other_campaign_id in progress_data.keys():
289
+ if other_campaign_id == current_campaign_id:
290
+ continue
291
+ with open(f"{ROOT}/data/tasks/{other_campaign_id}.json", "r") as f:
292
+ other_campaign = json.load(f)
293
+ other_assets = other_campaign.get("info", {}).get("assets")
294
+ if other_assets:
295
+ if other_assets.get("destination") == assets_destination:
296
+ raise ValueError(
297
+ f"Assets destination '{assets_destination}' is already used by campaign '{other_campaign_id}'."
298
+ )
301
299
  # Only allow overwrite if it's the same campaign
302
300
  if overwrite:
303
301
  os.remove(symlink_path)
@@ -305,7 +303,8 @@ def _add_single_campaign(data_file, overwrite, server):
305
303
  raise ValueError(f"Assets destination '{assets_destination}' is already taken.")
306
304
 
307
305
  # Ensure the assets directory exists
308
- os.makedirs(f"{STATIC_DIR}/assets", exist_ok=True)
306
+ # get parent of symlink_path dir
307
+ os.makedirs(os.path.dirname(symlink_path), exist_ok=True)
309
308
 
310
309
  os.symlink(assets_real_path, symlink_path, target_is_directory=True)
311
310
  print(f"Assets symlinked: {symlink_path} -> {assets_real_path}")
@@ -391,7 +390,7 @@ def main():
391
390
  campaign_data = json.load(f)
392
391
  destination = campaign_data.get("info", {}).get("assets", {}).get("destination")
393
392
  if destination:
394
- symlink_path = f"{STATIC_DIR}/{destination}"
393
+ symlink_path = f"{STATIC_DIR}/{destination}".rstrip("/")
395
394
  if os.path.islink(symlink_path):
396
395
  os.remove(symlink_path)
397
396
  print(f"Assets symlink removed: {symlink_path}")
@@ -436,12 +435,9 @@ def main():
436
435
  )
437
436
  if confirm.lower() == 'y':
438
437
  # Unlink all assets first
439
- tasks_dir = f"{ROOT}/data/tasks"
440
- if os.path.exists(tasks_dir):
441
- for task_file in os.listdir(tasks_dir):
442
- if task_file.endswith('.json'):
443
- campaign_id = task_file[:-5]
444
- _unlink_assets(campaign_id)
438
+ progress_data = load_progress_data()
439
+ for campaign_id in progress_data.keys():
440
+ _unlink_assets(campaign_id)
445
441
  shutil.rmtree(f"{ROOT}/data/tasks", ignore_errors=True)
446
442
  shutil.rmtree(f"{ROOT}/data/outputs", ignore_errors=True)
447
443
  if os.path.exists(f"{ROOT}/data/progress.json"):