Add tags prominence and SIM source grouping features
Database changes: - Add sim_source_id column to artifacts table for grouping multiple artifacts - Create Alembic migration (001_add_sim_source_id) for schema update - Add Alembic env.py for migration support with environment-based DB URLs API enhancements: - Add sim_source_id parameter to upload endpoint - Add sim_source_id filter to query endpoint - Add new /grouped-by-sim-source endpoint for getting artifacts by group - Update all API documentation to include sim_source_id UI improvements: - Make tags required field and more prominent in upload form - Add tags display directly in artifacts table (below filename) - Add SIM Source ID field in upload form with helper text for grouping - Update table to show sim_source_id (falls back to test_suite if null) - Tags now displayed as inline badges in main table view Seed data updates: - Generate sim_source_id for 70% of artifacts to demonstrate grouping - Multiple artifacts can share same sim_source_id - Improved seed data variety with tag combinations Features: - Tags are now prominently displayed in both table and detail views - Multiple artifacts can be grouped by SIM source ID - Users can filter/query by sim_source_id - Backward compatible - existing artifacts without sim_source_id still work 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from fastapi import APIRouter, UploadFile, File, Form, Depends, HTTPException, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Dict
|
||||
import uuid
|
||||
import json
|
||||
import io
|
||||
@@ -36,6 +36,7 @@ async def upload_artifact(
|
||||
test_suite: Optional[str] = Form(None),
|
||||
test_config: Optional[str] = Form(None),
|
||||
test_result: Optional[str] = Form(None),
|
||||
sim_source_id: Optional[str] = Form(None),
|
||||
custom_metadata: Optional[str] = Form(None),
|
||||
description: Optional[str] = Form(None),
|
||||
tags: Optional[str] = Form(None),
|
||||
@@ -51,6 +52,7 @@ async def upload_artifact(
|
||||
- **test_suite**: Test suite identifier
|
||||
- **test_config**: JSON string of test configuration
|
||||
- **test_result**: Test result (pass, fail, skip, error)
|
||||
- **sim_source_id**: SIM source ID to group multiple artifacts
|
||||
- **custom_metadata**: JSON string of additional metadata
|
||||
- **description**: Text description of the artifact
|
||||
- **tags**: JSON array of tags (as string)
|
||||
@@ -88,6 +90,7 @@ async def upload_artifact(
|
||||
test_suite=test_suite,
|
||||
test_config=test_config_dict,
|
||||
test_result=test_result,
|
||||
sim_source_id=sim_source_id,
|
||||
custom_metadata=metadata_dict,
|
||||
description=description,
|
||||
tags=tags_list,
|
||||
@@ -194,6 +197,7 @@ async def query_artifacts(query: ArtifactQuery, db: Session = Depends(get_db)):
|
||||
- **test_name**: Filter by test name
|
||||
- **test_suite**: Filter by test suite
|
||||
- **test_result**: Filter by test result
|
||||
- **sim_source_id**: Filter by SIM source ID
|
||||
- **tags**: Filter by tags (must contain all specified tags)
|
||||
- **start_date**: Filter by creation date (from)
|
||||
- **end_date**: Filter by creation date (to)
|
||||
@@ -212,6 +216,8 @@ async def query_artifacts(query: ArtifactQuery, db: Session = Depends(get_db)):
|
||||
q = q.filter(Artifact.test_suite == query.test_suite)
|
||||
if query.test_result:
|
||||
q = q.filter(Artifact.test_result == query.test_result)
|
||||
if query.sim_source_id:
|
||||
q = q.filter(Artifact.sim_source_id == query.sim_source_id)
|
||||
if query.tags:
|
||||
for tag in query.tags:
|
||||
q = q.filter(Artifact.tags.contains([tag]))
|
||||
@@ -240,3 +246,20 @@ async def list_artifacts(
|
||||
Artifact.created_at.desc()
|
||||
).offset(offset).limit(limit).all()
|
||||
return artifacts
|
||||
|
||||
|
||||
@router.get("/grouped-by-sim-source", response_model=Dict[str, List[ArtifactResponse]])
|
||||
async def get_artifacts_grouped_by_sim_source(
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get all artifacts grouped by SIM source ID"""
|
||||
from collections import defaultdict
|
||||
|
||||
artifacts = db.query(Artifact).order_by(Artifact.created_at.desc()).all()
|
||||
grouped = defaultdict(list)
|
||||
|
||||
for artifact in artifacts:
|
||||
sim_source = artifact.sim_source_id or "ungrouped"
|
||||
grouped[sim_source].append(artifact)
|
||||
|
||||
return dict(grouped)
|
||||
|
||||
Reference in New Issue
Block a user