Fotos: Gesichtssuche FH-weit (Studierende + Lektoren) als Job mit Live-Progress, Person-Info-Seiten, Scope-Auswahl
This commit is contained in:
@@ -49,7 +49,7 @@
|
||||
<div class="avatar-initials">{{ (p.nachname[:1] ~ p.vorname[:1]) | upper }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="photo-name">{{ p.nachname }}{% if p.vorname %}, {{ p.vorname }}{% endif %}</div>
|
||||
<div class="photo-name"><a href="/person/{{ p.matrikel }}">{{ p.nachname }}{% if p.vorname %}, {{ p.vorname }}{% endif %}</a></div>
|
||||
{% if p.matrikel == own_uid %}<div class="photo-own">Das bist du</div>{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -60,11 +60,23 @@
|
||||
|
||||
<div class="card">
|
||||
<h2>Gesichtssuche</h2>
|
||||
<p class="hint">Foto von dir hochladen - die App findet die passenden Profile in diesem Verband.</p>
|
||||
<p class="hint">Foto hochladen - die App vergleicht es mit den Profilfotos im gewählten Suchbereich.
|
||||
Beim ersten Mal für große Bereiche (z.B. FH-weit) kann die Suche etwas dauern;
|
||||
die Ergebnisse erscheinen live, während sie berechnet werden.</p>
|
||||
<form class="form-row form-stack" method="post" action="/fotos/suche" enctype="multipart/form-data">
|
||||
<input type="hidden" name="kz" value="{{ kz }}">
|
||||
<input type="hidden" name="sem" value="{{ sem }}">
|
||||
<input type="hidden" name="ver" value="{{ ver }}">
|
||||
<div class="field">
|
||||
<label>Suchbereich</label>
|
||||
<select name="scope">
|
||||
<option value="verband">Nur Verband {{ ver }} (Sem. {{ sem }})</option>
|
||||
<option value="stg">Gesamter Studiengang (alle Semester + Verbände)</option>
|
||||
<option value="stud">Alle Studierenden (FH-weit)</option>
|
||||
<option value="lektor">Alle Lektoren</option>
|
||||
<option value="alle">Alle Personen (Studierende + Lektoren)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Foto</label>
|
||||
<input type="file" name="file" accept="image/*" required>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% block title %}Gesichtssuche{% endblock %}
|
||||
{% block content %}
|
||||
<h1 class="page-title">Gesichtssuche
|
||||
{% if stg %}<span class="badge">{{ stg.code }} - {{ stg.name }}</span>{% endif %}
|
||||
<span class="badge badge-muted">{{ scope_label }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="card">
|
||||
@@ -42,33 +42,23 @@
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<h2>
|
||||
Ergebnisse
|
||||
<span class="count-pill">{{ results | selectattr('sim', 'ge', 0.35) | list | length }} Treffer</span>
|
||||
{% if took is defined %}<span class="badge badge-muted">{{ took }} s</span>{% endif %}
|
||||
<span id="search-state">Suche läuft…</span>
|
||||
<span class="count-pill" id="search-count">0 Treffer</span>
|
||||
<span class="badge badge-muted" id="search-progress">0/{{ job.total }}</span>
|
||||
<span class="badge badge-muted" id="search-time" style="display:none"></span>
|
||||
</h2>
|
||||
{% if results | selectattr('sim', 'ge', 0.35) | list | length %}
|
||||
<div class="match-grid">
|
||||
{% for r in results[:30] %}
|
||||
<div class="match-tile {{ 'match-strong' if r.sim >= 0.5 else ('match-mid' if r.sim >= 0.35 else '') }}">
|
||||
<img src="{{ r.img }}" alt="{{ r.nachname }}">
|
||||
<div class="match-name">
|
||||
<a href="/plan?uid={{ r.matrikel }}">{{ r.nachname }}{% if r.vorname %}, {{ r.vorname }}{% endif %}</a>
|
||||
</div>
|
||||
<div class="match-sim">
|
||||
<div class="sim-bar"><div style="width: {{ [r.sim * 100, 0] | max | round(0) }}%"></div></div>
|
||||
<span class="sim-num">{{ [r.sim * 100, 0] | max | round(0) }} %</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="hint" style="margin-top:12px">
|
||||
<div class="search-progressbar" id="search-progressbar"><div id="search-bar"></div></div>
|
||||
<div class="match-grid" id="match-grid"></div>
|
||||
<p class="hint" id="search-hint" style="display:none">
|
||||
<strong>Hinweis:</strong> Hohe Werte (grün, ≥ 50 %) deuten auf dieselbe Person hin.
|
||||
Werte um 35–50 % (gelb) sind mögliche Treffer - bitte prüfen. Unter 35 % wird nicht als Treffer gezählt.
|
||||
Werte um 35–50 % (gelb) sind mögliche Treffer - bitte prüfen.
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="empty">Kein Treffer mit ≥ 35 % Ähnlichkeit gefunden.
|
||||
Prüfe, ob das richtige Semester/Verband gewählt ist und das Foto ein klar sichtbares Gesicht zeigt.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
window.__SEARCH_JOB__ = {{ job | tojson if job else 'null' }};
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,49 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{% if prof %}{{ prof.nachname }}, {{ prof.vorname }}{% else %}Person{% endif %}{% endblock %}
|
||||
{% block content %}
|
||||
{% if not prof %}
|
||||
<h1 class="page-title">Person nicht gefunden</h1>
|
||||
<div class="card">
|
||||
<p class="empty">Zu <span class="mono">{{ uid }}</span> existiert kein CIS-Profil
|
||||
(oder das Profil ist nicht mehr aktiv).</p>
|
||||
<a class="btn" href="/fotos">Zurück zur Fotowand</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="person-card">
|
||||
<div class="person-photo">
|
||||
{% if prof.person_id %}
|
||||
<img src="/fotos/img/{{ prof.person_id }}" alt="{{ prof.nachname }}"
|
||||
onerror="this.replaceWith(Object.assign(document.createElement('div'),{className:'avatar-initials',textContent:'{{ (prof.nachname[:1] ~ prof.vorname[:1]) | upper }}'}))">
|
||||
{% else %}
|
||||
<div class="avatar-initials">{{ (prof.nachname[:1] ~ prof.vorname[:1]) | upper }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="person-info">
|
||||
<h1 class="person-name">
|
||||
{{ prof.titel }} {{ prof.vorname }} {{ prof.nachname }}{{ ' ' if prof.postnomen }}{{ prof.postnomen }}
|
||||
</h1>
|
||||
<div class="person-badges">
|
||||
<span class="badge">{{ prof.role or 'Person' }}</span>
|
||||
{% if dec %}<span class="badge badge-muted">Start {{ dec.start }}</span>
|
||||
{% if dec.stg_code %}<span class="badge badge-muted">{{ dec.stg_code }}</span>{% endif %}{% endif %}
|
||||
{% if uid == own_uid %}<span class="badge badge-green">Das bist du</span>{% endif %}
|
||||
</div>
|
||||
{% if prof.nachname or prof.username or prof.semester or prof.stg or prof.verband or prof.kurzzeichen or prof.mail %}
|
||||
<table class="person-kv">
|
||||
{% if prof.username %}<tr><td>Username</td><td class="mono">{{ prof.username }}</td></tr>{% endif %}
|
||||
{% if prof.stg %}<tr><td>Studiengang</td><td>{{ prof.stg }}</td></tr>{% endif %}
|
||||
{% if prof.semester %}<tr><td>Studien-Semester</td><td>{{ prof.semester }}</td></tr>{% endif %}
|
||||
{% if prof.verband %}<tr><td>Verband</td><td>{{ prof.verband }}{{ verband_label(prof.verband) }}</td></tr>{% endif %}
|
||||
{% if prof.kurzzeichen %}<tr><td>Kurzzeichen</td><td>{{ prof.kurzzeichen }}</td></tr>{% endif %}
|
||||
{% if prof.mail %}<tr><td>E-Mail</td><td><a href="mailto:{{ prof.mail }}">{{ prof.mail }}</a></td></tr>{% endif %}
|
||||
{% if prof.mail_alias %}<tr><td>E-Mail (Alias)</td><td><a href="mailto:{{ prof.mail_alias }}">{{ prof.mail_alias }}</a></td></tr>{% endif %}
|
||||
</table>
|
||||
{% endif %}
|
||||
<div class="person-actions">
|
||||
{% if uid.isdigit() %}<a class="btn btn-primary" href="/plan?uid={{ uid }}">LV-Plan anzeigen</a>{% endif %}
|
||||
<a class="btn" href="/fotos">Zurück zur Fotowand</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user