fix org
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import uuid
|
||||
import io
|
||||
|
||||
from bd import make_engine
|
||||
|
||||
@@ -102,3 +103,106 @@ def delete_organization(org_id: str):
|
||||
session.delete(org)
|
||||
session.commit()
|
||||
return {}
|
||||
|
||||
|
||||
def _extract_names_from_docx(content: bytes) -> list[str]:
|
||||
"""Извлекает уникальные непустые строки из docx.
|
||||
|
||||
Для таблиц берётся только ПЕРВЫЙ столбец каждой строки.
|
||||
Параграфы вне таблиц используются как запасной источник (если таблиц нет).
|
||||
"""
|
||||
from docx import Document as DocxDocument
|
||||
doc = DocxDocument(io.BytesIO(content))
|
||||
names: list[str] = []
|
||||
seen: set[str] = set()
|
||||
|
||||
def _add(text: str):
|
||||
t = text.strip()
|
||||
if t and t not in seen:
|
||||
seen.add(t)
|
||||
names.append(t)
|
||||
|
||||
# Если есть таблицы — берём только первый столбец
|
||||
if doc.tables:
|
||||
for table in doc.tables:
|
||||
for row in table.rows:
|
||||
if row.cells:
|
||||
_add(row.cells[0].text)
|
||||
else:
|
||||
# Нет таблиц — берём параграфы
|
||||
for para in doc.paragraphs:
|
||||
_add(para.text)
|
||||
|
||||
return names
|
||||
|
||||
|
||||
@router.post("/import/docx/preview", dependencies=[Depends(require_admin_key)])
|
||||
async def preview_import_from_docx(file: UploadFile = File(...)):
|
||||
"""Предпросмотр: возвращает список строк, которые будут импортированы, БЕЗ записи в БД.
|
||||
|
||||
Используйте перед /import/docx чтобы убедиться, что извлекаются нужные данные.
|
||||
"""
|
||||
if not file.filename or not file.filename.lower().endswith(".docx"):
|
||||
raise HTTPException(status_code=400, detail="Ожидается файл .docx")
|
||||
try:
|
||||
from docx import Document as DocxDocument # noqa: F401
|
||||
except ImportError:
|
||||
raise HTTPException(status_code=500, detail="python-docx не установлен на сервере")
|
||||
|
||||
content = await file.read()
|
||||
try:
|
||||
names = _extract_names_from_docx(content)
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail="Не удалось открыть файл")
|
||||
|
||||
return {"total": len(names), "names": names}
|
||||
|
||||
|
||||
@router.post("/import/docx", dependencies=[Depends(require_admin_key)])
|
||||
async def import_organizations_from_docx(file: UploadFile = File(...)):
|
||||
"""Массовый импорт организаций из .docx файла.
|
||||
|
||||
Каждая непустая строка (параграф или ячейка таблицы) становится отдельной организацией.
|
||||
Уже существующие названия пропускаются без ошибки.
|
||||
Перед использованием проверьте результат через /import/docx/preview.
|
||||
"""
|
||||
if not file.filename or not file.filename.lower().endswith(".docx"):
|
||||
raise HTTPException(status_code=400, detail="Ожидается файл .docx")
|
||||
|
||||
try:
|
||||
from docx import Document as DocxDocument # noqa: F401
|
||||
except ImportError:
|
||||
raise HTTPException(status_code=500, detail="python-docx не установлен на сервере")
|
||||
|
||||
content = await file.read()
|
||||
try:
|
||||
names = _extract_names_from_docx(content)
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail="Не удалось открыть файл. Убедитесь, что это корректный .docx")
|
||||
|
||||
if not names:
|
||||
raise HTTPException(status_code=400, detail="В файле не найдено ни одной строки")
|
||||
|
||||
Session = get_session()
|
||||
added: list[str] = []
|
||||
skipped: list[str] = []
|
||||
|
||||
with Session() as session:
|
||||
for name in names:
|
||||
existing = session.query(Organization).filter(
|
||||
Organization.name_organization == name
|
||||
).first()
|
||||
if existing:
|
||||
skipped.append(name)
|
||||
continue
|
||||
org = Organization(name_organization=name)
|
||||
session.add(org)
|
||||
added.append(name)
|
||||
session.commit()
|
||||
|
||||
return {
|
||||
"added": len(added),
|
||||
"skipped": len(skipped),
|
||||
"added_names": added,
|
||||
"skipped_names": skipped,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user