Files
game-library/src/app.py
Marius Mutu fd87ebca03 Initial commit: Organize project structure
- Create organized directory structure (src/, docs/, data/, static/, templates/)
- Add comprehensive .gitignore for Python projects
- Move Python source files to src/
- Move documentation files to docs/ with project/ and user/ subdirectories
- Move database files to data/
- Update all database path references in Python code
- Maintain Flask static/ and templates/ directories

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 00:40:39 +03:00

285 lines
8.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FLASK WEB APPLICATION - INDEX-SISTEM-JOCURI
Author: Claude AI Assistant
Date: 2025-09-09
Purpose: Web interface for searching educational activities
Features:
- Search interface matching interfata-web.jpg mockup exactly
- 9 filter dropdowns as specified in PRD
- Full-text search functionality
- Results display in table format
- Links to source files
- Activity sheet generation
PRD Requirements:
- RF6: Layout identical to mockup
- RF7: Search box for free text search
- RF8: 9 dropdown filters
- RF9: Apply and Reset buttons
- RF10: Results table display
- RF11: Links to source files
"""
from flask import Flask, request, render_template, jsonify, redirect, url_for
from database import DatabaseManager
import os
from pathlib import Path
import json
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
# Initialize database manager
db = DatabaseManager("../data/activities.db")
# Filter options for dropdowns (based on PRD RF8)
FILTER_OPTIONS = {
'valori': [
'Viziune și perspectivă',
'Recunoștință',
'Altele',
'Management timpul',
'Identitate personală'
],
'durata': [
'5-15min',
'15-30min',
'30+min'
],
'tematica': [
'cercetășesc',
'team building',
'educativ'
],
'domeniu': [
'sport',
'artă',
'știință'
],
'metoda': [
'joc',
'poveste',
'atelier'
],
'materiale': [
'fără',
'simple',
'complexe'
],
'competente_fizice': [
'fizice',
'mentale',
'sociale'
],
'competente_impactate': [
'fizice',
'mentale',
'sociale'
],
'participanti': [
'2-5',
'5-10',
'10-30',
'30+'
],
'varsta': [
'5-8',
'8-12',
'12-16',
'16+'
]
}
def get_dynamic_filter_options():
"""Get dynamic filter options from database"""
try:
return db.get_filter_options()
except:
return {}
@app.route('/')
def index():
"""Main search page"""
# Get dynamic filter options from database
dynamic_filters = get_dynamic_filter_options()
# Merge with static options
all_filters = FILTER_OPTIONS.copy()
all_filters.update(dynamic_filters)
return render_template('index.html', filters=all_filters)
@app.route('/search', methods=['GET', 'POST'])
def search():
"""Search activities based on filters and query"""
# Get search parameters
search_query = request.form.get('search_query', '').strip() or request.args.get('q', '').strip()
# Get filter values
filters = {}
for filter_name in FILTER_OPTIONS.keys():
value = request.form.get(filter_name) or request.args.get(filter_name)
if value and value != '':
filters[filter_name] = value
# Map filter names to database fields
db_filters = {}
if 'tematica' in filters:
db_filters['category'] = filters['tematica']
if 'varsta' in filters:
db_filters['age_group'] = filters['varsta'] + ' ani'
if 'participanti' in filters:
db_filters['participants'] = filters['participanti'] + ' persoane'
if 'durata' in filters:
db_filters['duration'] = filters['durata']
if 'materiale' in filters:
material_map = {'fără': 'Fără materiale', 'simple': 'Materiale simple', 'complexe': 'Materiale complexe'}
db_filters['materials'] = material_map.get(filters['materiale'], filters['materiale'])
# Search in database
try:
results = db.search_activities(
search_text=search_query if search_query else None,
**db_filters,
limit=100
)
# Convert results to list of dicts for template
activities = []
for result in results:
activities.append({
'id': result['id'],
'title': result['title'],
'description': result['description'][:200] + '...' if len(result['description']) > 200 else result['description'],
'category': result['category'],
'age_group': result['age_group'],
'participants': result['participants'],
'duration': result['duration'],
'materials': result['materials'],
'file_path': result['file_path'],
'tags': json.loads(result['tags']) if result['tags'] else []
})
except Exception as e:
print(f"Search error: {e}")
activities = []
# Get dynamic filter options for the form
dynamic_filters = get_dynamic_filter_options()
all_filters = FILTER_OPTIONS.copy()
all_filters.update(dynamic_filters)
return render_template('results.html',
activities=activities,
search_query=search_query,
applied_filters=filters,
filters=all_filters,
results_count=len(activities))
@app.route('/generate_sheet/<int:activity_id>')
def generate_sheet(activity_id):
"""Generate activity sheet for specific activity"""
try:
# Get activity from database
results = db.search_activities(limit=1000) # Get all to find by ID
activity_data = None
for result in results:
if result['id'] == activity_id:
activity_data = result
break
if not activity_data:
return "Activity not found", 404
# Get similar activities for recommendations
similar_activities = db.search_activities(
category=activity_data['category'],
limit=5
)
# Filter out current activity and limit to 3
recommendations = [act for act in similar_activities if act['id'] != activity_id][:3]
return render_template('fisa.html',
activity=activity_data,
recommendations=recommendations)
except Exception as e:
print(f"Sheet generation error: {e}")
return f"Error generating sheet: {e}", 500
@app.route('/file/<path:filename>')
def view_file(filename):
"""Serve activity files (PDFs, docs, etc.)"""
# Security: only serve files from the base directory
base_path = Path("/mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri")
file_path = base_path / filename
try:
if file_path.exists() and file_path.is_file():
# For now, just return file info - in production you'd serve the actual file
return f"File: {filename}<br>Path: {file_path}<br>Size: {file_path.stat().st_size} bytes"
else:
return "File not found", 404
except Exception as e:
return f"Error accessing file: {e}", 500
@app.route('/api/statistics')
def api_statistics():
"""API endpoint for database statistics"""
try:
stats = db.get_statistics()
return jsonify(stats)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/reset_filters')
def reset_filters():
"""Reset all filters and redirect to main page"""
return redirect(url_for('index'))
@app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
return render_template('500.html'), 500
def init_app():
"""Initialize application"""
print("🚀 Starting INDEX-SISTEM-JOCURI Flask Application")
print("=" * 50)
# Check if database exists and has data
try:
stats = db.get_statistics()
print(f"✅ Database connected: {stats['total_activities']} activities loaded")
if stats['total_activities'] == 0:
print("⚠️ Warning: No activities found in database!")
print(" Run: python indexer.py --clear-db to index files first")
except Exception as e:
print(f"❌ Database error: {e}")
print(f"🌐 Web interface will be available at: http://localhost:5000")
print("📱 Interface matches: interfata-web.jpg")
print("=" * 50)
if __name__ == '__main__':
init_app()
# Run Flask app
app.run(
host='0.0.0.0', # Accept connections from any IP
port=5000,
debug=True, # Enable debug mode for development
threaded=True # Handle multiple requests
)