Compare commits

...

2 Commits

Author SHA1 Message Date
1822fc20cb improve song analytics 2025-04-12 19:49:53 +00:00
eec9deefad improve indexing 2025-04-12 19:49:23 +00:00
6 changed files with 246 additions and 97 deletions

View File

@ -141,22 +141,34 @@ def return_file_access():
return [] return []
def songs_dashboard(): def songs_dashboard():
days_param = request.args.get("days", "30") if 'songs_dashboard_timeframe' not in session:
site = str(request.args.get("site", "Speyer")) session['songs_dashboard_timeframe'] = "30"
timeframe_param = request.args.get("timeframe", session['songs_dashboard_timeframe'])
session['songs_dashboard_timeframe'] = timeframe_param
if 'songs_dashboard_category' not in session:
session['songs_dashboard_category'] = "Gemeinsamer Gesang"
category = request.args.get("category", session['songs_dashboard_category'])
session['songs_dashboard_category'] = category
if 'songs_dashboard_site' not in session:
session['songs_dashboard_site'] = "Speyer"
site = request.args.get("site", session['songs_dashboard_site'])
session['songs_dashboard_site'] = site
# Determine cutoff_date based on the days parameter # Determine cutoff_date based on the days parameter
if days_param == "all": if timeframe_param == "all":
cutoff_date = None # No date filtering when analyzing all time cutoff_date = None # No date filtering when analyzing all time
timeframe = "all" # Pass the string to the template if needed timeframe = "all" # Pass the string to the template if needed
else: else:
timeframe = int(days_param) timeframe = int(timeframe_param)
now = datetime.now() now = datetime.now()
cutoff_date = now - timedelta(days=timeframe) cutoff_date = now - timedelta(days=timeframe)
cursor = search_db.cursor() cursor = search_db.cursor()
# Query rows with category "Gemeinsamer Gesang" # Query rows with category "Gemeinsamer Gesang"
query = "SELECT titel, performance_date FROM files WHERE category = ? and site = ?" query = "SELECT titel, performance_date FROM files WHERE category = ? and site = ?"
cursor.execute(query, ('Gemeinsamer Gesang', site)) cursor.execute(query, (category, site))
rows = cursor.fetchall() rows = cursor.fetchall()
# Group and count performances per titel (only if performance_date is within the timeframe, # Group and count performances per titel (only if performance_date is within the timeframe,
@ -177,7 +189,7 @@ def songs_dashboard():
performance_data = [(count, titel) for titel, count in performance_counts.items()] performance_data = [(count, titel) for titel, count in performance_counts.items()]
performance_data.sort(reverse=True, key=lambda x: x[0]) performance_data.sort(reverse=True, key=lambda x: x[0])
return render_template('songs_dashboard.html', timeframe=timeframe, performance_data=performance_data, site=site) return render_template('songs_dashboard.html', timeframe=timeframe_param, performance_data=performance_data, site=site, category=category)
@require_secret @require_secret
def connections(): def connections():

View File

@ -128,36 +128,46 @@ def updatefileindex():
# Prepend the foldername so it becomes part of the stored relative path. # Prepend the foldername so it becomes part of the stored relative path.
relative_path = os.path.join(foldername, rel_part).replace(os.sep, '/') relative_path = os.path.join(foldername, rel_part).replace(os.sep, '/')
filetype = os.path.splitext(entry.name)[1].lower() filetype = os.path.splitext(entry.name)[1].lower()
transcript = None
# Check for a corresponding transcript file in a sibling "Transkription" folder.
parent_dir = os.path.dirname(entry_path)
transcript_dir = os.path.join(parent_dir, "Transkription")
transcript_filename = os.path.splitext(entry.name)[0] + ".md"
transcript_path = os.path.join(transcript_dir, transcript_filename)
if os.path.exists(transcript_path):
try:
with open(transcript_path, 'r', encoding='utf-8') as tf:
transcript = tf.read()
except Exception:
transcript = None
# Retrieve the hit count for this file. # Retrieve the hit count for this file.
hit_count = get_hit_count(relative_path) hit_count = get_hit_count(relative_path)
category, titel, name, performance_date, site = None, None, None, None, None category, titel, name, performance_date, site = None, None, None, None, None
# Determine the site
if foldername == 'Gottesdienste Speyer':
site = 'Speyer'
elif foldername == 'Gottesdienste Schwegenheim':
site = 'Schwegenheim'
if filetype == '.mp3': if filetype == '.mp3':
# Determine the site transcript = None
if foldername == 'Gottesdienste Speyer':
site = 'Speyer' # Check for a corresponding transcript file in a sibling "Transkription" folder.
elif foldername == 'Gottesdienste Schwegenheim': parent_dir = os.path.dirname(entry_path)
site = 'Schwegenheim' transcript_dir = os.path.join(parent_dir, "Transkription")
transcript_filename = os.path.splitext(entry.name)[0] + ".md"
transcript_path = os.path.join(transcript_dir, transcript_filename)
if os.path.exists(transcript_path):
try:
with open(transcript_path, 'r', encoding='utf-8') as tf:
transcript = tf.read()
except Exception:
transcript = None
# extract category and titel from filename # extract category and titel from filename
filename_ext = os.path.splitext(entry.name)[0] filename_ext = os.path.splitext(entry.name)[0]
left_side, right_side = filename_ext.split('-', 1) if '-' in filename_ext else (filename_ext, None) left_side, right_side = filename_ext.split('-', 1) if '-' in filename_ext else (filename_ext, None)
if 'predigt' in left_side.lower(): try:
int(left_side.strip())
# first part is only a number
previous_right_side = right_side
left_side, right_side = previous_right_side.split('-', 1) if '-' in previous_right_side else (previous_right_side, None)
except:
# first part not a number
continue
if 'predig' in left_side.lower():
category = 'Predigt' category = 'Predigt'
elif 'wort' in left_side.lower() or 'einladung' in left_side.lower(): elif 'wort' in left_side.lower() or 'einladung' in left_side.lower():
category = 'Vorwort' category = 'Vorwort'
@ -186,22 +196,22 @@ def updatefileindex():
titel = None titel = None
name = None name = None
# extract the date from path using regex (dd.mm.yyyy or dd.mm.yy) # extract the date from path using regex (dd.mm.yyyy or dd.mm.yy)
date_match = re.search(r'(\d{1,2}\.\d{1,2}\.\d{2,4})', relative_path) date_match = re.search(r'(\d{1,2}\.\d{1,2}\.\d{2,4})', relative_path)
if date_match: if date_match:
date_str = date_match.group(1) date_str = date_match.group(1)
# Convert to YYYY-MM-DD format # Convert to YYYY-MM-DD format
try:
date_obj = datetime.strptime(date_str, '%d.%m.%Y')
performance_date = date_obj.strftime('%d.%m.%Y')
except ValueError:
try: try:
date_obj = datetime.strptime(date_str, '%d.%m.%Y') date_obj = datetime.strptime(date_str, '%d.%m.%y')
performance_date = date_obj.strftime('%d.%m.%Y') performance_date = date_obj.strftime('%d.%m.%Y')
except ValueError: except ValueError:
try: performance_date = None
date_obj = datetime.strptime(date_str, '%d.%m.%y') else:
performance_date = date_obj.strftime('%d.%m.%Y') performance_date = None
except ValueError:
performance_date = None
else:
performance_date = None
scanned_files.append((relative_path, foldername, entry.name, filetype, category, titel, name, performance_date, site, transcript, hit_count)) scanned_files.append((relative_path, foldername, entry.name, filetype, category, titel, name, performance_date, site, transcript, hit_count))
current_keys.add((relative_path, entry.name)) current_keys.add((relative_path, entry.name))

View File

@ -67,18 +67,11 @@
<a class="navbar-brand" href="#">Downloads der letzten 10 Minuten</a> <a class="navbar-brand" href="#">Downloads der letzten 10 Minuten</a>
<div class="navbar-collapse" id="navbarNav"> <div class="navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item"> <li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li>
<a href="{{ url_for('index') }}" class="nav-link">App</a> <li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li>
</li> <li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li>
<li class="nav-item"> <li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung-Downloads</a></li>
<a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a> <li class="nav-item"><a href="{{ url_for('songs_dashboard') }}" class="nav-link">Auswertung-Wiederholungen</a></li>
</li>
<li class="nav-item">
<a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a>
</li>
<li class="nav-item">
<a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -31,7 +31,8 @@
<li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li> <li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li>
<li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li> <li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li>
<li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li> <li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li>
<li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung</a></li> <li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung-Downloads</a></li>
<li class="nav-item"><a href="{{ url_for('songs_dashboard') }}" class="nav-link">Auswertung-Wiederholungen</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -272,8 +273,8 @@
</div> </div>
<!-- Scripts --> <!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> <script>
// Data passed from the backend as JSON // Data passed from the backend as JSON
let distinctDeviceData = {{ distinct_device_data|tojson }}; let distinctDeviceData = {{ distinct_device_data|tojson }};

View File

@ -33,7 +33,8 @@
<li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li> <li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li>
<li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li> <li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li>
<li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li> <li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li>
<li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung</a></li> <li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung-Downloads</a></li>
<li class="nav-item"><a href="{{ url_for('songs_dashboard') }}" class="nav-link">Auswertung-Wiederholungen</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -1,50 +1,182 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Gemeinsamer Gesang</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS --> <title>Dashboard - Verbindungsanalyse</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head> <style>
<body> html, body {
<div class="container mt-4"> height: 100%;
<h1>Dashboard: Gemeinsamer Gesang</h1> margin: 0;
padding: 0;
}
.container-fluid {
height: 100%;
display: flex;
flex-direction: column;
}
.card {
margin-bottom: 1.5rem;
}
</style>
</head>
<body>
<!-- Navigation Bar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-secondary mb-3">
<div class="container-fluid">
<a class="navbar-brand" href="#">Dashboard - Analyse Wiederholungen</a>
<div class="navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li>
<li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li>
<li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li>
<li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung-Downloads</a></li>
<li class="nav-item"><a href="{{ url_for('songs_dashboard') }}" class="nav-link">Auswertung-Wiederholungen</a></li>
</ul>
</div>
</div>
</nav>
<!-- Timeframe selection form --> <!-- Main Container -->
<form method="get" action="{{ url_for('songs_dashboard') }}" class="mb-3" onsubmit="this.submit();"> <div class="container-fluid px-4">
<div class="form-group"> <!-- Dropdown Controls -->
<label for="siteSelect">Gemeindehaus</label> <div class="mb-4 d-flex flex-wrap gap-2">
<select class="form-control" id="siteSelect" name="site" onchange="this.form.submit();">
<option value="Speyer" {% if site == "Speyer" %}selected{% endif %}>Speyer</option>
<option value="Schwegenheim" {% if site == "Schwegenheim" %}selected{% endif %}>Schwegenheim</option>
</select>
<label for="timeframeSelect">Zeitrahmen (in Tage)</label>
<select class="form-control" id="timeframeSelect" name="days" onchange="this.form.submit();">
<option value="7" {% if timeframe == 7 %}selected{% endif %}>letzte 7 Tage</option>
<option value="30" {% if timeframe == 30 %}selected{% endif %}>letzte 30 Tage</option>
<option value="365" {% if timeframe == 365 %}selected{% endif %}>letzte 365 Tage</option>
<option value="all" {% if timeframe == "all" %}selected{% endif %}>Alle Jahre</option>
</select>
</div>
</form>
<!-- Table Output --> <!-- Site Dropdown -->
<table class="table table-bordered"> <div class="dropdown">
<thead> <button class="btn btn-secondary dropdown-toggle"
<tr> type="button" id="siteDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<th>Anzahl</th> {% if session['songs_dashboard_site'] == 'Speyer' %}
<th>Titel</th> Speyer
</tr> {% elif session['songs_dashboard_site'] == 'Schwegenheim' %}
</thead> Schwegenheim
<tbody> {% else %}
{% for count, titel in performance_data %} Select Site
{% endif %}
</button>
<ul class="dropdown-menu" aria-labelledby="siteDropdown">
<li>
<a class="dropdown-item {% if session['songs_dashboard_site'] == 'Speyer' %}active{% endif %}"
href="{{ url_for('songs_dashboard', site='Speyer') }}">
Speyer
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_site'] == 'Schwegenheim' %}active{% endif %}"
href="{{ url_for('songs_dashboard', site='Schwegenheim') }}">
Schwegenheim
</a>
</li>
</ul>
</div>
<!-- Timeframe Dropdown -->
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle"
type="button" id="timeframeDropdown" data-bs-toggle="dropdown" aria-expanded="false">
{% if session['songs_dashboard_timeframe'] == '7' %}
Last 7 Days
{% elif session['songs_dashboard_timeframe'] == '30' %}
Last 30 Days
{% elif session['songs_dashboard_timeframe'] == '90' %}
Last 90 Days
{% elif session['songs_dashboard_timeframe'] == '365' %}
Last 365 Days
{% elif session['songs_dashboard_timeframe'] == 'all' %}
All Data
{% else %}
Select Timeframe
{% endif %}
</button>
<ul class="dropdown-menu" aria-labelledby="timeframeDropdown">
<li>
<a class="dropdown-item {% if session['songs_dashboard_timeframe'] == '7' %}active{% endif %}"
href="{{ url_for('songs_dashboard', timeframe='7') }}">
Last 7 Days
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_timeframe'] == '30' %}active{% endif %}"
href="{{ url_for('songs_dashboard', timeframe='30') }}">
Last 30 Days
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_timeframe'] == '90' %}active{% endif %}"
href="{{ url_for('songs_dashboard', timeframe='90') }}">
Last 90 Days
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_timeframe'] == '365' %}active{% endif %}"
href="{{ url_for('songs_dashboard', timeframe='365') }}">
Last 365 Days
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_timeframe'] == 'all' %}active{% endif %}"
href="{{ url_for('songs_dashboard', timeframe='all') }}">
All Data
</a>
</li>
</ul>
</div>
<!-- category Dropdown -->
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="categoryDropdown" data-bs-toggle="dropdown" aria-expanded="false">
{% if session['songs_dashboard_category'] == 'Gemeinsamer Gesang' %}
Gemeinsamer Gesang
{% elif session['songs_dashboard_category'] == 'Chor' %}
Chor
{% elif session['songs_dashboard_category'] == 'Gruppenlied' %}
Gruppenlied
{% else %}
Select Category
{% endif %}
</button>
<ul class="dropdown-menu" aria-labelledby="categoryDropdown">
<li>
<a class="dropdown-item {% if session['songs_dashboard_category'] == 'Gemeinsamer Gesang' %}active{% endif %}"
href="{{ url_for('songs_dashboard', category='Gemeinsamer Gesang') }}">
Gemeinsamer Gesang
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_category'] == 'Chor' %}active{% endif %}"
href="{{ url_for('songs_dashboard', category='Chor') }}">
Chor
</a>
</li>
<li>
<a class="dropdown-item {% if session['songs_dashboard_category'] == 'Gruppenlied' %}active{% endif %}"
href="{{ url_for('songs_dashboard', category='Gruppenlied') }}">
Gruppenlied
</a>
</li>
</ul>
</div>
</div>
<!-- Table Output -->
<table class="table table-bordered">
<thead>
<tr> <tr>
<td>{{ count }}</td> <th>Anzahl</th>
<td>{{ titel }}</td> <th>Titel</th>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for count, titel in performance_data %}
</div> <tr>
<td>{{ count }}</td>
<td>{{ titel }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body> </body>
</html> </html>