добавление отслеживания количества выгрузки
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -12,7 +12,7 @@ from fastapi.responses import FileResponse
|
|||||||
|
|
||||||
from config import DOCUMENTS_DIR, APP_TITLE, APP_DESCRIPTION, APP_VERSION
|
from config import DOCUMENTS_DIR, APP_TITLE, APP_DESCRIPTION, APP_VERSION
|
||||||
from utils import logger
|
from utils import logger
|
||||||
from api.schemas import ParserOneRequest, Parserall, Source, DownloadRange
|
from api.schemas import ParserOneRequest, Parserall, Source, DownloadRange, DownloadCountsResponse
|
||||||
from parsers import start_pars_one_istochnik, start_pars_two_istochnik, start_pars_all_istochnik
|
from parsers import start_pars_one_istochnik, start_pars_two_istochnik, start_pars_all_istochnik
|
||||||
import work_parser as wp
|
import work_parser as wp
|
||||||
|
|
||||||
@@ -178,6 +178,13 @@ def setup_routes(app: FastAPI) -> None:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Не удалось удалить архив: {e}")
|
logger.warning(f"Не удалось удалить архив: {e}")
|
||||||
|
|
||||||
|
def mark_as_downloaded():
|
||||||
|
try:
|
||||||
|
wp.mark_articles_as_downloaded(titles_from_db)
|
||||||
|
logger.info(f"Статьи помечены как скачанные: {len(titles_from_db)} записей")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка при обновлении download: {e}")
|
||||||
|
|
||||||
response = FileResponse(
|
response = FileResponse(
|
||||||
path=archive_path,
|
path=archive_path,
|
||||||
filename=archive_name,
|
filename=archive_name,
|
||||||
@@ -189,9 +196,27 @@ def setup_routes(app: FastAPI) -> None:
|
|||||||
response.headers["Access-Control-Expose-Headers"] = "Content-Disposition"
|
response.headers["Access-Control-Expose-Headers"] = "Content-Disposition"
|
||||||
|
|
||||||
background_tasks.add_task(cleanup_archive)
|
background_tasks.add_task(cleanup_archive)
|
||||||
|
background_tasks.add_task(mark_as_downloaded)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
# ==================== Выгрузка (download) ====================
|
||||||
|
|
||||||
|
@app.get("/download_counts", summary="Получить количество статей для выгрузки", response_model=DownloadCountsResponse)
|
||||||
|
async def get_download_counts():
|
||||||
|
"""
|
||||||
|
Возвращает количество статей для каждого поля (tematik, svodka, donesenie, bilutene, status),
|
||||||
|
где значение поля = TRUE и download = FALSE
|
||||||
|
"""
|
||||||
|
return wp.get_download_counts()
|
||||||
|
|
||||||
|
# @app.post("/mark_downloaded", summary="Отметить статьи как скачанные")
|
||||||
|
# async def mark_articles_as_downloaded(titles: List[str]):
|
||||||
|
# """
|
||||||
|
# Обновляет поле download = TRUE для списка заголовков статей
|
||||||
|
# """
|
||||||
|
# return wp.mark_articles_as_downloaded(titles)
|
||||||
|
|
||||||
@app.get("/logs", summary="Показать логи")
|
@app.get("/logs", summary="Показать логи")
|
||||||
async def get_logs():
|
async def get_logs():
|
||||||
with open("app.log", "r") as file:
|
with open("app.log", "r") as file:
|
||||||
|
|||||||
@@ -32,3 +32,11 @@ class DownloadRange(BaseModel):
|
|||||||
data_start: str
|
data_start: str
|
||||||
data_finish: str
|
data_finish: str
|
||||||
field_name: str = "status"
|
field_name: str = "status"
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadCountsResponse(BaseModel):
|
||||||
|
"""Ответ с количеством статей для выгрузки по каждому полю"""
|
||||||
|
tematik: int
|
||||||
|
svodka: int
|
||||||
|
donesenie: int
|
||||||
|
bilutene: int
|
||||||
|
|||||||
1
main.py
1
main.py
@@ -45,7 +45,6 @@ async def lifespan(app: FastAPI):
|
|||||||
total_minutes = int(idx * minutes_per_source)
|
total_minutes = int(idx * minutes_per_source)
|
||||||
scheduled_hour = total_minutes // 60
|
scheduled_hour = total_minutes // 60
|
||||||
scheduled_minute = total_minutes % 60
|
scheduled_minute = total_minutes % 60
|
||||||
|
|
||||||
# Для универсального парсера нужно передавать url и promt как аргументы
|
# Для универсального парсера нужно передавать url и promt как аргументы
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
scheduled_parser_universal,
|
scheduled_parser_universal,
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ def update_bd_and_create_document(
|
|||||||
data['donesenie'] = False
|
data['donesenie'] = False
|
||||||
data['bilutene'] = False
|
data['bilutene'] = False
|
||||||
data['other'] = other
|
data['other'] = other
|
||||||
|
data['download'] = False
|
||||||
|
|
||||||
# Сохранение в БД через pbd
|
# Сохранение в БД через pbd
|
||||||
parsed_data = wp.ParsedData(**data)
|
parsed_data = wp.ParsedData(**data)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class ParsedData(BaseModel):
|
|||||||
svodka: Optional[bool] = False
|
svodka: Optional[bool] = False
|
||||||
donesenie: Optional[bool] = False
|
donesenie: Optional[bool] = False
|
||||||
bilutene: Optional[bool] = False
|
bilutene: Optional[bool] = False
|
||||||
|
download: Optional[bool] = False
|
||||||
other: str
|
other: str
|
||||||
category: str
|
category: str
|
||||||
translation_text: str
|
translation_text: str
|
||||||
@@ -38,8 +39,8 @@ def save_parsed_data_to_db(data: ParsedData):
|
|||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
INSERT INTO url (url, parsed_at, title, original_text, article_date, status, viewed, tematik, svodka, donesenie, bilutene, other, category, translation_text, short_text)
|
INSERT INTO url (url, parsed_at, title, original_text, article_date, status, viewed, tematik, svodka, donesenie, download, bilutene, other, category, translation_text, short_text)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
ON CONFLICT (url) DO UPDATE SET
|
ON CONFLICT (url) DO UPDATE SET
|
||||||
parsed_at = EXCLUDED.parsed_at,
|
parsed_at = EXCLUDED.parsed_at,
|
||||||
title = EXCLUDED.title,
|
title = EXCLUDED.title,
|
||||||
@@ -48,6 +49,7 @@ def save_parsed_data_to_db(data: ParsedData):
|
|||||||
status = EXCLUDED.status,
|
status = EXCLUDED.status,
|
||||||
viewed = EXCLUDED.viewed,
|
viewed = EXCLUDED.viewed,
|
||||||
tematik = EXCLUDED.tematik,
|
tematik = EXCLUDED.tematik,
|
||||||
|
download = EXCLUDED.download,
|
||||||
svodka = EXCLUDED.svodka,
|
svodka = EXCLUDED.svodka,
|
||||||
donesenie = EXCLUDED.donesenie,
|
donesenie = EXCLUDED.donesenie,
|
||||||
bilutene = EXCLUDED.bilutene,
|
bilutene = EXCLUDED.bilutene,
|
||||||
@@ -55,7 +57,7 @@ def save_parsed_data_to_db(data: ParsedData):
|
|||||||
category = EXCLUDED.category,
|
category = EXCLUDED.category,
|
||||||
translation_text = EXCLUDED.translation_text,
|
translation_text = EXCLUDED.translation_text,
|
||||||
short_text = EXCLUDED.short_text;
|
short_text = EXCLUDED.short_text;
|
||||||
""", (data.url, data.parsed_at, data.title, data.original_text, data.article_date, data.status, data.viewed, data.tematik, data.svodka, data.donesenie, data.bilutene, data.other, data.category, data.translation_text, data.short_text))
|
""", (data.url, data.parsed_at, data.title, data.original_text, data.article_date, data.status, data.viewed, data.tematik, data.svodka, data.donesenie, data.download, data.bilutene, data.other, data.category, data.translation_text, data.short_text))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return {"status": "success", "message": "Данные успешно сохранены"}
|
return {"status": "success", "message": "Данные успешно сохранены"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -68,15 +70,7 @@ def save_parsed_data_to_db(data: ParsedData):
|
|||||||
|
|
||||||
def get_articles_by_filter(field_name: str, start_date: str, finish_date: str):
|
def get_articles_by_filter(field_name: str, start_date: str, finish_date: str):
|
||||||
"""
|
"""
|
||||||
Возвращает список заголовков статей по полю и диапазону дат
|
Возвращает список заголовков статей по полю и диапазону дат для выгрузки
|
||||||
|
|
||||||
Args:
|
|
||||||
field_name: имя поля (tematik, svodka, donesenie, bilutene, status)
|
|
||||||
start_date: дата начала в формате YYYY-MM-DD
|
|
||||||
finish_date: дата окончания в формате YYYY-MM-DD
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[str]: список заголовков (title)
|
|
||||||
"""
|
"""
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
@@ -100,6 +94,54 @@ def get_articles_by_filter(field_name: str, start_date: str, finish_date: str):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def get_download_counts():
|
||||||
|
"""
|
||||||
|
Возвращает количество статей для каждого поля, где поле = TRUE и download = FALSE
|
||||||
|
"""
|
||||||
|
conn = get_connection()
|
||||||
|
try:
|
||||||
|
allowed_fields = ['tematik', 'svodka', 'donesenie', 'bilutene']
|
||||||
|
|
||||||
|
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||||
|
counts = {}
|
||||||
|
for field in allowed_fields:
|
||||||
|
cur.execute(f"""
|
||||||
|
SELECT COUNT(*) as count FROM url
|
||||||
|
WHERE {field} = TRUE
|
||||||
|
AND download = FALSE;
|
||||||
|
""")
|
||||||
|
row = cur.fetchone()
|
||||||
|
counts[field] = row['count']
|
||||||
|
|
||||||
|
return counts
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка в get_download_counts: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def mark_articles_as_downloaded(titles: list):
|
||||||
|
"""
|
||||||
|
Обновляет download = TRUE для списка заголовков
|
||||||
|
"""
|
||||||
|
if not titles:
|
||||||
|
return {"message": "Список заголовков пуст", "updated_rows": 0}
|
||||||
|
|
||||||
|
conn = get_connection()
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
UPDATE url
|
||||||
|
SET download = TRUE
|
||||||
|
WHERE title = ANY(%s);
|
||||||
|
""", (titles,))
|
||||||
|
updated_rows = cur.rowcount
|
||||||
|
conn.commit()
|
||||||
|
return {"message": f"Статус download обновлён для {updated_rows} статей", "updated_rows": updated_rows}
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка в mark_articles_as_downloaded: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
# Глобальное подключение к БД
|
# Глобальное подключение к БД
|
||||||
conn = None
|
conn = None
|
||||||
|
|
||||||
@@ -367,7 +409,6 @@ def create_table_add_sourse():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка при создании таблицы sourse: {e}")
|
print(f"Ошибка при создании таблицы sourse: {e}")
|
||||||
|
|
||||||
|
|
||||||
def add_sources(url: str, promt: str, status: bool = False):
|
def add_sources(url: str, promt: str, status: bool = False):
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
@@ -384,7 +425,6 @@ def add_sources(url: str, promt: str, status: bool = False):
|
|||||||
print(f"Ошибка при добавлении источника: {e}")
|
print(f"Ошибка при добавлении источника: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_all_sources(category: str):
|
def get_all_sources(category: str):
|
||||||
"""Возвращает все записи из таблицы sourse. Сначала показываются записи со status=false"""
|
"""Возвращает все записи из таблицы sourse. Сначала показываются записи со status=false"""
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
@@ -408,7 +448,6 @@ def get_all_sources(category: str):
|
|||||||
print(f"Ошибка при получении источников: {e}")
|
print(f"Ошибка при получении источников: {e}")
|
||||||
return {"error": str(e), "sources": []}
|
return {"error": str(e), "sources": []}
|
||||||
|
|
||||||
|
|
||||||
def get_true_sources():
|
def get_true_sources():
|
||||||
"""Возвращает все записи из таблицы sourse. Сначала показываются записи со status=true"""
|
"""Возвращает все записи из таблицы sourse. Сначала показываются записи со status=true"""
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
@@ -427,7 +466,6 @@ def get_true_sources():
|
|||||||
print(f"Ошибка при получении источников: {e}")
|
print(f"Ошибка при получении источников: {e}")
|
||||||
return {"error": str(e), "sources": []}
|
return {"error": str(e), "sources": []}
|
||||||
|
|
||||||
|
|
||||||
def update_source_status(url: str, status: bool = True):
|
def update_source_status(url: str, status: bool = True):
|
||||||
"""Обновляет статус источника по URL"""
|
"""Обновляет статус источника по URL"""
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
@@ -443,7 +481,6 @@ def update_source_status(url: str, status: bool = True):
|
|||||||
print(f"Ошибка при обновлении статуса: {e}")
|
print(f"Ошибка при обновлении статуса: {e}")
|
||||||
return {"error": str(e), "updated_rows": 0}
|
return {"error": str(e), "updated_rows": 0}
|
||||||
|
|
||||||
|
|
||||||
def delete_sources(url: str):
|
def delete_sources(url: str):
|
||||||
"""Удаляет источник по URL из таблицы sourse"""
|
"""Удаляет источник по URL из таблицы sourse"""
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
|
|||||||
Reference in New Issue
Block a user