Files
game-library/app/templates/results.html
Claude Agent bcfb6841eb Faza 1 complete: bilingual+enrichment plumbing, UI/filters, frozen DB
Extraction finished (575/588 chunks; 6 content-filter-blocked, 7 await
re-extraction). DB rebuilt and frozen at 9418 activities — content_keys
are now stable for the enrichment overlay.

Part A (plumbing + UI):
- database.py: name_ro/description_ro/rules_ro/variations_ro, indoor_outdoor,
  space_needed, estimated_fields, source_id/source_ids/chunk_key columns;
  FTS5 indexes the 4 *_ro columns across CREATE + all 3 triggers; new equality
  filters + category counts for both axes.
- activity.py: new fields + bilingual display helpers (get_display_*,
  is_estimated, axis displays).
- config_taxonomy.py: INDOOR_OUTDOOR/SPACE_NEEDED enums + normalizers
  (None on unrecognised, no fabrication).
- search.py / routes.py / config.py / templates / css: new dropdowns,
  RO-primary rendering with "(estimat)" markers and collapsible original
  text, and a /source/<id> download route shipped DARK behind
  SOURCE_DOWNLOAD_ENABLED (copyright opt-in).
- build_database.py: source_id/chunk_key in dict_to_activity; merge_cluster
  unions source_ids without touching enrichment fields.

Part B (enrichment pipeline, built not yet run):
- build_database.py: load_enrichment + apply_enrichment (post-dedup, keyed on
  content_key) + --enrichment CLI + stated-vs-estimated QA.
- run_enrichment.py (resumable, --source/--limit pilot scoping, --collect),
  ENRICHMENT_PROMPT.md.

Repair: scripts/repair_extractions.py fixes the subagents' systematic
unescaped-ASCII-quote bug with a faithful char-scanner (escapes, never
truncates) + schema validation + a strictly-more-text guard. json_repair was
tried first, truncated silently, and is NOT used. build_database has no repair
dependency.

Tests: tests/test_enrichment.py added; 99 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 18:10:13 +00:00

285 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}Rezultate căutare - INDEX Sistem Jocuri{% endblock %}
{% block content %}
<div class="results-page">
<!-- Search form (compact version) -->
<form method="POST" action="{{ url_for('main.search') }}" class="search-form compact">
<div class="search-input-group">
<input
type="text"
name="search_query"
value="{{ search_query }}"
class="search-input"
placeholder="Caută activități..."
>
<button type="submit" class="search-button">Căutare</button>
</div>
{% if filters %}
<div class="filters-row">
{% if filters.category %}
<select name="category" class="filter-select compact">
<option value="">Toate categoriile</option>
{% for category in filters.category %}
<option value="{{ category }}" {% if applied_filters.category == category %}selected{% endif %}>
{{ display_names.get(category, category) }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.content_type %}
<select name="content_type" class="filter-select compact">
<option value="">Doar jocuri și activități</option>
{% for content_type in filters.content_type %}
<option value="{{ content_type }}" {% if applied_filters.content_type == content_type %}selected{% endif %}>
{{ display_names.get(content_type, content_type) }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.language %}
<select name="language" class="filter-select compact">
<option value="">Toate limbile</option>
{% for language in filters.language %}
<option value="{{ language }}" {% if applied_filters.language == language %}selected{% endif %}>
{{ display_names.get(language, language) }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.age_group %}
<select name="age_group" class="filter-select compact">
<option value="">Toate vârstele</option>
{% for age_group in filters.age_group %}
<option value="{{ age_group }}" {% if applied_filters.age_group == age_group %}selected{% endif %}>
{{ age_group }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.participants %}
<select name="participants" class="filter-select compact">
<option value="">Orice număr</option>
{% for participants in filters.participants %}
<option value="{{ participants }}" {% if applied_filters.participants == participants %}selected{% endif %}>
{{ participants }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.duration %}
<select name="duration" class="filter-select compact">
<option value="">Orice durată</option>
{% for duration in filters.duration %}
<option value="{{ duration }}" {% if applied_filters.duration == duration %}selected{% endif %}>
{{ duration }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.indoor_outdoor %}
<select name="indoor_outdoor" class="filter-select compact">
<option value="">Oriunde</option>
{% for io in filters.indoor_outdoor %}
<option value="{{ io }}" {% if applied_filters.indoor_outdoor == io %}selected{% endif %}>
{{ display_names.get(io, io) }}
</option>
{% endfor %}
</select>
{% endif %}
{% if filters.space_needed %}
<select name="space_needed" class="filter-select compact">
<option value="">Orice spațiu</option>
{% for sp in filters.space_needed %}
<option value="{{ sp }}" {% if applied_filters.space_needed == sp %}selected{% endif %}>
{{ display_names.get(sp, sp) }}
</option>
{% endfor %}
</select>
{% endif %}
<button type="button" class="btn btn-secondary btn-sm" onclick="clearFilters()">
Resetează
</button>
</div>
{% endif %}
</form>
<!-- Results header -->
<div class="results-header">
<h2 class="results-title">
Rezultate căutare
{% if search_query %}pentru "{{ search_query }}"{% endif %}
</h2>
<p class="results-count">
{% if results_count > 0 %}
{{ results_count }} activități găsite
{% else %}
Nu au fost găsite activități
{% endif %}
</p>
<!-- Applied filters display -->
{% if applied_filters %}
<div class="applied-filters">
<span class="applied-filters-label">Filtre aplicate:</span>
{% for filter_key, filter_value in applied_filters.items() %}
<span class="applied-filter">
{{ filter_value }}
<a href="javascript:removeFilter('{{ filter_key }}')" class="remove-filter">×</a>
</span>
{% endfor %}
</div>
{% endif %}
</div>
<!-- Results list -->
{% if activities %}
<div class="results-list">
{% for activity in activities %}
<article class="activity-card">
<header class="activity-header">
<h3 class="activity-title">
<a href="{{ url_for('main.activity_detail', activity_id=activity.id) }}">
{{ activity.get_display_name() }}
</a>
</h3>
<span class="activity-category">{{ display_names.get(activity.category, activity.category) }}</span>
{% if activity.needs_review %}
<span class="activity-badge needs-review" title="Această activitate necesită verificare">⚠ De verificat</span>
{% endif %}
</header>
<div class="activity-content">
<p class="activity-description">{{ activity.get_display_description() }}</p>
<div class="activity-metadata">
{% if activity.get_age_range_display() != "toate vârstele" %}
<span class="metadata-item">
<strong>Vârsta:</strong> {{ activity.get_age_range_display() }}{% if activity.is_estimated('age_group_min') or activity.is_estimated('age_group_max') %} <em class="estimated">(estimat)</em>{% endif %}
</span>
{% endif %}
{% if activity.get_participants_display() != "orice număr" %}
<span class="metadata-item">
<strong>Participanți:</strong> {{ activity.get_participants_display() }}{% if activity.is_estimated('participants_min') or activity.is_estimated('participants_max') %} <em class="estimated">(estimat)</em>{% endif %}
</span>
{% endif %}
{% if activity.get_duration_display() != "durată variabilă" %}
<span class="metadata-item">
<strong>Durata:</strong> {{ activity.get_duration_display() }}{% if activity.is_estimated('duration_min') or activity.is_estimated('duration_max') %} <em class="estimated">(estimat)</em>{% endif %}
</span>
{% endif %}
{% if activity.get_indoor_outdoor_display() %}
<span class="metadata-item">
<strong>Loc:</strong> {{ activity.get_indoor_outdoor_display() }}{% if activity.is_estimated('indoor_outdoor') %} <em class="estimated">(estimat)</em>{% endif %}
</span>
{% endif %}
{% if activity.get_space_needed_display() %}
<span class="metadata-item">
<strong>Spațiu:</strong> {{ activity.get_space_needed_display() }}{% if activity.is_estimated('space_needed') %} <em class="estimated">(estimat)</em>{% endif %}
</span>
{% endif %}
{% if activity.get_materials_display() != "nu specificate" %}
<span class="metadata-item">
<strong>Materiale:</strong> {{ activity.get_materials_display() }}
</span>
{% endif %}
</div>
{% if activity.source_file %}
<div class="activity-source">
{% if config.SOURCE_DOWNLOAD_ENABLED %}
<small>Sursă: <a href="{{ url_for('main.source_download', activity_id=activity.id) }}">{{ activity.source_file }}</a></small>
{% else %}
<small>Sursă: {{ activity.source_file }}</small>
{% endif %}
</div>
{% endif %}
</div>
<footer class="activity-footer">
<a href="{{ url_for('main.activity_detail', activity_id=activity.id) }}"
class="btn btn-primary btn-sm">
Vezi detalii
</a>
</footer>
</article>
{% endfor %}
</div>
{% else %}
<div class="no-results">
<h3>Nu au fost găsite activități</h3>
<p>Încearcă să:</p>
<ul>
<li>Modifici termenii de căutare</li>
<li>Elimini unele filtre</li>
<li>Verifici ortografia</li>
<li>Folosești termeni mai generali</li>
</ul>
<a href="{{ url_for('main.index') }}" class="btn btn-primary">
Întoarce-te la căutare
</a>
</div>
{% endif %}
{% if error %}
<div class="error-message">
<strong>Eroare:</strong> {{ error }}
</div>
{% endif %}
</div>
{% endblock %}
{% block scripts %}
<script>
function clearFilters() {
// Clear search query and all filters
const form = document.querySelector('.search-form');
const inputs = form.querySelectorAll('input, select');
inputs.forEach(input => {
if (input.type === 'text') {
input.value = '';
} else if (input.tagName === 'SELECT') {
input.selectedIndex = 0;
}
});
// Submit the form to show all results
form.submit();
}
function removeFilter(filterKey) {
// Remove specific filter by setting its value to empty
const filterElement = document.querySelector(`[name="${filterKey}"]`);
if (filterElement) {
filterElement.value = '';
document.querySelector('.search-form').submit();
}
}
// Auto-submit on filter change
document.addEventListener('DOMContentLoaded', function() {
const filterSelects = document.querySelectorAll('.filter-select');
filterSelects.forEach(select => {
select.addEventListener('change', function() {
document.querySelector('.search-form').submit();
});
});
});
</script>
{% endblock %}