Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
FinSim
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Fares
FinSim
Commits
7dfba0af
Commit
7dfba0af
authored
May 02, 2026
by
Fares
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Migrate backend to Supabase PostgreSQL
parent
57b01db5
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
98 additions
and
80 deletions
+98
-80
app.py
investment_engine/app.py
+29
-18
config.py
investment_engine/config.py
+2
-6
railway.toml
investment_engine/railway.toml
+9
-0
requirements.txt
investment_engine/requirements.txt
+23
-0
database.py
investment_engine/services/database.py
+35
-56
No files found.
investment_engine/app.py
View file @
7dfba0af
...
@@ -8,9 +8,11 @@ import uuid
...
@@ -8,9 +8,11 @@ import uuid
import
bcrypt
import
bcrypt
from
datetime
import
datetime
from
datetime
import
datetime
from
fastapi
import
FastAPI
,
HTTPException
,
Request
,
Depends
,
UploadFile
,
File
,
Form
from
fastapi
import
FastAPI
,
HTTPException
,
Request
,
Depends
,
UploadFile
,
File
,
Form
from
fastapi.middleware.cors
import
CORSMiddleware
from
fastapi.staticfiles
import
StaticFiles
from
fastapi.staticfiles
import
StaticFiles
from
fastapi.responses
import
FileResponse
,
JSONResponse
from
fastapi.responses
import
FileResponse
,
JSONResponse
from
pydantic
import
BaseModel
,
Field
from
pydantic
import
BaseModel
,
Field
import
psycopg2.extras
from
typing
import
Optional
from
typing
import
Optional
from
dotenv
import
load_dotenv
from
dotenv
import
load_dotenv
...
@@ -31,6 +33,15 @@ except Exception as e:
...
@@ -31,6 +33,15 @@ except Exception as e:
app
=
FastAPI
(
title
=
"FinSim"
,
version
=
"2.0.0"
)
app
=
FastAPI
(
title
=
"FinSim"
,
version
=
"2.0.0"
)
# ── CORS — allow Flutter web (and any dev origin) ────────────────
app
.
add_middleware
(
CORSMiddleware
,
allow_origins
=
[
"*"
],
allow_credentials
=
True
,
allow_methods
=
[
"*"
],
allow_headers
=
[
"*"
],
)
# ── In-memory session store ──────────────────────────────────────
# ── In-memory session store ──────────────────────────────────────
_tokens
:
dict
[
str
,
dict
]
=
{}
# token -> {user_id, username, role, ...}
_tokens
:
dict
[
str
,
dict
]
=
{}
# token -> {user_id, username, role, ...}
...
@@ -89,7 +100,7 @@ def get_ui():
...
@@ -89,7 +100,7 @@ def get_ui():
def
login
(
req
:
LoginRequest
):
def
login
(
req
:
LoginRequest
):
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
cursor
.
execute
(
"SELECT * FROM users WHERE email =
%
s"
,
(
req
.
email
,))
cursor
.
execute
(
"SELECT * FROM users WHERE email =
%
s"
,
(
req
.
email
,))
user
=
cursor
.
fetchone
()
user
=
cursor
.
fetchone
()
...
@@ -131,10 +142,10 @@ def register(req: RegisterRequest):
...
@@ -131,10 +142,10 @@ def register(req: RegisterRequest):
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
()
cursor
=
conn
.
cursor
()
cursor
.
execute
(
cursor
.
execute
(
"INSERT INTO users (username, email, password, role) VALUES (
%
s,
%
s,
%
s, 'user')"
,
"INSERT INTO users (username, email, password, role) VALUES (
%
s,
%
s,
%
s, 'user')
RETURNING id
"
,
(
req
.
username
,
req
.
email
,
hashed
)
(
req
.
username
,
req
.
email
,
hashed
)
)
)
user_id
=
cursor
.
lastrowid
user_id
=
cursor
.
fetchone
()[
0
]
except
Exception
as
e
:
except
Exception
as
e
:
if
"Duplicate"
in
str
(
e
):
if
"Duplicate"
in
str
(
e
):
raise
HTTPException
(
status_code
=
409
,
detail
=
"Username or email already exists"
)
raise
HTTPException
(
status_code
=
409
,
detail
=
"Username or email already exists"
)
...
@@ -158,7 +169,7 @@ def get_me(user: dict = Depends(get_current_user)):
...
@@ -158,7 +169,7 @@ def get_me(user: dict = Depends(get_current_user)):
# Refresh from DB
# Refresh from DB
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
cursor
.
execute
(
"SELECT total_score, quizzes_taken FROM users WHERE id =
%
s"
,
(
user
[
"user_id"
],))
cursor
.
execute
(
"SELECT total_score, quizzes_taken FROM users WHERE id =
%
s"
,
(
user
[
"user_id"
],))
fresh
=
cursor
.
fetchone
()
fresh
=
cursor
.
fetchone
()
if
fresh
:
if
fresh
:
...
@@ -369,7 +380,7 @@ def get_scenarios(page: int = 1, limit: int = 12, difficulty: str = "", category
...
@@ -369,7 +380,7 @@ def get_scenarios(page: int = 1, limit: int = 12, difficulty: str = "", category
where_sql
=
" AND "
.
join
(
where_clauses
)
where_sql
=
" AND "
.
join
(
where_clauses
)
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
cursor
.
execute
(
f
"SELECT COUNT(*) as total FROM scenarios WHERE {where_sql}"
,
params
)
cursor
.
execute
(
f
"SELECT COUNT(*) as total FROM scenarios WHERE {where_sql}"
,
params
)
total
=
cursor
.
fetchone
()[
"total"
]
total
=
cursor
.
fetchone
()[
"total"
]
...
@@ -393,7 +404,7 @@ def get_scenarios(page: int = 1, limit: int = 12, difficulty: str = "", category
...
@@ -393,7 +404,7 @@ def get_scenarios(page: int = 1, limit: int = 12, difficulty: str = "", category
def
get_scenario_detail
(
scenario_id
:
str
):
def
get_scenario_detail
(
scenario_id
:
str
):
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
cursor
.
execute
(
"SELECT * FROM scenarios WHERE id =
%
s"
,
(
scenario_id
,))
cursor
.
execute
(
"SELECT * FROM scenarios WHERE id =
%
s"
,
(
scenario_id
,))
scenario
=
cursor
.
fetchone
()
scenario
=
cursor
.
fetchone
()
if
not
scenario
:
if
not
scenario
:
...
@@ -430,7 +441,7 @@ def start_quiz(req: StartQuizRequest, user: dict = Depends(get_current_user)):
...
@@ -430,7 +441,7 @@ def start_quiz(req: StartQuizRequest, user: dict = Depends(get_current_user)):
where_sql
=
" AND "
.
join
(
where_clauses
)
where_sql
=
" AND "
.
join
(
where_clauses
)
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
# Get random scenarios
# Get random scenarios
cursor
.
execute
(
cursor
.
execute
(
...
@@ -448,10 +459,10 @@ def start_quiz(req: StartQuizRequest, user: dict = Depends(get_current_user)):
...
@@ -448,10 +459,10 @@ def start_quiz(req: StartQuizRequest, user: dict = Depends(get_current_user)):
# Create quiz
# Create quiz
cursor
.
execute
(
cursor
.
execute
(
"INSERT INTO quizzes (title, description, time_limit, passing_score, created_by) VALUES (
%
s,
%
s,
%
s,
%
s,
%
s)"
,
"INSERT INTO quizzes (title, description, time_limit, passing_score, created_by) VALUES (
%
s,
%
s,
%
s,
%
s,
%
s)
RETURNING id
"
,
(
f
"Quick Quiz - {datetime.now().strftime('
%
b
%
d,
%
H:
%
M')}"
,
f
"{len(scenarios)} questions"
,
0
,
60
,
user
[
"user_id"
])
(
f
"Quick Quiz - {datetime.now().strftime('
%
b
%
d,
%
H:
%
M')}"
,
f
"{len(scenarios)} questions"
,
0
,
60
,
user
[
"user_id"
])
)
)
quiz_id
=
cursor
.
lastrowid
quiz_id
=
cursor
.
fetchone
()[
'id'
]
# Link scenarios
# Link scenarios
for
i
,
s
in
enumerate
(
scenarios
):
for
i
,
s
in
enumerate
(
scenarios
):
...
@@ -462,10 +473,10 @@ def start_quiz(req: StartQuizRequest, user: dict = Depends(get_current_user)):
...
@@ -462,10 +473,10 @@ def start_quiz(req: StartQuizRequest, user: dict = Depends(get_current_user)):
# Create attempt
# Create attempt
cursor
.
execute
(
cursor
.
execute
(
"INSERT INTO quiz_attempts (user_id, quiz_id, total_questions, status) VALUES (
%
s,
%
s,
%
s, 'in_progress')"
,
"INSERT INTO quiz_attempts (user_id, quiz_id, total_questions, status) VALUES (
%
s,
%
s,
%
s, 'in_progress')
RETURNING id
"
,
(
user
[
"user_id"
],
quiz_id
,
len
(
scenarios
))
(
user
[
"user_id"
],
quiz_id
,
len
(
scenarios
))
)
)
attempt_id
=
cursor
.
lastrowid
attempt_id
=
cursor
.
fetchone
()[
'id'
]
# Process scenarios for frontend
# Process scenarios for frontend
questions
=
[]
questions
=
[]
...
@@ -526,7 +537,7 @@ def submit_quiz(attempt_id: int, answers: list[AnswerRequest], user: dict = Depe
...
@@ -526,7 +537,7 @@ def submit_quiz(attempt_id: int, answers: list[AnswerRequest], user: dict = Depe
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
# Verify attempt belongs to user
# Verify attempt belongs to user
cursor
.
execute
(
"SELECT * FROM quiz_attempts WHERE id =
%
s AND user_id =
%
s"
,
(
attempt_id
,
user
[
"user_id"
]))
cursor
.
execute
(
"SELECT * FROM quiz_attempts WHERE id =
%
s AND user_id =
%
s"
,
(
attempt_id
,
user
[
"user_id"
]))
...
@@ -551,7 +562,7 @@ def submit_quiz(attempt_id: int, answers: list[AnswerRequest], user: dict = Depe
...
@@ -551,7 +562,7 @@ def submit_quiz(attempt_id: int, answers: list[AnswerRequest], user: dict = Depe
# Record history
# Record history
cursor
.
execute
(
cursor
.
execute
(
"INSERT INTO user_scenario_history (user_id, scenario_id, selected_answer, is_correct, attempt_id) VALUES (
%
s,
%
s,
%
s,
%
s,
%
s)"
,
"INSERT INTO user_scenario_history (user_id, scenario_id, selected_answer, is_correct, attempt_id) VALUES (
%
s,
%
s,
%
s,
%
s,
%
s)"
,
(
user
[
"user_id"
],
ans
.
scenario_id
,
ans
.
selected_answer
,
1
if
is_correct
else
0
,
attempt_id
)
(
user
[
"user_id"
],
ans
.
scenario_id
,
ans
.
selected_answer
,
is_correct
,
attempt_id
)
)
)
# Build explanation
# Build explanation
...
@@ -604,7 +615,7 @@ def submit_quiz(attempt_id: int, answers: list[AnswerRequest], user: dict = Depe
...
@@ -604,7 +615,7 @@ def submit_quiz(attempt_id: int, answers: list[AnswerRequest], user: dict = Depe
def
get_user_stats
(
user
:
dict
=
Depends
(
get_current_user
)):
def
get_user_stats
(
user
:
dict
=
Depends
(
get_current_user
)):
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
# Get overall stats
# Get overall stats
cursor
.
execute
(
"SELECT total_score, quizzes_taken FROM users WHERE id =
%
s"
,
(
user
[
"user_id"
],))
cursor
.
execute
(
"SELECT total_score, quizzes_taken FROM users WHERE id =
%
s"
,
(
user
[
"user_id"
],))
...
@@ -627,14 +638,14 @@ def get_user_stats(user: dict = Depends(get_current_user)):
...
@@ -627,14 +638,14 @@ def get_user_stats(user: dict = Depends(get_current_user)):
# Get accuracy
# Get accuracy
cursor
.
execute
(
cursor
.
execute
(
"SELECT COUNT(*) as total, SUM(is_correct) as correct FROM user_scenario_history WHERE user_id =
%
s"
,
"SELECT COUNT(*) as total, SUM(is_correct
::int
) as correct FROM user_scenario_history WHERE user_id =
%
s"
,
(
user
[
"user_id"
],)
(
user
[
"user_id"
],)
)
)
accuracy
=
cursor
.
fetchone
()
accuracy
=
cursor
.
fetchone
()
# Get category breakdown
# Get category breakdown
cursor
.
execute
(
cursor
.
execute
(
"""SELECT s.category, COUNT(*) as attempts, SUM(ush.is_correct) as correct
"""SELECT s.category, COUNT(*) as attempts, SUM(ush.is_correct
::int
) as correct
FROM user_scenario_history ush
FROM user_scenario_history ush
JOIN scenarios s ON ush.scenario_id = s.id
JOIN scenarios s ON ush.scenario_id = s.id
WHERE ush.user_id =
%
s
WHERE ush.user_id =
%
s
...
@@ -659,7 +670,7 @@ def get_user_stats(user: dict = Depends(get_current_user)):
...
@@ -659,7 +670,7 @@ def get_user_stats(user: dict = Depends(get_current_user)):
def
get_leaderboard
():
def
get_leaderboard
():
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
cursor
.
execute
(
cursor
.
execute
(
"""SELECT username, total_score, quizzes_taken, avatar,
"""SELECT username, total_score, quizzes_taken, avatar,
CASE WHEN quizzes_taken > 0 THEN ROUND(total_score / quizzes_taken) ELSE 0 END as avg_score
CASE WHEN quizzes_taken > 0 THEN ROUND(total_score / quizzes_taken) ELSE 0 END as avg_score
...
@@ -679,7 +690,7 @@ def get_leaderboard():
...
@@ -679,7 +690,7 @@ def get_leaderboard():
def
get_filters
():
def
get_filters
():
from
services.database
import
get_connection
from
services.database
import
get_connection
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
cursor
=
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
cursor
.
execute
(
"SELECT DISTINCT category FROM scenarios WHERE is_active = 1 AND category IS NOT NULL"
)
cursor
.
execute
(
"SELECT DISTINCT category FROM scenarios WHERE is_active = 1 AND category IS NOT NULL"
)
categories
=
[
r
[
"category"
]
for
r
in
cursor
.
fetchall
()]
categories
=
[
r
[
"category"
]
for
r
in
cursor
.
fetchall
()]
cursor
.
execute
(
"SELECT DISTINCT difficulty FROM scenarios WHERE is_active = 1 AND difficulty IS NOT NULL"
)
cursor
.
execute
(
"SELECT DISTINCT difficulty FROM scenarios WHERE is_active = 1 AND difficulty IS NOT NULL"
)
...
...
investment_engine/config.py
View file @
7dfba0af
...
@@ -16,12 +16,8 @@ class Settings(BaseSettings):
...
@@ -16,12 +16,8 @@ class Settings(BaseSettings):
# ── SerpAPI ─────────────────────────────────────────────
# ── SerpAPI ─────────────────────────────────────────────
SERPAPI_API_KEY
:
str
=
""
SERPAPI_API_KEY
:
str
=
""
# ── MySQL (Al-Arcade Remote DB) ───────────────────────
# ── Supabase (PostgreSQL) ─────────────────────────────────
MYSQL_HOST
:
str
=
"scenariodb.caprover.al-arcade.com"
SUPABASE_DB_URL
:
str
=
""
MYSQL_PORT
:
int
=
3306
MYSQL_USER
:
str
=
"root"
MYSQL_PASSWORD
:
str
=
"Alarcade123#"
MYSQL_DATABASE
:
str
=
"mcq_app"
# ── Defaults for Z-Score ──────────────────────────────
# ── Defaults for Z-Score ──────────────────────────────
DEFAULT_ZSCORE_WINDOW
:
int
=
100
DEFAULT_ZSCORE_WINDOW
:
int
=
100
...
...
investment_engine/railway.toml
0 → 100644
View file @
7dfba0af
[build]
builder
=
"DOCKERFILE"
dockerfilePath
=
"Dockerfile"
[deploy]
healthcheckPath
=
"/"
healthcheckTimeout
=
300
restartPolicyType
=
"ON_FAILURE"
restartPolicyMaxRetries
=
3
investment_engine/requirements.txt
0 → 100644
View file @
7dfba0af
bcrypt>=5.0.0
beautifulsoup4>=4.14.3
chromadb>=1.5.5
fastapi>=0.135.1
langchain>=0.3.0
langchain-chroma>=1.1.0
langchain-community>=0.4.1
langchain-core>=0.3.0
langchain-groq>=0.2.0
langchain-huggingface>=1.2.1
langgraph>=1.1.1
psycopg2-binary>=2.9.9
numpy>=1.24
polars>=1.38.1
pyarrow>=23.0.1
pydantic>=2.12.5
pydantic-settings>=2.13.1
pypdf>=6.9.2
python-dotenv>=1.2.2
python-multipart>=0.0.22
sentence-transformers>=5.3.0
uvicorn>=0.41.0
yfinance>=1.2.0
investment_engine/services/database.py
View file @
7dfba0af
"""
"""
Database layer —
MySQL operations connecting directly to CapRover instanc
e.
Database layer —
PostgreSQL operations connecting directly to Supabas
e.
"""
"""
import
json
import
json
import
mysql.connector
import
os
from
mysql.connector
import
pooling
import
psycopg2
from
psycopg2
import
pool
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
from
config
import
get_settings
from
config
import
get_settings
from
models
import
Scenario
,
ScenarioGenerationResult
from
models
import
Scenario
,
ScenarioGenerationResult
_pool
:
pool
.
SimpleConnectionPool
|
None
=
None
_pool
:
pooling
.
MySQLConnectionPool
|
None
=
None
def
_get_pool
()
->
pool
.
SimpleConnectionPool
:
def
_get_pool
()
->
pooling
.
MySQLConnectionPool
:
global
_pool
global
_pool
if
_pool
is
None
:
if
_pool
is
None
:
s
=
get_settings
()
s
=
get_settings
()
_pool
=
pooling
.
MySQLConnectionPool
(
# Fallback to SUPABASE_DB_URL from env or settings
pool_name
=
"scenario_pool"
,
db_url
=
os
.
environ
.
get
(
"SUPABASE_DB_URL"
)
or
s
.
SUPABASE_DB_URL
pool_size
=
5
,
if
not
db_url
:
host
=
s
.
MYSQL_HOST
,
raise
ValueError
(
"SUPABASE_DB_URL is not set."
)
port
=
s
.
MYSQL_PORT
,
user
=
s
.
MYSQL_USER
,
_pool
=
pool
.
SimpleConnectionPool
(
password
=
s
.
MYSQL_PASSWORD
,
1
,
10
,
d
atabase
=
s
.
MYSQL_DATABASE
,
d
sn
=
db_url
)
)
return
_pool
return
_pool
@
contextmanager
@
contextmanager
def
get_connection
():
def
get_connection
():
conn
=
_get_pool
()
.
get_connection
()
conn_pool
=
_get_pool
()
conn
=
conn_pool
.
getconn
()
try
:
try
:
yield
conn
yield
conn
conn
.
commit
()
conn
.
commit
()
...
@@ -40,36 +40,12 @@ def get_connection():
...
@@ -40,36 +40,12 @@ def get_connection():
conn
.
rollback
()
conn
.
rollback
()
raise
raise
finally
:
finally
:
conn
.
close
(
)
conn
_pool
.
putconn
(
conn
)
def
init_db
():
def
init_db
():
"""Create the scenarios table if it doesn't exist."""
"""No-op for Supabase as tables are managed via Supabase UI/Migrations."""
ddl
=
"""
pass
CREATE TABLE IF NOT EXISTS scenarios (
id VARCHAR(20) PRIMARY KEY,
title TEXT NOT NULL,
short_description TEXT,
givens_table JSON,
scenario_paragraph TEXT,
best_answer TEXT,
best_answer_rationale TEXT,
other_option1 TEXT,
other_option1_exp TEXT,
other_option2 TEXT,
other_option2_exp TEXT,
other_option3 TEXT,
other_option3_exp TEXT,
event_type VARCHAR(20) DEFAULT 'normal',
difficulty VARCHAR(20) DEFAULT 'medium',
category VARCHAR(100) DEFAULT 'General',
risk_level VARCHAR(20) DEFAULT 'Medium',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"""
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
()
cursor
.
execute
(
ddl
)
def
insert_scenario
(
s
:
Scenario
):
def
insert_scenario
(
s
:
Scenario
):
...
@@ -85,12 +61,12 @@ def insert_scenario(s: Scenario):
...
@@ -85,12 +61,12 @@ def insert_scenario(s: Scenario):
) VALUES (
) VALUES (
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s
)
)
ON
DUPLICATE KEY UPDATE
ON
CONFLICT (id) DO UPDATE SET
title =
VALUES(title)
,
title =
EXCLUDED.title
,
short_description =
VALUES(short_description)
,
short_description =
EXCLUDED.short_description
,
givens_table =
VALUES(givens_table)
,
givens_table =
EXCLUDED.givens_table
,
scenario_paragraph =
VALUES(scenario_paragraph)
,
scenario_paragraph =
EXCLUDED.scenario_paragraph
,
risk_level =
VALUES(risk_level)
risk_level =
EXCLUDED.risk_level
"""
"""
# Pad extra options if Gemini returns less than 3
# Pad extra options if Gemini returns less than 3
...
@@ -124,12 +100,12 @@ def insert_scenario(s: Scenario):
...
@@ -124,12 +100,12 @@ def insert_scenario(s: Scenario):
)
)
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
()
with
conn
.
cursor
()
as
cursor
:
cursor
.
execute
(
sql
,
params
)
cursor
.
execute
(
sql
,
params
)
def
insert_all_scenarios
(
result
:
ScenarioGenerationResult
)
->
int
:
def
insert_all_scenarios
(
result
:
ScenarioGenerationResult
)
->
int
:
"""Bulk insert all scenarios to
CapRover
database. Returns count inserted."""
"""Bulk insert all scenarios to
Supabase
database. Returns count inserted."""
count
=
0
count
=
0
for
s
in
result
.
scenarios
:
for
s
in
result
.
scenarios
:
insert_scenario
(
s
)
insert_scenario
(
s
)
...
@@ -147,11 +123,14 @@ def get_random_scenario() -> dict | None:
...
@@ -147,11 +123,14 @@ def get_random_scenario() -> dict | None:
other_option3, other_option3_exp,
other_option3, other_option3_exp,
event_type, difficulty, category, risk_level
event_type, difficulty, category, risk_level
FROM scenarios
FROM scenarios
ORDER BY RAND()
ORDER BY RAND
OM
()
LIMIT 1
LIMIT 1
"""
"""
import
psycopg2.extras
with
get_connection
()
as
conn
:
with
get_connection
()
as
conn
:
cursor
=
conn
.
cursor
(
dictionary
=
True
)
with
conn
.
cursor
(
cursor_factory
=
psycopg2
.
extras
.
RealDictCursor
)
as
cursor
:
cursor
.
execute
(
sql
)
cursor
.
execute
(
sql
)
row
=
cursor
.
fetchone
()
row
=
cursor
.
fetchone
()
if
row
:
row
=
dict
(
row
)
return
row
return
row
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment