🔖 Release v1.4.5
This commit is contained in:
parent
691c9e00b6
commit
e7d98dba3a
10 changed files with 1322 additions and 147 deletions
34
.github/release.yml
vendored
Normal file
34
.github/release.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: 📦 Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: 🔖 GitHub Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 📥 Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: 🧪 Version anzeigen
|
||||||
|
run: |
|
||||||
|
echo "Tag: ${{ github.ref_name }}"
|
||||||
|
|
||||||
|
- name: 📜 Release erstellen aus CHANGELOG
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: Release ${{ github.ref_name }}
|
||||||
|
body_path: .github/release_notes.md
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: 📝 Release Notes aus CHANGELOG extrahieren
|
||||||
|
run: |
|
||||||
|
version="${{ github.ref_name }}"
|
||||||
|
awk "/## \[$version\]/,/^## \[/" CHANGELOG.md | head -n -1 > .github/release_notes.md || true
|
||||||
|
|
||||||
11
.gitignore
vendored
11
.gitignore
vendored
|
|
@ -24,11 +24,14 @@ Thumbs.db
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# JSON-Datenbanken / Zwischenspeicherung
|
# JSON-Datenbanken / Zwischenspeicherung
|
||||||
*.json
|
# *.json
|
||||||
!feeds.json # optional entfernbar, wenn du Beispielfeeds mit versionieren willst
|
# !feeds.json # optional entfernbar, wenn du Beispielfeeds mit versionieren willst
|
||||||
|
|
||||||
# LOG Files ausschließen, dienen nur zum Debuggen
|
# LOG Files ausschließen, dienen nur zum Debuggen
|
||||||
# *.log
|
# *.log
|
||||||
|
|
||||||
# Start Bash ignirieren
|
# Interne Informationen ignorieren
|
||||||
start.sh
|
internal/start.sh
|
||||||
|
internal/copy_files.sh
|
||||||
|
internal/_line.txt
|
||||||
|
|
||||||
|
|
|
||||||
52
app.py
52
app.py
|
|
@ -15,25 +15,16 @@ import os
|
||||||
st.set_page_config(page_title="📰 RSS Artikel Manager", layout="wide")
|
st.set_page_config(page_title="📰 RSS Artikel Manager", layout="wide")
|
||||||
st.title("📰 RSS Artikel Manager")
|
st.title("📰 RSS Artikel Manager")
|
||||||
|
|
||||||
# RSS Feed Verwaltung
|
# === Sidebar: Feed-Verwaltung ===
|
||||||
st.sidebar.header("📡 RSS Feeds verwalten")
|
st.sidebar.header("📡 RSS Feeds verwalten")
|
||||||
feeds = load_feeds()
|
feeds = load_feeds()
|
||||||
new_feed = st.sidebar.text_input("Neuen RSS Feed hinzufügen")
|
new_feed = st.sidebar.text_input("Neuen RSS Feed hinzufügen")
|
||||||
if st.sidebar.button("Feed hinzufügen"):
|
if st.sidebar.button("Feed hinzufügen"):
|
||||||
if new_feed and new_feed not in feeds:
|
if new_feed and new_feed not in [f.get("url", f) for f in feeds]:
|
||||||
feeds.append({"url": new_feed})
|
feeds.append({"url": new_feed})
|
||||||
save_feeds(feeds)
|
save_feeds(feeds)
|
||||||
st.sidebar.success("Feed hinzugefügt")
|
st.sidebar.success("Feed hinzugefügt")
|
||||||
|
|
||||||
#if feeds:
|
|
||||||
# st.sidebar.write("### Aktuelle Feeds:")
|
|
||||||
# for feed in feeds:
|
|
||||||
# url = feed["url"] if isinstance(feed, dict) else feed
|
|
||||||
# st.sidebar.markdown(f"- {url}")
|
|
||||||
#else:
|
|
||||||
# st.sidebar.info("Noch keine Feeds hinzugefügt.")
|
|
||||||
|
|
||||||
# Artikel laden
|
|
||||||
if st.sidebar.button("🔄 Alle Feeds neu laden"):
|
if st.sidebar.button("🔄 Alle Feeds neu laden"):
|
||||||
existing_ids = [a["id"] for a in load_articles()]
|
existing_ids = [a["id"] for a in load_articles()]
|
||||||
process_articles(existing_ids)
|
process_articles(existing_ids)
|
||||||
|
|
@ -43,27 +34,40 @@ if st.sidebar.button("✍️ Artikel umschreiben (Rewrite)"):
|
||||||
rewrite_articles()
|
rewrite_articles()
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
# Artikelübersicht
|
# === Hauptbereich: Artikelübersicht ===
|
||||||
st.header("📋 Artikelübersicht")
|
st.header("📋 Artikelübersicht")
|
||||||
status_filter = st.selectbox("Status filtern", ["Alle", "New", "Rewrite", "Process", "Online", "On Hold", "Trash"])
|
status_filter = st.selectbox("Status filtern", ["Alle", "New", "Rewrite", "Process", "Online", "On Hold", "Trash"], index=1)
|
||||||
|
|
||||||
articles = load_articles()
|
articles = load_articles()
|
||||||
if status_filter != "Alle":
|
if status_filter != "Alle":
|
||||||
articles = [a for a in articles if a.get("status") == status_filter]
|
articles = [a for a in articles if a.get("status") == status_filter]
|
||||||
|
|
||||||
# Tabelle anzeigen
|
# === Artikel-Tabelle ===
|
||||||
if articles:
|
if articles:
|
||||||
st.markdown("### 📄 Übersichtstabelle")
|
st.markdown("### 📄 Übersichtstabelle")
|
||||||
st.write("**Spaltenübersicht:** Auswahl | Datum | Titel | Zusammenfassung | Wörter | Tags | Status")
|
st.write("**Spaltenübersicht:** Auswahl | Datum | Titel | Zusammenfassung | Wörter | Tags | Status")
|
||||||
|
|
||||||
for article in articles:
|
for article in articles:
|
||||||
|
has_incomplete_images = any(
|
||||||
|
not all(k in img and img[k] for k in ("caption", "copyright", "copyright_url"))
|
||||||
|
for img in article.get("images", [])
|
||||||
|
)
|
||||||
|
|
||||||
cols = st.columns([0.05, 0.1, 0.2, 0.25, 0.05, 0.2, 0.15])
|
cols = st.columns([0.05, 0.1, 0.2, 0.25, 0.05, 0.2, 0.15])
|
||||||
with cols[0]:
|
with cols[0]:
|
||||||
st.checkbox("", key=f"select_{article['id']}")
|
st.checkbox("", key=f"select_{article['id']}")
|
||||||
with cols[1]:
|
with cols[1]:
|
||||||
st.markdown(datetime.strptime(article["date"], "%a, %d %b %Y %H:%M:%S %z").strftime("%d.%m.%y") if "GMT" in article["date"] or "+" in article["date"] else article["date"][:10])
|
date_str = article["date"]
|
||||||
|
if "GMT" in date_str or "+" in date_str:
|
||||||
|
date_str = datetime.strptime(date_str, "%a, %d %b %Y %H:%M:%S %z").strftime("%d.%m.%y")
|
||||||
|
else:
|
||||||
|
date_str = date_str[:10]
|
||||||
|
st.markdown(date_str)
|
||||||
with cols[2]:
|
with cols[2]:
|
||||||
st.markdown(f"**{article['title']}**")
|
title = f"**{article['title']}**"
|
||||||
|
if has_incomplete_images:
|
||||||
|
title += " ⚠️"
|
||||||
|
st.markdown(title)
|
||||||
with cols[3]:
|
with cols[3]:
|
||||||
st.markdown(article.get("summary", "")[:150])
|
st.markdown(article.get("summary", "")[:150])
|
||||||
with cols[4]:
|
with cols[4]:
|
||||||
|
|
@ -83,13 +87,23 @@ if articles:
|
||||||
st.markdown("#### ✍️ Artikeltext")
|
st.markdown("#### ✍️ Artikeltext")
|
||||||
st.code(f"{article['title']}\n\n{article['text']}\n\nQuelle: {article['link']}", language="markdown")
|
st.code(f"{article['title']}\n\n{article['text']}\n\nQuelle: {article['link']}", language="markdown")
|
||||||
|
|
||||||
st.markdown("#### 🏷️ Tags")
|
st.markdown("#### 🌿 Tags")
|
||||||
st.code(", ".join(article.get("tags", [])), language="markdown")
|
st.code(", ".join(article.get("tags", [])), language="markdown")
|
||||||
|
|
||||||
st.markdown("#### 🖼️ Bilder")
|
st.markdown("#### 🖼️ Bilder")
|
||||||
for img in article.get("images", []):
|
for i, img in enumerate(article.get("images", [])):
|
||||||
st.image(img["url"], caption=img.get("caption", "Kein Titel"), use_column_width=True)
|
st.image(img["url"], caption=img.get("caption", "Kein Titel"), use_column_width=True)
|
||||||
st.caption(f"© {img.get('copyright', 'Unbekannt')} | [Quelle]({img.get('copyright_url', '#')})")
|
|
||||||
|
with st.form(f"edit_image_{article['id']}_{i}", clear_on_submit=False):
|
||||||
|
caption = st.text_input("Bildtitel", value=img.get("caption", ""))
|
||||||
|
copyright = st.text_input("Copyright", value=img.get("copyright", ""))
|
||||||
|
copyright_url = st.text_input("Quelle", value=img.get("copyright_url", ""))
|
||||||
|
if st.form_submit_button("Änderungen speichern"):
|
||||||
|
img["caption"] = caption or "Kein Bildtitel vorhanden"
|
||||||
|
img["copyright"] = copyright or "Unbekannt"
|
||||||
|
img["copyright_url"] = copyright_url or "#"
|
||||||
|
save_articles(articles)
|
||||||
|
st.success("Bilddaten gespeichert")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
st.info("Keine Artikel für den gewählten Status gefunden.")
|
st.info("Keine Artikel für den gewählten Status gefunden.")
|
||||||
|
|
|
||||||
1013
data/articles.json
1013
data/articles.json
File diff suppressed because one or more lines are too long
|
|
@ -54,3 +54,112 @@ INFO:root:✅ Alle Feeds erfolgreich verarbeitet.
|
||||||
2025-07-05 16:19:34,796 - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
|
2025-07-05 16:19:34,796 - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
|
||||||
2025-07-05 16:19:34,799 - INFO - ✅ Artikel umgeschrieben: Komfort und Flexibilität für moderne Camper
|
2025-07-05 16:19:34,799 - INFO - ✅ Artikel umgeschrieben: Komfort und Flexibilität für moderne Camper
|
||||||
2025-07-05 16:19:34,803 - INFO - Alle Artikel mit Status 'Rewrite' wurden verarbeitet.
|
2025-07-05 16:19:34,803 - INFO - Alle Artikel mit Status 'Rewrite' wurden verarbeitet.
|
||||||
|
2025-07-05 16:41:31,067 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-05 16:41:34,773 - INFO - 8 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-05 16:41:34,775 - INFO - 8 neue Artikel gespeichert.
|
||||||
|
2025-07-05 16:43:12,785 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-05 16:43:13,160 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-05 16:43:13,160 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
2025-07-05 16:43:52,517 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-05 16:43:56,021 - INFO - 9 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-05 16:43:56,022 - INFO - 9 neue Artikel gespeichert.
|
||||||
|
INFO:root:🔄 Starte Artikelabruf...
|
||||||
|
INFO:root:✅ 10 neue Artikel aus https://www.camping-news.de/rss/
|
||||||
|
INFO:root:✅ Artikelverarbeitung abgeschlossen.
|
||||||
|
INFO:root:🔄 Starte Artikelabruf...
|
||||||
|
INFO:root:✅ 9 neue Artikel aus https://www.camping-news.de/rss/
|
||||||
|
INFO:root:✅ Artikelverarbeitung abgeschlossen.
|
||||||
|
2025-07-07 09:26:35,739 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 09:26:36,041 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 09:26:36,413 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 09:26:36,414 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 09:26:36,640 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 09:26:36,640 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 09:26:36,862 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 09:26:36,863 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 09:26:37,085 - INFO - 16 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 09:26:37,086 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5924
|
||||||
|
2025-07-07 09:26:37,315 - INFO - 9 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5924
|
||||||
|
2025-07-07 09:26:37,316 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5922
|
||||||
|
2025-07-07 09:26:37,544 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5922
|
||||||
|
2025-07-07 09:26:37,545 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5921
|
||||||
|
2025-07-07 09:26:37,779 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5921
|
||||||
|
2025-07-07 09:26:37,779 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5920
|
||||||
|
2025-07-07 09:26:38,001 - INFO - 9 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5920
|
||||||
|
2025-07-07 09:26:38,001 - INFO - 8 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 09:26:38,003 - INFO - 8 neue Artikel gespeichert.
|
||||||
|
2025-07-07 09:27:24,535 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 09:27:24,690 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 09:27:24,914 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 09:27:24,915 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 09:27:25,127 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 09:27:25,127 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 09:27:25,345 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 09:27:25,346 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 09:27:25,530 - INFO - 16 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 09:27:25,531 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926
|
||||||
|
2025-07-07 09:27:25,761 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926
|
||||||
|
2025-07-07 09:27:25,762 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5923
|
||||||
|
2025-07-07 09:27:25,983 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5923
|
||||||
|
2025-07-07 09:27:25,984 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5924
|
||||||
|
2025-07-07 09:27:26,176 - INFO - 9 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5924
|
||||||
|
2025-07-07 09:27:26,177 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5922
|
||||||
|
2025-07-07 09:27:26,391 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5922
|
||||||
|
2025-07-07 09:27:26,391 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5921
|
||||||
|
2025-07-07 09:27:26,583 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5921
|
||||||
|
2025-07-07 09:27:26,583 - INFO - 9 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 09:27:26,584 - INFO - 9 neue Artikel gespeichert.
|
||||||
|
2025-07-07 10:00:07,513 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:00:07,906 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 10:00:08,272 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 10:00:08,273 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 10:00:08,599 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 10:00:08,599 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 10:00:08,834 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 10:00:08,835 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 10:00:09,058 - INFO - 16 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 10:00:09,059 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926
|
||||||
|
2025-07-07 10:00:09,268 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926
|
||||||
|
2025-07-07 10:00:09,268 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5920
|
||||||
|
2025-07-07 10:00:09,486 - INFO - 9 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5920
|
||||||
|
2025-07-07 10:00:09,487 - INFO - 6 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:00:09,488 - INFO - 6 neue Artikel gespeichert.
|
||||||
|
2025-07-07 10:00:30,026 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:00:30,189 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:00:30,189 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
2025-07-07 10:00:51,192 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:00:51,355 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:00:51,355 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
2025-07-07 10:01:08,635 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:01:08,815 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:01:08,815 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
2025-07-07 10:01:50,297 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:01:50,469 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 10:01:50,741 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5927
|
||||||
|
2025-07-07 10:01:50,742 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 10:01:50,940 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5928
|
||||||
|
2025-07-07 10:01:50,941 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 10:01:51,127 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5929
|
||||||
|
2025-07-07 10:01:51,128 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 10:01:51,316 - INFO - 16 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5925
|
||||||
|
2025-07-07 10:01:51,317 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926
|
||||||
|
2025-07-07 10:01:51,508 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5926
|
||||||
|
2025-07-07 10:01:51,510 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5923
|
||||||
|
2025-07-07 10:01:51,703 - INFO - 10 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5923
|
||||||
|
2025-07-07 10:01:51,704 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5922
|
||||||
|
2025-07-07 10:01:51,944 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5922
|
||||||
|
2025-07-07 10:01:51,945 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5921
|
||||||
|
2025-07-07 10:01:52,167 - INFO - 17 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5921
|
||||||
|
2025-07-07 10:01:52,168 - INFO - 📷 Extrahiere Bilder von https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5920
|
||||||
|
2025-07-07 10:01:52,380 - INFO - 9 Bilder gefunden bei https://www.camping-in-deutschland.de/index.php?pid=meldung&id=5920
|
||||||
|
2025-07-07 10:01:52,381 - INFO - 9 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:01:52,382 - INFO - 9 neue Artikel gespeichert.
|
||||||
|
2025-07-07 10:13:46,271 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:13:46,597 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:13:46,598 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
2025-07-07 10:36:37,313 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:36:37,741 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:36:37,741 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
2025-07-07 10:38:21,362 - INFO - Lade Feed: https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:38:21,538 - INFO - 0 neue Artikel gefunden in https://www.camping-news.de/rss/
|
||||||
|
2025-07-07 10:38:21,538 - INFO - Keine neuen Artikel gefunden.
|
||||||
|
|
|
||||||
51
main.py
51
main.py
|
|
@ -12,11 +12,12 @@ import openai
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
# Logging konfigurieren
|
# === Logging konfigurieren ===
|
||||||
log_dir = "logs"
|
log_dir = "logs"
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
log_file = os.path.join(log_dir, "rss_tool.log")
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
filename=os.path.join(log_dir, "rss_tool.log"),
|
filename=log_file,
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
format="%(asctime)s - %(levelname)s - %(message)s"
|
format="%(asctime)s - %(levelname)s - %(message)s"
|
||||||
)
|
)
|
||||||
|
|
@ -25,7 +26,6 @@ openai.api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
|
||||||
ARTICLES_FILE = "data/articles.json"
|
ARTICLES_FILE = "data/articles.json"
|
||||||
FEEDS_FILE = "data/feeds.json"
|
FEEDS_FILE = "data/feeds.json"
|
||||||
|
|
||||||
VALID_STATUSES = ["New", "Rewrite", "Process", "Online", "On Hold", "Trash"]
|
VALID_STATUSES = ["New", "Rewrite", "Process", "Online", "On Hold", "Trash"]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,26 +35,29 @@ def load_feeds():
|
||||||
with open(FEEDS_FILE, "r") as f:
|
with open(FEEDS_FILE, "r") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
def save_feeds(feeds):
|
def save_feeds(feeds):
|
||||||
with open(FEEDS_FILE, "w") as f:
|
with open(FEEDS_FILE, "w") as f:
|
||||||
json.dump(feeds, f, indent=2)
|
json.dump(feeds, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
def load_articles():
|
def load_articles():
|
||||||
if not os.path.exists(ARTICLES_FILE):
|
if not os.path.exists(ARTICLES_FILE):
|
||||||
return []
|
return []
|
||||||
with open(ARTICLES_FILE, "r") as f:
|
with open(ARTICLES_FILE, "r") as f:
|
||||||
articles = json.load(f)
|
articles = json.load(f)
|
||||||
|
|
||||||
# Sicherstellen, dass jeder Artikel einen gültigen Status hat
|
|
||||||
for article in articles:
|
for article in articles:
|
||||||
if article.get("status") not in VALID_STATUSES:
|
if article.get("status") not in VALID_STATUSES:
|
||||||
article["status"] = "New"
|
article["status"] = "New"
|
||||||
return articles
|
return articles
|
||||||
|
|
||||||
|
|
||||||
def save_articles(articles):
|
def save_articles(articles):
|
||||||
with open(ARTICLES_FILE, "w") as f:
|
with open(ARTICLES_FILE, "w") as f:
|
||||||
json.dump(articles, f, indent=2)
|
json.dump(articles, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
def fetch_and_process_feed(feed_url, existing_ids):
|
def fetch_and_process_feed(feed_url, existing_ids):
|
||||||
feed = feedparser.parse(feed_url)
|
feed = feedparser.parse(feed_url)
|
||||||
new_articles = []
|
new_articles = []
|
||||||
|
|
@ -88,41 +91,44 @@ def fetch_and_process_feed(feed_url, existing_ids):
|
||||||
|
|
||||||
return new_articles
|
return new_articles
|
||||||
|
|
||||||
|
|
||||||
def process_articles(existing_ids):
|
def process_articles(existing_ids):
|
||||||
feeds = load_feeds()
|
feeds = load_feeds()
|
||||||
all_articles = load_articles()
|
all_articles = load_articles()
|
||||||
|
articles_by_id = {article["id"]: article for article in all_articles if "id" in article}
|
||||||
new_entries = []
|
new_entries = []
|
||||||
|
|
||||||
for feed in feeds:
|
for feed in feeds:
|
||||||
if isinstance(feed, dict):
|
url = feed.get("url") if isinstance(feed, dict) else feed
|
||||||
url = feed.get("url")
|
|
||||||
else:
|
|
||||||
url = feed
|
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logging.info(f"Lade Feed: {url}")
|
logging.info(f"Lade Feed: {url}")
|
||||||
entries = fetch_and_process_feed(url, existing_ids)
|
entries = fetch_and_process_feed(url, existing_ids)
|
||||||
new_entries.extend(entries)
|
new_entries.extend(entries)
|
||||||
logging.info(f"{len(entries)} neue Artikel gefunden in {url}")
|
logging.info(f"{len(entries)} neue Artikel gefunden in {url}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Fehler beim Verarbeiten von {url}: {e}")
|
logging.exception(f"Fehler beim Verarbeiten von {url}:")
|
||||||
|
|
||||||
# Nur neue Artikel speichern, deren ID noch nicht vorhanden ist
|
added = 0
|
||||||
existing_article_ids = set(article["id"] for article in all_articles)
|
for entry in new_entries:
|
||||||
unique_new_entries = [a for a in new_entries if a["id"] not in existing_article_ids]
|
if entry["id"] not in articles_by_id:
|
||||||
|
articles_by_id[entry["id"]] = entry
|
||||||
|
added += 1
|
||||||
|
else:
|
||||||
|
logging.info(f"Artikel bereits vorhanden, wird übersprungen: {entry['title']}")
|
||||||
|
|
||||||
if unique_new_entries:
|
if added > 0:
|
||||||
all_articles.extend(unique_new_entries)
|
save_articles(list(articles_by_id.values()))
|
||||||
save_articles(all_articles)
|
logging.info(f"{added} neue Artikel gespeichert.")
|
||||||
logging.info(f"{len(unique_new_entries)} neue Artikel gespeichert.")
|
|
||||||
else:
|
else:
|
||||||
logging.info("Keine neuen Artikel gefunden.")
|
logging.info("Keine neuen Artikel gefunden.")
|
||||||
|
|
||||||
|
|
||||||
def rewrite_articles():
|
def rewrite_articles():
|
||||||
articles = load_articles()
|
articles = load_articles()
|
||||||
|
changed = False
|
||||||
|
|
||||||
for article in articles:
|
for article in articles:
|
||||||
if article.get("status") == "Rewrite":
|
if article.get("status") == "Rewrite":
|
||||||
try:
|
try:
|
||||||
|
|
@ -151,7 +157,6 @@ def rewrite_articles():
|
||||||
tags = [tag.strip(" ,") for tag in tags_raw.replace("\n", ",").split(",") if tag.strip()]
|
tags = [tag.strip(" ,") for tag in tags_raw.replace("\n", ",").split(",") if tag.strip()]
|
||||||
article["tags"] = tags
|
article["tags"] = tags
|
||||||
|
|
||||||
# Sicherstellen, dass Bildmetadaten vollständig sind
|
|
||||||
for img in article.get("images", []):
|
for img in article.get("images", []):
|
||||||
if "caption" not in img:
|
if "caption" not in img:
|
||||||
img["caption"] = "Kein Bildtitel vorhanden"
|
img["caption"] = "Kein Bildtitel vorhanden"
|
||||||
|
|
@ -161,9 +166,11 @@ def rewrite_articles():
|
||||||
img["copyright_url"] = "#"
|
img["copyright_url"] = "#"
|
||||||
|
|
||||||
logging.info(f"✅ Artikel umgeschrieben: {article['title']}")
|
logging.info(f"✅ Artikel umgeschrieben: {article['title']}")
|
||||||
|
changed = True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"❌ Fehler beim Umschreiben von '{article['title']}': {e}")
|
logging.exception(f"❌ Fehler beim Umschreiben von '{article['title']}':")
|
||||||
|
|
||||||
save_articles(articles)
|
if changed:
|
||||||
logging.info("Alle Artikel mit Status 'Rewrite' wurden verarbeitet.")
|
save_articles(articles)
|
||||||
|
logging.info("Alle Artikel mit Status 'Rewrite' wurden verarbeitet.")
|
||||||
|
|
|
||||||
23
pages/log_viewer.py
Normal file
23
pages/log_viewer.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# log_viewer.py
|
||||||
|
|
||||||
|
import streamlit as st
|
||||||
|
import os
|
||||||
|
|
||||||
|
st.set_page_config(page_title="🧾 Log Viewer", layout="wide")
|
||||||
|
st.title("🧾 Letzte Logeinträge anzeigen")
|
||||||
|
|
||||||
|
LOG_FILE = "logs/rss_tool.log"
|
||||||
|
MAX_LINES = 500
|
||||||
|
|
||||||
|
if not os.path.exists(LOG_FILE):
|
||||||
|
st.warning("Keine Logdatei gefunden.")
|
||||||
|
else:
|
||||||
|
with open(LOG_FILE, "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
st.write(f"Letzte {min(len(lines), MAX_LINES)} Zeilen aus `{LOG_FILE}`:")
|
||||||
|
|
||||||
|
st.code("".join(lines[-MAX_LINES:]), language="text")
|
||||||
|
|
||||||
|
if st.button("🔄 Neu laden"):
|
||||||
|
st.rerun()
|
||||||
|
|
@ -52,3 +52,4 @@ typing-inspection==0.4.1
|
||||||
typing_extensions==4.14.0
|
typing_extensions==4.14.0
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
urllib3==2.5.0
|
urllib3==2.5.0
|
||||||
|
typer[all]==0.12.3
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,60 @@
|
||||||
|
# utils/image_extractor.py
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
def extract_images_with_metadata(article_url):
|
def extract_images_with_metadata(article_url):
|
||||||
"""
|
"""
|
||||||
Versucht, Bilder mit Bildunterschrift und Copyright aus dem Originalartikel zu extrahieren.
|
Versucht, Bilder mit Bildunterschrift und Copyright aus dem Originalartikel zu extrahieren.
|
||||||
Gibt eine Liste mit Dictionaries zurück: {url, alt, copyright_text, copyright_link}
|
Gibt eine Liste mit Dictionaries zurück: {url, alt, copyright, copyright_url, caption}
|
||||||
"""
|
"""
|
||||||
|
images = []
|
||||||
try:
|
try:
|
||||||
|
logging.info(f"📷 Extrahiere Bilder von {article_url}")
|
||||||
response = requests.get(article_url, timeout=10)
|
response = requests.get(article_url, timeout=10)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
|
logging.warning(f"Keine gültige Antwort von {article_url} (Status {response.status_code})")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
soup = BeautifulSoup(response.content, "html.parser")
|
soup = BeautifulSoup(response.content, "html.parser")
|
||||||
images = []
|
|
||||||
|
|
||||||
for img_tag in soup.find_all("img"):
|
for img_tag in soup.find_all("img"):
|
||||||
src = img_tag.get("src")
|
src = img_tag.get("src")
|
||||||
if not src:
|
if not src:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Vollständige URL bauen
|
|
||||||
img_url = urljoin(article_url, src)
|
img_url = urljoin(article_url, src)
|
||||||
alt_text = img_tag.get("alt", "").strip()
|
alt_text = img_tag.get("alt", "").strip()
|
||||||
|
|
||||||
# Copyright-Hinweis suchen: z. B. umgebender <figure> oder <div>
|
copyright_text = "Unbekannt"
|
||||||
copyright_text = ""
|
copyright_link = article_url
|
||||||
copyright_link = ""
|
caption = alt_text or "Bild aus Originalartikel"
|
||||||
|
|
||||||
parent = img_tag.find_parent(["figure", "div"])
|
parent = img_tag.find_parent(["figure", "div"])
|
||||||
if parent:
|
if parent:
|
||||||
caption = parent.find("figcaption")
|
figcaption = parent.find("figcaption")
|
||||||
if caption:
|
if figcaption:
|
||||||
copyright_text = caption.get_text(strip=True)
|
caption = figcaption.get_text(strip=True)
|
||||||
link_tag = caption.find("a")
|
link_tag = figcaption.find("a")
|
||||||
if link_tag and link_tag.has_attr("href"):
|
if link_tag and link_tag.has_attr("href"):
|
||||||
copyright_link = link_tag["href"]
|
copyright_link = link_tag["href"]
|
||||||
|
copyright_text = link_tag.get_text(strip=True)
|
||||||
|
|
||||||
images.append({
|
image_data = {
|
||||||
"url": img_url,
|
"url": img_url,
|
||||||
"alt": alt_text or "Bild aus Originalartikel",
|
"alt": alt_text,
|
||||||
"copyright_text": copyright_text or "Unbekannt",
|
"caption": caption or "Kein Bildtitel vorhanden",
|
||||||
"copyright_link": copyright_link or article_url
|
"copyright": copyright_text or "Unbekannt",
|
||||||
})
|
"copyright_url": copyright_link or article_url
|
||||||
|
}
|
||||||
|
images.append(image_data)
|
||||||
|
|
||||||
|
logging.info(f"{len(images)} Bilder gefunden bei {article_url}")
|
||||||
return images
|
return images
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[extract_images_with_metadata] Fehler bei {article_url}: {e}")
|
logging.exception(f"Fehler bei der Bildextraktion aus {article_url}:")
|
||||||
return []
|
return []
|
||||||
132
versioning.py
Normal file
132
versioning.py
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
# versioning.py
|
||||||
|
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
import typer
|
||||||
|
|
||||||
|
app = typer.Typer()
|
||||||
|
|
||||||
|
CHANGELOG_FILE = Path("CHANGELOG.md")
|
||||||
|
VERSION_FILE = Path("__version__.py")
|
||||||
|
VERSION_PATTERN = r"## \[v?(\d+\.\d+\.\d+)\]"
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_version():
|
||||||
|
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
|
matches = re.findall(VERSION_PATTERN, content)
|
||||||
|
return matches[0] if matches else "0.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
def bump_version(version: str, level: str = "patch") -> str:
|
||||||
|
major, minor, patch = map(int, version.split("."))
|
||||||
|
if level == "major":
|
||||||
|
return f"{major + 1}.0.0"
|
||||||
|
elif level == "minor":
|
||||||
|
return f"{major}.{minor + 1}.0"
|
||||||
|
return f"{major}.{minor}.{patch + 1}"
|
||||||
|
|
||||||
|
|
||||||
|
def write_version_file(version: str):
|
||||||
|
VERSION_FILE.write_text(f"VERSION = \"{version}\"\n", encoding="utf-8")
|
||||||
|
typer.echo(f"🔢 __version__.py aktualisiert auf {version}")
|
||||||
|
|
||||||
|
|
||||||
|
def prepend_changelog(version: str):
|
||||||
|
today = datetime.today().strftime("%Y-%m-%d")
|
||||||
|
new_entry = f"\n\n## [v{version}] – {today}\n\n### 💡 Neue Funktionen\n- \n\n### 🔧 Änderungen & Fixes\n- \n\n### 📦 Internes\n- "
|
||||||
|
original = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
|
CHANGELOG_FILE.write_text(new_entry + original, encoding="utf-8")
|
||||||
|
typer.echo(f"📝 Neuer Eintrag für v{version} zu CHANGELOG.md hinzugefügt")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_changelog(version: str) -> bool:
|
||||||
|
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
|
pattern = rf"## \[v?{re.escape(version)}\](.*?)^## \["
|
||||||
|
match = re.search(pattern, content + "\n## [", re.DOTALL | re.MULTILINE)
|
||||||
|
if match:
|
||||||
|
section = match.group(1).strip()
|
||||||
|
if any(line.strip() != "-" for line in section.splitlines() if line.strip()):
|
||||||
|
return True
|
||||||
|
typer.echo("⚠️ CHANGELOG-Eintrag ist noch leer oder unvollständig.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def create_git_tag(version: str):
|
||||||
|
try:
|
||||||
|
subprocess.run(["git", "add", str(CHANGELOG_FILE), str(VERSION_FILE)], check=True)
|
||||||
|
subprocess.run(["git", "commit", "-m", f"🔖 Release v{version}"], check=True)
|
||||||
|
subprocess.run(["git", "tag", f"v{version}"], check=True)
|
||||||
|
typer.echo(f"🏷️ Git-Tag 'v{version}' erstellt und commit durchgeführt.")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
typer.echo("⚠️ Git-Fehler beim Taggen oder Committen. Bitte manuell prüfen.")
|
||||||
|
|
||||||
|
|
||||||
|
def push_to_github():
|
||||||
|
try:
|
||||||
|
subprocess.run(["git", "push"], check=True)
|
||||||
|
subprocess.run(["git", "push", "--tags"], check=True)
|
||||||
|
typer.echo("🚀 Änderungen und Tags an GitHub gepusht.")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
typer.echo("⚠️ Fehler beim Pushen zu GitHub. Bitte Zugang oder Netzwerk prüfen.")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def list():
|
||||||
|
"Listet alle verfügbaren Versionen aus dem CHANGELOG"
|
||||||
|
typer.echo("\n📚 Verfügbare Versionen im CHANGELOG:")
|
||||||
|
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
|
versions = re.findall(VERSION_PATTERN, content)
|
||||||
|
for v in versions:
|
||||||
|
typer.echo(f"- v{v}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def rollback():
|
||||||
|
"Letzte Version zurückrollen (Tag löschen + Commit zurücknehmen)"
|
||||||
|
last_version = get_latest_version()
|
||||||
|
if typer.confirm(f"⚠️ Letzte Version 'v{last_version}' wirklich zurücknehmen?"):
|
||||||
|
try:
|
||||||
|
subprocess.run(["git", "tag", "-d", f"v{last_version}"], check=True)
|
||||||
|
subprocess.run(["git", "reset", "--hard", "HEAD~1"], check=True)
|
||||||
|
typer.echo(f"🔙 Version 'v{last_version}' wurde zurückgerollt.")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
typer.echo("❌ Rollback fehlgeschlagen.")
|
||||||
|
else:
|
||||||
|
typer.echo("⛔ Abgebrochen.")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def create(level: str = typer.Option("patch", help="Versionstyp: patch, minor oder major"),
|
||||||
|
push: bool = typer.Option(False, help="Änderungen direkt an GitHub pushen")):
|
||||||
|
"Neue Version erstellen inkl. CHANGELOG, Git-Tag und optional Push"
|
||||||
|
current_version = get_latest_version()
|
||||||
|
next_version = bump_version(current_version, level)
|
||||||
|
|
||||||
|
typer.echo(f"💡 Aktuelle Version: {current_version}")
|
||||||
|
typer.echo(f"🚀 Neue Version: {next_version}")
|
||||||
|
|
||||||
|
if typer.confirm("Version übernehmen und eintragen?"):
|
||||||
|
write_version_file(next_version)
|
||||||
|
prepend_changelog(next_version)
|
||||||
|
|
||||||
|
typer.echo("\nBitte CHANGELOG.md bearbeiten und danach fortfahren.")
|
||||||
|
typer.prompt("Drücke Enter, sobald du den neuen Abschnitt ausgefüllt hast")
|
||||||
|
|
||||||
|
if not validate_changelog(next_version):
|
||||||
|
typer.echo("❌ Release abgebrochen: Bitte fülle den CHANGELOG-Eintrag aus.")
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
create_git_tag(next_version)
|
||||||
|
|
||||||
|
if push:
|
||||||
|
push_to_github()
|
||||||
|
|
||||||
|
typer.echo(f"✅ Version {next_version} erfolgreich erstellt.")
|
||||||
|
else:
|
||||||
|
typer.echo("❌ Abgebrochen.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue