feat: add automation activity and employer intelligence
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Activiteit · VacatureRadar{% endblock %}
|
||||
{% block content %}
|
||||
<header class="cockpit-heading"><div><p class="eyebrow">Radar audit trail</p><h1>Activiteit</h1><p>Betrouwbare gebeurtenissen uit bronruns, vacatureversies, feedback, sollicitaties, notificaties en profielrevisies.</p></div><div class="radar-status"><span class="status-beacon is-live" aria-hidden="true"></span><span>{{ event_page.paginator.count }} gebeurtenissen in deze selectie</span></div></header>
|
||||
<form class="activity-filterbar" method="get"><label class="grow"><span class="field-label">Zoek in activiteit</span><span class="input-with-icon"><svg class="icon"><use href="#icon-search"></use></svg><input type="search" name="q" value="{{ request.GET.q }}" placeholder="Vacature, werkgever of gebeurtenis"></span></label><label><span class="field-label">Type</span><select name="type">{% for value,label in activity_filters %}<option value="{{ value }}" {% if current_type == value %}selected{% endif %}>{{ label }}</option>{% endfor %}</select></label><button class="button button-secondary" type="submit"><svg class="icon"><use href="#icon-filter"></use></svg>Filter</button></form>
|
||||
|
||||
<section class="activity-timeline" aria-label="Activiteitstijdlijn">
|
||||
{% for event in event_page %}<article class="activity-event"><span class="activity-marker {% if event.tone == 'positive' %}is-positive{% elif event.tone == 'attention' %}is-attention{% endif %}"><svg class="icon"><use href="{% if event.event_type == 'source' %}#icon-sources{% elif event.event_type == 'application' %}#icon-pipeline{% elif event.event_type == 'profile' %}#icon-profile{% elif event.event_type == 'notification' %}#icon-mail{% else %}#icon-activity{% endif %}"></use></svg></span><div class="activity-card"><div class="activity-card-head"><div><span class="pill pill-{{ event.tone }}">{{ event.event_type }}</span><h2>{{ event.title }}</h2></div><time datetime="{{ event.occurred_at|date:'c' }}">{{ event.occurred_at|date:"d/m/Y H:i" }}</time></div><p class="activity-object">{{ event.object_label }}</p><div class="decision-note"><span class="field-label">Gebeurtenisdetails</span><p>{{ event.detail }}</p></div><a class="section-link" href="{{ event.url }}">Open betrokken onderdeel <svg class="icon"><use href="#icon-arrow"></use></svg></a></div></article>{% empty %}<div class="empty-state"><div class="empty-icon"><svg class="icon"><use href="#icon-activity"></use></svg></div><h2>Geen activiteit gevonden</h2><p>Deze filter bevat nog geen auditwaardige gebeurtenis.</p><a class="button button-ghost" href="{% url 'jobs:activity' %}">Alle activiteit tonen</a></div>{% endfor %}
|
||||
</section>
|
||||
{% if event_page.paginator.num_pages > 1 %}<nav class="pagination" aria-label="Activiteitspaginering">{% if event_page.has_previous %}<a class="button button-ghost" href="?page={{ event_page.previous_page_number }}&type={{ current_type }}&q={{ request.GET.q|urlencode }}">Vorige</a>{% endif %}<span>Pagina {{ event_page.number }} van {{ event_page.paginator.num_pages }}</span>{% if event_page.has_next %}<a class="button button-ghost" href="?page={{ event_page.next_page_number }}&type={{ current_type }}&q={{ request.GET.q|urlencode }}">Volgende</a>{% endif %}</nav>{% endif %}
|
||||
{% endblock %}
|
||||
@@ -33,11 +33,13 @@
|
||||
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'detail' %}is-active{% endif %}" href="{% url 'jobs:list' %}?focus=all"><svg class="icon"><use href="#icon-briefcase"></use></svg><span>Alle vacatures</span></a>
|
||||
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'skill-insights' %}is-active{% endif %}" href="{% url 'jobs:skill-insights' %}"><svg class="icon"><use href="#icon-chart"></use></svg><span>Skillsradar</span></a>
|
||||
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'applications' or request.resolver_match.url_name == 'application-edit' %}is-active{% endif %}" href="{% url 'jobs:applications' %}"><svg class="icon"><use href="#icon-pipeline"></use></svg><span>Sollicitaties</span></a>
|
||||
<a class="nav-item {% if request.resolver_match.url_name == 'employers' or request.resolver_match.url_name == 'employer-detail' %}is-active{% endif %}" href="{% url 'jobs:employers' %}"><svg class="icon"><use href="#icon-building"></use></svg><span>Werkgevers</span></a>
|
||||
|
||||
<p class="nav-caption">Radarbeheer</p>
|
||||
<a class="nav-item {% if request.resolver_match.namespace == 'profiles' %}is-active{% endif %}" href="{% url 'profiles:list' %}"><svg class="icon"><use href="#icon-profile"></use></svg><span>Zoekprofiel</span></a>
|
||||
<a class="nav-item {% if request.resolver_match.namespace == 'sources' %}is-active{% endif %}" href="{% url 'sources:list' %}"><svg class="icon"><use href="#icon-sources"></use></svg><span>Bronnen</span></a>
|
||||
<a class="nav-item {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'system' %}is-active{% endif %}" href="{% url 'dashboard:system' %}"><svg class="icon"><use href="#icon-system"></use></svg><span>Automatisering</span></a>
|
||||
<a class="nav-item {% if request.resolver_match.url_name == 'activity' %}is-active{% endif %}" href="{% url 'jobs:activity' %}"><svg class="icon"><use href="#icon-activity"></use></svg><span>Activiteit</span></a>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-radar-card">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ employer.name }} intelligence · VacatureRadar{% endblock %}
|
||||
{% block content %}
|
||||
<a class="back-link" href="{% url 'jobs:employers' %}">← Terug naar werkgeversradar</a>
|
||||
<header class="detail-hero employer-detail-hero"><div><p class="eyebrow">Employer intelligence #{{ employer.pk }}</p><div class="title-row">{% if employer.is_recruiter %}<span class="pill">Recruiter</span>{% elif employer.is_direct_employer %}<span class="pill pill-success">Directe werkgever</span>{% endif %}{% if employer.employer_type %}<span class="pill">{{ employer.employer_type }}</span>{% endif %}</div><h1>{{ employer.name }}</h1><p class="detail-employer">{{ employer.domain|default:"Domein onbekend" }}</p></div><span class="employer-mark hero-mark">{{ employer.name|slice:":2"|upper }}</span></header>
|
||||
<section class="intelligence-strip"><div><span class="field-label">Actieve vacatures</span><strong>{{ employer.active_vacancies }}</strong><small>alle functies</small></div><div><span class="field-label">Relevante IT</span><strong>{{ employer.relevant_vacancies }}</strong><small>deterministisch gefilterd</small></div><div><span class="field-label">Gemiddelde match</span><strong>{% if employer.average_match is not None %}{{ employer.average_match|floatformat:0 }}%{% else %}—{% endif %}</strong><small>{% if active_profile %}{{ active_profile.name }}{% else %}geen actief profiel{% endif %}</small></div><div><span class="field-label">Brondekking</span><strong>{{ employer.source_count }}</strong><small>unieke bronnen</small></div></section>
|
||||
<div class="employer-detail-grid"><main><section class="detail-section"><div class="section-heading"><div><span class="section-kicker">Vacaturehistorie</span><h2>Recente vacatures</h2><p>Actief en historisch, uitsluitend uit opgeslagen bronrecords.</p></div></div><div class="employer-job-grid">{% for job in recent_jobs %}<article><div class="title-row"><span class="pill pill-{{ job.status }}">{{ job.get_status_display }}</span>{% if job.match_recommendation %}<span class="pill pill-{{ job.match_recommendation }}">{{ job.match_recommendation }}</span>{% endif %}</div><h3><a href="{% url 'jobs:detail' job.pk %}">{{ job.original_title }}</a></h3><p>{{ job.raw_location|default:"Locatie onbekend" }} · {{ job.get_workplace_type_display }}</p><div class="job-meta"><span>{{ job.first_seen|date:"d/m/Y" }}</span>{% if job.match_score is not None %}<strong>{{ job.match_score|floatformat:0 }}% match</strong>{% endif %}</div></article>{% empty %}<div class="empty-state compact-empty"><h2>Geen vacaturehistorie</h2></div>{% endfor %}</div></section>{% if match_history %}<section class="detail-section"><span class="section-kicker">Matchpatroon</span><h2>Recente profielbeoordelingen</h2><div class="match-history">{% for score in match_history %}<div><span>{{ score.created_at|date:"d/m" }}</span><i style="--match: {{ score.score }}%"></i><strong>{{ score.score|floatformat:0 }}%</strong><a href="{% url 'jobs:detail' score.job.pk %}">{{ score.job.original_title }}</a></div>{% endfor %}</div></section>{% endif %}</main><aside class="detail-sidebar"><section class="panel"><span class="section-kicker">Waargenomen context</span><h2>Werkgeversprofiel</h2><dl class="intel-facts"><div><dt>Locaties</dt><dd>{% if locations %}{{ locations|join:", " }}{% else %}Onbekend{% endif %}</dd></div><div><dt>Type</dt><dd>{{ employer.employer_type|default:"Onbekend" }}</dd></div><div><dt>Laatste signaal</dt><dd>{{ employer.latest_job_at|date:"d/m/Y"|default:"—" }}</dd></div><div><dt>Confidence</dt><dd>{% widthratio employer.confidence 1 100 %}%</dd></div></dl></section><section class="panel"><span class="section-kicker">Brongezondheid</span><h2>{{ sources|length }} gekoppelde bronnen</h2>{% for source in sources %}<a class="employer-source" href="{% url 'sources:list' %}"><span>{{ source.name }}</span><span class="pill {% if source.status == 'active' %}pill-success{% else %}pill-review{% endif %}">{{ source.get_status_display }}</span></a>{% empty %}<p class="muted">Geen gekoppelde bron opgeslagen.</p>{% endfor %}</section><section class="panel"><span class="section-kicker">Eerdere sollicitaties</span><h2>{{ applications|length }} dossier{% if applications|length != 1 %}s{% endif %}</h2>{% for application in applications %}<a class="employer-source" href="{% url 'jobs:application-edit' application.pk %}"><span>{{ application.job.original_title }}</span><span>{{ application.get_status_display }}</span></a>{% empty %}<p class="muted">Nog geen dossier bij deze werkgever.</p>{% endfor %}</section></aside></div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Werkgevers intelligence · VacatureRadar{% endblock %}
|
||||
{% block content %}
|
||||
<header class="cockpit-heading"><div><p class="eyebrow">Intelligent tracking</p><h1>Werkgeversradar</h1><p>Genormaliseerde werkgeversdata uit echte vacatures, bronnen, profielscores en sollicitatiedossiers.</p></div><div class="employer-kpis"><div><span>Gemonitord</span><strong>{{ employer_total }}</strong></div><div><span>Met actieve vacatures</span><strong>{{ employer_active_total }}</strong></div></div></header>
|
||||
<form class="activity-filterbar" method="get"><label class="grow"><span class="field-label">Zoek werkgevers</span><span class="input-with-icon"><svg class="icon"><use href="#icon-search"></use></svg><input type="search" name="q" value="{{ request.GET.q }}" placeholder="Naam, domein of locatie"></span></label><label><span class="field-label">Kanaal</span><select name="channel"><option value="">Alle kanalen</option><option value="direct" {% if request.GET.channel == 'direct' %}selected{% endif %}>Directe werkgever</option><option value="recruiter" {% if request.GET.channel == 'recruiter' %}selected{% endif %}>Recruiter</option></select></label><label class="toggle-field"><input type="checkbox" name="active" value="1" {% if request.GET.active == '1' %}checked{% endif %}><span>Alleen actief</span></label><button class="button button-secondary" type="submit">Filter</button></form>
|
||||
<div class="employer-radar-layout">
|
||||
<section class="employer-table-panel" aria-labelledby="employer-list-heading"><div class="section-heading"><div><span class="section-kicker">Gevolgde werkgevers</span><h2 id="employer-list-heading">{{ page_obj.paginator.count }} werkgevers</h2></div></div><div class="data-table-wrap" tabindex="0" aria-label="Werkgevers, horizontaal scrolbaar"><table class="data-table employer-table"><thead><tr><th>Werkgever</th><th>Type</th><th>Actief</th><th>Relevant IT</th><th>Bronnen</th><th>Gem. match</th><th>Laatste signaal</th></tr></thead><tbody>{% for item in employers %}<tr class="{% if selected_employer.pk == item.pk %}is-selected{% endif %}"><td><a href="?selected={{ item.pk }}&q={{ request.GET.q|urlencode }}&channel={{ request.GET.channel }}&active={{ request.GET.active }}"><span class="employer-mark">{{ item.name|slice:":2"|upper }}</span><span><strong>{{ item.name }}</strong><small>{{ item.domain|default:"Geen domein" }}</small></span></a></td><td>{% if item.is_recruiter %}<span class="pill">Recruiter</span>{% elif item.is_direct_employer %}<span class="pill pill-success">Direct</span>{% else %}<span class="pill">Onbekend</span>{% endif %}</td><td>{{ item.active_vacancies }}</td><td><strong class="signal-number">{{ item.relevant_vacancies }}</strong></td><td>{{ item.source_count }}</td><td>{% if item.average_match is not None %}{{ item.average_match|floatformat:0 }}%{% else %}—{% endif %}</td><td>{{ item.latest_job_at|date:"d/m/Y"|default:"—" }}</td></tr>{% empty %}<tr><td colspan="7">Geen werkgevers gevonden.</td></tr>{% endfor %}</tbody></table></div>{% if is_paginated %}<nav class="pagination" aria-label="Werkgeverspaginering">{% if page_obj.has_previous %}<a class="button button-ghost" href="?page={{ page_obj.previous_page_number }}&q={{ request.GET.q|urlencode }}&channel={{ request.GET.channel }}&active={{ request.GET.active }}">Vorige</a>{% endif %}<span>{{ page_obj.number }}/{{ page_obj.paginator.num_pages }}</span>{% if page_obj.has_next %}<a class="button button-ghost" href="?page={{ page_obj.next_page_number }}&q={{ request.GET.q|urlencode }}&channel={{ request.GET.channel }}&active={{ request.GET.active }}">Volgende</a>{% endif %}</nav>{% endif %}</section>
|
||||
|
||||
<aside class="employer-intelligence-rail" aria-label="Geselecteerde werkgever">{% if selected_employer %}<div class="employer-intel-header"><span class="section-kicker">Employer report #{{ selected_employer.pk }}</span><span class="employer-mark large">{{ selected_employer.name|slice:":2"|upper }}</span><h2>{{ selected_employer.name }}</h2><p>{{ selected_employer.domain|default:"Domein onbekend" }}</p></div><dl class="intel-facts"><div><dt>Actieve vacatures</dt><dd>{{ selected_employer.active_vacancies }}</dd></div><div><dt>Relevante IT</dt><dd>{{ selected_employer.relevant_vacancies }}</dd></div><div><dt>Eerdere dossiers</dt><dd>{{ selected_employer.previous_applications }}</dd></div><div><dt>Bronnen</dt><dd>{{ selected_employer.source_count }}</dd></div><div><dt>Gem. match</dt><dd>{% if selected_employer.average_match is not None %}{{ selected_employer.average_match|floatformat:0 }}%{% else %}Nog niet gemeten{% endif %}</dd></div><div><dt>Kanaal</dt><dd>{% if selected_employer.is_recruiter %}Recruiter{% elif selected_employer.is_direct_employer %}Direct{% else %}Onbekend{% endif %}</dd></div></dl><section class="employer-intel-section"><span class="field-label">Recente vacatures</span>{% for job in selected_detail.recent_jobs|slice:":4" %}<a class="employer-job-link" href="{% url 'jobs:detail' job.pk %}"><span><strong>{{ job.original_title }}</strong><small>{{ job.first_seen|date:"d/m/Y" }}</small></span>{% if job.match_score is not None %}<b>{{ job.match_score|floatformat:0 }}%</b>{% endif %}</a>{% empty %}<p class="muted">Nog geen vacaturehistorie.</p>{% endfor %}</section><section class="employer-intel-section"><span class="field-label">Waargenomen locaties</span><p>{% if selected_detail.locations %}{{ selected_detail.locations|join:", " }}{% else %}Onbekend{% endif %}</p></section><a class="button button-primary button-block" href="{% url 'jobs:employer-detail' selected_employer.pk %}">Volledig werkgeversrapport</a>{% else %}<div class="empty-state"><h2>Kies een werkgever</h2></div>{% endif %}</aside>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,15 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Systeemstatus · VacatureRadar{% endblock %}
|
||||
{% block title %}Automation center · VacatureRadar{% endblock %}
|
||||
{% block content %}
|
||||
<header class="page-header"><div><p class="eyebrow">Automatisering centrum</p><h1>Radarbesturing</h1><p>Readiness, bronruns en veilige beheeracties op basis van actuele systeemdata.</p></div><span class="pill {% if health.ok %}pill-success{% else %}pill-failed{% endif %}">{% if health.ok %}Operationeel{% else %}Aandacht nodig{% endif %}</span></header>
|
||||
<section class="system-hero">
|
||||
<article class="system-readiness {% if health.ok %}is-healthy{% endif %}"><span class="source-icon"><svg class="icon"><use href="#icon-system"></use></svg></span><div><span class="field-label">Systeemreadiness</span><h2>{% if health.ok %}Radar is operationeel{% else %}Controle vereist{% endif %}</h2><p>Databasecontrole: {{ health.checks.database }}</p></div><span class="signal-dot" aria-hidden="true"></span></article>
|
||||
<div class="system-counts">{% for row in source_summary %}<article><span class="field-label">Bronnen · {{ row.status }}</span><strong>{{ row.total }}</strong></article>{% endfor %}{% for row in job_summary %}<article><span class="field-label">Vacatures · {{ row.status }}</span><strong>{{ row.total }}</strong></article>{% endfor %}</div>
|
||||
</section>
|
||||
<header class="cockpit-heading"><div><p class="eyebrow">Automation center</p><h1>Radarbesturing</h1><p>Echte 24-uursverwerking, bronruns, notificaties en configuratiestatus. Niet-gemeten runtime-informatie blijft expliciet onbekend.</p></div><div class="page-actions"><a class="button button-ghost" href="{% url 'jobs:activity' %}"><svg class="icon"><use href="#icon-activity"></use></svg>Activiteitenlog</a><a class="button button-primary" href="{% url 'sources:list' %}"><svg class="icon"><use href="#icon-sources"></use></svg>Bronnen beheren</a></div></header>
|
||||
<section class="automation-status {% if health.ok %}is-healthy{% endif %}"><div><span class="field-label">Systeemstatus</span><h2><span class="status-beacon {% if health.ok %}is-live{% endif %}" aria-hidden="true"></span>{% if health.ok %}Database operationeel{% else %}Databasecontrole vereist{% endif %}</h2></div><dl><div><dt>Queue</dt><dd>{{ queue_state }}</dd></div><div><dt>AI</dt><dd>{{ ai_state }}{% if ai_profiles %} · {{ ai_profiles }} profiel{% endif %}</dd></div><div><dt>Browserextractie</dt><dd>{{ browser_state }}</dd></div></dl></section>
|
||||
|
||||
<div class="section-heading"><div><span class="section-kicker">Brontelemetrie</span><h2>Recente rungezondheid</h2><p>Meetwaarden uit maximaal de laatste 30 runs per bron.</p></div><a class="section-link" href="{% url 'sources:list' %}">Bronnen beheren <svg class="icon"><use href="#icon-arrow"></use></svg></a></div>
|
||||
<div class="health-grid">{% for row in source_health %}<article class="health-card"><div class="health-card-head"><div><span class="field-label">{{ row.source_type }}</span><h3>{{ row.source_name }}</h3></div><span class="pill {% if row.success_ratio >= 0.8 %}pill-success{% elif row.monitored_runs == 0 %}pill-review{% else %}pill-failed{% endif %}">{% if row.monitored_runs == 0 %}Geen runs{% else %}Ratio {{ row.success_ratio|floatformat:2 }}{% endif %}</span></div><dl><div><dt>Runs</dt><dd>{{ row.monitored_runs }}</dd></div><div><dt>Gevonden</dt><dd>{{ row.extracted_count }}</dd></div><div><dt>Bijgewerkt</dt><dd>{{ row.updated_count }}</dd></div><div><dt>Latency</dt><dd>{% if row.avg_latency_ms is not None %}{{ row.avg_latency_ms|floatformat:0 }} ms{% else %}—{% endif %}</dd></div></dl>{% if row.last_health_reason %}<p class="attention">{{ row.last_health_reason }}</p>{% endif %}</article>{% empty %}<div class="empty-state compact-empty"><h2>Nog geen brontelemetrie</h2><p>Bronruns verschijnen hier zodra een toegestane bron is uitgevoerd.</p></div>{% endfor %}</div>
|
||||
<section class="automation-pipeline" aria-labelledby="pipeline-title"><span class="section-kicker">Verwerkingspipeline · laatste 24 uur</span><h2 id="pipeline-title" class="sr-only">Verwerkingspipeline</h2>{% for phase in phase_rows %}<article><span class="pipeline-phase-index">0{{ forloop.counter }}</span><svg class="icon"><use href="{% if phase.key == 'discover' %}#icon-radar{% elif phase.key == 'fetch' %}#icon-refresh{% elif phase.key == 'notify' %}#icon-mail{% else %}#icon-system{% endif %}"></use></svg><strong>{{ phase.label }}</strong><b>{{ phase.value }}</b><small>{{ phase.unit }}</small></article>{% endfor %}</section>
|
||||
|
||||
<div class="section-heading"><div><span class="section-kicker">Operaties</span><h2>Veilige beheeropdrachten</h2><p>Uitsluitend uitvoeren vanaf een vertrouwde shell op de server.</p></div></div>
|
||||
<section class="panel command-panel"><div><span class="field-label">Verificatie</span><pre><code>make verify</code></pre></div><div><span class="field-label">Back-up</span><pre><code>./scripts/backup.sh</code></pre></div><div><span class="field-label">Herstel</span><pre><code>./scripts/restore.sh /pad/naar/backup.sql.gz</code></pre></div><div><span class="field-label">Bronnen initialiseren</span><pre><code>uv run python manage.py seed_sources</code></pre></div></section>
|
||||
<section class="automation-kpis"><article><span class="field-label">Bronruns / 24u</span><strong>{{ run_count }}</strong><small>{{ run_metrics.failures|default:0 }} mislukt</small></article><article><span class="field-label">Vacatures gevonden</span><strong>{{ run_metrics.discovered|default:0 }}</strong><small>{{ run_metrics.extracted|default:0 }} geëxtraheerd</small></article><article><span class="field-label">IT-vacatures actief</span><strong>{{ active_jobs }}</strong><small>{{ review_jobs }} beoordeelde kansen</small></article><article><span class="field-label">Notificatie-outbox</span><strong>{{ notification_counts.pending }}</strong><small>{{ notification_counts.failed }} mislukt · {{ notification_counts.sent_24h }} verzonden</small></article></section>
|
||||
|
||||
<div class="automation-layout"><section class="automation-source-table"><div class="section-heading"><div><span class="section-kicker">Brongezondheid</span><h2>{{ source_active }}/{{ source_total }} actief</h2></div><a class="section-link" href="{% url 'sources:list' %}">Alle bronnen <svg class="icon"><use href="#icon-arrow"></use></svg></a></div><div class="data-table-wrap" tabindex="0"><table class="data-table"><thead><tr><th>Bron</th><th>Status</th><th>Runs</th><th>Succesratio</th><th>Extractie</th><th>Latency</th></tr></thead><tbody>{% for row in source_health %}<tr><td><strong>{{ row.source_name }}</strong><small>{{ row.source_type }}</small></td><td><span class="pill {% if row.success_ratio >= 0.8 %}pill-success{% elif row.monitored_runs == 0 %}pill-review{% else %}pill-failed{% endif %}">{% if row.monitored_runs == 0 %}Geen meting{% else %}{{ row.source_status }}{% endif %}</span></td><td>{{ row.monitored_runs }}</td><td>{% widthratio row.success_ratio 1 100 %}%</td><td>{{ row.extracted_count }}</td><td>{% if row.avg_latency_ms is not None %}{{ row.avg_latency_ms|floatformat:0 }} ms{% else %}—{% endif %}</td></tr>{% empty %}<tr><td colspan="6">Nog geen brontelemetrie.</td></tr>{% endfor %}</tbody></table></div></section><aside class="technical-log-panel"><div class="section-heading"><div><span class="section-kicker">Technische logs</span><h2>Recente bronruns</h2></div></div><div class="live-log">{% for run in recent_runs %}<article class="{% if run.status == 'failed' %}is-error{% elif run.status == 'success' %}is-success{% endif %}"><time>[{{ run.started_at|date:"H:i:s" }}]</time><strong>{{ run.get_status_display|upper }}</strong><span>{{ run.source.name }}</span><small>{% if run.error_category %}{{ run.error_category }} · {{ run.error_message }}{% else %}{{ run.extracted_count }} geëxtraheerd · {{ run.duplicate_count }} duplicaat{% endif %}</small></article>{% empty %}<p class="muted">Nog geen echte bronrun geregistreerd.</p>{% endfor %}</div><a class="button button-ghost button-block" href="{% url 'jobs:activity' %}?type=source">Volledig logboek</a></aside></div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user