Files
duck-prod-manager/backend/mayo/client.py
2026-04-20 23:21:16 +02:00

140 lines
4.8 KiB
Python

import httpx
import logging
from typing import List, Optional
from .exceptions import MayoConnectionError, MayoResponseError, MayoAuthError, MayoOrderNotFound
from .parser import MayoParser
from .models import MayoSearchResult, MayoGuitarDetails, MayoResponse
logger = logging.getLogger(__name__)
class MayoClient:
def __init__(self, base_url: str, login: str, password: str, db: str = "1"):
self.base_url = base_url.rstrip("/")
self.credentials = {
"login": login,
"pass": password,
"baza": db,
}
self.client: httpx.AsyncClient | None = None
async def __aenter__(self):
self.client = httpx.AsyncClient(
base_url=self.base_url,
follow_redirects=True,
timeout=30.0,
)
try:
await self.login()
return self
except Exception:
await self.client.aclose()
self.client = None
raise
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.client is not None:
await self.client.aclose()
self.client = None
def _require_client(self) -> httpx.AsyncClient:
if self.client is None:
raise RuntimeError(
"HTTP client is not initialized. Use 'async with MayoClient(...) as client:' first."
)
return self.client
async def login(self):
client = self._require_client()
try:
response = await client.post("/login.php", data=self.credentials)
response.raise_for_status()
except httpx.TimeoutException as exc:
raise MayoConnectionError(
f"Mayo connection failed during login: timeout for {self.base_url}/login.php"
) from exc
except httpx.RequestError as exc:
raise MayoConnectionError(
f"Mayo connection failed during login: {exc}"
) from exc
except httpx.HTTPStatusError as exc:
raise MayoResponseError(
f"Mayo returned HTTP {exc.response.status_code} during login"
) from exc
if "Zaloguj się" in response.text or "login" in str(response.url):
raise MayoAuthError(
"Mayo login failed: invalid credentials or login page was returned again"
)
logger.info("✅ Zalogowano poprawnie do systemu Mayo.")
async def search_order(self, order_number: str) -> List[MayoSearchResult]:
"""Wyszukuje zamówienia na podstawie numeru."""
# Formatowanie numeru zamówienia (np. 0027)
formatted_nr = str(order_number).zfill(4)
params = {
"filtr": "1",
"strona": "0",
"sort_order": "1"
}
payload = {
"zaw": "",
"r_od": "",
"nr_zam": formatted_nr,
"typ_kl": "",
"klient": "",
"r_do": "",
"row_count": "25"
}
client = self._require_client()
response = await client.post("/index.php", params=params, data=payload)
return MayoParser.parse_search_results(response.text, self.base_url)
async def get_guitar_links(self, order_url: str) -> List[str]:
"""Pobiera listę linków do konkretnych instrumentów w zamówieniu."""
client = self._require_client()
response = await client.get(order_url)
return MayoParser.parse_guitar_links(response.text, self.base_url)
async def get_guitar_details(self, guitar_url: str) -> MayoGuitarDetails:
"""Pobiera szczegóły konkretnego instrumentu."""
client = self._require_client()
response = await client.get(guitar_url)
return MayoParser.parse_specification(response.text)
async def get_full_order_data(self, order_num: str, year: str, item_idx: str) -> MayoResponse:
orders = await self.search_order(order_num)
order = next((order for order in orders if order.order_id == f"{order_num}/{year}"), None)
if order is None:
raise MayoOrderNotFound(f"Order {order_num}/{year} was not found")
guitars_url = await self.get_guitar_links(order.url)
guitar_index = int(item_idx) - 1
if guitar_index < 0 or guitar_index >= len(guitars_url):
raise MayoOrderNotFound(
f"Product {order_num}/{year}/{item_idx} was not found"
)
guitar_url = guitars_url[guitar_index]
details = await self.get_guitar_details(guitar_url)
return MayoResponse(
order_number=details.order_number,
completion_date=details.completion_date,
prod_list=order.prod_list,
url=guitar_url,
client=details.client,
model=details.model,
spec=details.spec,
)