Loren commited on
Commit
f555d70
·
verified ·
1 Parent(s): 2214021

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +18 -0
  2. app/database.py +47 -0
  3. app/main.py +39 -0
  4. requirements.txt +4 -0
  5. script/create_sqlite_db.py +99 -0
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Utiliser Python 3.11 slim
2
+ FROM python:3.11-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # Copier le code et la base SQLite
7
+ COPY requirements.txt .
8
+ COPY app ./app
9
+ COPY data ./data/
10
+
11
+ # Installer les dépendances
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Exposer le port FastAPI
15
+ EXPOSE 8000
16
+
17
+ # Lancer FastAPI
18
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
app/database.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ from typing import List, Dict
3
+ from pathlib import Path
4
+
5
+ # Chemin vers la base SQLite
6
+ DB_PATH = Path("data/articles.db")
7
+
8
+ def get_connection():
9
+ conn = sqlite3.connect(DB_PATH)
10
+ conn.row_factory = sqlite3.Row
11
+ return conn
12
+
13
+ def fetch_tags() -> List[str]:
14
+ """Retourne tous les tags"""
15
+ conn = get_connection()
16
+ cur = conn.cursor()
17
+ cur.execute("SELECT tag_name FROM tags ORDER BY tag_name")
18
+ tags = [row["tag_name"] for row in cur.fetchall()]
19
+ conn.close()
20
+ return tags
21
+
22
+
23
+ def fetch_articles_by_tags(tags: List[str]) -> List[Dict]:
24
+ """
25
+ Retourne les articles correspondant aux tags.
26
+ """
27
+ if not tags:
28
+ return []
29
+
30
+ conn = get_connection()
31
+ conn.row_factory = sqlite3.Row
32
+ cur = conn.cursor()
33
+
34
+ # Créer la liste de placeholders "?" dynamiquement
35
+ placeholders = ",".join(["?"] * len(tags))
36
+
37
+ query = ("""SELECT a.article_id, a.article_title, a.article_url
38
+ FROM tags t, articles a, tag_article ta
39
+ WHERE ta.tag_id = t.tag_id
40
+ AND ta.article_id = a.article_id
41
+ AND t.tag_name IN (""" + placeholders + """)"""
42
+ )
43
+
44
+ cur.execute(query, tags)
45
+ results = [dict(row) for row in cur.fetchall()]
46
+ conn.close()
47
+ return results
app/main.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Query
2
+ from typing import List
3
+ from app import database
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+
6
+ app = FastAPI(
7
+ title="Articles API",
8
+ description="API pour récupérer articles et tags depuis SQLite",
9
+ version="1.0"
10
+ )
11
+
12
+ # CORS pour permettre l'accès depuis le navigateur
13
+ app.add_middleware(
14
+ CORSMiddleware,
15
+ allow_origins=["*"], # autorise toutes les origines
16
+ allow_credentials=True,
17
+ allow_methods=["*"],
18
+ allow_headers=["*"],
19
+ )
20
+
21
+ @app.get("/get_tags")
22
+ def get_tags():
23
+ """
24
+ Retourne la liste de tous les tags
25
+ """
26
+ tags = database.fetch_tags()
27
+ return {"tags": tags}
28
+
29
+ @app.get("/get_articles_with_tags")
30
+ def get_articles_with_tags(
31
+ tags: List[str] = Query(..., description="Liste des tags à filtrer"),
32
+ mode: str = Query("AND", description="Mode de filtrage : AND ou OR")
33
+ ):
34
+ """
35
+ Retourne les articles correspondant aux tags donnés
36
+ """
37
+ articles = database.fetch_articles_by_tags(tags, mode)
38
+ return {"tags": tags,
39
+ "articles": articles}
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi==0.109.2
2
+ uvicorn[standard]==0.23.2
3
+ pandas==2.1.1
4
+ pyarrow==12.0.1
script/create_sqlite_db.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ import pandas as pd
3
+ import itertools
4
+ import ast
5
+ import uuid
6
+ from pathlib import Path
7
+
8
+ # Initialisations
9
+ print("Initialisations ...")
10
+ DATA_DIR = Path("../data") # dossier parent du script
11
+ PARQUET_FILE = DATA_DIR / "medium_articles.parquet"
12
+ SQLITE_FILE = DATA_DIR / "articles.db"
13
+ # Créer le dossier data s'il n'existe pas
14
+ DATA_DIR.mkdir(exist_ok=True)
15
+
16
+ # Chargement des données
17
+ print("Chargement des données ...")
18
+ df = pd.read_parquet(PARQUET_FILE)
19
+
20
+ # Initialisations de la base SQLite
21
+ print("Initialisations de la base SQLite ...")
22
+ conn = sqlite3.connect(SQLITE_FILE)
23
+ cur = conn.cursor()
24
+
25
+ # Suppression des anciennes tables
26
+ cur.execute("DROP TABLE IF EXISTS tag_article")
27
+ cur.execute("DROP TABLE IF EXISTS tags")
28
+ cur.execute("DROP TABLE IF EXISTS articles")
29
+
30
+ # Création des tables Articles, Tags, et de la table d'association articles <-> tags
31
+ cur.execute("""
32
+ CREATE TABLE articles (
33
+ article_id TEXT PRIMARY KEY, -- UUID
34
+ article_title TEXT,
35
+ article_text TEXT,
36
+ article_url TEXT,
37
+ article_authors TEXT,
38
+ article_date TEXT -- YYYY-MM-DD
39
+ )""")
40
+
41
+ cur.execute("""
42
+ CREATE TABLE tags (
43
+ tag_id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ tag_name TEXT UNIQUE
45
+ )""")
46
+
47
+ cur.execute("""
48
+ CREATE TABLE tag_article (
49
+ tag_article_id INTEGER PRIMARY KEY AUTOINCREMENT,
50
+ article_id TEXT,
51
+ tag_id INTEGER,
52
+ FOREIGN KEY(article_id) REFERENCES articles(article_id),
53
+ FOREIGN KEY(tag_id) REFERENCES tags(tag_id)
54
+ )""")
55
+
56
+ # Extraction des tags en une liste unique
57
+ print("Extraction des tags en une liste unique ...")
58
+ df['list_tags'] = df['tags'].apply(lambda x: ast.literal_eval(x) if isinstance(x, str) else [])
59
+ # Extraire tous les tags uniques
60
+ all_tags = list(set(itertools.chain.from_iterable(df['list_tags'])))
61
+
62
+ # Insertion des tags dans la table
63
+ print("Insertion des tags dans la table ...")
64
+ cur.executemany("INSERT INTO tags (tag_name) VALUES (?)", [(tag,) for tag in all_tags])
65
+
66
+ # Récupération des correspondances tag_name -> tag_id
67
+ print("Récupération des correspondances tag_name -> tag_id ...")
68
+ cur.execute("SELECT tag_id, tag_name FROM tags")
69
+ dict_tag_map = {tag_name: tag_id for tag_id, tag_name in cur.fetchall()}
70
+
71
+ # Insertion des articles et table d'association dans les tables
72
+ print("Insertion des articles et table d'association dans les tables ...")
73
+ for _, row in df.iterrows():
74
+ # Détermination de l'id article
75
+ article_id = str(uuid.uuid4())
76
+
77
+ # Extraction de la date du timestamp
78
+ date_value = None
79
+ if pd.notna(row["timestamp"]):
80
+ try:
81
+ date_value = str(pd.to_datetime(row["timestamp"]).date())
82
+ except Exception:
83
+ date_value = None
84
+
85
+ # Insertion dans la table Articles
86
+ cur.execute("""
87
+ INSERT INTO articles (article_id, article_title, article_text, article_url, article_authors, article_date)
88
+ VALUES (?, ?, ?, ?, ?, ?)""",
89
+ (article_id, row["title"], row["text"], row["url"], row["authors"], date_value))
90
+
91
+ # Association aux tags
92
+ for tag_name in row['list_tags']:
93
+ tag_id = dict_tag_map[tag_name]
94
+ cur.execute("INSERT INTO tag_article (article_id, tag_id) VALUES (?, ?)",
95
+ (article_id, tag_id))
96
+
97
+ conn.commit()
98
+ conn.close()
99
+ print("Traitement terminé.")