Add listings/annonces feature: matrix view, ad content storage, copy-paste
This commit is contained in:
@@ -63,6 +63,21 @@ CREATE TABLE IF NOT EXISTS rentals (
|
||||
created_at TEXT DEFAULT (datetime('now', 'localtime'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS listings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
tool_id INTEGER NOT NULL REFERENCES tools(id) ON DELETE CASCADE,
|
||||
platform_id INTEGER NOT NULL REFERENCES platforms(id) ON DELETE CASCADE,
|
||||
is_active INTEGER DEFAULT 1,
|
||||
title TEXT DEFAULT '',
|
||||
description TEXT DEFAULT '',
|
||||
price REAL,
|
||||
url TEXT DEFAULT '',
|
||||
notes TEXT DEFAULT '',
|
||||
created_at TEXT DEFAULT (datetime('now', 'localtime')),
|
||||
updated_at TEXT DEFAULT (datetime('now', 'localtime')),
|
||||
UNIQUE(tool_id, platform_id)
|
||||
);
|
||||
|
||||
INSERT OR IGNORE INTO platforms (name) VALUES ('Direct');
|
||||
INSERT OR IGNORE INTO platforms (name) VALUES ('Leboncoin');
|
||||
"""
|
||||
|
||||
+2
-1
@@ -6,7 +6,7 @@ from datetime import datetime, date
|
||||
import os
|
||||
|
||||
from .database import init_db, get_db
|
||||
from .routers import tools, clients, rentals, platforms
|
||||
from .routers import tools, clients, rentals, platforms, listings
|
||||
|
||||
app = FastAPI(title="LocOutil")
|
||||
|
||||
@@ -21,6 +21,7 @@ app.include_router(tools.router)
|
||||
app.include_router(clients.router)
|
||||
app.include_router(rentals.router)
|
||||
app.include_router(platforms.router)
|
||||
app.include_router(listings.router)
|
||||
|
||||
@app.on_event("startup")
|
||||
def startup():
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from ..database import get_db
|
||||
|
||||
router = APIRouter(prefix="/api/listings", tags=["listings"])
|
||||
|
||||
class ListingUpsert(BaseModel):
|
||||
tool_id: int
|
||||
platform_id: int
|
||||
is_active: bool = True
|
||||
title: str = ""
|
||||
description: str = ""
|
||||
price: Optional[float] = None
|
||||
url: str = ""
|
||||
notes: str = ""
|
||||
|
||||
class ListingUpdate(BaseModel):
|
||||
is_active: Optional[bool] = None
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
price: Optional[float] = None
|
||||
url: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
def listing_full(conn, row):
|
||||
l = dict(row)
|
||||
tool = conn.execute("SELECT id, name, daily_price, weekend_price FROM tools WHERE id = ?", (l["tool_id"],)).fetchone()
|
||||
platform = conn.execute("SELECT id, name FROM platforms WHERE id = ?", (l["platform_id"],)).fetchone()
|
||||
l["tool"] = dict(tool) if tool else None
|
||||
l["platform"] = dict(platform) if platform else None
|
||||
return l
|
||||
|
||||
@router.get("")
|
||||
def list_listings(tool_id: Optional[int] = None, platform_id: Optional[int] = None):
|
||||
with get_db() as conn:
|
||||
q = "SELECT * FROM listings WHERE 1=1"
|
||||
params = []
|
||||
if tool_id:
|
||||
q += " AND tool_id = ?"; params.append(tool_id)
|
||||
if platform_id:
|
||||
q += " AND platform_id = ?"; params.append(platform_id)
|
||||
q += " ORDER BY tool_id, platform_id"
|
||||
rows = conn.execute(q, params).fetchall()
|
||||
return [listing_full(conn, r) for r in rows]
|
||||
|
||||
@router.get("/matrix")
|
||||
def listings_matrix():
|
||||
with get_db() as conn:
|
||||
tools = conn.execute("SELECT id, name, daily_price, weekend_price FROM tools ORDER BY name").fetchall()
|
||||
platforms = conn.execute("SELECT id, name FROM platforms ORDER BY name").fetchall()
|
||||
listings = conn.execute("SELECT * FROM listings").fetchall()
|
||||
listing_map = {(l["tool_id"], l["platform_id"]): dict(l) for l in listings}
|
||||
return {
|
||||
"tools": [dict(t) for t in tools],
|
||||
"platforms": [dict(p) for p in platforms],
|
||||
"listings": {f"{k[0]},{k[1]}": v for k, v in listing_map.items()}
|
||||
}
|
||||
|
||||
@router.post("", status_code=201)
|
||||
def create_or_update_listing(data: ListingUpsert):
|
||||
with get_db() as conn:
|
||||
existing = conn.execute(
|
||||
"SELECT id FROM listings WHERE tool_id = ? AND platform_id = ?",
|
||||
(data.tool_id, data.platform_id)
|
||||
).fetchone()
|
||||
if existing:
|
||||
conn.execute("""
|
||||
UPDATE listings SET is_active=?, title=?, description=?, price=?, url=?, notes=?,
|
||||
updated_at=datetime('now','localtime') WHERE id=?
|
||||
""", (1 if data.is_active else 0, data.title, data.description, data.price,
|
||||
data.url, data.notes, existing["id"]))
|
||||
row = conn.execute("SELECT * FROM listings WHERE id = ?", (existing["id"],)).fetchone()
|
||||
else:
|
||||
cur = conn.execute("""
|
||||
INSERT INTO listings (tool_id, platform_id, is_active, title, description, price, url, notes)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
""", (data.tool_id, data.platform_id, 1 if data.is_active else 0,
|
||||
data.title, data.description, data.price, data.url, data.notes))
|
||||
row = conn.execute("SELECT * FROM listings WHERE id = ?", (cur.lastrowid,)).fetchone()
|
||||
return listing_full(conn, row)
|
||||
|
||||
@router.put("/{listing_id}")
|
||||
def update_listing(listing_id: int, data: ListingUpdate):
|
||||
with get_db() as conn:
|
||||
if not conn.execute("SELECT id FROM listings WHERE id = ?", (listing_id,)).fetchone():
|
||||
raise HTTPException(404, "Annonce introuvable")
|
||||
fields, values = ["updated_at = datetime('now','localtime')"], []
|
||||
for field, val in data.model_dump(exclude_none=True).items():
|
||||
if field == "is_active":
|
||||
fields.append("is_active = ?"); values.append(1 if val else 0)
|
||||
else:
|
||||
fields.append(f"{field} = ?"); values.append(val)
|
||||
values.append(listing_id)
|
||||
conn.execute(f"UPDATE listings SET {', '.join(fields)} WHERE id = ?", values)
|
||||
return listing_full(conn, conn.execute("SELECT * FROM listings WHERE id = ?", (listing_id,)).fetchone())
|
||||
|
||||
@router.delete("/{listing_id}", status_code=204)
|
||||
def delete_listing(listing_id: int):
|
||||
with get_db() as conn:
|
||||
conn.execute("DELETE FROM listings WHERE id = ?", (listing_id,))
|
||||
|
||||
@router.post("/{listing_id}/toggle", status_code=200)
|
||||
def toggle_listing(listing_id: int):
|
||||
with get_db() as conn:
|
||||
row = conn.execute("SELECT * FROM listings WHERE id = ?", (listing_id,)).fetchone()
|
||||
if not row:
|
||||
raise HTTPException(404, "Annonce introuvable")
|
||||
new_status = 0 if row["is_active"] else 1
|
||||
conn.execute("UPDATE listings SET is_active = ?, updated_at = datetime('now','localtime') WHERE id = ?",
|
||||
(new_status, listing_id))
|
||||
return listing_full(conn, conn.execute("SELECT * FROM listings WHERE id = ?", (listing_id,)).fetchone())
|
||||
Reference in New Issue
Block a user