добавление отслеживания количества выгрузки
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-05-19 22:36:49 +10:00
parent e9d7a2d51f
commit 63ab6ed108
5 changed files with 90 additions and 20 deletions

View File

@@ -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:

View 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

View File

@@ -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,

View File

@@ -52,7 +52,8 @@ 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)
wp.save_parsed_data_to_db(parsed_data) wp.save_parsed_data_to_db(parsed_data)

View File

@@ -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()