Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,64 +2,134 @@
|
|
| 2 |
|
| 3 |
from fastapi import FastAPI, HTTPException
|
| 4 |
from pydantic import BaseModel
|
| 5 |
-
from typing import Union
|
| 6 |
import sqlite3
|
| 7 |
import os
|
|
|
|
| 8 |
|
| 9 |
# Define the Pydantic model for Item creation/update
|
| 10 |
class Item(BaseModel):
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
# Define the Pydantic model for Item update specifically (allowing partial updates)
|
| 15 |
-
# This model allows all fields to be optional, so you can update just 'name' or just 'description'
|
| 16 |
class ItemUpdate(BaseModel):
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
app = FastAPI()
|
| 21 |
|
| 22 |
# Define the path for your SQLite database file
|
| 23 |
-
# This will be created in the /app directory of your Hugging Face Space.
|
| 24 |
-
# Files in /app are generally persisted across restarts within a Space.
|
| 25 |
DATABASE_FILE = os.path.join(os.getcwd(), "data.db")
|
|
|
|
| 26 |
|
| 27 |
def get_db_connection():
|
| 28 |
"""Establishes and returns a SQLite database connection."""
|
| 29 |
try:
|
| 30 |
conn = sqlite3.connect(DATABASE_FILE)
|
| 31 |
-
# Configure connection to return rows as dictionaries for easier access
|
| 32 |
conn.row_factory = sqlite3.Row
|
| 33 |
return conn
|
| 34 |
except sqlite3.Error as err:
|
| 35 |
print(f"Error connecting to database: {err}")
|
| 36 |
-
# In a real-world scenario, you might want to log this error and potentially
|
| 37 |
-
# use a more sophisticated error handling strategy.
|
| 38 |
raise HTTPException(status_code=500, detail="Database connection error")
|
| 39 |
|
| 40 |
@app.on_event("startup")
|
| 41 |
async def startup_event():
|
| 42 |
"""
|
| 43 |
Initializes the database: creates the 'items' table if it doesn't exist.
|
|
|
|
| 44 |
This runs once when the FastAPI application starts.
|
| 45 |
"""
|
| 46 |
-
conn = None
|
| 47 |
try:
|
| 48 |
conn = get_db_connection()
|
| 49 |
cursor = conn.cursor()
|
|
|
|
|
|
|
| 50 |
cursor.execute("""
|
| 51 |
CREATE TABLE IF NOT EXISTS items (
|
| 52 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
)
|
| 56 |
""")
|
| 57 |
conn.commit()
|
| 58 |
print(f"Database table 'items' checked/created successfully at {DATABASE_FILE}.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
except Exception as e:
|
| 60 |
print(f"Error during database startup: {e}")
|
| 61 |
-
# If critical tables cannot be created, the app might not function.
|
| 62 |
-
# Consider raising the exception to prevent the app from starting in a broken state.
|
| 63 |
finally:
|
| 64 |
if conn:
|
| 65 |
conn.close()
|
|
@@ -67,20 +137,39 @@ async def startup_event():
|
|
| 67 |
@app.get("/")
|
| 68 |
async def root():
|
| 69 |
"""Root endpoint for the API."""
|
| 70 |
-
return {"message": "Welcome to the API!"}
|
| 71 |
|
| 72 |
@app.post("/items/")
|
| 73 |
-
async def create_item(item: Item):
|
| 74 |
"""Creates a new item in the database."""
|
| 75 |
conn = get_db_connection()
|
| 76 |
cursor = conn.cursor()
|
| 77 |
try:
|
| 78 |
-
query = "
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
conn.commit()
|
| 81 |
return {"message": "Item created successfully", "id": cursor.lastrowid}
|
| 82 |
except Exception as e:
|
| 83 |
-
conn.rollback()
|
| 84 |
raise HTTPException(status_code=500, detail=f"Error creating item: {e}")
|
| 85 |
finally:
|
| 86 |
cursor.close()
|
|
@@ -94,7 +183,6 @@ async def read_items():
|
|
| 94 |
try:
|
| 95 |
cursor.execute("SELECT * FROM items")
|
| 96 |
items = cursor.fetchall()
|
| 97 |
-
# Convert sqlite3.Row objects to dictionaries for JSON serialization
|
| 98 |
return {"items": [dict(item) for item in items]}
|
| 99 |
except Exception as e:
|
| 100 |
raise HTTPException(status_code=500, detail=f"Error reading items: {e}")
|
|
@@ -120,7 +208,7 @@ async def read_item(item_id: int):
|
|
| 120 |
conn.close()
|
| 121 |
|
| 122 |
@app.put("/items/{item_id}")
|
| 123 |
-
async def update_item(item_id: int, item: ItemUpdate):
|
| 124 |
"""Updates an existing item by its ID."""
|
| 125 |
conn = get_db_connection()
|
| 126 |
cursor = conn.cursor()
|
|
@@ -128,31 +216,68 @@ async def update_item(item_id: int, item: ItemUpdate): # Use ItemUpdate for part
|
|
| 128 |
updates = []
|
| 129 |
params = []
|
| 130 |
|
| 131 |
-
if item.
|
| 132 |
-
updates.append("
|
| 133 |
-
params.append(item.
|
| 134 |
-
if item.
|
| 135 |
-
updates.append("
|
| 136 |
-
params.append(item.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
if not updates:
|
| 139 |
-
# If no fields were provided for update, return a 400 Bad Request
|
| 140 |
raise HTTPException(status_code=400, detail="No fields to update provided")
|
| 141 |
|
| 142 |
query = f"UPDATE items SET {', '.join(updates)} WHERE id = ?"
|
| 143 |
-
params.append(item_id)
|
| 144 |
|
| 145 |
cursor.execute(query, tuple(params))
|
| 146 |
conn.commit()
|
| 147 |
|
| 148 |
if cursor.rowcount == 0:
|
| 149 |
-
# If no rows were affected, the item with the given ID was not found
|
| 150 |
raise HTTPException(status_code=404, detail="Item not found")
|
| 151 |
return {"message": "Item updated successfully"}
|
| 152 |
-
except HTTPException:
|
| 153 |
raise
|
| 154 |
except Exception as e:
|
| 155 |
-
conn.rollback()
|
| 156 |
raise HTTPException(status_code=500, detail=f"Error updating item: {e}")
|
| 157 |
finally:
|
| 158 |
cursor.close()
|
|
@@ -167,11 +292,10 @@ async def delete_item(item_id: int):
|
|
| 167 |
cursor.execute("DELETE FROM items WHERE id = ?", (item_id,))
|
| 168 |
conn.commit()
|
| 169 |
if cursor.rowcount == 0:
|
| 170 |
-
# If no rows were affected, the item with the given ID was not found
|
| 171 |
raise HTTPException(status_code=404, detail="Item not found")
|
| 172 |
return {"message": "Item deleted successfully"}
|
| 173 |
except Exception as e:
|
| 174 |
-
conn.rollback()
|
| 175 |
raise HTTPException(status_code=500, detail=f"Error deleting item: {e}")
|
| 176 |
finally:
|
| 177 |
cursor.close()
|
|
|
|
| 2 |
|
| 3 |
from fastapi import FastAPI, HTTPException
|
| 4 |
from pydantic import BaseModel
|
| 5 |
+
from typing import Union
|
| 6 |
import sqlite3
|
| 7 |
import os
|
| 8 |
+
import pandas as pd # Import pandas for CSV reading
|
| 9 |
|
| 10 |
# Define the Pydantic model for Item creation/update
|
| 11 |
class Item(BaseModel):
|
| 12 |
+
date: str
|
| 13 |
+
time: str
|
| 14 |
+
lat: float
|
| 15 |
+
lon: float
|
| 16 |
+
depth: float
|
| 17 |
+
ML: float
|
| 18 |
+
nstn: int
|
| 19 |
+
dmin: float
|
| 20 |
+
gap: int
|
| 21 |
+
trms: float
|
| 22 |
+
ERH: float
|
| 23 |
+
ERZ: float
|
| 24 |
+
fixed: str
|
| 25 |
+
nph: int
|
| 26 |
+
quality: str
|
| 27 |
|
| 28 |
# Define the Pydantic model for Item update specifically (allowing partial updates)
|
|
|
|
| 29 |
class ItemUpdate(BaseModel):
|
| 30 |
+
date: Union[str, None] = None
|
| 31 |
+
time: Union[str, None] = None
|
| 32 |
+
lat: Union[float, None] = None
|
| 33 |
+
lon: Union[float, None] = None
|
| 34 |
+
depth: Union[float, None] = None
|
| 35 |
+
ML: Union[float, None] = None
|
| 36 |
+
nstn: Union[int, None] = None
|
| 37 |
+
dmin: Union[float, None] = None
|
| 38 |
+
gap: Union[int, None] = None
|
| 39 |
+
trms: Union[float, None] = None
|
| 40 |
+
ERH: Union[float, None] = None
|
| 41 |
+
ERZ: Union[float, None] = None
|
| 42 |
+
fixed: Union[str, None] = None
|
| 43 |
+
nph: Union[int, None] = None
|
| 44 |
+
quality: Union[str, None] = None
|
| 45 |
|
| 46 |
app = FastAPI()
|
| 47 |
|
| 48 |
# Define the path for your SQLite database file
|
|
|
|
|
|
|
| 49 |
DATABASE_FILE = os.path.join(os.getcwd(), "data.db")
|
| 50 |
+
CSV_FILE_PATH = "GDMScatalog.csv" # Path to your CSV file
|
| 51 |
|
| 52 |
def get_db_connection():
|
| 53 |
"""Establishes and returns a SQLite database connection."""
|
| 54 |
try:
|
| 55 |
conn = sqlite3.connect(DATABASE_FILE)
|
|
|
|
| 56 |
conn.row_factory = sqlite3.Row
|
| 57 |
return conn
|
| 58 |
except sqlite3.Error as err:
|
| 59 |
print(f"Error connecting to database: {err}")
|
|
|
|
|
|
|
| 60 |
raise HTTPException(status_code=500, detail="Database connection error")
|
| 61 |
|
| 62 |
@app.on_event("startup")
|
| 63 |
async def startup_event():
|
| 64 |
"""
|
| 65 |
Initializes the database: creates the 'items' table if it doesn't exist.
|
| 66 |
+
Also, populates the database with data from the CSV file if the table is empty.
|
| 67 |
This runs once when the FastAPI application starts.
|
| 68 |
"""
|
| 69 |
+
conn = None
|
| 70 |
try:
|
| 71 |
conn = get_db_connection()
|
| 72 |
cursor = conn.cursor()
|
| 73 |
+
|
| 74 |
+
# Create table if it doesn't exist
|
| 75 |
cursor.execute("""
|
| 76 |
CREATE TABLE IF NOT EXISTS items (
|
| 77 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 78 |
+
date TEXT NOT NULL,
|
| 79 |
+
time TEXT NOT NULL,
|
| 80 |
+
lat REAL NOT NULL,
|
| 81 |
+
lon REAL NOT NULL,
|
| 82 |
+
depth REAL NOT NULL,
|
| 83 |
+
ML REAL NOT NULL,
|
| 84 |
+
nstn INTEGER NOT NULL,
|
| 85 |
+
dmin REAL NOT NULL,
|
| 86 |
+
gap INTEGER NOT NULL,
|
| 87 |
+
trms REAL NOT NULL,
|
| 88 |
+
ERH REAL NOT NULL,
|
| 89 |
+
ERZ REAL NOT NULL,
|
| 90 |
+
fixed TEXT NOT NULL,
|
| 91 |
+
nph INTEGER NOT NULL,
|
| 92 |
+
quality TEXT NOT NULL
|
| 93 |
)
|
| 94 |
""")
|
| 95 |
conn.commit()
|
| 96 |
print(f"Database table 'items' checked/created successfully at {DATABASE_FILE}.")
|
| 97 |
+
|
| 98 |
+
# Check if the table is empty and populate from CSV
|
| 99 |
+
cursor.execute("SELECT COUNT(*) FROM items")
|
| 100 |
+
count = cursor.fetchone()[0]
|
| 101 |
+
|
| 102 |
+
if count == 0:
|
| 103 |
+
print("Table 'items' is empty. Populating from CSV file...")
|
| 104 |
+
if os.path.exists(CSV_FILE_PATH):
|
| 105 |
+
try:
|
| 106 |
+
df = pd.read_csv(CSV_FILE_PATH)
|
| 107 |
+
# Convert DataFrame to a list of tuples for insertion
|
| 108 |
+
# Ensure the order of columns matches the INSERT query
|
| 109 |
+
data_to_insert = [
|
| 110 |
+
(row['date'], row['time'], row['lat'], row['lon'], row['depth'],
|
| 111 |
+
row['ML'], row['nstn'], row['dmin'], row['gap'], row['trms'],
|
| 112 |
+
row['ERH'], row['ERZ'], row['fixed'], row['nph'], row['quality'])
|
| 113 |
+
for index, row in df.iterrows()
|
| 114 |
+
]
|
| 115 |
+
|
| 116 |
+
insert_query = """
|
| 117 |
+
INSERT INTO items (date, time, lat, lon, depth, ML, nstn, dmin, gap, trms, ERH, ERZ, fixed, nph, quality)
|
| 118 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
| 119 |
+
"""
|
| 120 |
+
cursor.executemany(insert_query, data_to_insert)
|
| 121 |
+
conn.commit()
|
| 122 |
+
print(f"Successfully inserted {len(data_to_insert)} records from {CSV_FILE_PATH}.")
|
| 123 |
+
except Exception as e:
|
| 124 |
+
print(f"Error populating database from CSV: {e}")
|
| 125 |
+
conn.rollback() # Rollback if CSV insertion fails
|
| 126 |
+
else:
|
| 127 |
+
print(f"CSV file not found at {CSV_FILE_PATH}. Database not populated.")
|
| 128 |
+
else:
|
| 129 |
+
print(f"Table 'items' already contains {count} records. Skipping CSV import.")
|
| 130 |
+
|
| 131 |
except Exception as e:
|
| 132 |
print(f"Error during database startup: {e}")
|
|
|
|
|
|
|
| 133 |
finally:
|
| 134 |
if conn:
|
| 135 |
conn.close()
|
|
|
|
| 137 |
@app.get("/")
|
| 138 |
async def root():
|
| 139 |
"""Root endpoint for the API."""
|
| 140 |
+
return {"message": "Welcome to the API! https://cwadayi-sqlite-api.hf.space/items/"}
|
| 141 |
|
| 142 |
@app.post("/items/")
|
| 143 |
+
async def create_item(item: Item):
|
| 144 |
"""Creates a new item in the database."""
|
| 145 |
conn = get_db_connection()
|
| 146 |
cursor = conn.cursor()
|
| 147 |
try:
|
| 148 |
+
query = """
|
| 149 |
+
INSERT INTO items (date, time, lat, lon, depth, ML, nstn, dmin, gap, trms, ERH, ERZ, fixed, nph, quality)
|
| 150 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
| 151 |
+
"""
|
| 152 |
+
cursor.execute(query, (
|
| 153 |
+
item.date,
|
| 154 |
+
item.time,
|
| 155 |
+
item.lat,
|
| 156 |
+
item.lon,
|
| 157 |
+
item.depth,
|
| 158 |
+
item.ML,
|
| 159 |
+
item.nstn,
|
| 160 |
+
item.dmin,
|
| 161 |
+
item.gap,
|
| 162 |
+
item.trms,
|
| 163 |
+
item.ERH,
|
| 164 |
+
item.ERZ,
|
| 165 |
+
item.fixed,
|
| 166 |
+
item.nph,
|
| 167 |
+
item.quality
|
| 168 |
+
))
|
| 169 |
conn.commit()
|
| 170 |
return {"message": "Item created successfully", "id": cursor.lastrowid}
|
| 171 |
except Exception as e:
|
| 172 |
+
conn.rollback()
|
| 173 |
raise HTTPException(status_code=500, detail=f"Error creating item: {e}")
|
| 174 |
finally:
|
| 175 |
cursor.close()
|
|
|
|
| 183 |
try:
|
| 184 |
cursor.execute("SELECT * FROM items")
|
| 185 |
items = cursor.fetchall()
|
|
|
|
| 186 |
return {"items": [dict(item) for item in items]}
|
| 187 |
except Exception as e:
|
| 188 |
raise HTTPException(status_code=500, detail=f"Error reading items: {e}")
|
|
|
|
| 208 |
conn.close()
|
| 209 |
|
| 210 |
@app.put("/items/{item_id}")
|
| 211 |
+
async def update_item(item_id: int, item: ItemUpdate):
|
| 212 |
"""Updates an existing item by its ID."""
|
| 213 |
conn = get_db_connection()
|
| 214 |
cursor = conn.cursor()
|
|
|
|
| 216 |
updates = []
|
| 217 |
params = []
|
| 218 |
|
| 219 |
+
if item.date is not None:
|
| 220 |
+
updates.append("date = ?")
|
| 221 |
+
params.append(item.date)
|
| 222 |
+
if item.time is not None:
|
| 223 |
+
updates.append("time = ?")
|
| 224 |
+
params.append(item.time)
|
| 225 |
+
if item.lat is not None:
|
| 226 |
+
updates.append("lat = ?")
|
| 227 |
+
params.append(item.lat)
|
| 228 |
+
if item.lon is not None:
|
| 229 |
+
updates.append("lon = ?")
|
| 230 |
+
params.append(item.lon)
|
| 231 |
+
if item.depth is not None:
|
| 232 |
+
updates.append("depth = ?")
|
| 233 |
+
params.append(item.depth)
|
| 234 |
+
if item.ML is not None:
|
| 235 |
+
updates.append("ML = ?")
|
| 236 |
+
params.append(item.ML)
|
| 237 |
+
if item.nstn is not None:
|
| 238 |
+
updates.append("nstn = ?")
|
| 239 |
+
params.append(item.nstn)
|
| 240 |
+
if item.dmin is not None:
|
| 241 |
+
updates.append("dmin = ?")
|
| 242 |
+
params.append(item.dmin)
|
| 243 |
+
if item.gap is not None:
|
| 244 |
+
updates.append("gap = ?")
|
| 245 |
+
params.append(item.gap)
|
| 246 |
+
if item.trms is not None:
|
| 247 |
+
updates.append("trms = ?")
|
| 248 |
+
params.append(item.trms)
|
| 249 |
+
if item.ERH is not None:
|
| 250 |
+
updates.append("ERH = ?")
|
| 251 |
+
params.append(item.ERH)
|
| 252 |
+
if item.ERZ is not None:
|
| 253 |
+
updates.append("ERZ = ?")
|
| 254 |
+
params.append(item.ERZ)
|
| 255 |
+
if item.fixed is not None:
|
| 256 |
+
updates.append("fixed = ?")
|
| 257 |
+
params.append(item.fixed)
|
| 258 |
+
if item.nph is not None:
|
| 259 |
+
updates.append("nph = ?")
|
| 260 |
+
params.append(item.nph)
|
| 261 |
+
if item.quality is not None:
|
| 262 |
+
updates.append("quality = ?")
|
| 263 |
+
params.append(item.quality)
|
| 264 |
|
| 265 |
if not updates:
|
|
|
|
| 266 |
raise HTTPException(status_code=400, detail="No fields to update provided")
|
| 267 |
|
| 268 |
query = f"UPDATE items SET {', '.join(updates)} WHERE id = ?"
|
| 269 |
+
params.append(item_id)
|
| 270 |
|
| 271 |
cursor.execute(query, tuple(params))
|
| 272 |
conn.commit()
|
| 273 |
|
| 274 |
if cursor.rowcount == 0:
|
|
|
|
| 275 |
raise HTTPException(status_code=404, detail="Item not found")
|
| 276 |
return {"message": "Item updated successfully"}
|
| 277 |
+
except HTTPException:
|
| 278 |
raise
|
| 279 |
except Exception as e:
|
| 280 |
+
conn.rollback()
|
| 281 |
raise HTTPException(status_code=500, detail=f"Error updating item: {e}")
|
| 282 |
finally:
|
| 283 |
cursor.close()
|
|
|
|
| 292 |
cursor.execute("DELETE FROM items WHERE id = ?", (item_id,))
|
| 293 |
conn.commit()
|
| 294 |
if cursor.rowcount == 0:
|
|
|
|
| 295 |
raise HTTPException(status_code=404, detail="Item not found")
|
| 296 |
return {"message": "Item deleted successfully"}
|
| 297 |
except Exception as e:
|
| 298 |
+
conn.rollback()
|
| 299 |
raise HTTPException(status_code=500, detail=f"Error deleting item: {e}")
|
| 300 |
finally:
|
| 301 |
cursor.close()
|