Compare commits
5 Commits
fa5c708b31
...
bc626870d8
| Author | SHA1 | Date | |
|---|---|---|---|
| bc626870d8 | |||
| 88c4eecb3b | |||
| eccfce1a0c | |||
| 2e8f586f5b | |||
| 9f3e5bea93 |
28
analytics.py
28
analytics.py
@ -10,6 +10,8 @@ import auth
|
||||
|
||||
file_access_temp = []
|
||||
|
||||
app_config = auth.return_app_config()
|
||||
|
||||
# Example database name; you can change to whatever you want:
|
||||
DB_NAME = 'access_log.db'
|
||||
|
||||
@ -190,11 +192,26 @@ def songs_dashboard():
|
||||
performance_data = [(count, titel) for titel, count in performance_counts.items()]
|
||||
performance_data.sort(reverse=True, key=lambda x: x[0])
|
||||
|
||||
return render_template('songs_dashboard.html', timeframe=timeframe_param, performance_data=performance_data, site=site, category=category, admin_enabled=auth.is_admin())
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
|
||||
return render_template('songs_dashboard.html',
|
||||
timeframe=timeframe_param,
|
||||
performance_data=performance_data,
|
||||
site=site,
|
||||
category=category,
|
||||
admin_enabled=auth.is_admin(),
|
||||
title_short=title_short,
|
||||
title_long=title_long)
|
||||
|
||||
@require_secret
|
||||
def connections():
|
||||
return render_template('connections.html', admin_enabled=auth.is_admin())
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
return render_template('connections.html',
|
||||
admin_enabled=auth.is_admin(),
|
||||
title_short=title_short,
|
||||
title_long=title_long)
|
||||
|
||||
@require_secret
|
||||
def dashboard():
|
||||
@ -465,6 +482,9 @@ def dashboard():
|
||||
# Convert the top-files rows to a list of dictionaries
|
||||
rows = [dict(rel_path=r[0], access_count=r[1]) for r in rows]
|
||||
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
|
||||
return render_template(
|
||||
"dashboard.html",
|
||||
timeframe=session['timeframe'],
|
||||
@ -478,7 +498,9 @@ def dashboard():
|
||||
unique_user=unique_user,
|
||||
cached_percentage=cached_percentage,
|
||||
timeframe_data=timeframe_data,
|
||||
admin_enabled=auth.is_admin()
|
||||
admin_enabled=auth.is_admin(),
|
||||
title_short=title_short,
|
||||
title_long=title_long
|
||||
)
|
||||
|
||||
def export_to_excel():
|
||||
|
||||
8
auth.py
8
auth.py
@ -235,6 +235,9 @@ def mylinks():
|
||||
token_url[token] = url
|
||||
token_valid_to[token] = token_item.get('validity', 'Unbekannt')
|
||||
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
|
||||
return render_template('mylinks.html',
|
||||
valid_secrets=valid_secrets,
|
||||
secret_qr_codes=secret_qr_codes,
|
||||
@ -247,7 +250,10 @@ def mylinks():
|
||||
token_folders=token_folders,
|
||||
token_url=token_url,
|
||||
token_valid_to=token_valid_to,
|
||||
admin_enabled=is_admin()
|
||||
admin_enabled=is_admin(),
|
||||
|
||||
title_short=title_short,
|
||||
title_long=title_long
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import secrets
|
||||
import string
|
||||
import auth
|
||||
|
||||
app_config = auth.return_app_config()
|
||||
|
||||
# Secret alphabet
|
||||
ALPHABET = string.ascii_letters + string.digits
|
||||
@ -13,7 +14,13 @@ ALPHABET = string.ascii_letters + string.digits
|
||||
|
||||
@auth.require_admin
|
||||
def folder_secret_config_editor():
|
||||
return render_template('folder_secret_config_editor.html', alphabet=ALPHABET, admin_enabled=auth.is_admin())
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
return render_template('folder_secret_config_editor.html',
|
||||
alphabet=ALPHABET,
|
||||
admin_enabled=auth.is_admin(),
|
||||
title_short=title_short,
|
||||
title_long=title_long)
|
||||
|
||||
@auth.require_admin
|
||||
def folder_secret_config_action():
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
migrate_cache.py
|
||||
|
||||
Migrate DiskCache caches from “old” in‐memory entries (bytes, mime) into
|
||||
“new” on‐disk read=True entries so that Flask can send them via send_file(path).
|
||||
|
||||
Usage:
|
||||
python migrate_cache.py /path/to/filecache_audio \
|
||||
/path/to/filecache_image \
|
||||
/path/to/filecache_video \
|
||||
/path/to/filecache_other
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
from diskcache import Cache
|
||||
|
||||
def migrate_cache(cache_path):
|
||||
"""
|
||||
Walks every key in the cache at `cache_path`. If the value is a tuple of
|
||||
(bytes, mime), re‐writes it via read=True so DiskCache stores it as a file.
|
||||
"""
|
||||
print(f"➡ Migrating cache at {cache_path!r}")
|
||||
cache = Cache(cache_path)
|
||||
migrated = 0
|
||||
skipped = 0
|
||||
|
||||
# Iterate keys without loading everything into memory
|
||||
for key in cache.iterkeys():
|
||||
try:
|
||||
val = cache.get(key)
|
||||
except Exception as e:
|
||||
print(f" [ERROR] key={key!r} get failed: {e}")
|
||||
continue
|
||||
|
||||
# Detect old‐style entries: (bytes, mime)
|
||||
if (
|
||||
isinstance(val, tuple)
|
||||
and len(val) == 2
|
||||
and isinstance(val[0], (bytes, bytearray))
|
||||
and isinstance(val[1], str)
|
||||
):
|
||||
data, mime = val
|
||||
buf = io.BytesIO(data)
|
||||
buf.seek(0)
|
||||
# Re‐store as an on‐disk file
|
||||
cache.set(key, buf, read=True)
|
||||
migrated += 1
|
||||
else:
|
||||
skipped += 1
|
||||
|
||||
cache.close()
|
||||
print(f" → Done: migrated={migrated}, skipped={skipped}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: migrate_cache.py <cache_dir1> [<cache_dir2> ...]")
|
||||
sys.exit(1)
|
||||
|
||||
for directory in sys.argv[1:]:
|
||||
migrate_cache(directory)
|
||||
12
search.py
12
search.py
@ -16,12 +16,18 @@ with open("app_config.json", 'r') as file:
|
||||
def searchcommand():
|
||||
query = request.form.get("query", "").strip()
|
||||
category = request.form.get("category", "").strip()
|
||||
searchfolder = request.form.get("folder", "").strip()
|
||||
|
||||
include_transcript = request.form.get("includeTranscript") in ["true", "on"]
|
||||
words = [w for w in query.split() if w]
|
||||
cursor = search_db.cursor()
|
||||
|
||||
allowed_basefolders = list(session['folders'].keys())
|
||||
print("Allowed base folders:", allowed_basefolders)
|
||||
|
||||
# if the search folder is allowed to be searched, select it
|
||||
# if not just allowed_basefolders rules apply
|
||||
if searchfolder != "" and searchfolder in allowed_basefolders:
|
||||
allowed_basefolders = [searchfolder]
|
||||
|
||||
if not include_transcript:
|
||||
conditions = []
|
||||
@ -93,10 +99,12 @@ def searchcommand():
|
||||
return jsonify(results=results)
|
||||
|
||||
def search():
|
||||
allowed_basefolders = list(session['folders'].keys())
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
return render_template("search.html",
|
||||
title_short=title_short,
|
||||
title_long=title_long
|
||||
title_long=title_long,
|
||||
search_folders=allowed_basefolders
|
||||
)
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ html, body {
|
||||
color: var(--main-text-color);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
/* padding: 0; */
|
||||
}
|
||||
|
||||
/* Global Styles */
|
||||
@ -23,6 +24,7 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
/* width: 100%; */
|
||||
}
|
||||
.site-header img.logo {
|
||||
height: 50px;
|
||||
@ -51,6 +53,12 @@ body {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Breadcrumb Styles */
|
||||
.breadcrumb {
|
||||
margin-bottom: 15px;
|
||||
@ -294,3 +302,17 @@ footer {
|
||||
.image-item.loaded .thumbnail {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
max-width: 300px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.locked { background-color:#eee; }
|
||||
|
||||
.unlocked { background-color: #fff3cd; }
|
||||
@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
|
||||
# — CONFIGURE —
|
||||
DB_NAME = 'access_log.db'
|
||||
INDICATORS = [
|
||||
"Germany",
|
||||
"Canada",
|
||||
"Paraguay",
|
||||
"Uruguay",
|
||||
"France",
|
||||
"Australia",
|
||||
"Ukraine",
|
||||
"India"
|
||||
]
|
||||
# — END CONFIG —
|
||||
|
||||
# connect exactly as you use elsewhere
|
||||
log_db = sqlite3.connect(DB_NAME, check_same_thread=False)
|
||||
|
||||
# build and run the swap
|
||||
placeholders = ",".join("?" for _ in INDICATORS)
|
||||
sql = f"""
|
||||
UPDATE file_access_log
|
||||
SET city = country,
|
||||
country = city
|
||||
WHERE city IN ({placeholders})
|
||||
"""
|
||||
cur = log_db.cursor()
|
||||
cur.execute(sql, INDICATORS)
|
||||
log_db.commit()
|
||||
|
||||
print(f"Swapped city↔country on {cur.rowcount} row{'s' if cur.rowcount!=1 else ''}.")
|
||||
log_db.close()
|
||||
@ -7,52 +7,39 @@
|
||||
<title>{% block title %}Meine Links{% endblock %}</title>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
/* common styles */
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container-fluid {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.qr-code {
|
||||
max-width: 300px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
.card {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.locked { background-color:#eee; }
|
||||
.unlocked { background-color: #fff3cd; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
|
||||
|
||||
{% block head_extra %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<a href="/">
|
||||
<img src="/custom_logo/logoW.png" alt="Logo" class="logo">
|
||||
</a>
|
||||
<h1>{{ title_long }}</h1>
|
||||
</header>
|
||||
<div class="wrapper">
|
||||
<!-- Navigation Bar -->
|
||||
<div style="margin-bottom: 10px; background-color:rgb(187, 187, 187); padding: 5px; ">
|
||||
<span> </span>
|
||||
<a href="{{ url_for('index') }}" class="btn btn-sm">App</a>
|
||||
<span class="btn btn-sm">|</span>
|
||||
<a href="{{ url_for('mylinks') }}" class="btn btn-sm">Meine Links</a>
|
||||
<span class="btn btn-sm">|</span>
|
||||
<a href="{{ url_for('connections') }}" class="btn btn-sm">Verbindungen</a>
|
||||
<span class="btn btn-sm">|</span>
|
||||
<a href="{{ url_for('dashboard') }}" class="btn btn-sm">Auswertung-Downloads</a>
|
||||
<span class="btn btn-sm">|</span>
|
||||
<a href="{{ url_for('songs_dashboard') }}" class="btn btn-sm">Auswertung-Wiederholungen</a>
|
||||
{% if admin_enabled %}
|
||||
<span class="btn btn-sm">|</span>
|
||||
<a href="{{ url_for('folder_secret_config_editor') }}" class="btn btn-sm" id="edit-folder-config">Ordnerkonfiguration</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Navigation Bar -->
|
||||
<div style="background-color: #979797; padding: 5px; display: flex; gap: 5px; flex-wrap: wrap;">
|
||||
<a href="{{ url_for('index') }}" class="btn btn-secondary btn-sm">App</a>
|
||||
<a href="{{ url_for('mylinks') }}" class="btn btn-secondary btn-sm">Meine Links</a>
|
||||
<a href="{{ url_for('connections') }}" class="btn btn-secondary btn-sm">Verbindungen</a>
|
||||
<a href="{{ url_for('dashboard') }}" class="btn btn-secondary btn-sm">Auswertung-Downloads</a>
|
||||
<a href="{{ url_for('songs_dashboard') }}" class="btn btn-secondary btn-sm">Auswertung-Wiederholungen</a>
|
||||
{% if admin_enabled %}
|
||||
<a href="{{ url_for('folder_secret_config_editor') }}" class="btn btn-secondary btn-sm" id="edit-folder-config">Ordnerkonfiguration</a>
|
||||
{% endif %}
|
||||
{% block nav_extra %}{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<div class="container">
|
||||
<h2>{% block nav_brand %}Übersicht deiner gültigen Links{% endblock %}</h2>
|
||||
</div>
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
|
||||
@ -3,14 +3,6 @@
|
||||
|
||||
{% block title %}Recent Connections{% endblock %}
|
||||
|
||||
{% block nav_brand %}Downloads der letzten 10 Minuten{% endblock %}
|
||||
|
||||
{% block nav_extra %}
|
||||
<span class="ms-3">
|
||||
Anzahl Verbindungen: <strong id="totalConnections">0</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
/* page-specific styles */
|
||||
@ -34,6 +26,10 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<h2>Übersicht deiner gültigen Links</h2>
|
||||
<span class="ms-3">
|
||||
Anzahl Verbindungen: <strong id="totalConnections">0</strong>
|
||||
</span>
|
||||
<div class="table-responsive table-container">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-secondary">
|
||||
|
||||
@ -4,14 +4,12 @@
|
||||
{# page title #}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
|
||||
{# override navbar text: #}
|
||||
{% block nav_brand %}Auswertung-Downloads{% endblock %}
|
||||
|
||||
{# page content #}
|
||||
{% block content %}
|
||||
|
||||
<!-- Main Container -->
|
||||
<div class="container-fluid px-4">
|
||||
<h2>Auswertung-Downloads</h2>
|
||||
<!-- Dropdown Controls -->
|
||||
<div class="mb-4 d-flex flex-wrap gap-2">
|
||||
<!-- Timeframe Dropdown -->
|
||||
|
||||
@ -4,12 +4,10 @@
|
||||
{# page title #}
|
||||
{% block title %}Ordnerkonfiguration{% endblock %}
|
||||
|
||||
{# override navbar text: #}
|
||||
{% block nav_brand %}Ordnerkonfiguration{% endblock %}
|
||||
|
||||
{# page content #}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Ordnerkonfiguration</h2>
|
||||
<div id="records"></div>
|
||||
<button id="add-btn" class="btn btn-primary mt-3">Add New Record</button>
|
||||
</div>
|
||||
|
||||
@ -4,16 +4,10 @@
|
||||
{# page title #}
|
||||
{% block title %}Meine Links{% endblock %}
|
||||
|
||||
{# override navbar text: #}
|
||||
{#
|
||||
{% block nav_brand %}
|
||||
Übersicht deiner gültigen Links
|
||||
{% endblock %}
|
||||
#}
|
||||
|
||||
{# page‐specific content #}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<h2>Übersicht deiner gültigen Links</h2>
|
||||
<div class="row">
|
||||
{% if valid_secrets %}
|
||||
{% for secret in valid_secrets %}
|
||||
|
||||
@ -35,12 +35,6 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<style>
|
||||
.site-header {
|
||||
width: 100%;
|
||||
}
|
||||
.card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@ -50,12 +44,12 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<a href="/">
|
||||
<img src="/custom_logo/logoW.png" alt="Logo" class="logo">
|
||||
</a>
|
||||
<h1>{{ title_long }}</h1>
|
||||
</header>
|
||||
<header class="site-header">
|
||||
<a href="/">
|
||||
<img src="/custom_logo/logoW.png" alt="Logo" class="logo">
|
||||
</a>
|
||||
<h1>{{ title_long }}</h1>
|
||||
</header>
|
||||
<div class="search-container">
|
||||
<h1>Suche</h1>
|
||||
<form id="searchForm" method="post" class="mb-4">
|
||||
@ -94,6 +88,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- dropdown search folder-->
|
||||
<div class="mb-3">
|
||||
<label for="folder" class="form-label">In Ordner suchen:</label>
|
||||
<select id="folder" name="folder" class="form-select">
|
||||
<option value="">Alle</option>
|
||||
{% for folder in search_folders %}
|
||||
<option value="{{ folder }}">{{ folder }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<!-- Checkbox for transcript search -->
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" class="form-check-input" id="includeTranscript" name="includeTranscript">
|
||||
@ -204,6 +208,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const formData = new FormData();
|
||||
formData.append('query', query);
|
||||
formData.append('category', category);
|
||||
formData.append('folder', document.getElementById('folder').value);
|
||||
formData.append('includeTranscript', includeTranscript);
|
||||
|
||||
fetch('/searchcommand', {
|
||||
|
||||
@ -2,16 +2,14 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{# page title #}
|
||||
{% block title %}Edit Folder Config{% endblock %}
|
||||
|
||||
{# override navbar text: #}
|
||||
{% block nav_brand %}Dashboard - Analyse Wiederholungen{% endblock %}
|
||||
{% block title %}Analyse Wiederholungen{% endblock %}
|
||||
|
||||
{# page content #}
|
||||
{% block content %}
|
||||
|
||||
<!-- Main Container -->
|
||||
<div class="container-fluid px-4">
|
||||
<h2>Analyse Wiederholungen</h2>
|
||||
<!-- Dropdown Controls -->
|
||||
<div class="mb-4 d-flex flex-wrap gap-2">
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user