Loren commited on
Commit
70ca2a3
·
verified ·
1 Parent(s): 753fe36

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile +17 -15
  2. app/database.py +80 -80
  3. app/main.py +1 -1
  4. requirements.txt +5 -5
  5. script/create_dataset.py +10 -4
  6. script/test_api.py +4 -3
Dockerfile CHANGED
@@ -1,15 +1,17 @@
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
-
10
- # Installer les dépendances
11
- RUN pip install --no-cache-dir -r requirements.txt
12
-
13
-
14
- # Lancer FastAPI
15
- CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
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
+
10
+ # Installer les dépendances
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # Exposer port (Hugging Face Spaces utilise 7860 par d�faut)
14
+ EXPOSE 7860
15
+
16
+ # Lancer FastAPI
17
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
app/database.py CHANGED
@@ -1,81 +1,81 @@
1
- import os
2
- from typing import List, Dict
3
- import duckdb
4
- from huggingface_hub import hf_hub_download
5
-
6
- # Initialisations
7
- REPO_ID = "Loren/articles_database"
8
- cache_dir = "/tmp"
9
- os.makedirs(cache_dir, exist_ok=True)
10
- # Rediriger le cache HF globalement
11
- os.environ["HF_HOME"] = cache_dir
12
- os.environ["HF_DATASETS_CACHE"] = cache_dir
13
- os.environ["TRANSFORMERS_CACHE"] = cache_dir
14
-
15
- # Téléchargement des fichiers Parquet depuis Hugging Face
16
- articles_parquet = hf_hub_download(
17
- repo_id=REPO_ID,
18
- filename="articles.parquet",
19
- repo_type="dataset",
20
- cache_dir=cache_dir)
21
- tags_parquet = hf_hub_download(
22
- repo_id=REPO_ID,
23
- filename="tags.parquet",
24
- repo_type="dataset",
25
- cache_dir=cache_dir)
26
- tag_article_parquet = hf_hub_download(
27
- repo_id=REPO_ID,
28
- filename="tag_article.parquet",
29
- repo_type="dataset",
30
- cache_dir=cache_dir)
31
-
32
- # Connexion DuckDB en mémoire
33
- con = duckdb.connect()
34
-
35
- # Créer des tables DuckDB directement à partir des fichiers Parquet
36
- con.execute(f"CREATE VIEW articles AS SELECT * FROM parquet_scan('{articles_parquet}')")
37
- con.execute(f"CREATE VIEW tags AS SELECT * FROM parquet_scan('{tags_parquet}')")
38
- con.execute(f"CREATE VIEW tag_article AS SELECT * FROM parquet_scan('{tag_article_parquet}')")
39
-
40
- # Fonctions d'accès aux données
41
-
42
- def fetch_tags() -> List[str]:
43
- """
44
- Récupère la liste de tous les tags disponibles dans la base de données.
45
-
46
- Returns:
47
- List[str]: Une liste de chaînes de caractères correspondant aux noms des tags, triés par ordre alphabétique.
48
- """
49
- query = "SELECT tag_name FROM tags ORDER BY tag_name"
50
- result = con.execute(query).fetchall()
51
- return [row[0] for row in result]
52
-
53
- def fetch_articles_by_tags(tags: List[str]) -> List[Dict]:
54
- """
55
- Récupère les articles associés à un ou plusieurs tags.
56
-
57
- Args:
58
- tags (List[str]): Une liste de noms de tags pour filtrer les articles.
59
-
60
- Returns:
61
- List[Dict]: Une liste de dictionnaires, chacun représentant un article avec les clés:
62
- - 'article_id': ID de l'article
63
- - 'article_title': Titre de l'article
64
- - 'article_url': URL de l'article
65
-
66
- Notes:
67
- - Si la liste `tags` est vide, la fonction retourne une liste vide.
68
- - Les résultats incluent uniquement les articles correspondant à au moins un des tags fournis.
69
- """
70
- if not tags:
71
- return []
72
-
73
- placeholders = ",".join(["?"] * len(tags))
74
- query = f"""SELECT distinct a.article_id, a.article_title, a.article_url
75
- FROM tags t, tag_article ta, articles a
76
- WHERE t.tag_id = ta.tag_id
77
- AND ta.article_id = a.article_id
78
- AND t.tag_name IN ({placeholders})
79
- """
80
- result = con.execute(query, tags).fetchdf()
81
  return result.to_dict(orient="records")
 
1
+ import os
2
+ from typing import List, Dict
3
+ import duckdb
4
+ from huggingface_hub import hf_hub_download
5
+
6
+ # Initialisations
7
+ REPO_ID = "Loren/articles_database"
8
+ cache_dir = "/tmp"
9
+ os.makedirs(cache_dir, exist_ok=True)
10
+ # Rediriger le cache HF globalement
11
+ os.environ["HF_HOME"] = cache_dir
12
+ os.environ["HF_DATASETS_CACHE"] = cache_dir
13
+ os.environ["TRANSFORMERS_CACHE"] = cache_dir
14
+
15
+ # Téléchargement des fichiers Parquet depuis Hugging Face
16
+ articles_parquet = hf_hub_download(
17
+ repo_id=REPO_ID,
18
+ filename="articles.parquet",
19
+ repo_type="dataset",
20
+ cache_dir=cache_dir)
21
+ tags_parquet = hf_hub_download(
22
+ repo_id=REPO_ID,
23
+ filename="tags.parquet",
24
+ repo_type="dataset",
25
+ cache_dir=cache_dir)
26
+ tag_article_parquet = hf_hub_download(
27
+ repo_id=REPO_ID,
28
+ filename="tag_article.parquet",
29
+ repo_type="dataset",
30
+ cache_dir=cache_dir)
31
+
32
+ # Connexion DuckDB en mémoire
33
+ con = duckdb.connect()
34
+
35
+ # Créer des tables DuckDB directement à partir des fichiers Parquet
36
+ con.execute(f"CREATE VIEW articles AS SELECT * FROM parquet_scan('{articles_parquet}')")
37
+ con.execute(f"CREATE VIEW tags AS SELECT * FROM parquet_scan('{tags_parquet}')")
38
+ con.execute(f"CREATE VIEW tag_article AS SELECT * FROM parquet_scan('{tag_article_parquet}')")
39
+
40
+ # Fonctions d'accès aux données
41
+
42
+ def fetch_tags() -> List[str]:
43
+ """
44
+ Récupère la liste de tous les tags disponibles dans la base de données.
45
+
46
+ Returns:
47
+ List[str]: Une liste de chaînes de caractères correspondant aux noms des tags, triés par ordre alphabétique.
48
+ """
49
+ query = "SELECT tag_name FROM tags ORDER BY tag_name"
50
+ result = con.execute(query).fetchall()
51
+ return [row[0] for row in result]
52
+
53
+ def fetch_articles_by_tags(tags: List[str]) -> List[Dict]:
54
+ """
55
+ Récupère les articles associés à un ou plusieurs tags.
56
+
57
+ Args:
58
+ tags (List[str]): Une liste de noms de tags pour filtrer les articles.
59
+
60
+ Returns:
61
+ List[Dict]: Une liste de dictionnaires, chacun représentant un article avec les clés:
62
+ - 'article_id': ID de l'article
63
+ - 'article_title': Titre de l'article
64
+ - 'article_url': URL de l'article
65
+
66
+ Notes:
67
+ - Si la liste `tags` est vide, la fonction retourne une liste vide.
68
+ - Les résultats incluent uniquement les articles correspondant à au moins un des tags fournis.
69
+ """
70
+ if not tags:
71
+ return []
72
+
73
+ placeholders = ",".join(["?"] * len(tags))
74
+ query = f"""SELECT distinct a.article_id, a.article_title, a.article_url
75
+ FROM tags t, tag_article ta, articles a
76
+ WHERE t.tag_id = ta.tag_id
77
+ AND ta.article_id = a.article_id
78
+ AND t.tag_name IN ({placeholders})
79
+ """
80
+ result = con.execute(query, tags).fetchdf()
81
  return result.to_dict(orient="records")
app/main.py CHANGED
@@ -34,6 +34,6 @@ def get_articles_with_tags(
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}
 
34
  """
35
  Retourne les articles correspondant aux tags donnés
36
  """
37
+ articles = database.fetch_articles_by_tags(tags)
38
  return {"tags": tags,
39
  "articles": articles}
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
- fastapi==0.109.2
2
- uvicorn[standard]==0.23.2
3
- pandas==2.1.1
4
- pyarrow==12.0.1
5
- huggingface_hub==0.35.3
6
  duckdb==1.4.0
 
1
+ fastapi==0.109.2
2
+ uvicorn[standard]==0.23.2
3
+ pandas==2.1.1
4
+ pyarrow==12.0.1
5
+ huggingface_hub==0.35.3
6
  duckdb==1.4.0
script/create_dataset.py CHANGED
@@ -86,11 +86,11 @@ CREATE TABLE tag_article (
86
  FOREIGN KEY(tag_id) REFERENCES tags(tag_id)
87
  )""")
88
 
89
- # Extraction des tags en une liste unique
90
- print("Extraction des tags en une liste unique ...")
91
  df['list_tags'] = df['tags'].apply(lambda x: ast.literal_eval(x) if isinstance(x, str) else [])
92
  # Extraire tous les tags uniques
93
- all_tags = list(set(itertools.chain.from_iterable(df['list_tags'])))
94
  # Comptage du nombre d'occurrences de chaque tag
95
  tag_counts = Counter(all_tags)
96
  # On ne va conserver que les tags avec au moins 100 occurrences
@@ -134,7 +134,13 @@ for _, row in df.iterrows():
134
  except:
135
  pass
136
 
137
- # Commit et fermeture de la connexion
 
 
 
 
 
 
138
  print("Commit ...")
139
  conn.commit()
140
 
 
86
  FOREIGN KEY(tag_id) REFERENCES tags(tag_id)
87
  )""")
88
 
89
+ # Extraction des tags en une liste
90
+ print("Extraction des tags en une liste ...")
91
  df['list_tags'] = df['tags'].apply(lambda x: ast.literal_eval(x) if isinstance(x, str) else [])
92
  # Extraire tous les tags uniques
93
+ all_tags = list(itertools.chain.from_iterable(df['list_tags']))
94
  # Comptage du nombre d'occurrences de chaque tag
95
  tag_counts = Counter(all_tags)
96
  # On ne va conserver que les tags avec au moins 100 occurrences
 
134
  except:
135
  pass
136
 
137
+ print("-> ", len(list_tags), " tags")
138
+ cur.execute("SELECT COUNT(*) FROM ma_table")
139
+ nb_lignes = cur.fetchone()[0]
140
+ print("-> ", nb_lignes, " associations articles <-> tags")
141
+ print("-> ", len(df), " articles")
142
+
143
+ # Commit
144
  print("Commit ...")
145
  conn.commit()
146
 
script/test_api.py CHANGED
@@ -1,6 +1,6 @@
1
  import requests
2
 
3
- BASE_URL = "http://localhost:8000" # ⚠️ changer en URL HF Space si déployé
4
 
5
  def test_get_tags():
6
  print("🔍 Test: /get_tags")
@@ -9,7 +9,7 @@ def test_get_tags():
9
  print("❌ Erreur", resp.status_code, resp.text)
10
  return
11
  tags = resp.json().get("tags", [])
12
- print(f"✅ {len(tags)} tags récupérés : {tags[:10]}...") # affiche 10 premiers
13
 
14
  return tags
15
 
@@ -29,7 +29,8 @@ def test_get_articles_with_tags(tags):
29
 
30
  def main():
31
  tags = test_get_tags()
32
- test_get_articles_with_tags(tags)
 
33
 
34
  if __name__ == "__main__":
35
  main()
 
1
  import requests
2
 
3
+ BASE_URL = "https://loren-api-search-articles.hf.space"
4
 
5
  def test_get_tags():
6
  print("🔍 Test: /get_tags")
 
9
  print("❌ Erreur", resp.status_code, resp.text)
10
  return
11
  tags = resp.json().get("tags", [])
12
+ print(f"✅ {len(tags)} tags récupérés : {tags[:3]}...") # affiche 3 premiers
13
 
14
  return tags
15
 
 
29
 
30
  def main():
31
  tags = test_get_tags()
32
+ print(tags[0:1])
33
+ test_get_articles_with_tags(tags[0:1])
34
 
35
  if __name__ == "__main__":
36
  main()