diff --git a/.gitignore b/.gitignore index 4c49bd7..50bc5d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,34 @@ +# Byte-compiled / optimierte / andere Python-Dateien +__pycache__/ +*.py[cod] +*$py.class + +# Virtuelle Umgebungen +.venv/ +env/ +venv/ +ENV/ + +# VS Code / PyCharm / andere IDEs +.vscode/ +.idea/ + +# Betriebssystemdateien +.DS_Store +Thumbs.db + +# Streamlit spezifisch +.streamlit/config.toml + +# dotenv / Umgebungsvariablen .env + +# JSON-Datenbanken / Zwischenspeicherung +*.json +!feeds.json # optional entfernbar, wenn du Beispielfeeds mit versionieren willst + +# LOG Files ausschließen, dienen nur zum Debuggen +# *.log + +# Start Bash ignirieren +start.sh diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4bac105 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# CHANGELOG.md + +## [1.1.0] - 2025-07-04 +### Hinzugefügt +- Visuell aufgewertete Box zur Darstellung eines Artikels mit: + - Kopierbutton für Titel + - Kopierbutton für Artikeltext + - Kopierbutton für Tags + - Button zum Öffnen des Originalartikels im neuen Tab +- Artikelansicht ist nun in einer grauen, abgerundeten Box gekapselt +- Icons unterstützen visuelle Orientierung (📝, 📋, 📎 etc.) + +### Geändert +- Artikelkopierfunktion für WordPress ist nun interaktiv über Buttons möglich +- HTML-Markup innerhalb von Streamlit für flexibleres Styling + +### Behoben +- Keine + +--- + +## [1.0.0] - 2025-07-03 +### Initialversion +- Artikel aus RSS-Feeds einlesen +- Speichern in JSON-Datei +- Anzeige in Tabelle mit Statusfilter +- Rewrite per ChatGPT mit Zusammenfassung und Tag-Generierung +- Exportierbare Inhalte für manuelles Posting auf WordPress diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..3182fb8 --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Oliver Giertz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ee9a68 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# 📰 RSS Artikel Manager + +Ein Python-Webtool, das RSS-Artikel automatisch einliest, per ChatGPT umschreibt und mit Tags versieht. Die Artikel lassen sich nach Status filtern und über eine tabellarische Streamlit-Oberfläche verwalten. + +--- + +## 🚀 Features + +- Verwaltung von RSS-Feeds direkt in der Weboberfläche +- Artikel laden, duplikatfrei speichern und anzeigen +- Artikelstatus: `New`, `Rewrite`, `Process`, `Online`, `On Hold`, `Trash` +- Artikel per ChatGPT umformulieren und automatisch taggen +- Filterbare und editierbare Artikelübersicht in Tabellenform +- Speicherung in `articles.json` (lokale JSON-Datei) + +--- + +## 🛠️ Installation + +```bash +git clone https://github.com/dein-user/rss-artikel-manager.git +cd rss-artikel-manager +python -m venv .venv +source .venv/bin/activate # oder .venv\\Scripts\\activate auf Windows +pip install -r requirements.txt diff --git a/app.log b/app.log new file mode 100644 index 0000000..8bce609 --- /dev/null +++ b/app.log @@ -0,0 +1,4 @@ +2025-07-04 09:29:50,207 - INFO - Status von 1 Artikel(n) auf 'Rewrite' gesetzt. +2025-07-04 09:30:17,000 - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" +2025-07-04 09:30:17,010 - INFO - ✅ Artikel umgeschrieben: Das weltweit größte Caravaning-Erlebnis +2025-07-04 09:46:03,001 - INFO - Status von 1 Artikel(n) auf 'Online' gesetzt. diff --git a/app.py b/app.py index 91fc475..90ba8a7 100644 --- a/app.py +++ b/app.py @@ -6,7 +6,20 @@ import os from datetime import datetime import pandas as pd import openai +from openai import OpenAI from dotenv import load_dotenv +import logging + +# ==== Version ==== +APP_VERSION = "1.1.0" + +# ==== Logging konfigurieren ==== +LOG_FILE = "app.log" +logging.basicConfig( + filename=LOG_FILE, + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", +) # ==== Konfiguration ==== ARTICLES_FILE = "articles.json" @@ -16,7 +29,8 @@ ALL_STATUSES = ["New", "Rewrite", "Process", "Online", "On Hold", "Trash"] # ==== API Schlüssel laden ==== load_dotenv() -openai.api_key = os.getenv("OPENAI_API_KEY") +api_key = os.getenv("OPENAI_API_KEY") +client = OpenAI(api_key=api_key) # ==== Hilfsfunktionen ==== def load_articles(): @@ -26,6 +40,7 @@ def load_articles(): with open(ARTICLES_FILE, "r") as f: return json.load(f) except json.JSONDecodeError: + logging.error("Fehler beim Laden von articles.json") return [] def save_articles(articles): @@ -39,6 +54,7 @@ def load_feeds(): with open(FEEDS_FILE, "r") as f: return json.load(f) except json.JSONDecodeError: + logging.error("Fehler beim Laden von feeds.json") return [] def save_feeds(feeds): @@ -81,28 +97,31 @@ def format_date(date_str): except Exception: return date_str -def rewrite_article_with_gpt(original_text): +def rewrite_article_with_gpt(original_text, title): prompt = ( "Schreibe folgenden Artikel um und formuliere ihn in journalistischem Stil neu. " "Füge am Ende eine Liste von 2–3 passenden Tags hinzu (nur Schlagwörter, keine Hashtags):\n" f"{original_text}" ) try: - response = openai.ChatCompletion.create( + response = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": prompt}], temperature=0.7 ) - return response.choices[0].message.content + result = response.choices[0].message.content + logging.info(f"✅ Artikel umgeschrieben: {title}") + return result except Exception as e: + logging.error(f"❌ Fehler beim Umschreiben von '{title}': {e}") return f"FEHLER: {e}" # ==== UI ==== st.set_page_config(page_title="RSS Artikel Manager", layout="wide") st.title("📰 RSS Artikel Manager") +st.sidebar.markdown(f"🧩 Version: `{APP_VERSION}`") # Bereich: Feed-Verwaltung -st.sidebar.header("RSS Feeds verwalten") feeds = load_feeds() new_feed = st.sidebar.text_input("Neuen Feed hinzufügen") if st.sidebar.button("➕ Feed hinzufügen") and new_feed: @@ -126,6 +145,7 @@ if st.button("🔄 Artikel aus Feeds laden"): all_articles = load_articles() + new save_articles(all_articles) st.success(f"{len(new)} neue Artikel geladen.") + logging.info(f"{len(new)} neue Artikel geladen.") else: st.info("Keine neuen Artikel gefunden.") @@ -134,12 +154,16 @@ rewrite_articles = [a for a in load_articles() if a["status"] == "Rewrite"] if rewrite_articles: if st.button("✍️ Alle Artikel mit Status 'Rewrite' umschreiben"): all_articles = load_articles() + progress_text = st.empty() with st.spinner("Artikel werden umgeschrieben..."): + total = len([a for a in all_articles if a["status"] == "Rewrite"]) + count = 0 for a in all_articles: if a["status"] == "Rewrite": - result = rewrite_article_with_gpt(a["content"]) + count += 1 + progress_text.markdown(f"➡️ Umschreibe Artikel {count} von {total}: **{a['title']}**") + result = rewrite_article_with_gpt(a["content"], a["title"]) if "FEHLER:" not in result: - # Aufteilen in Text und Tags, falls möglich if "Tags:" in result: rewritten, tags = result.rsplit("Tags:", 1) a["content"] = rewritten.strip() @@ -161,12 +185,11 @@ if articles: st.markdown("---") st.subheader(f"Artikel mit Status '{status_filter}'") - # Checkbox-Auswahl manuell verwalten selected_ids = [] for i, article in enumerate(articles): cols = st.columns([0.5, 1.5, 3, 4, 1, 2, 1]) with cols[0]: - if st.checkbox("", key=article["id"]): + if st.checkbox("Auswählen", key=article["id"], label_visibility="collapsed"): selected_ids.append(article["id"]) cols[1].markdown(format_date(article["date"])) cols[2].markdown(f"**{article['title']}**") @@ -177,14 +200,42 @@ if articles: st.markdown("---") if selected_ids: + all_articles = load_articles() + selected_articles = [a for a in all_articles if a["id"] in selected_ids] + + + with st.expander("📋 Inhalte für WordPress kopieren"): + for a in selected_articles: + with st.container(): + st.markdown(""" +
Der CARAVAN SALON ist die größte und wichtigste Messe weltweit, die sich ausschließlich dem Thema Caravaning widmet - und macht di", - "content": "
Der CARAVAN SALON ist die größte und wichtigste Messe weltweit, die sich ausschließlich dem Thema Caravaning widmet - und macht dieses Lebensgefühl zu einem einzigartigen Messe-Erlebnis. Als unangefochtener Branchentreffpunkt präsentiert die weltweit führende Leitmesse die größte Auswahl an Reisemobilen, Caravans und Campervans. Ergänzt wird das Angebot durch eine unvergleichliche Vielfalt an Zubehör, technischem Equipment, Ausbauteilen, Dachzelten, Mobilheimen, Caravaning- und Campingzubehör, Reisedestinationen, Campingplätzen sowie Reisemobilstellplätzen.
Messeerlebnis nicht zu ersetzen
„Gerade in herausfordernden Zeiten zeigt sich eindrucksvoll, welchen unschätzbaren Wert Messen für unsere Branche haben. Potenzielle Kunden wollen die Fahrzeuge vor der Kaufentscheidung nicht nur sehen – sie wollen erleben, anfassen, vergleichen, einsteigen und spüren, welcher Grundriss zu ihrem Lebensstil passt. Der CARAVAN SALON ist der einzige Ort, an dem alle renommierten nationalen und internationalen Hersteller versammelt sind – und Besucher die ganze Bandbreite des Marktes auf einen Blick erfassen können“, sagt Director Stefan Koschke. In Düsseldorf werden die Premieren der kommenden Saison gezeigt: Top-Produkte, Innovationen und Weltneuheiten – erstmals öffentlich erlebbar, ergänzt durch zahlreiche spannende Neuaussteller, die die Modellvielfalt nochmals erweitern. „Das Messeerlebnis CARAVAN SALON wird damit noch intensiver, vom passenden Fahrzeug bis zum ganz persönlichen Traumziel“, ergänzt Stefan Koschke.
Neue Halleneinteilung – noch mehr entdecken, noch leichter finden
Dank einer neuen, übersichtlich gestalteten Hallenstruktur, gelangen Besucherinnen und Besucher weiterhin schnell und gezielt in ihre persönliche Caravaning-Welt. Ob Camping-Neuling, Technik-Fan, kreativer Selbstausbauer oder Luxus-Liebhaber – auf dem CARAVAN SALON kommt jeder auf seine Kosten und entdeckt genau das, was das eigene Herz höherschlagen lässt. „Mit der neuen Aufteilung schaffen wir ein inspirierendes Messeerlebnis für unsere Gäste. Durch laufende Modernisierungen des Geländes und der Hallen sichern wir auch langfristig optimale Bedingungen für Beratung, Austausch und Networking. Im Zuge dieser Maßnahmen wird Halle 9 vorübergehend nicht genutzt – dafür dürfen sich Besucher auf interessante und spannende Platzierungen vieler Marken in einem frischen Umfeld freuen“, erklärt Stefan Koschke.
Caravaning so beliebt wie nie zuvor
„Caravaning erfreut sich seit vielen Jahren wachsender Beliebtheit, denn die individuelle und flexible Art des Reisens im Einklang mit der Natur entspricht dem Wunsch vieler Menschen nach einem selbstbestimmten Urlaub. Im Zuge dieser Entwicklung hat sich der mobile Urlaub längst generationenübergreifend zu einer der beliebtesten Reiseformen entwickelt – und das nicht nur in Europa, sondern weltweit. Noch nie waren so viele Menschen mit Reisemobil oder Caravan unterwegs wie heute. Diese Dynamik spiegelt sich auch auf dem CARAVAN SALON wider, der seit jeher die wichtigste internationale Bühne der Caravaningbranche ist. Nirgendwo sonst ist die Vielfalt an Fahrzeugen, Zubehör, Dienstleistungen und Reiseinspirationen größer als in Düsseldorf. Jahr für Jahr untermauert der CARAVAN SALON damit seine Rolle als Leuchtturm der Branche“, erklärt Daniel Onggowinarso, Geschäftsführer des Caravaning Industrie Verbandes (CIVD).
Der Preview Day am 29. August bietet mit begrenztem Ticketkontingent eine besonders entspannte Atmosphäre für alle, die die Neuheiten der Branche in Ruhe entdecken wollen.
\nDer Ticketverkauf startet am 11. Juni. Aktuelle Informationen und Neuigkeiten gibt es im Internet unter www.caravan-salon.de.
", - "word_count": 487, - "tags": [], - "status": "Rewrite", + "summary": "Als f\u00fchrendes internationales Forum f\u00fcr die Caravaning-Industrie setzt der CARAVAN SALON neue Standards und bietet Besuchern ein unvergleichliches Erl", + "content": "Als f\u00fchrendes internationales Forum f\u00fcr die Caravaning-Industrie setzt der CARAVAN SALON neue Standards und bietet Besuchern ein unvergleichliches Erlebnis. Der Salon hat sich als weltweite Drehscheibe f\u00fcr mobile Reiseenthusiasten etabliert und stellt eine umfangreiche Auswahl an Reisemobilen, Caravans und Campervans vor. Die Ausstellung wird durch ein breites Spektrum an Zubeh\u00f6r, technischen Ger\u00e4ten, Ausbauteilen, Dachzelten, Mobilheimen, Campingbedarf und Reisedestinationen erg\u00e4nzt.\n\nStefan Koschke, Direktor des CARAVAN SALON, hebt die entscheidende Rolle hervor, die Messen - besonders in herausfordernden Zeiten - f\u00fcr die Branche spielen. \"Potenzielle Kunden wollen die Fahrzeuge nicht nur sehen, sondern sie erleben, anfassen und vergleichen. Der CARAVAN SALON bietet die unvergleichliche Gelegenheit, alle nationalen und internationalen Hersteller unter einem Dach zu erleben und einen umfassenden \u00dcberblick \u00fcber den Markt zu gewinnen\", so Koschke.\n\nEine Neuerung auf dem CARAVAN SALON ist die klar strukturierte Hallenorganisation, die Besuchern hilft, schnell und gezielt ihre gew\u00fcnschten Ausstellungen zu erreichen. Koschke f\u00fcgt hinzu, dass st\u00e4ndige Modernisierungen des Veranstaltungsortes und der Hallen ideale Bedingungen f\u00fcr Beratung, Austausch und Networking gew\u00e4hrleisten.\n\nDaniel Onggowinarso, Gesch\u00e4ftsf\u00fchrer des Caravaning Industrie Verbandes (CIVD), unterstreicht die wachsende Attraktivit\u00e4t des Caravaning. \"Die individuelle und flexible Art des Reisens in Harmonie mit der Natur erf\u00fcllt den Wunsch vieler Menschen nach einem selbstbestimmten Urlaub. Der CARAVAN SALON reflektiert diese Trenddynamik und best\u00e4tigt seine Position als internationale Plattform der Caravaningbranche\", so Onggowinarso.\n\nAm 29. August bietet der Preview Day den Besuchern die M\u00f6glichkeit, die Branchenneuheiten in einer entspannten Umgebung zu erkunden. Der Ticketverkauf startet am 11. Juni. Weitere Informationen und Updates sind auf der Website des CARAVAN SALON verf\u00fcgbar.", + "word_count": 254, + "tags": [ + "CARAVAN SALON", + "Caravaning", + "Mobiles Reisen" + ], + "status": "Online", "link": "https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926" }, { @@ -69,11 +73,15 @@ "id": "4f83b2d1-f43c-4d41-8d68-bcb6b0675c46", "date": "Wed, 07 May 2025 00:00:00 +0200", "title": "Fr\u00fchjahrsaktionen vom Verein WOHNmobil f\u00fcr Klimaschutz", - "summary": "Jagsthausen in Baden-Württemberg, Uchte in Niedersachsen, Wernigerode in Sachsen-Anhalt und Harkebrügger See in Niedersachsen - so heiß", - "content": "Jagsthausen in Baden-Württemberg, Uchte in Niedersachsen, Wernigerode in Sachsen-Anhalt und Harkebrügger See in Niedersachsen - so heißen die Stationen der \"Frühjahrs-Tour\" des Vereins WOHNmobil für Klimaschutz e.V.