Spaces:
Running
Running
import csv | |
import io | |
import json | |
import os | |
from datetime import datetime | |
from enum import Enum | |
from pathlib import Path | |
from typing import List | |
import pandas as pd | |
from fastapi import Response | |
from modal import web_endpoint | |
import modal | |
from pydantic import BaseModel | |
from rating import compute_mle_elo | |
# ----------------------- | |
# Data Model Definition | |
# ----------------------- | |
class ExperienceEnum(int, Enum): | |
novice = 1 | |
intermediate = 2 | |
expert = 3 | |
class Winner(str, Enum): | |
model_a = "model_a" | |
model_b = "model_b" | |
tie = "tie" | |
class Model(str, Enum): | |
porestar_deepfault_unet_baseline_1 = "porestar/deepfault-unet-baseline-1" | |
porestar_deepfault_unet_baseline_2 = "porestar/deepfault-unet-baseline-2" | |
class Battle(BaseModel): | |
model_a: Model | |
model_b: Model | |
winner: Winner | |
judge: str | |
image_idx: int | |
experience: ExperienceEnum = ExperienceEnum.novice | |
tstamp: str = str(datetime.now()) | |
class EloRating(BaseModel): | |
model: Model | |
elo_rating: float | |
# ----------------------- | |
# Modal Configuration | |
# ----------------------- | |
# Create a volume to persist data | |
data_volume = modal.Volume.from_name("seisbase-data", create_if_missing=True) | |
JSON_FILE_PATH = Path("/data/battles.json") | |
RESULTS_FILE_PATH = Path("/data/ratings.csv") | |
app_image = modal.Image.debian_slim(python_version="3.10").pip_install("pandas", "scikit-learn", "tqdm", "sympy") | |
app = modal.App( | |
image=app_image, | |
name="seisbase-eval", | |
volumes={"/data": data_volume}, | |
) | |
def ensure_json_file(): | |
"""Ensure the JSON file exists and is initialized with an empty array if necessary.""" | |
if not os.path.exists(JSON_FILE_PATH): | |
JSON_FILE_PATH.parent.mkdir(parents=True, exist_ok=True) | |
with open(JSON_FILE_PATH, "w") as f: | |
json.dump([], f) | |
def append_to_json_file(data): | |
"""Append data to the JSON file.""" | |
ensure_json_file() | |
try: | |
with open(JSON_FILE_PATH, "r+") as f: | |
try: | |
battles = json.load(f) | |
except json.JSONDecodeError: | |
# Reset the file if corrupted | |
battles = [] | |
battles.append(data) | |
f.seek(0) | |
json.dump(battles, f, indent=4) | |
f.truncate() | |
except Exception as e: | |
raise RuntimeError(f"Failed to append data to JSON file: {e}") | |
def read_json_file(): | |
"""Read data from the JSON file.""" | |
ensure_json_file() | |
try: | |
with open(JSON_FILE_PATH, "r") as f: | |
try: | |
return json.load(f) | |
except json.JSONDecodeError: | |
return [] # Return an empty list if the file is corrupted | |
except Exception as e: | |
raise RuntimeError(f"Failed to read JSON file: {e}") | |
def add_battle(battle: Battle): | |
"""Add a new battle to the JSON file.""" | |
append_to_json_file(battle.dict()) | |
return {"status": "success", "battle": battle.dict()} | |
def export_csv(): | |
"""Fetch all battles and return as CSV.""" | |
battles = read_json_file() | |
# Create CSV in memory | |
output = io.StringIO() | |
writer = csv.DictWriter(output, fieldnames=["model_a", "model_b", "winner", "judge", "imaged_idx", "experience", "tstamp"]) | |
writer.writeheader() | |
writer.writerows(battles) | |
csv_data = output.getvalue() | |
return Response(content=csv_data, media_type="text/csv") | |
def compute_ratings() -> List[EloRating]: | |
"""Compute ratings from battles.""" | |
battles = pd.read_json(JSON_FILE_PATH, dtype=[str, str, str, str, int, int, str]).sort_values(ascending=True, by=["tstamp"]).reset_index(drop=True) | |
elo_mle_ratings = compute_mle_elo(battles) | |
elo_mle_ratings.to_csv(RESULTS_FILE_PATH) | |
df = pd.read_csv(RESULTS_FILE_PATH) | |
df.columns = ["Model", "Elo rating"] | |
df = df.sort_values("Elo rating", ascending=False).reset_index(drop=True) | |
scores = [] | |
for i in range(len(df)): | |
scores.append(EloRating(model=df["Model"][i], elo_rating=df["Elo rating"][i])) | |
return scores | |
def main(): | |
print("Local entrypoint running. Check endpoints for functionality.") | |