fix
This commit is contained in:
parent
9cbfbd041f
commit
ec5ab34686
@ -20,17 +20,18 @@ def validate_file_name(file_name: str) -> bool:
|
|||||||
return any(pattern.match(file_name) for pattern in FILE_NAME_PATTERNS)
|
return any(pattern.match(file_name) for pattern in FILE_NAME_PATTERNS)
|
||||||
|
|
||||||
|
|
||||||
def build_date_parts(now: datetime | None = None) -> tuple[str, str, str]:
|
def build_date_parts(now: datetime | None = None) -> tuple[str, str, str, str]:
|
||||||
current = now or datetime.now()
|
current = now or datetime.now()
|
||||||
year = current.strftime("%Y")
|
year = str(current.year)
|
||||||
month_year = current.strftime("%m_%Y")
|
month = str(current.month)
|
||||||
|
day = str(current.day)
|
||||||
date_key = current.strftime("%Y%m%d")
|
date_key = current.strftime("%Y%m%d")
|
||||||
return year, month_year, date_key
|
return year, month, day, date_key
|
||||||
|
|
||||||
|
|
||||||
def build_out_dir(base_dir: str, now: datetime | None = None) -> str:
|
def build_out_dir(base_dir: str, now: datetime | None = None) -> str:
|
||||||
year, month_year, date_key = build_date_parts(now)
|
year, month, day, _ = build_date_parts(now)
|
||||||
return join_path(base_dir, DEFAULT_OUTPUT_DIR_NAME, year, month_year, date_key)
|
return join_path(base_dir, DEFAULT_OUTPUT_DIR_NAME, year, month, day)
|
||||||
|
|
||||||
|
|
||||||
def build_run_dir(base_dir: str, run_number: int, now: datetime | None = None) -> str:
|
def build_run_dir(base_dir: str, run_number: int, now: datetime | None = None) -> str:
|
||||||
@ -59,8 +60,22 @@ def build_process_log_file_name(date_key: str, run_number: int) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def join_path(*parts: str) -> str:
|
def join_path(*parts: str) -> str:
|
||||||
cleaned = [part.strip("/\\") for part in parts if part.strip("/\\")]
|
raw_parts = [part for part in parts if part and part.strip("/\\")]
|
||||||
return "\\".join(cleaned)
|
if not raw_parts:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
first = raw_parts[0]
|
||||||
|
is_unc = (
|
||||||
|
first.startswith("//")
|
||||||
|
or first.startswith("\\\\")
|
||||||
|
or first.startswith("/")
|
||||||
|
or first.startswith("\\")
|
||||||
|
)
|
||||||
|
cleaned = [part.replace("/", "\\").strip("\\") for part in raw_parts]
|
||||||
|
joined = "\\".join(cleaned)
|
||||||
|
if is_unc:
|
||||||
|
return "\\\\" + joined.lstrip("\\")
|
||||||
|
return joined
|
||||||
|
|
||||||
|
|
||||||
def get_parent_dir(path: str) -> str:
|
def get_parent_dir(path: str) -> str:
|
||||||
|
|||||||
@ -67,8 +67,10 @@ class SMBClientConnection:
|
|||||||
def listdir(self, path: str) -> list[str]:
|
def listdir(self, path: str) -> list[str]:
|
||||||
smbclient = _import_smbclient()
|
smbclient = _import_smbclient()
|
||||||
full_path = self._resolve_path(path)
|
full_path = self._resolve_path(path)
|
||||||
with smbclient.scandir(full_path) as entries:
|
# In some smbclient/smbprotocol versions scandir() returns a generator,
|
||||||
return [entry.name for entry in entries]
|
# not a context manager, so we iterate directly.
|
||||||
|
entries = smbclient.scandir(full_path)
|
||||||
|
return [entry.name for entry in entries]
|
||||||
|
|
||||||
def is_file(self, path: str) -> bool:
|
def is_file(self, path: str) -> bool:
|
||||||
smbclient = _import_smbclient()
|
smbclient = _import_smbclient()
|
||||||
@ -168,6 +170,8 @@ class SMBClientConnection:
|
|||||||
def _resolve_path(self, path: str) -> str:
|
def _resolve_path(self, path: str) -> str:
|
||||||
if path.startswith("//") or path.startswith("\\\\"):
|
if path.startswith("//") or path.startswith("\\\\"):
|
||||||
return _normalize_unc_path(path)
|
return _normalize_unc_path(path)
|
||||||
|
if _looks_like_unc_without_prefix(path, self.smb_base_path):
|
||||||
|
return _normalize_unc_path("//" + path.lstrip("/\\"))
|
||||||
return _normalize_unc_path(join_path(self.smb_base_path, path))
|
return _normalize_unc_path(join_path(self.smb_base_path, path))
|
||||||
|
|
||||||
|
|
||||||
@ -207,7 +211,7 @@ def create_run_context(
|
|||||||
run_number = next_run_number(out_day_dir, samba_conn)
|
run_number = next_run_number(out_day_dir, samba_conn)
|
||||||
run_dir = join_path(out_day_dir, str(run_number))
|
run_dir = join_path(out_day_dir, str(run_number))
|
||||||
ensure_dir_exists(run_dir, samba_conn)
|
ensure_dir_exists(run_dir, samba_conn)
|
||||||
_, _, date_key = build_date_parts(now)
|
_, _, _, date_key = build_date_parts(now)
|
||||||
return RunContext(
|
return RunContext(
|
||||||
run_dir=run_dir,
|
run_dir=run_dir,
|
||||||
out_day_dir=out_day_dir,
|
out_day_dir=out_day_dir,
|
||||||
@ -384,3 +388,11 @@ def _import_smbclient():
|
|||||||
except ImportError as exc: # noqa: BLE001
|
except ImportError as exc: # noqa: BLE001
|
||||||
raise RuntimeError("Библиотека smbprotocol/smbclient не установлена.") from exc
|
raise RuntimeError("Библиотека smbprotocol/smbclient не установлена.") from exc
|
||||||
return smbclient
|
return smbclient
|
||||||
|
|
||||||
|
|
||||||
|
def _looks_like_unc_without_prefix(path: str, smb_base_path: str) -> bool:
|
||||||
|
normalized_path = path.replace("/", "\\").lstrip("\\")
|
||||||
|
if not normalized_path:
|
||||||
|
return False
|
||||||
|
server = _extract_server(smb_base_path).lower()
|
||||||
|
return normalized_path.lower().startswith(f"{server}\\")
|
||||||
|
|||||||
@ -25,6 +25,8 @@ from .parser import FileParseResult, parse_xml_content
|
|||||||
from .protocol import ProtocolRow, write_protocol
|
from .protocol import ProtocolRow, write_protocol
|
||||||
from .report import ReportRow, write_report
|
from .report import ReportRow, write_report
|
||||||
|
|
||||||
|
EMPTY_BATCH_STATUS = "info"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BatchResult:
|
class BatchResult:
|
||||||
@ -84,7 +86,7 @@ def run_batch(
|
|||||||
|
|
||||||
scan_result = scan_input_files(input_dir=input_dir, samba_conn=samba_conn)
|
scan_result = scan_input_files(input_dir=input_dir, samba_conn=samba_conn)
|
||||||
log_lines.append(
|
log_lines.append(
|
||||||
f"{_now()} scan files: valid={len(scan_result.valid_files)} invalid={len(scan_result.invalid_files)}"
|
f"{_now()} | scan files: valid={len(scan_result.valid_files)} invalid={len(scan_result.invalid_files)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for remote_file in scan_result.invalid_files:
|
for remote_file in scan_result.invalid_files:
|
||||||
@ -106,7 +108,7 @@ def run_batch(
|
|||||||
processed_at=datetime.now(),
|
processed_at=datetime.now(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
log_lines.append(f"{_now()} invalid -> err: {remote_file.path} => {moved_to}")
|
log_lines.append(f"{_now()} | invalid -> err: {remote_file.path} => {moved_to}")
|
||||||
|
|
||||||
for remote_file in scan_result.valid_files:
|
for remote_file in scan_result.valid_files:
|
||||||
parse_result = _parse_remote_xml(remote_file.name, remote_file.path, samba_conn)
|
parse_result = _parse_remote_xml(remote_file.name, remote_file.path, samba_conn)
|
||||||
@ -131,7 +133,7 @@ def run_batch(
|
|||||||
samba_conn=samba_conn,
|
samba_conn=samba_conn,
|
||||||
)
|
)
|
||||||
log_lines.append(
|
log_lines.append(
|
||||||
f"{_now()} success -> processed: {remote_file.path} => {moved_to}"
|
f"{_now()} | success -> processed: {remote_file.path} => {moved_to}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
moved_to = move_file_to_error(
|
moved_to = move_file_to_error(
|
||||||
@ -141,14 +143,14 @@ def run_batch(
|
|||||||
samba_conn=samba_conn,
|
samba_conn=samba_conn,
|
||||||
)
|
)
|
||||||
log_lines.append(
|
log_lines.append(
|
||||||
f"{_now()} {status} -> err: {remote_file.path} => {moved_to}"
|
f"{_now()} | {status} -> err: {remote_file.path} => {moved_to}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not protocol_rows:
|
if not protocol_rows:
|
||||||
protocol_rows.append(
|
protocol_rows.append(
|
||||||
ProtocolRow(
|
ProtocolRow(
|
||||||
file_name="-",
|
file_name="-",
|
||||||
status="success",
|
status=EMPTY_BATCH_STATUS,
|
||||||
message="Новых файлов для обработки нет.",
|
message="Новых файлов для обработки нет.",
|
||||||
operations_total=0,
|
operations_total=0,
|
||||||
rows_written=0,
|
rows_written=0,
|
||||||
@ -156,7 +158,7 @@ def run_batch(
|
|||||||
processed_at=datetime.now(),
|
processed_at=datetime.now(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
log_lines.append(f"{_now()} empty batch")
|
log_lines.append(f"{_now()} | empty batch")
|
||||||
|
|
||||||
report_file_name = build_report_file_name(
|
report_file_name = build_report_file_name(
|
||||||
run_context.date_key, run_context.run_number
|
run_context.date_key, run_context.run_number
|
||||||
|
|||||||
14
main.py
14
main.py
@ -43,16 +43,7 @@ def main() -> None:
|
|||||||
help="Пароль учетной записи для доступа к SMB-шаре.",
|
help="Пароль учетной записи для доступа к SMB-шаре.",
|
||||||
)
|
)
|
||||||
if st.button("Изменить путь", use_container_width=True):
|
if st.button("Изменить путь", use_container_width=True):
|
||||||
ok, message, _ = check_access_and_list_files(
|
open_path_editor(current_input_dir)
|
||||||
smb_base_path=SFM_SMB_BASE_PATH,
|
|
||||||
input_dir=resolved_input_dir,
|
|
||||||
samba_user=samba_user,
|
|
||||||
samba_password=samba_password,
|
|
||||||
)
|
|
||||||
if ok:
|
|
||||||
open_path_editor(current_input_dir)
|
|
||||||
else:
|
|
||||||
st.error(f"{message} Обратитесь к {ACCESS_SUPPORT_CONTACT}.")
|
|
||||||
|
|
||||||
edited_path = render_path_editor()
|
edited_path = render_path_editor()
|
||||||
if edited_path is not None:
|
if edited_path is not None:
|
||||||
@ -78,12 +69,13 @@ def main() -> None:
|
|||||||
use_container_width=True,
|
use_container_width=True,
|
||||||
):
|
):
|
||||||
with st.spinner("Выполняется обработка..."):
|
with st.spinner("Выполняется обработка..."):
|
||||||
|
launcher_name = samba_user.strip() or user_id
|
||||||
batch_result = run_batch(
|
batch_result = run_batch(
|
||||||
smb_base_path=SFM_SMB_BASE_PATH,
|
smb_base_path=SFM_SMB_BASE_PATH,
|
||||||
input_dir=resolved_input_dir,
|
input_dir=resolved_input_dir,
|
||||||
samba_user=samba_user,
|
samba_user=samba_user,
|
||||||
samba_password=samba_password,
|
samba_password=samba_password,
|
||||||
launcher=user_id,
|
launcher=launcher_name,
|
||||||
)
|
)
|
||||||
if batch_result.status == "success":
|
if batch_result.status == "success":
|
||||||
st.success("Обработка завершена успешно.")
|
st.success("Обработка завершена успешно.")
|
||||||
|
|||||||
@ -112,4 +112,4 @@ def test_run_batch_smoke_and_idempotency(tmp_path: Path, monkeypatch) -> None:
|
|||||||
launcher="tester",
|
launcher="tester",
|
||||||
)
|
)
|
||||||
assert second.status == "success"
|
assert second.status == "success"
|
||||||
assert second.processed >= 1
|
assert second.processed == 0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user