pearmut 0.2.7__py3-none-any.whl → 0.2.9__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 +55 -0
- pearmut/cli.py +22 -24
- pearmut/static/dashboard.bundle.js +1 -1
- pearmut/static/dashboard.html +28 -1
- pearmut/static/listwise.bundle.js +1 -1
- pearmut/static/pointwise.bundle.js +1 -1
- {pearmut-0.2.7.dist-info → pearmut-0.2.9.dist-info}/METADATA +13 -3
- pearmut-0.2.9.dist-info/RECORD +19 -0
- pearmut-0.2.7.dist-info/RECORD +0 -19
- {pearmut-0.2.7.dist-info → pearmut-0.2.9.dist-info}/WHEEL +0 -0
- {pearmut-0.2.7.dist-info → pearmut-0.2.9.dist-info}/entry_points.txt +0 -0
- {pearmut-0.2.7.dist-info → pearmut-0.2.9.dist-info}/licenses/LICENSE +0 -0
- {pearmut-0.2.7.dist-info → pearmut-0.2.9.dist-info}/top_level.txt +0 -0
pearmut/app.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import collections
|
|
1
2
|
import json
|
|
2
3
|
import os
|
|
4
|
+
import statistics
|
|
3
5
|
from typing import Any
|
|
4
6
|
|
|
5
7
|
from fastapi import FastAPI, Query
|
|
@@ -12,6 +14,7 @@ from .assignment import get_i_item, get_next_item, reset_task, update_progress
|
|
|
12
14
|
from .utils import (
|
|
13
15
|
ROOT,
|
|
14
16
|
check_validation_threshold,
|
|
17
|
+
get_db_log,
|
|
15
18
|
load_progress_data,
|
|
16
19
|
save_db_payload,
|
|
17
20
|
save_progress_data,
|
|
@@ -191,6 +194,58 @@ async def _dashboard_data(request: DashboardDataRequest):
|
|
|
191
194
|
)
|
|
192
195
|
|
|
193
196
|
|
|
197
|
+
class DashboardResultsRequest(BaseModel):
|
|
198
|
+
campaign_id: str
|
|
199
|
+
token: str
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@app.post("/dashboard-results")
|
|
203
|
+
async def _dashboard_results(request: DashboardResultsRequest):
|
|
204
|
+
campaign_id = request.campaign_id
|
|
205
|
+
token = request.token
|
|
206
|
+
|
|
207
|
+
if campaign_id not in progress_data:
|
|
208
|
+
return JSONResponse(content="Unknown campaign ID", status_code=400)
|
|
209
|
+
|
|
210
|
+
# Check if token is valid
|
|
211
|
+
if token != tasks_data[campaign_id]["token"]:
|
|
212
|
+
return JSONResponse(content="Invalid token", status_code=400)
|
|
213
|
+
|
|
214
|
+
# Compute model scores from annotations
|
|
215
|
+
model_scores = collections.defaultdict(dict)
|
|
216
|
+
|
|
217
|
+
# Iterate through all tasks to find items with 'model' field
|
|
218
|
+
log = get_db_log(campaign_id)
|
|
219
|
+
for entry in log:
|
|
220
|
+
if "item" not in entry or "annotations" not in entry:
|
|
221
|
+
continue
|
|
222
|
+
for item, annotation in zip(entry["item"], entry["annotations"]):
|
|
223
|
+
if "model" in item:
|
|
224
|
+
# pointwise
|
|
225
|
+
if "score" in annotation:
|
|
226
|
+
# make sure to only keep the latest score for each item
|
|
227
|
+
# json.dumps(item) creates a unique item key
|
|
228
|
+
model_scores[item["model"]][json.dumps(item)] = annotation["score"]
|
|
229
|
+
elif "models" in item:
|
|
230
|
+
# listwise
|
|
231
|
+
for model, annotation_cand in zip(item["models"], annotation):
|
|
232
|
+
if "score" in annotation_cand:
|
|
233
|
+
model_scores[model][json.dumps(item)] = (
|
|
234
|
+
annotation_cand["score"]
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
results = [
|
|
238
|
+
{
|
|
239
|
+
"model": model,
|
|
240
|
+
"score": statistics.mean(scores.values()),
|
|
241
|
+
"count": len(scores),
|
|
242
|
+
}
|
|
243
|
+
for model, scores in model_scores.items()
|
|
244
|
+
]
|
|
245
|
+
results.sort(key=lambda x: x["score"], reverse=True)
|
|
246
|
+
return JSONResponse(content=results, status_code=200)
|
|
247
|
+
|
|
248
|
+
|
|
194
249
|
class ResetTaskRequest(BaseModel):
|
|
195
250
|
campaign_id: str
|
|
196
251
|
user_id: str
|
pearmut/cli.py
CHANGED
|
@@ -41,7 +41,9 @@ def _run(args_unknown):
|
|
|
41
41
|
args.server + "/dashboard.html?" + "&".join([
|
|
42
42
|
f"campaign_id={urllib.parse.quote_plus(campaign_id)}&token={campaign_data["token"]}"
|
|
43
43
|
for campaign_id, campaign_data in tasks_data.items()
|
|
44
|
-
])
|
|
44
|
+
]),
|
|
45
|
+
# this is important to flush
|
|
46
|
+
flush=True,
|
|
45
47
|
)
|
|
46
48
|
|
|
47
49
|
uvicorn.run(
|
|
@@ -278,26 +280,24 @@ def _add_single_campaign(data_file, overwrite, server):
|
|
|
278
280
|
)
|
|
279
281
|
|
|
280
282
|
# Symlink path is based on the destination, stripping the 'assets/' prefix
|
|
281
|
-
symlink_path = f"{STATIC_DIR}/{assets_destination}"
|
|
283
|
+
symlink_path = f"{STATIC_DIR}/{assets_destination}".rstrip("/")
|
|
282
284
|
|
|
283
285
|
# Remove existing symlink if present and we are overriding the same campaign
|
|
284
286
|
if os.path.lexists(symlink_path):
|
|
285
287
|
# Check if any other campaign is using this destination
|
|
286
288
|
current_campaign_id = campaign_data['campaign_id']
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
f"Assets destination '{assets_destination}' is already used by campaign '{other_campaign_id}'."
|
|
300
|
-
)
|
|
289
|
+
|
|
290
|
+
for other_campaign_id in progress_data.keys():
|
|
291
|
+
if other_campaign_id == current_campaign_id:
|
|
292
|
+
continue
|
|
293
|
+
with open(f"{ROOT}/data/tasks/{other_campaign_id}.json", "r") as f:
|
|
294
|
+
other_campaign = json.load(f)
|
|
295
|
+
other_assets = other_campaign.get("info", {}).get("assets")
|
|
296
|
+
if other_assets:
|
|
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
|
+
)
|
|
301
301
|
# Only allow overwrite if it's the same campaign
|
|
302
302
|
if overwrite:
|
|
303
303
|
os.remove(symlink_path)
|
|
@@ -305,7 +305,8 @@ def _add_single_campaign(data_file, overwrite, server):
|
|
|
305
305
|
raise ValueError(f"Assets destination '{assets_destination}' is already taken.")
|
|
306
306
|
|
|
307
307
|
# Ensure the assets directory exists
|
|
308
|
-
|
|
308
|
+
# get parent of symlink_path dir
|
|
309
|
+
os.makedirs(os.path.dirname(symlink_path), exist_ok=True)
|
|
309
310
|
|
|
310
311
|
os.symlink(assets_real_path, symlink_path, target_is_directory=True)
|
|
311
312
|
print(f"Assets symlinked: {symlink_path} -> {assets_real_path}")
|
|
@@ -391,7 +392,7 @@ def main():
|
|
|
391
392
|
campaign_data = json.load(f)
|
|
392
393
|
destination = campaign_data.get("info", {}).get("assets", {}).get("destination")
|
|
393
394
|
if destination:
|
|
394
|
-
symlink_path = f"{STATIC_DIR}/{destination}"
|
|
395
|
+
symlink_path = f"{STATIC_DIR}/{destination}".rstrip("/")
|
|
395
396
|
if os.path.islink(symlink_path):
|
|
396
397
|
os.remove(symlink_path)
|
|
397
398
|
print(f"Assets symlink removed: {symlink_path}")
|
|
@@ -436,12 +437,9 @@ def main():
|
|
|
436
437
|
)
|
|
437
438
|
if confirm.lower() == 'y':
|
|
438
439
|
# Unlink all assets first
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if task_file.endswith('.json'):
|
|
443
|
-
campaign_id = task_file[:-5]
|
|
444
|
-
_unlink_assets(campaign_id)
|
|
440
|
+
progress_data = load_progress_data()
|
|
441
|
+
for campaign_id in progress_data.keys():
|
|
442
|
+
_unlink_assets(campaign_id)
|
|
445
443
|
shutil.rmtree(f"{ROOT}/data/tasks", ignore_errors=True)
|
|
446
444
|
shutil.rmtree(f"{ROOT}/data/outputs", ignore_errors=True)
|
|
447
445
|
if os.path.exists(f"{ROOT}/data/progress.json"):
|