feat(reporting): Implement 'Report Mistake' feature with API and UI [2f388f42]

This commit is contained in:
2026-01-27 09:00:20 +00:00
parent 5908c2e403
commit cb63df28af
6 changed files with 520 additions and 13 deletions

View File

@@ -17,7 +17,7 @@ setup_logging()
import logging
logger = logging.getLogger(__name__)
from .database import init_db, get_db, Company, Signal, EnrichmentData, RoboticsCategory, Contact, Industry, JobRoleMapping
from .database import init_db, get_db, Company, Signal, EnrichmentData, RoboticsCategory, Contact, Industry, JobRoleMapping, ReportedMistake
from .services.deduplication import Deduplicator
from .services.discovery import DiscoveryService
from .services.scraping import ScraperService
@@ -61,6 +61,14 @@ class AnalysisRequest(BaseModel):
class IndustryUpdateModel(BaseModel):
industry_ai: str
class ReportMistakeRequest(BaseModel):
field_name: str
wrong_value: Optional[str] = None
corrected_value: Optional[str] = None
source_url: Optional[str] = None
quote: Optional[str] = None
user_comment: Optional[str] = None
# --- Events ---
@app.on_event("startup")
def on_startup():
@@ -240,6 +248,47 @@ def list_industries(db: Session = Depends(get_db)):
def list_job_roles(db: Session = Depends(get_db)):
return db.query(JobRoleMapping).order_by(JobRoleMapping.pattern.asc()).all()
@app.get("/api/mistakes")
def list_reported_mistakes(
status: Optional[str] = Query(None),
skip: int = 0,
limit: int = 50,
db: Session = Depends(get_db)
):
query = db.query(ReportedMistake).options(joinedload(ReportedMistake.company))
if status:
query = query.filter(ReportedMistake.status == status.upper())
total = query.count()
items = query.order_by(ReportedMistake.created_at.desc()).offset(skip).limit(limit).all()
return {"total": total, "items": items}
class MistakeUpdateStatusRequest(BaseModel):
status: str # PENDING, APPROVED, REJECTED
@app.put("/api/mistakes/{mistake_id}")
def update_reported_mistake_status(
mistake_id: int,
request: MistakeUpdateStatusRequest,
db: Session = Depends(get_db)
):
mistake = db.query(ReportedMistake).filter(ReportedMistake.id == mistake_id).first()
if not mistake:
raise HTTPException(404, detail="Reported mistake not found")
if request.status.upper() not in ["PENDING", "APPROVED", "REJECTED"]:
raise HTTPException(400, detail="Invalid status. Must be PENDING, APPROVED, or REJECTED.")
mistake.status = request.status.upper()
mistake.updated_at = datetime.utcnow()
db.commit()
db.refresh(mistake)
logger.info(f"Updated status for mistake {mistake_id} to {mistake.status}")
return {"status": "success", "mistake": mistake}
@app.post("/api/enrich/discover")
def discover_company(req: AnalysisRequest, background_tasks: BackgroundTasks, db: Session = Depends(get_db)):
company = db.query(Company).filter(Company.id == req.company_id).first()
@@ -317,35 +366,115 @@ def override_website(company_id: int, url: str, db: Session = Depends(get_db)):
return {"status": "updated", "website": company.website}
@app.post("/api/companies/{company_id}/override/impressum")
def override_impressum(company_id: int, url: str, background_tasks: BackgroundTasks, db: Session = Depends(get_db)):
company = db.query(Company).filter(Company.id == company_id).first()
if not company:
raise HTTPException(404, detail="Company not found")
# Create or update manual impressum lock
existing = db.query(EnrichmentData).filter(
EnrichmentData.company_id == company_id,
EnrichmentData.source_type == "impressum_override"
).first()
if not existing:
db.add(EnrichmentData(
company_id=company_id,
source_type="impressum_override",
content={"url": url},
is_locked=True
))
else:
existing.content = {"url": url}
existing.is_locked = True
db.commit()
return {"status": "updated"}
@app.post("/api/companies/{company_id}/report-mistake")
def report_company_mistake(
company_id: int,
request: ReportMistakeRequest,
db: Session = Depends(get_db)
):
company = db.query(Company).filter(Company.id == company_id).first()
if not company:
raise HTTPException(404, detail="Company not found")
new_mistake = ReportedMistake(
company_id=company_id,
field_name=request.field_name,
wrong_value=request.wrong_value,
corrected_value=request.corrected_value,
source_url=request.source_url,
quote=request.quote,
user_comment=request.user_comment
)
db.add(new_mistake)
db.commit()
db.refresh(new_mistake)
logger.info(f"Reported mistake for company {company_id}: {request.field_name} -> {request.corrected_value}")
return {"status": "success", "mistake_id": new_mistake.id}
def run_wikipedia_reevaluation_task(company_id: int):
from .database import SessionLocal
db = SessionLocal()
try:
company = db.query(Company).filter(Company.id == company_id).first()

View File

@@ -58,6 +58,7 @@ class Company(Base):
# Relationships
signals = relationship("Signal", back_populates="company", cascade="all, delete-orphan")
enrichment_data = relationship("EnrichmentData", back_populates="company", cascade="all, delete-orphan")
reported_mistakes = relationship("ReportedMistake", back_populates="company", cascade="all, delete-orphan")
contacts = relationship("Contact", back_populates="company", cascade="all, delete-orphan")
@@ -203,6 +204,25 @@ class ImportLog(Base):
duplicate_rows = Column(Integer)
created_at = Column(DateTime, default=datetime.utcnow)
class ReportedMistake(Base):
__tablename__ = "reported_mistakes"
id = Column(Integer, primary_key=True, index=True)
company_id = Column(Integer, ForeignKey("companies.id"), index=True, nullable=False)
field_name = Column(String, nullable=False)
wrong_value = Column(Text, nullable=True)
corrected_value = Column(Text, nullable=True)
source_url = Column(String, nullable=True)
quote = Column(Text, nullable=True)
user_comment = Column(Text, nullable=True)
status = Column(String, default="PENDING", nullable=False) # PENDING, APPROVED, REJECTED
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
company = relationship("Company", back_populates="reported_mistakes")
# ==============================================================================
# UTILS
# ==============================================================================

View File

@@ -69,6 +69,26 @@ def migrate_tables():
logger.info(f"Adding column '{col}' to 'companies' table...")
cursor.execute(f"ALTER TABLE companies ADD COLUMN {col} {col_type}")
# 3. Create REPORTED_MISTAKES Table
logger.info("Checking 'reported_mistakes' table schema...")
cursor.execute("""
CREATE TABLE IF NOT EXISTS reported_mistakes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
company_id INTEGER NOT NULL,
field_name TEXT NOT NULL,
wrong_value TEXT,
corrected_value TEXT,
source_url TEXT,
quote TEXT,
user_comment TEXT,
status TEXT NOT NULL DEFAULT 'PENDING',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (company_id) REFERENCES companies (id)
)
""")
logger.info("Table 'reported_mistakes' ensured to exist.")
conn.commit()
logger.info("All migrations completed successfully.")