Compare commits
No commits in common. "9227b98431575bd0a303dd88a1c830aa1faa68f7" and "390b0ecb73cb9993b148499e4de6903979c20720" have entirely different histories.
9227b98431
...
390b0ecb73
|
|
@ -120,9 +120,7 @@ async def preview_import(
|
||||||
"wiki_page_title": page_title,
|
"wiki_page_title": page_title,
|
||||||
"wiki_page_id": page_id,
|
"wiki_page_id": page_id,
|
||||||
"already_imported": existing_ref is not None,
|
"already_imported": existing_ref is not None,
|
||||||
"last_imported_at": existing_ref["last_imported"].isoformat()
|
"last_imported_at": existing_ref["last_imported"].isoformat() if existing_ref else None,
|
||||||
if existing_ref and existing_ref.get("last_imported") is not None
|
|
||||||
else None,
|
|
||||||
"mapped_fields": mapped_fields,
|
"mapped_fields": mapped_fields,
|
||||||
"warnings": warnings,
|
"warnings": warnings,
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
|
|
@ -641,38 +639,21 @@ def _upsert_skill(mapped: dict, reimport: bool) -> Optional[int]:
|
||||||
return skill_id
|
return skill_id
|
||||||
|
|
||||||
|
|
||||||
def _upsert_method(mapped: dict, _reimport: bool) -> Optional[int]:
|
def _upsert_method(mapped: dict, reimport: bool) -> Optional[int]:
|
||||||
"""Legt Trainingsmethode an oder aktualisiert Beschreibung nach Namen.
|
"""Legt Trainingsmethode an oder aktualisiert Beschreibung."""
|
||||||
|
|
||||||
training_methods.name hat keinen UNIQUE-Constraint — daher kein ON CONFLICT (name).
|
|
||||||
"""
|
|
||||||
name = (mapped.get("name") or "").strip()
|
|
||||||
desc = mapped.get("description")
|
|
||||||
if not name:
|
|
||||||
return None
|
|
||||||
|
|
||||||
with get_db() as conn:
|
with get_db() as conn:
|
||||||
cur = get_cursor(conn)
|
cur = get_cursor(conn)
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT id FROM training_methods WHERE TRIM(name) ILIKE TRIM(%s)",
|
"""INSERT INTO training_methods (name, description)
|
||||||
(name,),
|
VALUES (%s, %s)
|
||||||
)
|
ON CONFLICT (name) DO UPDATE SET
|
||||||
row = cur.fetchone()
|
description = EXCLUDED.description
|
||||||
if row:
|
RETURNING id""",
|
||||||
mid = row["id"]
|
(mapped["name"], mapped.get("description"))
|
||||||
cur.execute(
|
|
||||||
"UPDATE training_methods SET description = %s, updated_at = NOW() WHERE id = %s",
|
|
||||||
(desc, mid),
|
|
||||||
)
|
)
|
||||||
|
method_id = cur.fetchone()['id']
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return mid
|
return method_id
|
||||||
cur.execute(
|
|
||||||
"INSERT INTO training_methods (name, description) VALUES (%s, %s) RETURNING id",
|
|
||||||
(name, desc),
|
|
||||||
)
|
|
||||||
mid = cur.fetchone()["id"]
|
|
||||||
conn.commit()
|
|
||||||
return mid
|
|
||||||
|
|
||||||
|
|
||||||
def _upsert_wiki_ref(
|
def _upsert_wiki_ref(
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,7 @@ class SmwClient:
|
||||||
"format": "json",
|
"format": "json",
|
||||||
})
|
})
|
||||||
r1.raise_for_status()
|
r1.raise_for_status()
|
||||||
j = self._parse_mw_response(r1.json())
|
token = r1.json()["query"]["tokens"]["logintoken"]
|
||||||
tok = (((j.get("query") or {}).get("tokens") or {}).get("logintoken"))
|
|
||||||
if not tok:
|
|
||||||
raise SmwClientError("MediaWiki Login-Token fehlt in API-Antwort")
|
|
||||||
|
|
||||||
cookies = dict(r1.cookies)
|
cookies = dict(r1.cookies)
|
||||||
|
|
||||||
# Schritt 2: Einloggen
|
# Schritt 2: Einloggen
|
||||||
|
|
@ -60,10 +56,10 @@ class SmwClient:
|
||||||
"action": "login",
|
"action": "login",
|
||||||
"lgname": self.user,
|
"lgname": self.user,
|
||||||
"lgpassword": self.password,
|
"lgpassword": self.password,
|
||||||
"lgtoken": tok,
|
"lgtoken": token,
|
||||||
}, cookies=cookies)
|
}, cookies=cookies)
|
||||||
r2.raise_for_status()
|
r2.raise_for_status()
|
||||||
result = self._parse_mw_response(r2.json())
|
result = r2.json()
|
||||||
|
|
||||||
if result.get("login", {}).get("result") != "Success":
|
if result.get("login", {}).get("result") != "Success":
|
||||||
reason = result.get("login", {}).get("reason", "unbekannt")
|
reason = result.get("login", {}).get("reason", "unbekannt")
|
||||||
|
|
@ -73,31 +69,15 @@ class SmwClient:
|
||||||
self._logged_in = True
|
self._logged_in = True
|
||||||
logger.info("SMW Login erfolgreich als '%s'", self.user)
|
logger.info("SMW Login erfolgreich als '%s'", self.user)
|
||||||
|
|
||||||
def _parse_mw_response(self, data: dict) -> dict:
|
|
||||||
"""MediaWiki liefert oft HTTP 200 mit {\"error\": {...}} — sonst KeyErrors in Client-Code."""
|
|
||||||
if not isinstance(data, dict):
|
|
||||||
raise SmwClientError("Ungültige API-Antwort (kein JSON-Objekt)")
|
|
||||||
err = data.get("error")
|
|
||||||
if isinstance(err, dict):
|
|
||||||
code = err.get("code", "")
|
|
||||||
info = err.get("info") or err.get("*") or err.get("message") or ""
|
|
||||||
raise SmwClientError(f"MediaWiki API: {code}: {info}".strip())
|
|
||||||
return data
|
|
||||||
|
|
||||||
async def _get(self, params: dict) -> dict:
|
async def _get(self, params: dict) -> dict:
|
||||||
"""Authentifizierter GET-Request gegen die API."""
|
"""Authentifizierter GET-Request gegen die API."""
|
||||||
if not self._logged_in:
|
if not self._logged_in:
|
||||||
await self.login()
|
await self.login()
|
||||||
params["format"] = "json"
|
params["format"] = "json"
|
||||||
try:
|
|
||||||
async with httpx.AsyncClient(timeout=30, cookies=self._cookies) as client:
|
async with httpx.AsyncClient(timeout=30, cookies=self._cookies) as client:
|
||||||
r = await client.get(self.api_url, params=params)
|
r = await client.get(self.api_url, params=params)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return self._parse_mw_response(r.json())
|
return r.json()
|
||||||
except SmwClientError:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
raise SmwClientError(str(e))
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
# Kategorien #
|
# Kategorien #
|
||||||
|
|
@ -124,8 +104,7 @@ class SmwClient:
|
||||||
params["cmcontinue"] = cmcontinue
|
params["cmcontinue"] = cmcontinue
|
||||||
|
|
||||||
data = await self._get(params)
|
data = await self._get(params)
|
||||||
q = data.get("query") or {}
|
members.extend(data["query"]["categorymembers"])
|
||||||
members.extend(q.get("categorymembers") or [])
|
|
||||||
|
|
||||||
if "continue" in data and len(members) < limit:
|
if "continue" in data and len(members) < limit:
|
||||||
cmcontinue = data["continue"].get("cmcontinue")
|
cmcontinue = data["continue"].get("cmcontinue")
|
||||||
|
|
@ -164,8 +143,7 @@ class SmwClient:
|
||||||
params["cmcontinue"] = cmcontinue
|
params["cmcontinue"] = cmcontinue
|
||||||
|
|
||||||
data = await self._get(params)
|
data = await self._get(params)
|
||||||
q = data.get("query") or {}
|
subcats.extend(data["query"]["categorymembers"])
|
||||||
subcats.extend(q.get("categorymembers") or [])
|
|
||||||
|
|
||||||
if "continue" in data:
|
if "continue" in data:
|
||||||
cmcontinue = data["continue"].get("cmcontinue")
|
cmcontinue = data["continue"].get("cmcontinue")
|
||||||
|
|
@ -215,9 +193,12 @@ class SmwClient:
|
||||||
"action": "browsebysubject",
|
"action": "browsebysubject",
|
||||||
"subject": title,
|
"subject": title,
|
||||||
})
|
})
|
||||||
|
if "error" in data:
|
||||||
|
raise SmwClientError(f"SMW Browse-Fehler für '{title}': {data['error']}")
|
||||||
|
|
||||||
# Normalisiere: {property_label: [wert1, wert2]}
|
# Normalisiere: {property_label: [wert1, wert2]}
|
||||||
result = {}
|
result = {}
|
||||||
for prop_data in (data.get("query") or {}).get("data") or []:
|
for prop_data in data.get("query", {}).get("data", []):
|
||||||
prop_label = prop_data.get("property", "")
|
prop_label = prop_data.get("property", "")
|
||||||
if prop_label.startswith("_"): # Interne SMW-Properties überspringen
|
if prop_label.startswith("_"): # Interne SMW-Properties überspringen
|
||||||
continue
|
continue
|
||||||
|
|
@ -242,6 +223,8 @@ class SmwClient:
|
||||||
"action": "ask",
|
"action": "ask",
|
||||||
"query": f"{query}|limit={limit}",
|
"query": f"{query}|limit={limit}",
|
||||||
})
|
})
|
||||||
|
if "error" in data:
|
||||||
|
raise SmwClientError(f"SMW Ask-Fehler: {data['error']}")
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
for title, props in data.get("query", {}).get("results", {}).items():
|
for title, props in data.get("query", {}).get("results", {}).items():
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,6 @@ services:
|
||||||
APP_URL: "${APP_URL:-https://shinkan.jinkendo.de}"
|
APP_URL: "${APP_URL:-https://shinkan.jinkendo.de}"
|
||||||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://shinkan.jinkendo.de}"
|
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://shinkan.jinkendo.de}"
|
||||||
ENVIRONMENT: "${ENVIRONMENT:-production}"
|
ENVIRONMENT: "${ENVIRONMENT:-production}"
|
||||||
# MediaWiki/SMW Import — in dev-env.yml bereits gesetzt; Prod brauchte diese Zeilen ebenfalls,
|
|
||||||
# sonst: leere MEDIAWIKI_API_URL im Container → Import bricht ab (auf Test/Dev war es immer gesetzt).
|
|
||||||
MEDIAWIKI_API_URL: "${MEDIAWIKI_API_URL:-https://karatetrainer.net/api.php}"
|
|
||||||
MEDIAWIKI_USER: "${MEDIAWIKI_USER:-}"
|
|
||||||
MEDIAWIKI_PASSWORD: "${MEDIAWIKI_PASSWORD:-}"
|
|
||||||
MEDIAWIKI_CATEGORY_EXERCISES: "${MEDIAWIKI_CATEGORY_EXERCISES:-Übungen}"
|
|
||||||
MEDIAWIKI_CATEGORY_SKILLS: "${MEDIAWIKI_CATEGORY_SKILLS:-Fähigkeitsbeschreibung}"
|
|
||||||
MEDIAWIKI_CATEGORY_METHODS: "${MEDIAWIKI_CATEGORY_METHODS:-Methodenbeschreibung}"
|
|
||||||
MEDIAWIKI_CATEGORY_MODELS: "${MEDIAWIKI_CATEGORY_MODELS:-Reifegradmodelle}"
|
|
||||||
volumes:
|
volumes:
|
||||||
- shinkan-media:/app/media
|
- shinkan-media:/app/media
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user