Quellen-Filter über Trefferliste, dynamisches Filtering; Umlaut-Filter hinzugefügt/angepasst; Rahmen um Trefferliste
This commit is contained in:
26
app.py
26
app.py
@@ -51,10 +51,12 @@ def filter_words(words: List[str], position_letters: List[str], includes_text: s
|
|||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
def index():
|
def index():
|
||||||
all_words, sources_map = load_words()
|
all_words, sources_map = load_words()
|
||||||
results: List[str] | None = None
|
results_display: List[str] | None = None
|
||||||
pos: List[str] = ["", "", "", "", ""]
|
pos: List[str] = ["", "", "", "", ""]
|
||||||
includes: str = ""
|
includes: str = ""
|
||||||
excludes: str = ""
|
excludes: str = ""
|
||||||
|
use_ot: bool = True
|
||||||
|
use_wf: bool = False
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
pos = [
|
pos = [
|
||||||
(request.form.get("pos1") or "").strip().lower(),
|
(request.form.get("pos1") or "").strip().lower(),
|
||||||
@@ -65,15 +67,33 @@ def index():
|
|||||||
]
|
]
|
||||||
includes = (request.form.get("includes") or "").strip()
|
includes = (request.form.get("includes") or "").strip()
|
||||||
excludes = (request.form.get("excludes") or "").strip()
|
excludes = (request.form.get("excludes") or "").strip()
|
||||||
results = filter_words(all_words, pos, includes, excludes)
|
use_ot = request.form.get("use_ot") is not None
|
||||||
|
use_wf = request.form.get("use_wf") is not None
|
||||||
|
|
||||||
|
# 1) Buchstaben-/Positionssuche über alle Wörter
|
||||||
|
matched = filter_words(all_words, pos, includes, excludes)
|
||||||
|
# 2) Quellen-Filter nur auf Ergebnisansicht anwenden
|
||||||
|
allowed = set()
|
||||||
|
if use_ot:
|
||||||
|
allowed.add("ot")
|
||||||
|
if use_wf:
|
||||||
|
allowed.add("wf")
|
||||||
|
if allowed:
|
||||||
|
results_display = [w for w in matched if any(src in allowed for src in sources_map.get(w, []))]
|
||||||
|
else:
|
||||||
|
# Keine Quelle gewählt → leere Anzeige (Suche wurde dennoch ausgeführt)
|
||||||
|
results_display = []
|
||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
results=results,
|
results=results_display,
|
||||||
pos=pos,
|
pos=pos,
|
||||||
includes=includes,
|
includes=includes,
|
||||||
excludes=excludes,
|
excludes=excludes,
|
||||||
words_count=len(all_words),
|
words_count=len(all_words),
|
||||||
sources_map=sources_map,
|
sources_map=sources_map,
|
||||||
|
use_ot=use_ot,
|
||||||
|
use_wf=use_wf,
|
||||||
|
error_message=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -29,46 +29,19 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root { --bg:#ffffff; --text:#111827; --muted:#6b7280; --badge-bg:#e5e7eb; --badge-text:#111827; --border:#e5e7eb; --skip-bg:#111827; --skip-text:#ffffff; --button-bg:#111827; --button-text:#ffffff; --input-bg:#ffffff; --input-text:#111827; --error:#b91c1c; }
|
||||||
--bg: #ffffff;
|
[data-theme="dark"] { --bg:#0b1220; --text:#e5e7eb; --muted:#9ca3af; --badge-bg:#374151; --badge-text:#f9fafb; --border:#334155; --skip-bg:#e5e7eb; --skip-text:#111827; --button-bg:#e5e7eb; --button-text:#111827; --input-bg:#111827; --input-text:#e5e7eb; --error:#ef4444; }
|
||||||
--text: #111827;
|
|
||||||
--muted: #6b7280;
|
|
||||||
--badge-bg: #e5e7eb;
|
|
||||||
--badge-text: #111827;
|
|
||||||
--border: #e5e7eb;
|
|
||||||
--skip-bg: #111827;
|
|
||||||
--skip-text: #ffffff;
|
|
||||||
--button-bg: #111827;
|
|
||||||
--button-text: #ffffff;
|
|
||||||
--input-bg: #ffffff;
|
|
||||||
--input-text: #111827;
|
|
||||||
}
|
|
||||||
[data-theme="dark"] {
|
|
||||||
--bg: #0b1220;
|
|
||||||
--text: #e5e7eb;
|
|
||||||
--muted: #9ca3af;
|
|
||||||
--badge-bg: #374151;
|
|
||||||
--badge-text: #f9fafb;
|
|
||||||
--border: #334155;
|
|
||||||
--skip-bg: #e5e7eb;
|
|
||||||
--skip-text: #111827;
|
|
||||||
--button-bg: #e5e7eb;
|
|
||||||
--button-text: #111827;
|
|
||||||
--input-bg: #111827;
|
|
||||||
--input-text: #e5e7eb;
|
|
||||||
}
|
|
||||||
html, body { background: var(--bg); color: var(--text); }
|
html, body { background: var(--bg); color: var(--text); }
|
||||||
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 2rem; }
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 2rem; }
|
||||||
.container { max-width: 800px; margin: 0 auto; }
|
.container { max-width: 800px; margin: 0 auto; }
|
||||||
.page-header { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
|
.page-header { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
|
||||||
.page-header h1 { margin: 0; }
|
.page-header h1 { margin: 0; }
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) { .page-header h1 { font-size: 1.5rem; } }
|
||||||
.page-header h1 { font-size: 1.5rem; }
|
|
||||||
}
|
|
||||||
.grid { display: grid; grid-template-columns: repeat(5, 3rem); gap: .5rem; }
|
.grid { display: grid; grid-template-columns: repeat(5, 3rem); gap: .5rem; }
|
||||||
.grid input { text-align: center; font-size: 1.25rem; padding: .4rem; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .375rem; }
|
.grid input { text-align: center; font-size: 1.25rem; padding: .4rem; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .375rem; }
|
||||||
label { font-weight: 600; display: block; margin-top: 1rem; margin-bottom: .25rem; }
|
label { font-weight: 600; display: block; margin-top: 1rem; margin-bottom: .25rem; }
|
||||||
.results { margin-top: 1.5rem; }
|
.results { margin-top: 1.5rem; }
|
||||||
|
.results-box { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; }
|
||||||
.badge { display: inline-block; padding: .25rem .5rem; background: var(--badge-bg); color: var(--badge-text); border-radius: .375rem; margin-right: .25rem; margin-bottom: .25rem; }
|
.badge { display: inline-block; padding: .25rem .5rem; background: var(--badge-bg); color: var(--badge-text); border-radius: .375rem; margin-right: .25rem; margin-bottom: .25rem; }
|
||||||
.source { font-size: .75rem; padding: .1rem .35rem; border-radius: .25rem; margin-left: .25rem; }
|
.source { font-size: .75rem; padding: .1rem .35rem; border-radius: .25rem; margin-left: .25rem; }
|
||||||
.source.ot { background: #dbeafe; color: #1e40af; }
|
.source.ot { background: #dbeafe; color: #1e40af; }
|
||||||
@@ -81,6 +54,8 @@
|
|||||||
.skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }
|
.skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }
|
||||||
.skip-link:focus { position: static; width: auto; height: auto; padding: .5rem .75rem; background: var(--skip-bg); color: var(--skip-text); border-radius: .25rem; }
|
.skip-link:focus { position: static; width: auto; height: auto; padding: .5rem .75rem; background: var(--skip-bg); color: var(--skip-text); border-radius: .25rem; }
|
||||||
.hint { margin-top: .25rem; color: var(--muted); font-size: .9rem; }
|
.hint { margin-top: .25rem; color: var(--muted); font-size: .9rem; }
|
||||||
|
.inline-controls { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
|
||||||
|
.filter-box { border: 1px solid var(--border); border-radius: .5rem; padding: .5rem .75rem; margin: .5rem 0 1rem; }
|
||||||
.word-list { list-style: none; padding: 0; margin: 0; }
|
.word-list { list-style: none; padding: 0; margin: 0; }
|
||||||
.word-list li { display: inline-block; margin: 0 .25rem .25rem 0; }
|
.word-list li { display: inline-block; margin: 0 .25rem .25rem 0; }
|
||||||
fieldset { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; }
|
fieldset { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; }
|
||||||
@@ -99,7 +74,7 @@
|
|||||||
<p>Wortliste geladen: <strong>{{ words_count }}</strong> Wörter</p>
|
<p>Wortliste geladen: <strong>{{ words_count }}</strong> Wörter</p>
|
||||||
|
|
||||||
<main id="main" role="main">
|
<main id="main" role="main">
|
||||||
<form method="post" aria-describedby="form-hint">
|
<form id="search-form" method="post" aria-describedby="form-hint">
|
||||||
<p id="form-hint" class="hint">Gib bekannte Buchstaben ein. Leere Felder werden ignoriert.</p>
|
<p id="form-hint" class="hint">Gib bekannte Buchstaben ein. Leere Felder werden ignoriert.</p>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -127,7 +102,21 @@
|
|||||||
|
|
||||||
{% if results is not none %}
|
{% if results is not none %}
|
||||||
<div class="results" id="results" role="region" aria-labelledby="results-title">
|
<div class="results" id="results" role="region" aria-labelledby="results-title">
|
||||||
<h2 id="results-title">Vorschläge ({{ results|length }})</h2>
|
<h2 id="results-title">Vorschläge (<span id="visible-count">{{ results|length }}</span>)</h2>
|
||||||
|
<fieldset class="filter-box" role="group">
|
||||||
|
<legend>Wortquellen‑Filter</legend>
|
||||||
|
<div class="inline-controls">
|
||||||
|
<label><input id="filter-ot" type="checkbox" name="use_ot" form="search-form" {% if use_ot %}checked{% endif %}/> OT (OpenThesaurus)</label>
|
||||||
|
<label><input id="filter-wf" type="checkbox" name="use_wf" form="search-form" {% if use_wf %}checked{% endif %}/> WF (wordfreq)</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="filter-box" role="group">
|
||||||
|
<legend>Umlaute</legend>
|
||||||
|
<div class="inline-controls">
|
||||||
|
<label><input id="filter-umlaut" type="checkbox" /> Umlaute einbeziehen (ä, ö, ü, ß)</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<div class="results-box">
|
||||||
{% if results|length == 0 %}
|
{% if results|length == 0 %}
|
||||||
<p>Keine Treffer. Bitte Bedingungen anpassen.</p>
|
<p>Keine Treffer. Bitte Bedingungen anpassen.</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
@@ -135,9 +124,10 @@
|
|||||||
<summary>Liste anzeigen</summary>
|
<summary>Liste anzeigen</summary>
|
||||||
<ul class="word-list">
|
<ul class="word-list">
|
||||||
{% for w in results %}
|
{% for w in results %}
|
||||||
<li>
|
|
||||||
<span class="badge">{{ w }}
|
|
||||||
{% set srcs = sources_map.get(w, []) %}
|
{% set srcs = sources_map.get(w, []) %}
|
||||||
|
{% set has_umlaut = ('ä' in w or 'ö' in w or 'ü' in w or 'ß' in w) %}
|
||||||
|
<li data-sources="{{ srcs|join(' ') }}" data-umlaut="{{ 1 if has_umlaut else 0 }}">
|
||||||
|
<span class="badge">{{ w }}
|
||||||
{% for s in srcs %}
|
{% for s in srcs %}
|
||||||
{% if s == 'ot' %}
|
{% if s == 'ot' %}
|
||||||
<span class="source ot">OT</span>
|
<span class="source ot">OT</span>
|
||||||
@@ -151,6 +141,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<strong>Legende:</strong>
|
<strong>Legende:</strong>
|
||||||
<span class="source wf">WF</span> = <a href="https://pypi.org/project/wordfreq/" target="_blank" rel="noopener noreferrer">Wordfreq</a>,
|
<span class="source wf">WF</span> = <a href="https://pypi.org/project/wordfreq/" target="_blank" rel="noopener noreferrer">Wordfreq</a>,
|
||||||
@@ -177,7 +168,6 @@
|
|||||||
btn.textContent = theme === 'dark' ? '🌙' : '🌞';
|
btn.textContent = theme === 'dark' ? '🌙' : '🌞';
|
||||||
btn.title = 'Theme umschalten (' + (theme === 'dark' ? 'Dunkel' : 'Hell') + ')';
|
btn.title = 'Theme umschalten (' + (theme === 'dark' ? 'Dunkel' : 'Hell') + ')';
|
||||||
}
|
}
|
||||||
// init label/state
|
|
||||||
setTheme(currentTheme());
|
setTheme(currentTheme());
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function() {
|
||||||
var next = currentTheme() === 'dark' ? 'light' : 'dark';
|
var next = currentTheme() === 'dark' ? 'light' : 'dark';
|
||||||
@@ -185,6 +175,39 @@
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
function applyFilters() {
|
||||||
|
var ot = document.getElementById('filter-ot');
|
||||||
|
var wf = document.getElementById('filter-wf');
|
||||||
|
var uml = document.getElementById('filter-umlaut');
|
||||||
|
if (!ot || !wf) return;
|
||||||
|
var allowed = [];
|
||||||
|
if (ot.checked) allowed.push('ot');
|
||||||
|
if (wf.checked) allowed.push('wf');
|
||||||
|
var items = document.querySelectorAll('.word-list li');
|
||||||
|
var visible = 0;
|
||||||
|
items.forEach(function(li) {
|
||||||
|
var sources = (li.dataset.sources || '').split(/\s+/).filter(Boolean);
|
||||||
|
var hasUmlaut = li.dataset.umlaut === '1';
|
||||||
|
var showSource = allowed.length === 0 ? false : sources.some(function(s){ return allowed.indexOf(s) !== -1; });
|
||||||
|
var showUmlaut = uml && uml.checked ? true : !hasUmlaut; // an => alle, aus => ohne Umlaute
|
||||||
|
var show = showSource && showUmlaut;
|
||||||
|
li.style.display = show ? '' : 'none';
|
||||||
|
if (show) visible++;
|
||||||
|
});
|
||||||
|
var countEl = document.getElementById('visible-count');
|
||||||
|
if (countEl) countEl.textContent = String(visible);
|
||||||
|
}
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
var ot = document.getElementById('filter-ot');
|
||||||
|
var wf = document.getElementById('filter-wf');
|
||||||
|
var uml = document.getElementById('filter-umlaut');
|
||||||
|
if (ot) ot.addEventListener('change', applyFilters);
|
||||||
|
if (wf) wf.addEventListener('change', applyFilters);
|
||||||
|
if (uml) uml.addEventListener('change', applyFilters);
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function() {
|
||||||
|
Reference in New Issue
Block a user