Bump version to v1.6.3

This commit is contained in:
Oliver 2025-08-18 10:33:27 +02:00
parent a02f825274
commit 0bb7d246c1
No known key found for this signature in database
9 changed files with 1758 additions and 221 deletions

View file

@ -2,8 +2,13 @@
import streamlit as st
from main import load_feeds, save_feeds, load_articles
from utils.css_loader import load_css, apply_dark_theme
import logging
# === CSS & Theme laden ===
load_css()
apply_dark_theme()
# === Logging vorbereiten ===
log_dir = "logs"
log_file = f"{log_dir}/rss_tool.log"
@ -15,17 +20,29 @@ logging.basicConfig(
st.set_page_config(page_title="📡 Feed-Verwaltung")
st.title("📡 RSS Feed-Verwaltung")
# Header
st.markdown("""
<div class="main-header">
<h1>📡 RSS Feed-Verwaltung</h1>
<p>Verwalte deine RSS-Feeds zentral und effizient</p>
</div>
""", unsafe_allow_html=True)
feeds = load_feeds()
articles = load_articles()
# === Neuen Feed hinzufügen ===
st.markdown('<div class="filter-section">', unsafe_allow_html=True)
st.subheader(" Neuen Feed hinzufügen")
with st.form("add_feed_form"):
new_url = st.text_input("Feed URL", "")
new_name = st.text_input("Feed Name", "")
submitted = st.form_submit_button("Feed hinzufügen")
col1, col2 = st.columns(2)
with col1:
new_url = st.text_input("Feed URL", "", placeholder="https://example.com/feed.xml")
with col2:
new_name = st.text_input("Feed Name", "", placeholder="Beispiel News")
submitted = st.form_submit_button("Feed hinzufügen", use_container_width=True)
if submitted:
if new_url and new_name:
if not any(f.get("url") == new_url for f in feeds):
@ -39,33 +56,185 @@ with st.form("add_feed_form"):
else:
st.error("❌ Bitte gib sowohl URL als auch Name ein.")
st.markdown('</div>', unsafe_allow_html=True)
# === Bestehende Feeds bearbeiten ===
st.subheader("🛠️ Vorhandene Feeds bearbeiten oder löschen")
st.subheader("🛠️ Vorhandene Feeds verwalten")
for idx, feed in enumerate(feeds):
with st.expander(f"🔗 {feed.get('name')}"):
url = st.text_input(f"Feed-URL {idx}", value=feed.get("url"), key=f"url_{idx}")
name = st.text_input(f"Feed-Name {idx}", value=feed.get("name"), key=f"name_{idx}")
count = sum(1 for a in articles if a.get("source") == feed.get("url"))
col1, col2 = st.columns(2)
if not feeds:
st.info("Noch keine Feeds konfiguriert. Füge oben deinen ersten Feed hinzu!")
else:
for idx, feed in enumerate(feeds):
feed_url = feed.get("url", "")
feed_name = feed.get("name", "Unbekannt")
article_count = sum(1 for a in articles if a.get("source") == feed_url)
# Feed Card
st.markdown(f"""
<div class="article-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<div>
<h3 class="article-title">{feed_name}</h3>
<div class="article-meta">{feed_url}</div>
</div>
<div>
<span class="status-badge status-online">{article_count} Artikel</span>
</div>
</div>
<div class="article-footer">
📰 Verknüpfte Artikel: {article_count}
</div>
</div>
""", unsafe_allow_html=True)
# Actions
col1, col2, col3 = st.columns(3)
with col1:
if st.button("💾 Änderungen speichern", key=f"save_{idx}"):
old_url, old_name = feed.get("url"), feed.get("name")
feeds[idx]["url"] = url
feeds[idx]["name"] = name
save_feeds(feeds)
logging.info(f"✏️ Feed geändert: '{old_name}' ({old_url}) → '{name}' ({url})")
st.success("Änderungen gespeichert.")
st.rerun()
if st.button("💾 Bearbeiten", key=f"edit_{idx}", use_container_width=True):
st.session_state[f"edit_mode_{idx}"] = not st.session_state.get(f"edit_mode_{idx}", False)
with col2:
if st.button("🗑️ Feed löschen", key=f"delete_{idx}"):
deleted_feed = feeds.pop(idx)
save_feeds(feeds)
logging.info(f"❌ Feed gelöscht: {deleted_feed.get('name')} ({deleted_feed.get('url')})")
st.warning("Feed gelöscht.")
st.rerun()
if st.button("🔄 Aktualisieren", key=f"refresh_{idx}", use_container_width=True):
with st.spinner(f"Aktualisiere Feed '{feed_name}'..."):
# Hier könntest du eine einzelne Feed-Update-Funktion implementieren
from main import process_articles
existing_ids = [a["id"] for a in articles]
process_articles(existing_ids)
st.success(f"Feed '{feed_name}' aktualisiert!")
st.rerun()
st.caption(f"📰 Verknüpfte Artikel: {count}")
with col3:
if st.button("🗑️ Löschen", key=f"delete_{idx}", use_container_width=True):
# Bestätigung über Session State
if not st.session_state.get(f"confirm_delete_{idx}", False):
st.session_state[f"confirm_delete_{idx}"] = True
st.warning(f"Klicke erneut um '{feed_name}' wirklich zu löschen!")
else:
deleted_feed = feeds.pop(idx)
save_feeds(feeds)
logging.info(f"❌ Feed gelöscht: {deleted_feed.get('name')} ({deleted_feed.get('url')})")
st.success(f"Feed '{feed_name}' wurde gelöscht.")
# Cleanup Session State
if f"confirm_delete_{idx}" in st.session_state:
del st.session_state[f"confirm_delete_{idx}"]
st.rerun()
# Edit Form (wenn aktiviert)
if st.session_state.get(f"edit_mode_{idx}", False):
st.markdown('<div class="filter-section" style="margin-top: 1rem;">', unsafe_allow_html=True)
st.write("**Feed bearbeiten:**")
with st.form(f"edit_form_{idx}"):
col1, col2 = st.columns(2)
with col1:
edited_url = st.text_input("Feed-URL", value=feed_url, key=f"edit_url_{idx}")
with col2:
edited_name = st.text_input("Feed-Name", value=feed_name, key=f"edit_name_{idx}")
form_col1, form_col2 = st.columns(2)
with form_col1:
if st.form_submit_button("💾 Änderungen speichern", use_container_width=True):
old_url, old_name = feed.get("url"), feed.get("name")
feeds[idx]["url"] = edited_url
feeds[idx]["name"] = edited_name
save_feeds(feeds)
logging.info(f"✏️ Feed geändert: '{old_name}' ({old_url}) → '{edited_name}' ({edited_url})")
st.success("Änderungen gespeichert!")
st.session_state[f"edit_mode_{idx}"] = False
st.rerun()
with form_col2:
if st.form_submit_button("❌ Abbrechen", use_container_width=True):
st.session_state[f"edit_mode_{idx}"] = False
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
# === Feed-Statistiken ===
if feeds:
st.markdown("---")
st.subheader("📊 Feed-Statistiken")
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>Feeds Gesamt</div>
</div>
""".format(len(feeds)), unsafe_allow_html=True)
with col2:
total_articles = len(articles)
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>Artikel Gesamt</div>
</div>
""".format(total_articles), unsafe_allow_html=True)
with col3:
avg_articles = total_articles // len(feeds) if feeds else 0
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>Ø Artikel pro Feed</div>
</div>
""".format(avg_articles), unsafe_allow_html=True)
# === Bulk Actions ===
if feeds:
st.markdown("---")
st.subheader("⚡ Bulk-Aktionen")
col1, col2 = st.columns(2)
with col1:
if st.button("🔄 Alle Feeds aktualisieren", use_container_width=True):
with st.spinner("Aktualisiere alle Feeds..."):
from main import process_articles
existing_ids = [a["id"] for a in articles]
process_articles(existing_ids)
st.success(f"Alle {len(feeds)} Feeds wurden aktualisiert!")
st.rerun()
with col2:
if st.button("📊 Feed-Performance anzeigen", use_container_width=True):
st.subheader("📈 Feed-Performance")
# Performance-Daten sammeln
feed_performance = []
for feed in feeds:
feed_url = feed.get("url", "")
feed_name = feed.get("name", "Unbekannt")
feed_articles = [a for a in articles if a.get("source") == feed_url]
performance = {
"name": feed_name,
"url": feed_url,
"total_articles": len(feed_articles),
"new_articles": len([a for a in feed_articles if a.get("status") == "New"]),
"processed_articles": len([a for a in feed_articles if a.get("status") in ["Process", "Online", "WordPress Pending"]])
}
feed_performance.append(performance)
# Sortiere nach Artikel-Anzahl
feed_performance.sort(key=lambda x: x["total_articles"], reverse=True)
# Anzeige als Cards
for perf in feed_performance:
success_rate = (perf["processed_articles"] / perf["total_articles"] * 100) if perf["total_articles"] > 0 else 0
st.markdown(f"""
<div class="article-card">
<h3 class="article-title">{perf["name"]}</h3>
<div class="article-footer">
📰 {perf["total_articles"]} Artikel |
🆕 {perf["new_articles"]} Neu |
{perf["processed_articles"]} Verarbeitet |
📊 {success_rate:.1f}% Success Rate
</div>
</div>
""", unsafe_allow_html=True)