From 9ba1aa7b1015fb6a50702ed099b1c2fe6e00e943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 1 Oct 2025 08:30:07 +0200 Subject: [PATCH] =?UTF-8?q?Bugfix:=20Tag-Duplikate,=20Post-Duplikate=20und?= =?UTF-8?q?=20Ver=C3=B6ffentlichungsdatum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: - Tag/Kategorie-Erstellung: Bessere Fehlerbehandlung für bereits existierende Tags - Post-Duplikatsprüfung: Verbesserte Suche mit status='any' und case-insensitive Vergleich - Veröffentlichungsdatum: datePublished aus Frontmatter wird als WordPress-Datum gesetzt - Erweiterte Datumsextraktion aus verschiedenen Frontmatter-Strukturen Neue Datei: - USAGE_MODES.md: Übersicht der drei Verwendungsmodi --- USAGE_MODES.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++ markdown_parser.py | 11 ++++-- wordpress_api.py | 54 ++++++++++++++++++++++++-- workflow.py | 18 ++++++++- 4 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 USAGE_MODES.md diff --git a/USAGE_MODES.md b/USAGE_MODES.md new file mode 100644 index 0000000..38e1001 --- /dev/null +++ b/USAGE_MODES.md @@ -0,0 +1,94 @@ +# Verwendungs-Modi + +## Modus 1: Einzelne URL (Empfohlen für Tests) + +```bash +python workflow.py "https://example.com/artikel.md" +``` + +oder lokale Datei: + +```bash +python workflow.py "content/beispiel-beitrag.md" +``` + +**Vorteile:** +- Schnellster Weg zum Testen +- Keine YAML-Konfiguration nötig +- Alle Metadaten aus Frontmatter + +## Modus 2: YAML-Batch (Für kuratierte Listen) + +Erstellen Sie `posts.yaml`: + +```yaml +posts: + - url: "https://example.com/artikel1.md" + - url: "https://example.com/artikel2.md" + - file: "content/artikel3.md" + +settings: + default_status: "draft" +``` + +Dann: + +```bash +python workflow.py posts.yaml +``` + +**Vorteile:** +- Kontrollierte Liste von Beiträgen +- Metadaten können überschrieben werden +- Wiederverwendbar + +## Modus 3: Forgejo-Repository (Für Bulk-Import) + +```bash +python workflow.py --repo "https://codeberg.org/user/repo" main +``` + +**Vorteile:** +- Alle Markdown-Dateien eines Repos auf einmal +- Perfekt für bestehende Dokumentationen +- Automatische Erkennung aller .md-Dateien + +## Kombinationen + +### Test → Produktion Workflow + +1. **Einzelne URL testen:** + ```bash + python workflow.py "https://example.com/test-artikel.md" + ``` + +2. **Bei Erfolg: YAML für mehrere erstellen** +3. **Bei Erfolg: Status auf "publish" setzen** + +### Repository → Kuratierte Liste + +1. **Repository scannen:** + ```bash + python workflow.py --repo "https://codeberg.org/user/repo" main + ``` + +2. **Prüfen welche Beiträge erstellt wurden** +3. **Gewünschte in YAML übertragen für Feinabstimmung** + +## Empfohlener Workflow + +1. ✅ Test mit Beispiel-Beitrag: + ```bash + python workflow.py "content/beispiel-beitrag.md" + ``` + +2. ✅ Test mit eigener URL: + ```bash + python workflow.py "https://ihre-url.de/artikel.md" + ``` + +3. ✅ Bei Erfolg: Batch oder Repo-Import + +4. ✅ In WordPress überprüfen + +5. ✅ Status auf "publish" setzen (in .yaml oder Frontmatter) diff --git a/markdown_parser.py b/markdown_parser.py index 6c4ef1b..a28c2e0 100644 --- a/markdown_parser.py +++ b/markdown_parser.py @@ -146,14 +146,19 @@ def extract_wordpress_metadata(frontmatter: Dict[str, Any], metadata['status'] = 'draft' # Datum extrahieren (falls vorhanden) + # Priorität: date > datePublished > (aus commonMetadata) > (aus staticSiteGenerator) if 'date' in frontmatter: - metadata['date'] = frontmatter['date'] + metadata['date'] = str(frontmatter['date']) elif 'datePublished' in frontmatter: - metadata['date'] = frontmatter['datePublished'] + metadata['date'] = str(frontmatter['datePublished']) elif isinstance(frontmatter.get('#commonMetadata'), dict): common = frontmatter['#commonMetadata'] if 'datePublished' in common: - metadata['date'] = common['datePublished'] + metadata['date'] = str(common['datePublished']) + elif isinstance(frontmatter.get('#staticSiteGenerator'), dict): + static_gen = frontmatter['#staticSiteGenerator'] + if 'datePublished' in static_gen: + metadata['date'] = str(static_gen['datePublished']) return metadata diff --git a/wordpress_api.py b/wordpress_api.py index 0368463..ca31dc1 100644 --- a/wordpress_api.py +++ b/wordpress_api.py @@ -57,13 +57,31 @@ class WordPressAPI: Post-ID wenn gefunden, sonst None """ try: - response = self._get('posts', params={'search': title, 'per_page': 10}) + # Suche mit verschiedenen Parametern + response = self._get('posts', params={ + 'search': title, + 'per_page': 100, # Erhöht für bessere Suche + 'status': 'any' # Alle Status (draft, publish, etc.) + }) posts = response.json() + # Normalisiere Titel für Vergleich + title_lower = title.lower().strip() + # Exakte Übereinstimmung prüfen for post in posts: - if post.get('title', {}).get('rendered', '') == title: + # Prüfe rendered Titel + rendered_title = post.get('title', {}).get('rendered', '').strip() + if rendered_title.lower() == title_lower: + print(f" → Beitrag gefunden (ID: {post['id']}, Status: {post['status']})") return post['id'] + + # Prüfe auch raw Titel falls vorhanden + raw_title = post.get('title', {}).get('raw', '').strip() + if raw_title and raw_title.lower() == title_lower: + print(f" → Beitrag gefunden (ID: {post['id']}, Status: {post['status']})") + return post['id'] + return None except requests.exceptions.RequestException as e: print(f"Fehler bei der Suche nach Beitrag: {e}") @@ -241,7 +259,21 @@ class WordPressAPI: response = self._post('categories', data={'name': name}) return response.json()['id'] except requests.exceptions.RequestException as e: - print(f"Fehler beim Erstellen der Kategorie: {e}") + # Prüfe ob Fehler durch bereits existierende Kategorie + if e.response is not None and e.response.status_code == 400: + # Kategorie könnte durch Race Condition gerade erstellt worden sein + # Erneut suchen + categories = self.get_categories() + for cat in categories: + if cat['name'].lower() == name.lower(): + return cat['id'] + print(f"Fehler beim Erstellen der Kategorie '{name}': {e}") + if hasattr(e, 'response') and e.response is not None: + try: + error_data = e.response.json() + print(f"Details: {error_data}") + except: + print(f"Response: {e.response.text}") return None def get_tags(self) -> List[Dict[str, Any]]: @@ -265,7 +297,21 @@ class WordPressAPI: response = self._post('tags', data={'name': name}) return response.json()['id'] except requests.exceptions.RequestException as e: - print(f"Fehler beim Erstellen des Tags: {e}") + # Prüfe ob Fehler durch bereits existierenden Tag + if e.response is not None and e.response.status_code == 400: + # Tag könnte durch Race Condition gerade erstellt worden sein + # Erneut suchen + tags = self.get_tags() + for tag in tags: + if tag['name'].lower() == name.lower(): + return tag['id'] + print(f"Fehler beim Erstellen des Tags '{name}': {e}") + if hasattr(e, 'response') and e.response is not None: + try: + error_data = e.response.json() + print(f"Details: {error_data}") + except: + print(f"Response: {e.response.text}") return None diff --git a/workflow.py b/workflow.py index 36bedf6..4377eb6 100644 --- a/workflow.py +++ b/workflow.py @@ -232,6 +232,21 @@ def process_post(wp_api: WordPressAPI, post_config: Dict[str, Any], # Autor author_name = metadata.get('author') or post_config.get('author') or default_author + # Veröffentlichungsdatum + # WordPress erwartet ISO 8601 Format: 2025-09-02T12:00:00 + publish_date = metadata.get('date') or post_config.get('date') + + # Zusätzliche WordPress-Felder vorbereiten + extra_fields = {} + if publish_date: + # Datum formatieren falls nötig + if isinstance(publish_date, str): + # Wenn nur Datum (YYYY-MM-DD), füge Uhrzeit hinzu + if len(publish_date) == 10: # Format: 2025-09-02 + publish_date = f"{publish_date}T00:00:00" + extra_fields['date'] = publish_date + print(f"Veröffentlichungsdatum: {publish_date}") + # Beitrag erstellen skip_duplicates = global_settings.get('skip_duplicates', True) post_id = wp_api.create_post( @@ -242,7 +257,8 @@ def process_post(wp_api: WordPressAPI, post_config: Dict[str, Any], categories=category_ids if category_ids else None, tags=tag_ids if tag_ids else None, excerpt=excerpt, - check_duplicate=skip_duplicates + check_duplicate=skip_duplicates, + **extra_fields # Datum und andere Felder ) return post_id