Spaces:
Running
Running
fix
Browse files
frontend/src/pages/AnalyticsPage/AnalyticsPage.tsx
CHANGED
|
@@ -184,7 +184,7 @@ export default function AnalyticsPage() {
|
|
| 184 |
setTypesLookup(types);
|
| 185 |
setRegionsLookup(regions);
|
| 186 |
} catch {
|
| 187 |
-
|
| 188 |
}
|
| 189 |
}, []);
|
| 190 |
|
|
@@ -218,7 +218,7 @@ export default function AnalyticsPage() {
|
|
| 218 |
|
| 219 |
return Object.entries(allRegions)
|
| 220 |
.sort(([,a], [,b]) => b.count - a.count)
|
| 221 |
-
.map(([
|
| 222 |
id: index + 1,
|
| 223 |
name,
|
| 224 |
count,
|
|
@@ -237,7 +237,7 @@ export default function AnalyticsPage() {
|
|
| 237 |
count,
|
| 238 |
percentage: Math.round((count / data.totalCaptions) * 100)
|
| 239 |
}));
|
| 240 |
-
}, [data, typesLookup]);
|
| 241 |
|
| 242 |
const sourcesTableData = useMemo(() => {
|
| 243 |
if (!data) return [];
|
|
@@ -250,7 +250,7 @@ export default function AnalyticsPage() {
|
|
| 250 |
count,
|
| 251 |
percentage: Math.round((count / data.totalCaptions) * 100)
|
| 252 |
}));
|
| 253 |
-
}, [data, sourcesLookup]);
|
| 254 |
|
| 255 |
const modelsTableData = useMemo(() => {
|
| 256 |
if (!data) return [];
|
|
|
|
| 184 |
setTypesLookup(types);
|
| 185 |
setRegionsLookup(regions);
|
| 186 |
} catch {
|
| 187 |
+
// Silently handle errors for lookup data
|
| 188 |
}
|
| 189 |
}, []);
|
| 190 |
|
|
|
|
| 218 |
|
| 219 |
return Object.entries(allRegions)
|
| 220 |
.sort(([,a], [,b]) => b.count - a.count)
|
| 221 |
+
.map(([, { name, count }], index) => ({
|
| 222 |
id: index + 1,
|
| 223 |
name,
|
| 224 |
count,
|
|
|
|
| 237 |
count,
|
| 238 |
percentage: Math.round((count / data.totalCaptions) * 100)
|
| 239 |
}));
|
| 240 |
+
}, [data, typesLookup, getTypeLabel]);
|
| 241 |
|
| 242 |
const sourcesTableData = useMemo(() => {
|
| 243 |
if (!data) return [];
|
|
|
|
| 250 |
count,
|
| 251 |
percentage: Math.round((count / data.totalCaptions) * 100)
|
| 252 |
}));
|
| 253 |
+
}, [data, sourcesLookup, getSourceLabel]);
|
| 254 |
|
| 255 |
const modelsTableData = useMemo(() => {
|
| 256 |
if (!data) return [];
|
frontend/src/pages/DevPage.tsx
CHANGED
|
@@ -26,10 +26,10 @@ export default function DevPage() {
|
|
| 26 |
|
| 27 |
const persistedModel = localStorage.getItem(SELECTED_MODEL_KEY);
|
| 28 |
if (modelsData.models && modelsData.models.length > 0) {
|
| 29 |
-
if (persistedModel && modelsData.models.find((m:
|
| 30 |
setSelectedModel(persistedModel);
|
| 31 |
} else {
|
| 32 |
-
const firstAvailableModel = modelsData.models.find((m:
|
| 33 |
setSelectedModel(firstAvailableModel.m_code);
|
| 34 |
localStorage.setItem(SELECTED_MODEL_KEY, firstAvailableModel.m_code);
|
| 35 |
}
|
|
@@ -64,7 +64,7 @@ export default function DevPage() {
|
|
| 64 |
const errorData = await response.json();
|
| 65 |
alert(`Failed to toggle model availability: ${errorData.error || 'Unknown error'}`);
|
| 66 |
}
|
| 67 |
-
} catch
|
| 68 |
alert('Error toggling model availability');
|
| 69 |
}
|
| 70 |
};
|
|
|
|
| 26 |
|
| 27 |
const persistedModel = localStorage.getItem(SELECTED_MODEL_KEY);
|
| 28 |
if (modelsData.models && modelsData.models.length > 0) {
|
| 29 |
+
if (persistedModel && modelsData.models.find((m: { m_code: string; is_available: boolean }) => m.m_code === persistedModel && m.is_available)) {
|
| 30 |
setSelectedModel(persistedModel);
|
| 31 |
} else {
|
| 32 |
+
const firstAvailableModel = modelsData.models.find((m: { is_available: boolean }) => m.is_available) || modelsData.models[0];
|
| 33 |
setSelectedModel(firstAvailableModel.m_code);
|
| 34 |
localStorage.setItem(SELECTED_MODEL_KEY, firstAvailableModel.m_code);
|
| 35 |
}
|
|
|
|
| 64 |
const errorData = await response.json();
|
| 65 |
alert(`Failed to toggle model availability: ${errorData.error || 'Unknown error'}`);
|
| 66 |
}
|
| 67 |
+
} catch {
|
| 68 |
alert('Error toggling model availability');
|
| 69 |
}
|
| 70 |
};
|
frontend/src/pages/ExplorePage/ExplorePage.tsx
CHANGED
|
@@ -9,7 +9,7 @@ interface ImageWithCaptionOut {
|
|
| 9 |
prompt: string;
|
| 10 |
model: string;
|
| 11 |
schema_id: string;
|
| 12 |
-
raw_json:
|
| 13 |
generated: string;
|
| 14 |
edited?: string;
|
| 15 |
accuracy?: number;
|
|
@@ -59,7 +59,7 @@ export default function ExplorePage() {
|
|
| 59 |
})
|
| 60 |
.then(data => {
|
| 61 |
if (Array.isArray(data)) {
|
| 62 |
-
const imagesWithCaptions = data.filter((item:
|
| 63 |
const hasCaption = item.title && item.generated && item.model;
|
| 64 |
return hasCaption;
|
| 65 |
});
|
|
|
|
| 9 |
prompt: string;
|
| 10 |
model: string;
|
| 11 |
schema_id: string;
|
| 12 |
+
raw_json: Record<string, unknown>;
|
| 13 |
generated: string;
|
| 14 |
edited?: string;
|
| 15 |
accuracy?: number;
|
|
|
|
| 59 |
})
|
| 60 |
.then(data => {
|
| 61 |
if (Array.isArray(data)) {
|
| 62 |
+
const imagesWithCaptions = data.filter((item: { title?: string; generated?: string; model?: string }) => {
|
| 63 |
const hasCaption = item.title && item.generated && item.model;
|
| 64 |
return hasCaption;
|
| 65 |
});
|
frontend/src/pages/MapDetailsPage/MapDetailPage.tsx
CHANGED
|
@@ -22,7 +22,7 @@ interface MapOut {
|
|
| 22 |
prompt?: string;
|
| 23 |
model?: string;
|
| 24 |
schema_id?: string;
|
| 25 |
-
raw_json?:
|
| 26 |
generated?: string;
|
| 27 |
edited?: string;
|
| 28 |
accuracy?: number;
|
|
@@ -77,8 +77,8 @@ export default function MapDetailPage() {
|
|
| 77 |
|
| 78 |
// Check for previous/next items
|
| 79 |
await checkNavigationAvailability(id);
|
| 80 |
-
} catch (err:
|
| 81 |
-
setError(err.message);
|
| 82 |
} finally {
|
| 83 |
setLoading(false);
|
| 84 |
setIsNavigating(false);
|
|
@@ -101,7 +101,7 @@ export default function MapDetailPage() {
|
|
| 101 |
const response = await fetch('/api/images');
|
| 102 |
if (response.ok) {
|
| 103 |
const images = await response.json();
|
| 104 |
-
const currentIndex = images.findIndex((img:
|
| 105 |
|
| 106 |
setHasPrevious(images.length > 1 && currentIndex > 0);
|
| 107 |
setHasNext(images.length > 1 && currentIndex < images.length - 1);
|
|
@@ -119,7 +119,7 @@ export default function MapDetailPage() {
|
|
| 119 |
if (!response.ok) return;
|
| 120 |
|
| 121 |
const images = await response.json();
|
| 122 |
-
const currentIndex = images.findIndex((img:
|
| 123 |
|
| 124 |
let targetIndex: number;
|
| 125 |
if (direction === 'previous') {
|
|
@@ -223,9 +223,9 @@ export default function MapDetailPage() {
|
|
| 223 |
const url = `/upload?imageUrl=${encodeURIComponent(json.image_url)}&isContribution=true&step=2a&imageId=${newId}`;
|
| 224 |
navigate(url);
|
| 225 |
|
| 226 |
-
} catch (error:
|
| 227 |
console.error('Contribution failed:', error);
|
| 228 |
-
alert(`Contribution failed: ${error.message
|
| 229 |
} finally {
|
| 230 |
setIsGenerating(false);
|
| 231 |
}
|
|
|
|
| 22 |
prompt?: string;
|
| 23 |
model?: string;
|
| 24 |
schema_id?: string;
|
| 25 |
+
raw_json?: Record<string, unknown>;
|
| 26 |
generated?: string;
|
| 27 |
edited?: string;
|
| 28 |
accuracy?: number;
|
|
|
|
| 77 |
|
| 78 |
// Check for previous/next items
|
| 79 |
await checkNavigationAvailability(id);
|
| 80 |
+
} catch (err: unknown) {
|
| 81 |
+
setError(err instanceof Error ? err.message : 'Unknown error occurred');
|
| 82 |
} finally {
|
| 83 |
setLoading(false);
|
| 84 |
setIsNavigating(false);
|
|
|
|
| 101 |
const response = await fetch('/api/images');
|
| 102 |
if (response.ok) {
|
| 103 |
const images = await response.json();
|
| 104 |
+
const currentIndex = images.findIndex((img: { image_id: string }) => img.image_id === currentId);
|
| 105 |
|
| 106 |
setHasPrevious(images.length > 1 && currentIndex > 0);
|
| 107 |
setHasNext(images.length > 1 && currentIndex < images.length - 1);
|
|
|
|
| 119 |
if (!response.ok) return;
|
| 120 |
|
| 121 |
const images = await response.json();
|
| 122 |
+
const currentIndex = images.findIndex((img: { image_id: string }) => img.image_id === mapId);
|
| 123 |
|
| 124 |
let targetIndex: number;
|
| 125 |
if (direction === 'previous') {
|
|
|
|
| 223 |
const url = `/upload?imageUrl=${encodeURIComponent(json.image_url)}&isContribution=true&step=2a&imageId=${newId}`;
|
| 224 |
navigate(url);
|
| 225 |
|
| 226 |
+
} catch (error: unknown) {
|
| 227 |
console.error('Contribution failed:', error);
|
| 228 |
+
alert(`Contribution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
| 229 |
} finally {
|
| 230 |
setIsGenerating(false);
|
| 231 |
}
|
frontend/src/pages/UploadPage/UploadPage.tsx
CHANGED
|
@@ -142,7 +142,7 @@ export default function UploadPage() {
|
|
| 142 |
document.removeEventListener('click', handleGlobalClick, true);
|
| 143 |
handleCleanup();
|
| 144 |
};
|
| 145 |
-
}, []);
|
| 146 |
|
| 147 |
const [imageUrl, setImageUrl] = useState<string|null>(null);
|
| 148 |
const [draft, setDraft] = useState('');
|
|
|
|
| 142 |
document.removeEventListener('click', handleGlobalClick, true);
|
| 143 |
handleCleanup();
|
| 144 |
};
|
| 145 |
+
}, [handleNavigation]);
|
| 146 |
|
| 147 |
const [imageUrl, setImageUrl] = useState<string|null>(null);
|
| 148 |
const [draft, setDraft] = useState('');
|
py_backend/app/main.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import os
|
| 2 |
from fastapi import FastAPI, HTTPException
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -10,27 +11,21 @@ from app.routers.images import router as images_router
|
|
| 10 |
|
| 11 |
app = FastAPI(title="PromptAid Vision")
|
| 12 |
|
| 13 |
-
# CORS: localhost dev + *.hf.space
|
| 14 |
app.add_middleware(
|
| 15 |
CORSMiddleware,
|
| 16 |
-
allow_origins=[
|
| 17 |
-
"http://localhost:3000",
|
| 18 |
-
"http://localhost:5173",
|
| 19 |
-
],
|
| 20 |
allow_origin_regex=r"https://.*\.hf\.space$",
|
| 21 |
allow_credentials=False,
|
| 22 |
allow_methods=["*"],
|
| 23 |
allow_headers=["*"],
|
| 24 |
)
|
| 25 |
|
| 26 |
-
# API routers
|
| 27 |
app.include_router(caption.router, prefix="/api", tags=["captions"])
|
| 28 |
app.include_router(metadata.router, prefix="/api", tags=["metadata"])
|
| 29 |
app.include_router(models.router, prefix="/api", tags=["models"])
|
| 30 |
app.include_router(upload.router, prefix="/api/images", tags=["images"])
|
| 31 |
app.include_router(images_router, prefix="/api/contribute", tags=["contribute"])
|
| 32 |
|
| 33 |
-
# Health & simple root
|
| 34 |
@app.get("/health", include_in_schema=False, response_class=JSONResponse)
|
| 35 |
async def health():
|
| 36 |
return {"status": "ok"}
|
|
@@ -43,12 +38,10 @@ def root():
|
|
| 43 |
<p>OK</p>
|
| 44 |
<p><a href="/app/">Open UI</a> β’ <a href="/docs">API Docs</a></p>"""
|
| 45 |
|
| 46 |
-
# Serve built frontend under /app (expects files in py_backend/static)
|
| 47 |
STATIC_DIR = os.path.join(os.path.dirname(__file__), "..", "static")
|
| 48 |
if os.path.isdir(STATIC_DIR):
|
| 49 |
app.mount("/app", StaticFiles(directory=STATIC_DIR, html=True), name="static")
|
| 50 |
|
| 51 |
-
# SPA fallback only for /app/* routes
|
| 52 |
@app.get("/app/{full_path:path}", include_in_schema=False)
|
| 53 |
def spa_fallback(full_path: str):
|
| 54 |
index = os.path.join(STATIC_DIR, "index.html")
|
|
@@ -56,6 +49,7 @@ def spa_fallback(full_path: str):
|
|
| 56 |
return FileResponse(index)
|
| 57 |
raise HTTPException(status_code=404, detail="Not Found")
|
| 58 |
|
|
|
|
| 59 |
print("π PromptAid Vision API server ready")
|
| 60 |
print("π Endpoints: /api/images, /api/captions, /api/metadata, /api/models")
|
| 61 |
print(f"π Environment: {settings.ENVIRONMENT}")
|
|
|
|
| 1 |
+
# py_backend/app/main.py
|
| 2 |
import os
|
| 3 |
from fastapi import FastAPI, HTTPException
|
| 4 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 11 |
|
| 12 |
app = FastAPI(title="PromptAid Vision")
|
| 13 |
|
|
|
|
| 14 |
app.add_middleware(
|
| 15 |
CORSMiddleware,
|
| 16 |
+
allow_origins=["http://localhost:3000","http://localhost:5173"],
|
|
|
|
|
|
|
|
|
|
| 17 |
allow_origin_regex=r"https://.*\.hf\.space$",
|
| 18 |
allow_credentials=False,
|
| 19 |
allow_methods=["*"],
|
| 20 |
allow_headers=["*"],
|
| 21 |
)
|
| 22 |
|
|
|
|
| 23 |
app.include_router(caption.router, prefix="/api", tags=["captions"])
|
| 24 |
app.include_router(metadata.router, prefix="/api", tags=["metadata"])
|
| 25 |
app.include_router(models.router, prefix="/api", tags=["models"])
|
| 26 |
app.include_router(upload.router, prefix="/api/images", tags=["images"])
|
| 27 |
app.include_router(images_router, prefix="/api/contribute", tags=["contribute"])
|
| 28 |
|
|
|
|
| 29 |
@app.get("/health", include_in_schema=False, response_class=JSONResponse)
|
| 30 |
async def health():
|
| 31 |
return {"status": "ok"}
|
|
|
|
| 38 |
<p>OK</p>
|
| 39 |
<p><a href="/app/">Open UI</a> β’ <a href="/docs">API Docs</a></p>"""
|
| 40 |
|
|
|
|
| 41 |
STATIC_DIR = os.path.join(os.path.dirname(__file__), "..", "static")
|
| 42 |
if os.path.isdir(STATIC_DIR):
|
| 43 |
app.mount("/app", StaticFiles(directory=STATIC_DIR, html=True), name="static")
|
| 44 |
|
|
|
|
| 45 |
@app.get("/app/{full_path:path}", include_in_schema=False)
|
| 46 |
def spa_fallback(full_path: str):
|
| 47 |
index = os.path.join(STATIC_DIR, "index.html")
|
|
|
|
| 49 |
return FileResponse(index)
|
| 50 |
raise HTTPException(status_code=404, detail="Not Found")
|
| 51 |
|
| 52 |
+
|
| 53 |
print("π PromptAid Vision API server ready")
|
| 54 |
print("π Endpoints: /api/images, /api/captions, /api/metadata, /api/models")
|
| 55 |
print(f"π Environment: {settings.ENVIRONMENT}")
|