adding statistics page for admin

This commit is contained in:
2025-09-14 17:07:05 +02:00
parent e947520be9
commit c4a5ed56b5
4 changed files with 117 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ from web.db import (
set_user_keywords, set_user_keywords,
get_all_regions, get_all_regions,
get_all_keywords, get_all_keywords,
stats_overview,
upsert_region, upsert_region,
upsert_keyword, upsert_keyword,
list_regions_full, list_regions_full,
@@ -487,6 +488,34 @@ def admin_taxonomy():
return render_template('admin/taxonomy.html', title='Taxonomy', regions=regions, keywords=keywords) return render_template('admin/taxonomy.html', title='Taxonomy', regions=regions, keywords=keywords)
@app.route('/admin/stats', methods=['GET'])
def admin_stats():
if not require_admin():
return redirect(url_for('login'))
# Optional filters via query params
keyword = request.args.get('keyword')
region = request.args.get('region')
try:
stats = stats_overview()
# For detailed jobs table, reuse get_all_jobs() and filter
jobs = get_all_jobs()
if keyword:
jobs = [j for j in jobs if (j.get('keyword') or '') == keyword]
if region:
jobs = [j for j in jobs if (j.get('region') or '') == region]
except Exception as e:
flash(f'Error computing stats: {e}')
stats = {
'total_jobs': 0,
'total_keywords': 0,
'total_regions': 0,
'jobs_per_keyword': [],
'jobs_per_region': []
}
jobs = []
return render_template('admin/stats.html', title='Statistics', stats=stats, jobs=jobs, regions=get_all_regions(), keywords=get_all_keywords())
def init(): def init():
"""Main function to run the Flask app.""" """Main function to run the Flask app."""
# Ensure DB is initialized # Ensure DB is initialized

View File

@@ -783,3 +783,44 @@ def change_keyword_color(keyword_id: int, new_color: str) -> bool:
except Exception: except Exception:
session.rollback() session.rollback()
return False return False
def stats_overview() -> Dict[str, Any]:
"""Return an overview of job DB statistics.
Returns a dict with keys:
- total_jobs: int
- total_keywords: int (distinct keywords in listings)
- total_regions: int (distinct regions in listings)
- jobs_per_keyword: List[{"keyword": str, "count": int}]
- jobs_per_region: List[{"region": str, "count": int}]
"""
with _ensure_session() as session:
total_jobs = session.execute(text(
"SELECT COUNT(*) FROM job_listings l INNER JOIN job_descriptions d ON l.job_id = d.job_id AND l.url = d.url"
)).scalar_one()
total_keywords = session.execute(text(
"SELECT COUNT(DISTINCT keyword) FROM job_listings WHERE keyword IS NOT NULL AND keyword != ''"
)).scalar_one()
total_regions = session.execute(text(
"SELECT COUNT(DISTINCT region) FROM job_listings WHERE region IS NOT NULL AND region != ''"
)).scalar_one()
rows = session.execute(text(
"SELECT COALESCE(keyword, '') AS keyword, COUNT(*) as cnt FROM job_listings l INNER JOIN job_descriptions d ON l.job_id = d.job_id AND l.url = d.url GROUP BY keyword ORDER BY cnt DESC"
)).fetchall()
jobs_per_keyword = [
{"keyword": r[0], "count": int(r[1])} for r in rows]
rows = session.execute(text(
"SELECT COALESCE(region, '') AS region, COUNT(*) as cnt FROM job_listings l INNER JOIN job_descriptions d ON l.job_id = d.job_id AND l.url = d.url GROUP BY region ORDER BY cnt DESC"
)).fetchall()
jobs_per_region = [{"region": r[0], "count": int(r[1])} for r in rows]
return {
"total_jobs": int(total_jobs or 0),
"total_keywords": int(total_keywords or 0),
"total_regions": int(total_regions or 0),
"jobs_per_keyword": jobs_per_keyword,
"jobs_per_region": jobs_per_region,
}

View File

@@ -0,0 +1,46 @@
{% extends 'base.html' %} {% block content %}
<div id="admin-stats">
<h2>Database Statistics</h2>
<div class="stats-summary">
<p><strong>Total jobs:</strong> {{ stats.total_jobs }}</p>
<p><strong>Total keywords:</strong> {{ stats.total_keywords }}</p>
<p><strong>Total regions:</strong> {{ stats.total_regions }}</p>
</div>
<h3>Jobs per keyword</h3>
<table>
<thead>
<tr>
<th>Keyword</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{% for row in stats.jobs_per_keyword %}
<tr>
<td>{{ row.keyword or '(empty)' }}</td>
<td>{{ row.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h3>Jobs per region</h3>
<table>
<thead>
<tr>
<th>Region</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{% for row in stats.jobs_per_region %}
<tr>
<td>{{ row.region or '(empty)' }}</td>
<td>{{ row.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -22,6 +22,7 @@
{% if current_user and current_user.is_admin %} | {% if current_user and current_user.is_admin %} |
<a href="{{ url_for('scrape_page') }}">Scrape Jobs</a> | <a href="{{ url_for('scrape_page') }}">Scrape Jobs</a> |
<a href="{{ url_for('admin_taxonomy') }}">Taxonomy</a> | <a href="{{ url_for('admin_taxonomy') }}">Taxonomy</a> |
<a href="{{ url_for('admin_stats') }}">Statistics</a> |
<a href="{{ url_for('admin_users') }}">Users</a> {% endif %} {% if <a href="{{ url_for('admin_users') }}">Users</a> {% endif %} {% if
session.get('username') %} | session.get('username') %} |
<a href="{{ url_for('logout') }}">Logout</a> {% else %} | <a href="{{ url_for('logout') }}">Logout</a> {% else %} |