Álvaro Valenzuela Valdes commited on
Commit
1c92e64
·
1 Parent(s): 1a73a61

feat: Final auto-sync logic, manual seed button and REW branding

Browse files
backend/app/main.py CHANGED
@@ -1,29 +1,27 @@
1
  from fastapi import FastAPI
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from app.routers import analysis, company, health, tenders, documents
4
- from app.config import settings
5
- from app.database import engine, Base
 
 
 
 
6
 
7
  # Create tables
8
  Base.metadata.create_all(bind=engine)
9
 
10
  app = FastAPI(title="AndesOps AI")
11
 
12
- origins = [
13
- "http://localhost:3000",
14
- "http://localhost:3001",
15
- "http://127.0.0.1:3000",
16
- ]
17
-
18
  app.add_middleware(
19
  CORSMiddleware,
20
- allow_origins=origins,
21
  allow_credentials=True,
22
  allow_methods=["*"],
23
  allow_headers=["*"],
24
  )
25
 
26
- # Move health under /api for consistency and to fix 404s
27
  app.include_router(health.router, prefix="/api", tags=["Health"])
28
  app.include_router(tenders.router, prefix="/api", tags=["Tenders"])
29
  app.include_router(analysis.router, prefix="/api", tags=["Analysis"])
@@ -32,81 +30,50 @@ app.include_router(documents.router, prefix="/api", tags=["Documents"])
32
 
33
  @app.on_event("startup")
34
  async def startup_event():
35
- from app.database import SessionLocal
36
- from app.models.tender import TenderModel
37
- from app.models.analysis import AnalysisHistoryModel
38
- from app.models.company import CompanyProfileModel
39
- from datetime import datetime, timedelta
40
- import json
41
-
42
  db = SessionLocal()
43
  try:
44
  count = db.query(TenderModel).count()
45
  if count == 0:
46
- print("Database empty. Seeding data...")
47
- # 1. Company Profile
48
- profile = CompanyProfileModel(
49
- name="Andes Digital Solutions",
50
- industry="Software Engineering & AI",
51
- services="Machine Learning, Custom ERP, Cloud Infrastructure",
52
- experience="10+ years delivering enterprise software for the public sector.",
53
- certifications="AWS Partner, ISO 9001, SCRUM Master Team",
54
- regions="Metropolitana, Valparaíso, Biobío, Araucanía",
55
- documents_available="RUT, Financial Statements 2023, Technical Portfolio, Staff Certifications"
56
- )
57
- db.add(profile)
58
-
59
- # 2. Software Tenders
60
- tenders_data = [
61
- TenderModel(
62
- code="2394-15-LR24",
63
- name="Implementación Sistema ERP para Red de Salud Oriente",
64
- description="Suministro, instalación y soporte de sistema de gestión de recursos empresariales para red hospitalaria.",
65
- buyer="Servicio de Salud Metropolitano",
66
- status="Publicada",
67
- closing_date=(datetime.now() + timedelta(days=20)).strftime("%Y-%m-%d"),
68
- estimated_amount=450000000,
69
- region="Metropolitana",
70
- sector="Tecnología de la Información",
71
- source="Mercado Público"
72
- ),
73
- TenderModel(
74
- code="5021-10-LP24",
75
- name="Plataforma de IA para Análisis de Datos Criminalísticos",
76
- description="Desarrollo de algoritmos de visión computacional y análisis predictivo para seguridad ciudadana.",
77
- buyer="Subsecretaría de Prevención del Delito",
78
- status="Publicada",
79
- closing_date=(datetime.now() + timedelta(days=12)).strftime("%Y-%m-%d"),
80
- estimated_amount=180000000,
81
- region="Metropolitana",
82
- sector="Software & IA",
83
- source="Mercado Público"
84
- )
85
- ]
86
- for t in tenders_data:
87
- db.add(t)
88
 
89
- # 3. Initial Analysis
90
- history = AnalysisHistoryModel(
91
- tender_code="5021-10-LP24",
92
- tender_name="Plataforma de IA para Análisis de Datos Criminalísticos",
93
- decision="Recommended",
94
- score=92,
95
- summary="Oportunidad estratégica de alto valor. Tenemos el stack tecnológico (Gemini, Python) y la experiencia previa en seguridad ciudadana.",
96
- risks=json.dumps([
97
- {"severity": "High", "description": "Requisito de disponibilidad 99.9% 24/7"},
98
- {"severity": "Medium", "description": "Integración con bases de datos legacy de Carabineros"}
99
- ]),
100
- technical_analysis="Factibilidad técnica excelente.",
101
- legal_analysis="Cumplimos con las bases.",
102
- commercial_analysis="ROI del 35%.",
103
- proposal_draft="Propuesta técnica...",
104
- report_markdown="# Reporte",
105
- created_at=datetime.now()
106
- )
107
- db.add(history)
 
 
 
 
 
 
108
  db.commit()
109
- print("Database seeded successfully!")
 
110
  finally:
111
  db.close()
112
 
 
1
  from fastapi import FastAPI
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from app.routers import analysis, company, health, tenders, documents
4
+ from app.database import engine, Base, SessionLocal
5
+ from app.models.tender import TenderModel
6
+ from app.models.analysis import AnalysisHistoryModel
7
+ from app.models.company import CompanyProfileModel
8
+ from datetime import datetime, timedelta
9
+ import json
10
 
11
  # Create tables
12
  Base.metadata.create_all(bind=engine)
13
 
14
  app = FastAPI(title="AndesOps AI")
15
 
 
 
 
 
 
 
16
  app.add_middleware(
17
  CORSMiddleware,
18
+ allow_origins=["*"],
19
  allow_credentials=True,
20
  allow_methods=["*"],
21
  allow_headers=["*"],
22
  )
23
 
24
+ # Routes
25
  app.include_router(health.router, prefix="/api", tags=["Health"])
26
  app.include_router(tenders.router, prefix="/api", tags=["Tenders"])
27
  app.include_router(analysis.router, prefix="/api", tags=["Analysis"])
 
30
 
31
  @app.on_event("startup")
32
  async def startup_event():
 
 
 
 
 
 
 
33
  db = SessionLocal()
34
  try:
35
  count = db.query(TenderModel).count()
36
  if count == 0:
37
+ print("Auto-seeding database...")
38
+ # Basic Company Profile
39
+ if not db.query(CompanyProfileModel).first():
40
+ db.add(CompanyProfileModel(
41
+ name="Andes Digital Solutions",
42
+ industry="Software Engineering",
43
+ services="AI & Cloud",
44
+ experience="10 years",
45
+ regions="Nacional",
46
+ documents_available="RUT, Portfolio"
47
+ ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # Software Tenders
50
+ db.add(TenderModel(
51
+ code="2394-15-LR24",
52
+ name="ERP Implementation - Health Sector",
53
+ description="Large scale ERP for hospital network.",
54
+ buyer="Servicio de Salud",
55
+ status="Publicada",
56
+ closing_date=(datetime.now() + timedelta(days=15)).strftime("%Y-%m-%d"),
57
+ estimated_amount=450000000,
58
+ region="Metropolitana",
59
+ sector="Software",
60
+ source="Mercado Público"
61
+ ))
62
+ db.add(TenderModel(
63
+ code="5021-10-LP24",
64
+ name="AI Platform for Crime Analysis",
65
+ description="Machine learning and predictive models.",
66
+ buyer="Subsecretaría de Prevención del Delito",
67
+ status="Publicada",
68
+ closing_date=(datetime.now() + timedelta(days=10)).strftime("%Y-%m-%d"),
69
+ estimated_amount=180000000,
70
+ region="Nacional",
71
+ sector="AI & Software",
72
+ source="Mercado Público"
73
+ ))
74
  db.commit()
75
+ except Exception as e:
76
+ print(f"Seed error: {e}")
77
  finally:
78
  db.close()
79
 
frontend/components/SystemInfo.tsx CHANGED
@@ -1,6 +1,27 @@
1
  "use client";
2
 
 
 
 
3
  export default function SystemInfo() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  const techStack = [
5
  { name: "FastAPI", role: "Backend Engine", desc: "High-performance Python framework for AI orchestration." },
6
  { name: "Next.js 14", role: "Frontend Framework", desc: "Modern React framework with server-side capabilities." },
@@ -20,11 +41,25 @@ export default function SystemInfo() {
20
  REW
21
  </div>
22
  <h2 className="text-4xl font-bold text-white mb-4 tracking-tight">About AndesOps AI</h2>
23
- <p className="text-slate-400 max-w-2xl mx-auto text-lg leading-relaxed">
24
  AndesOps AI is a state-of-the-art platform developed by <span className="text-white font-bold italic">REW Agency</span>.
25
  Engineered to transform public procurement through agentic intelligence.
26
  </p>
27
- <div className="mt-8 flex items-center justify-center gap-6">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  <a href="https://www.rew.cl" target="_blank" className="px-6 py-3 rounded-2xl bg-white/5 border border-white/10 text-sm font-bold text-slate-300 hover:bg-white/10 transition-all">
29
  Visit rew.cl
30
  </a>
 
1
  "use client";
2
 
3
+ import { useState } from "react";
4
+ import { syncDatabase } from "../lib/api";
5
+
6
  export default function SystemInfo() {
7
+ const [isSyncing, setIsSyncing] = useState(false);
8
+ const [syncStatus, setSyncStatus] = useState<string | null>(null);
9
+
10
+ const handleSync = async () => {
11
+ setIsSyncing(true);
12
+ setSyncStatus("Syncing...");
13
+ try {
14
+ await syncDatabase();
15
+ setSyncStatus("Success! Refreshing...");
16
+ setTimeout(() => window.location.reload(), 1500);
17
+ } catch (e) {
18
+ setSyncStatus("Failed to sync.");
19
+ console.error(e);
20
+ } finally {
21
+ setIsSyncing(false);
22
+ }
23
+ };
24
+
25
  const techStack = [
26
  { name: "FastAPI", role: "Backend Engine", desc: "High-performance Python framework for AI orchestration." },
27
  { name: "Next.js 14", role: "Frontend Framework", desc: "Modern React framework with server-side capabilities." },
 
41
  REW
42
  </div>
43
  <h2 className="text-4xl font-bold text-white mb-4 tracking-tight">About AndesOps AI</h2>
44
+ <p className="text-slate-400 max-w-2xl mx-auto text-lg leading-relaxed mb-8">
45
  AndesOps AI is a state-of-the-art platform developed by <span className="text-white font-bold italic">REW Agency</span>.
46
  Engineered to transform public procurement through agentic intelligence.
47
  </p>
48
+
49
+ {/* Rescue Button */}
50
+ <div className="flex flex-col items-center gap-4 mb-12">
51
+ <button
52
+ onClick={handleSync}
53
+ disabled={isSyncing}
54
+ className="px-8 py-4 rounded-2xl premium-gradient text-white font-bold shadow-xl shadow-purple-500/20 active:scale-95 transition-all disabled:opacity-50"
55
+ >
56
+ {isSyncing ? "Initializing..." : "Seed Demo Data (Manual Sync)"}
57
+ </button>
58
+ {syncStatus && <p className="text-xs font-mono text-purple-400">{syncStatus}</p>}
59
+ <p className="text-[10px] text-slate-500 max-w-xs uppercase tracking-widest">Use this if the dashboard shows 0 items on first load</p>
60
+ </div>
61
+
62
+ <div className="flex items-center justify-center gap-6">
63
  <a href="https://www.rew.cl" target="_blank" className="px-6 py-3 rounded-2xl bg-white/5 border border-white/10 text-sm font-bold text-slate-300 hover:bg-white/10 transition-all">
64
  Visit rew.cl
65
  </a>
frontend/lib/api.ts CHANGED
@@ -1,15 +1,6 @@
1
  import type { AnalysisHistoryItem, AnalysisResult, CompanyProfile, Tender } from "./types";
2
 
3
- const getApiBase = () => {
4
- if (process.env.NEXT_PUBLIC_API_BASE) return process.env.NEXT_PUBLIC_API_BASE;
5
- if (typeof window !== "undefined") {
6
- // If we are in a Hugging Face Space, we need to ensure we point to the right origin
7
- return window.location.origin;
8
- }
9
- return "http://localhost:8000";
10
- };
11
-
12
- const API_BASE = getApiBase();
13
 
14
  const jsonHeaders = {
15
  "Content-Type": "application/json",
@@ -113,3 +104,11 @@ export async function fetchAnalysisHistory(): Promise<AnalysisHistoryItem[]> {
113
  }
114
  return res.json();
115
  }
 
 
 
 
 
 
 
 
 
1
  import type { AnalysisHistoryItem, AnalysisResult, CompanyProfile, Tender } from "./types";
2
 
3
+ const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "";
 
 
 
 
 
 
 
 
 
4
 
5
  const jsonHeaders = {
6
  "Content-Type": "application/json",
 
104
  }
105
  return res.json();
106
  }
107
+
108
+ export async function syncDatabase() {
109
+ const res = await fetch(`${API_BASE}/api/tenders/sync`, { method: "POST" });
110
+ if (!res.ok) {
111
+ throw new Error("Error syncing database");
112
+ }
113
+ return res.json();
114
+ }