Files
game-library/PLAN_IMPLEMENTARE_S8_DETALIAT.md
Marius Mutu 1b6b7e06ad Add strategic implementation plan for S8 Hybrid extraction strategy
- Complete detailed plan for automated activity extraction from 2000+ files
- Hybrid approach: Python scripts for HTML/TXT/MD + Claude for PDF/DOC
- Includes full Python extractors with error handling and batch processing
- Template for Claude-assisted PDF/DOC processing (high-value files)
- Orchestrator script for complete automation workflow
- Estimated result: 2000+ activities indexed in 8 hours total work

Key components:
- HTML extractor for 1876 files (BeautifulSoup + pattern recognition)
- Text/MD extractor for 45 files (regex patterns + markdown parsing)
- Unified processor with progress tracking and batch saving
- Claude extraction templates with JSON import system
- Complete automation for 90% of files, manual assist for 10% high-value

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 01:45:47 +03:00

42 KiB

PLAN DETALIAT IMPLEMENTARE STRATEGIA S8 - HYBRID CLAUDE + SCRIPTS

Pentru Indexare Activități și Jocuri Cercetășești

CONTEXT ȘI OBIECTIVE

  • Proiect: INDEX-SISTEM-JOCURI v2.0
  • Situație actuală: 63 activități indexate din INDEX_MASTER.md
  • Țintă: 2000+ activități din 2086 fișiere diverse
  • Strategie: S8 Hybrid - Scripts Python pentru 90% volum + Claude pentru 10% high-value
  • Timp total estimat: 8 ore (poate fi împărțit în mai multe sesiuni)
  • Buget: $0 (folosind doar Claude Code existent)

DISTRIBUȚIA FIȘIERELOR (VERIFICATĂ)

1876 HTML files (89.9%) - Procesare automată cu BeautifulSoup
122 PDF files (5.8%) - Procesare cu Claude (high-value, densitate mare)
29 DOC files (1.4%) - Procesare cu Claude
14 DOCX files (0.7%) - Procesare semi-automată cu python-docx
35 TXT files (1.7%) - Procesare automată simplă
10 MD files (0.5%) - Procesare automată simplă

STRUCTURA BAZEI DE DATE EXISTENTE

-- Tabela activities cu toate câmpurile necesare
id, name, description, rules, variations, category, subcategory, 
source_file, page_reference, age_group_min, age_group_max,
participants_min, participants_max, duration_min, duration_max,
materials_category, materials_list, skills_developed, 
difficulty_level, keywords, tags, popularity_score

FAZA 1: SETUP ȘI PREGĂTIRE (30 minute)

Pasul 1.1: Verificare și instalare dependențe

# Claude Code să execute:
cd /mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri/INDEX-SISTEM-JOCURI

# Verificare Python packages existente
pip list | grep -E "beautifulsoup4|pypdf2|python-docx|lxml"

# Instalare pachete lipsă
pip install beautifulsoup4 lxml pypdf2 python-docx chardet

# Creare director pentru scripturi noi
mkdir -p scripts/extractors

Pasul 1.2: Creare structură fișiere

# Claude Code să creeze următoarele fișiere:
touch scripts/extractors/__init__.py
touch scripts/extractors/html_extractor.py
touch scripts/extractors/text_extractor.py
touch scripts/extractors/pdf_extractor.py
touch scripts/extractors/unified_processor.py
touch scripts/run_extraction.py

Pasul 1.3: Backup bază de date

# IMPORTANT: Backup înainte de procesare
cp data/activities.db data/activities_backup_$(date +%Y%m%d_%H%M%S).db

FAZA 2: DEZVOLTARE EXTRACTOARE AUTOMATE (3 ore)

Pasul 2.1: HTML Extractor (cel mai important - 1876 fișiere)

Claude Code să creeze /scripts/extractors/html_extractor.py:

#!/usr/bin/env python3
"""
HTML Activity Extractor - Procesează 1876 fișiere HTML
Extrage automat activități folosind pattern recognition
"""

import os
import re
import json
from pathlib import Path
from bs4 import BeautifulSoup
import chardet
from typing import List, Dict, Optional
import sqlite3
from datetime import datetime

class HTMLActivityExtractor:
    def __init__(self, db_path='data/activities.db'):
        self.db_path = db_path
        # Pattern-uri pentru detectare activități în română
        self.activity_patterns = {
            'title_patterns': [
                r'(?i)(joc|activitate|exerci[țt]iu|team[\s-]?building|energizer|ice[\s-]?breaker)[\s:]+([^\.]{5,100})',
                r'(?i)<h[1-6][^>]*>([^<]*(?:joc|activitate|exerci[țt]iu)[^<]*)</h[1-6]>',
                r'(?i)<strong>([^<]*(?:joc|activitate|exerci[țt]iu)[^<]*)</strong>',
                r'(?i)^[\d]+\.?\s*([A-Z][^\.]{10,100}(?:joc|activitate|exerci[țt]iu)[^\.]{0,50})$',
            ],
            'description_markers': [
                'descriere', 'reguli', 'cum se joac[ăa]', 'instructiuni', 
                'obiectiv', 'desfasurare', 'explicatie', 'mod de joc'
            ],
            'materials_markers': [
                'materiale', 'necesare', 'echipament', 'ce avem nevoie',
                'se folosesc', 'trebuie sa avem', 'dotari'
            ],
            'age_patterns': [
                r'(?i)v[âa]rst[ăa][\s:]+(\d+)[\s-]+(\d+)',
                r'(?i)(\d+)[\s-]+(\d+)\s*ani',
                r'(?i)pentru\s+(\d+)[\s-]+(\d+)\s*ani',
                r'(?i)categoria?\s*(?:de\s*)?v[âa]rst[ăa][\s:]+(\d+)[\s-]+(\d+)',
            ],
            'participants_patterns': [
                r'(?i)(\d+)[\s-]+(\d+)\s*(?:participan[țt]i|juc[ăa]tori|persoane|copii)',
                r'(?i)num[ăa]r\s*(?:de\s*)?(?:participan[țt]i|juc[ăa]tori)[\s:]+(\d+)[\s-]+(\d+)',
                r'(?i)grup\s*de\s*(\d+)[\s-]+(\d+)',
            ],
            'duration_patterns': [
                r'(?i)durat[ăa][\s:]+(\d+)[\s-]+(\d+)\s*(?:minute|min)',
                r'(?i)timp[\s:]+(\d+)[\s-]+(\d+)\s*(?:minute|min)',
                r'(?i)(\d+)[\s-]+(\d+)\s*minute',
            ]
        }
        
        # Categorii predefinite bazate pe sistemul existent
        self.categories = {
            '[A]': ['joc', 'joaca', 'distractie', 'amuzament'],
            '[B]': ['aventura', 'explorare', 'descoperire'],
            '[C]': ['camping', 'tabara', 'excursie', 'drumetie'],
            '[D]': ['foc', 'flacara', 'lumina'],
            '[E]': ['noduri', 'frânghii', 'sfori', 'legare'],
            '[F]': ['bushcraft', 'supravietuire', 'survival'],
            '[G]': ['educatie', 'educativ', 'invatare', 'scoala'],
            '[H]': ['orientare', 'busola', 'harta', 'navigare']
        }
    
    def detect_encoding(self, file_path):
        """Detectează encoding-ul fișierului"""
        with open(file_path, 'rb') as f:
            result = chardet.detect(f.read())
        return result['encoding'] or 'utf-8'
    
    def extract_from_html(self, html_path: str) -> List[Dict]:
        """Extrage activități dintr-un singur fișier HTML"""
        activities = []
        
        try:
            # Detectare encoding și citire
            encoding = self.detect_encoding(html_path)
            with open(html_path, 'r', encoding=encoding, errors='ignore') as f:
                content = f.read()
            
            soup = BeautifulSoup(content, 'lxml')
            
            # Metodă 1: Caută liste de activități
            activities.extend(self._extract_from_lists(soup, html_path))
            
            # Metodă 2: Caută activități în headings
            activities.extend(self._extract_from_headings(soup, html_path))
            
            # Metodă 3: Caută pattern-uri în text
            activities.extend(self._extract_from_patterns(soup, html_path))
            
            # Metodă 4: Caută în tabele
            activities.extend(self._extract_from_tables(soup, html_path))
            
        except Exception as e:
            print(f"Error processing {html_path}: {e}")
        
        return activities
    
    def _extract_from_lists(self, soup, source_file):
        """Extrage activități din liste HTML (ul, ol)"""
        activities = []
        
        for list_elem in soup.find_all(['ul', 'ol']):
            # Verifică dacă lista pare să conțină activități
            list_text = list_elem.get_text().lower()
            if any(marker in list_text for marker in ['joc', 'activitate', 'exercitiu']):
                for li in list_elem.find_all('li'):
                    text = li.get_text(strip=True)
                    if len(text) > 20:  # Minim 20 caractere pentru o activitate validă
                        activity = self._create_activity_from_text(text, source_file)
                        if activity:
                            activities.append(activity)
        
        return activities
    
    def _extract_from_headings(self, soup, source_file):
        """Extrage activități bazate pe headings"""
        activities = []
        
        for heading in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']):
            heading_text = heading.get_text(strip=True)
            
            # Verifică dacă heading-ul conține cuvinte cheie
            if any(keyword in heading_text.lower() for keyword in ['joc', 'activitate', 'exercitiu']):
                # Caută descrierea în elementele următoare
                description = ""
                next_elem = heading.find_next_sibling()
                
                while next_elem and next_elem.name not in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
                    if next_elem.name in ['p', 'div', 'ul']:
                        description += next_elem.get_text(strip=True) + " "
                        if len(description) > 500:  # Limită descriere
                            break
                    next_elem = next_elem.find_next_sibling()
                
                if description:
                    activity = {
                        'name': heading_text[:200],
                        'description': description[:1000],
                        'source_file': str(source_file),
                        'category': self._detect_category(heading_text + " " + description)
                    }
                    activities.append(activity)
        
        return activities
    
    def _extract_from_patterns(self, soup, source_file):
        """Extrage activități folosind pattern matching"""
        activities = []
        text = soup.get_text()
        
        # Caută pattern-uri de activități
        for pattern in self.activity_patterns['title_patterns']:
            matches = re.finditer(pattern, text, re.MULTILINE)
            for match in matches:
                title = match.group(0) if match.lastindex == 0 else match.group(match.lastindex)
                if len(title) > 10:
                    # Extrage context în jurul match-ului
                    start = max(0, match.start() - 200)
                    end = min(len(text), match.end() + 500)
                    context = text[start:end]
                    
                    activity = self._create_activity_from_text(context, source_file, title)
                    if activity:
                        activities.append(activity)
        
        return activities
    
    def _extract_from_tables(self, soup, source_file):
        """Extrage activități din tabele"""
        activities = []
        
        for table in soup.find_all('table'):
            rows = table.find_all('tr')
            if len(rows) > 1:  # Cel puțin header și o linie de date
                # Detectează coloanele relevante
                headers = [th.get_text(strip=True).lower() for th in rows[0].find_all(['th', 'td'])]
                
                for row in rows[1:]:
                    cells = row.find_all(['td'])
                    if cells:
                        activity_data = {}
                        for i, cell in enumerate(cells):
                            if i < len(headers):
                                activity_data[headers[i]] = cell.get_text(strip=True)
                        
                        # Creează activitate din date tabel
                        if any(key in activity_data for key in ['joc', 'activitate', 'nume', 'titlu']):
                            activity = self._create_activity_from_table_data(activity_data, source_file)
                            if activity:
                                activities.append(activity)
        
        return activities
    
    def _create_activity_from_text(self, text, source_file, title=None):
        """Creează un dicționar de activitate din text"""
        if not text or len(text) < 30:
            return None
        
        activity = {
            'name': title or text[:100].split('.')[0].strip(),
            'description': text[:1000],
            'source_file': str(source_file),
            'category': self._detect_category(text),
            'keywords': self._extract_keywords(text),
            'created_at': datetime.now().isoformat()
        }
        
        # Extrage metadata suplimentară
        activity.update(self._extract_metadata(text))
        
        return activity
    
    def _create_activity_from_table_data(self, data, source_file):
        """Creează activitate din date de tabel"""
        activity = {
            'source_file': str(source_file),
            'created_at': datetime.now().isoformat()
        }
        
        # Mapare câmpuri tabel la câmpuri DB
        field_mapping = {
            'nume': 'name', 'titlu': 'name', 'joc': 'name', 'activitate': 'name',
            'descriere': 'description', 'detalii': 'description', 'explicatie': 'description',
            'materiale': 'materials_list', 'echipament': 'materials_list',
            'varsta': 'age_group_min', 'categoria': 'category',
            'participanti': 'participants_min', 'numar': 'participants_min',
            'durata': 'duration_min', 'timp': 'duration_min'
        }
        
        for table_field, db_field in field_mapping.items():
            if table_field in data:
                activity[db_field] = data[table_field]
        
        # Validare minimă
        if 'name' in activity and len(activity.get('name', '')) > 5:
            return activity
        
        return None
    
    def _extract_metadata(self, text):
        """Extrage metadata din text folosind pattern-uri"""
        metadata = {}
        
        # Extrage vârsta
        for pattern in self.activity_patterns['age_patterns']:
            match = re.search(pattern, text)
            if match:
                metadata['age_group_min'] = int(match.group(1))
                metadata['age_group_max'] = int(match.group(2)) if match.lastindex >= 2 else int(match.group(1))
                break
        
        # Extrage număr participanți
        for pattern in self.activity_patterns['participants_patterns']:
            match = re.search(pattern, text)
            if match:
                metadata['participants_min'] = int(match.group(1))
                metadata['participants_max'] = int(match.group(2)) if match.lastindex >= 2 else int(match.group(1))
                break
        
        # Extrage durata
        for pattern in self.activity_patterns['duration_patterns']:
            match = re.search(pattern, text)
            if match:
                metadata['duration_min'] = int(match.group(1))
                metadata['duration_max'] = int(match.group(2)) if match.lastindex >= 2 else int(match.group(1))
                break
        
        # Extrage materiale
        materials = []
        text_lower = text.lower()
        for marker in self.activity_patterns['materials_markers']:
            idx = text_lower.find(marker)
            if idx != -1:
                # Extrage următoarele 200 caractere după marker
                materials_text = text[idx:idx+200]
                # Extrage items din listă
                items = re.findall(r'[-•]\s*([^\n-•]+)', materials_text)
                if items:
                    materials.extend(items)
        
        if materials:
            metadata['materials_list'] = ', '.join(materials[:10])  # Maxim 10 materiale
        
        return metadata
    
    def _detect_category(self, text):
        """Detectează categoria activității bazată pe cuvinte cheie"""
        text_lower = text.lower()
        
        for category, keywords in self.categories.items():
            if any(keyword in text_lower for keyword in keywords):
                return category
        
        return '[A]'  # Default categoria jocuri
    
    def _extract_keywords(self, text):
        """Extrage cuvinte cheie din text"""
        keywords = []
        text_lower = text.lower()
        
        # Lista de cuvinte cheie relevante
        keyword_list = [
            'cooperare', 'competitie', 'echipa', 'creativitate', 'miscare',
            'strategie', 'comunicare', 'incredere', 'coordonare', 'atentie',
            'reflexe', 'logica', 'imaginatie', 'muzica', 'dans', 'sport',
            'natura', 'mediu', 'stiinta', 'matematica', 'limba', 'cultura'
        ]
        
        for keyword in keyword_list:
            if keyword in text_lower:
                keywords.append(keyword)
        
        return ', '.join(keywords[:5])  # Maxim 5 keywords
    
    def save_to_database(self, activities):
        """Salvează activitățile în baza de date"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        saved_count = 0
        duplicate_count = 0
        
        for activity in activities:
            try:
                # Verifică duplicate
                cursor.execute(
                    "SELECT id FROM activities WHERE name = ? AND source_file = ?",
                    (activity.get('name'), activity.get('source_file'))
                )
                
                if cursor.fetchone():
                    duplicate_count += 1
                    continue
                
                # Pregătește valorile pentru insert
                columns = []
                values = []
                placeholders = []
                
                for key, value in activity.items():
                    if key != 'created_at':  # Skip created_at, it has default
                        columns.append(key)
                        values.append(value)
                        placeholders.append('?')
                
                # Insert în DB
                query = f"INSERT INTO activities ({', '.join(columns)}) VALUES ({', '.join(placeholders)})"
                cursor.execute(query, values)
                saved_count += 1
                
            except Exception as e:
                print(f"Error saving activity: {e}")
                continue
        
        conn.commit()
        conn.close()
        
        return saved_count, duplicate_count
    
    def process_all_html_files(self, base_path='/mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri'):
        """Procesează toate fișierele HTML din directorul specificat"""
        base_path = Path(base_path)
        html_files = list(base_path.rglob("*.html"))
        html_files.extend(list(base_path.rglob("*.htm")))
        
        print(f"Found {len(html_files)} HTML files to process")
        
        all_activities = []
        processed = 0
        errors = 0
        
        for i, html_file in enumerate(html_files):
            try:
                activities = self.extract_from_html(str(html_file))
                all_activities.extend(activities)
                processed += 1
                
                # Progress update
                if (i + 1) % 100 == 0:
                    print(f"Progress: {i+1}/{len(html_files)} files processed, {len(all_activities)} activities found")
                    # Save batch to DB
                    if all_activities:
                        saved, dupes = self.save_to_database(all_activities)
                        print(f"Batch saved: {saved} new activities, {dupes} duplicates skipped")
                        all_activities = []  # Clear buffer
                
            except Exception as e:
                print(f"Error processing {html_file}: {e}")
                errors += 1
        
        # Save remaining activities
        if all_activities:
            saved, dupes = self.save_to_database(all_activities)
            print(f"Final batch saved: {saved} new activities, {dupes} duplicates skipped")
        
        print(f"\nProcessing complete!")
        print(f"Files processed: {processed}")
        print(f"Errors: {errors}")
        
        return processed, errors

# Funcție main pentru test
if __name__ == "__main__":
    extractor = HTMLActivityExtractor()
    
    # Test pe un fișier sample mai întâi
    print("Testing on sample file first...")
    # Găsește un fișier HTML pentru test
    test_files = list(Path('/mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri').rglob("*.html"))[:3]
    
    for test_file in test_files:
        print(f"\nTesting: {test_file}")
        activities = extractor.extract_from_html(str(test_file))
        print(f"Found {len(activities)} activities")
        if activities:
            print(f"Sample activity: {activities[0]['name'][:50]}...")
    
    # Întreabă dacă să continue cu procesarea completă
    response = input("\nContinue with full processing? (y/n): ")
    if response.lower() == 'y':
        extractor.process_all_html_files()

Pasul 2.2: Text/MD Extractor (simplu - 45 fișiere)

Claude Code să creeze /scripts/extractors/text_extractor.py:

#!/usr/bin/env python3
"""
Text/Markdown Activity Extractor
Procesează fișiere TXT și MD pentru extracție activități
"""

import re
from pathlib import Path
from typing import List, Dict
import sqlite3
from datetime import datetime

class TextActivityExtractor:
    def __init__(self, db_path='data/activities.db'):
        self.db_path = db_path
        self.activity_patterns = {
            'section_headers': [
                r'^#{1,6}\s*(.+)$',  # Markdown headers
                r'^([A-Z][^\.]{10,100})$',  # Titluri simple
                r'^\d+\.\s*(.+)$',  # Numbered lists
                r'^[•\-\*]\s*(.+)$',  # Bullet points
            ],
            'activity_markers': [
                'joc:', 'activitate:', 'exercitiu:', 'team building:',
                'nume:', 'titlu:', 'denumire:'
            ]
        }
    
    def extract_from_text(self, file_path: str) -> List[Dict]:
        """Extrage activități din fișier text/markdown"""
        activities = []
        
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()
            
            # Metodă 1: Caută secțiuni markdown
            if file_path.endswith('.md'):
                activities.extend(self._extract_from_markdown(content, file_path))
            
            # Metodă 2: Caută pattern-uri generale
            activities.extend(self._extract_from_patterns(content, file_path))
            
            # Metodă 3: Caută blocuri de text structurate
            activities.extend(self._extract_from_blocks(content, file_path))
            
        except Exception as e:
            print(f"Error processing {file_path}: {e}")
        
        return activities
    
    def _extract_from_markdown(self, content, source_file):
        """Extrage activități din format markdown"""
        activities = []
        lines = content.split('\n')
        
        current_activity = None
        current_content = []
        
        for line in lines:
            # Verifică dacă e header de activitate
            if re.match(r'^#{1,3}\s*(.+)', line):
                # Salvează activitatea anterioară dacă există
                if current_activity and current_content:
                    current_activity['description'] = '\n'.join(current_content[:20])  # Max 20 linii
                    activities.append(current_activity)
                
                # Verifică dacă noul header e o activitate
                header_text = re.sub(r'^#{1,3}\s*', '', line)
                if any(marker in header_text.lower() for marker in ['joc', 'activitate', 'exercitiu']):
                    current_activity = {
                        'name': header_text[:200],
                        'source_file': str(source_file),
                        'category': '[A]'
                    }
                    current_content = []
                else:
                    current_activity = None
            
            elif current_activity:
                # Adaugă conținut la activitatea curentă
                if line.strip():
                    current_content.append(line)
        
        # Salvează ultima activitate
        if current_activity and current_content:
            current_activity['description'] = '\n'.join(current_content[:20])
            activities.append(current_activity)
        
        return activities
    
    def _extract_from_patterns(self, content, source_file):
        """Extrage folosind pattern matching"""
        activities = []
        
        # Caută markeri specifici de activități
        for marker in self.activity_patterns['activity_markers']:
            pattern = re.compile(f'{re.escape(marker)}\\s*(.+?)(?=\\n\\n|{re.escape(marker)}|$)', 
                               re.IGNORECASE | re.DOTALL)
            matches = pattern.finditer(content)
            
            for match in matches:
                activity_text = match.group(1)
                if len(activity_text) > 20:
                    activity = {
                        'name': activity_text.split('\n')[0][:200],
                        'description': activity_text[:1000],
                        'source_file': str(source_file),
                        'category': '[A]'
                    }
                    activities.append(activity)
        
        return activities
    
    def _extract_from_blocks(self, content, source_file):
        """Extrage din blocuri de text separate"""
        activities = []
        
        # Împarte în blocuri separate de linii goale
        blocks = re.split(r'\n\s*\n', content)
        
        for block in blocks:
            if len(block) > 50:  # Minim 50 caractere
                lines = block.strip().split('\n')
                first_line = lines[0].strip()
                
                # Verifică dacă blocul pare o activitate
                if any(keyword in first_line.lower() for keyword in ['joc', 'activitate', 'exercitiu']):
                    activity = {
                        'name': first_line[:200],
                        'description': block[:1000],
                        'source_file': str(source_file),
                        'category': '[A]'
                    }
                    activities.append(activity)
        
        return activities
    
    def save_to_database(self, activities):
        """Salvează în baza de date"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        saved_count = 0
        
        for activity in activities:
            try:
                # Check for duplicates
                cursor.execute(
                    "SELECT id FROM activities WHERE name = ? AND source_file = ?",
                    (activity.get('name'), activity.get('source_file'))
                )
                
                if not cursor.fetchone():
                    columns = list(activity.keys())
                    values = list(activity.values())
                    placeholders = ['?' for _ in values]
                    
                    query = f"INSERT INTO activities ({', '.join(columns)}) VALUES ({', '.join(placeholders)})"
                    cursor.execute(query, values)
                    saved_count += 1
                    
            except Exception as e:
                print(f"Error saving: {e}")
        
        conn.commit()
        conn.close()
        
        return saved_count
    
    def process_all_text_files(self, base_path='/mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri'):
        """Procesează toate fișierele text și markdown"""
        base_path = Path(base_path)
        
        text_files = list(base_path.rglob("*.txt"))
        md_files = list(base_path.rglob("*.md"))
        all_files = text_files + md_files
        
        print(f"Found {len(all_files)} text/markdown files")
        
        all_activities = []
        
        for file_path in all_files:
            activities = self.extract_from_text(str(file_path))
            all_activities.extend(activities)
            print(f"Processed {file_path.name}: {len(activities)} activities")
        
        # Save to database
        saved = self.save_to_database(all_activities)
        print(f"\nTotal saved: {saved} activities from {len(all_files)} files")
        
        return len(all_files), saved

if __name__ == "__main__":
    extractor = TextActivityExtractor()
    extractor.process_all_text_files()

Pasul 2.3: Unified Processor (orchestrator)

Claude Code să creeze /scripts/extractors/unified_processor.py:

#!/usr/bin/env python3
"""
Unified Activity Processor
Orchestrează toate extractoarele pentru procesare completă
"""

import time
from pathlib import Path
from html_extractor import HTMLActivityExtractor
from text_extractor import TextActivityExtractor
import sqlite3

class UnifiedProcessor:
    def __init__(self, db_path='data/activities.db'):
        self.db_path = db_path
        self.html_extractor = HTMLActivityExtractor(db_path)
        self.text_extractor = TextActivityExtractor(db_path)
        self.stats = {
            'html_processed': 0,
            'text_processed': 0,
            'pdf_to_process': 0,
            'doc_to_process': 0,
            'total_activities': 0,
            'start_time': None,
            'end_time': None
        }
    
    def get_current_activity_count(self):
        """Obține numărul curent de activități din DB"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute("SELECT COUNT(*) FROM activities")
        count = cursor.fetchone()[0]
        conn.close()
        return count
    
    def count_files_to_process(self, base_path):
        """Numără fișierele care trebuie procesate"""
        base_path = Path(base_path)
        
        counts = {
            'html': len(list(base_path.rglob("*.html"))) + len(list(base_path.rglob("*.htm"))),
            'txt': len(list(base_path.rglob("*.txt"))),
            'md': len(list(base_path.rglob("*.md"))),
            'pdf': len(list(base_path.rglob("*.pdf"))),
            'doc': len(list(base_path.rglob("*.doc"))),
            'docx': len(list(base_path.rglob("*.docx")))
        }
        
        return counts
    
    def process_automated_formats(self, base_path='/mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri'):
        """Procesează toate formatele care pot fi automatizate"""
        print("="*60)
        print("UNIFIED ACTIVITY PROCESSOR - AUTOMATED PHASE")
        print("="*60)
        
        self.stats['start_time'] = time.time()
        initial_count = self.get_current_activity_count()
        
        # Afișează statistici inițiale
        file_counts = self.count_files_to_process(base_path)
        print(f"\nFiles to process:")
        for format, count in file_counts.items():
            print(f"  {format.upper()}: {count} files")
        print(f"\nCurrent activities in database: {initial_count}")
        print("-"*60)
        
        # FAZA 1: Procesare HTML (prioritate maximă - volum mare)
        print("\n[1/2] Processing HTML files...")
        print("-"*40)
        html_processed, html_errors = self.html_extractor.process_all_html_files(base_path)
        self.stats['html_processed'] = html_processed
        
        # FAZA 2: Procesare Text/MD
        print("\n[2/2] Processing Text/Markdown files...")
        print("-"*40)
        text_processed, text_saved = self.text_extractor.process_all_text_files(base_path)
        self.stats['text_processed'] = text_processed
        
        # Statistici finale
        self.stats['end_time'] = time.time()
        final_count = self.get_current_activity_count()
        self.stats['total_activities'] = final_count - initial_count
        
        # Identifică fișierele care necesită procesare manuală
        self.stats['pdf_to_process'] = file_counts['pdf']
        self.stats['doc_to_process'] = file_counts['doc'] + file_counts['docx']
        
        self.print_summary()
        self.save_pdf_doc_list(base_path)
    
    def print_summary(self):
        """Afișează rezumatul procesării"""
        print("\n" + "="*60)
        print("PROCESSING SUMMARY")
        print("="*60)
        
        duration = self.stats['end_time'] - self.stats['start_time']
        
        print(f"\nAutomated Processing Results:")
        print(f"  HTML files processed: {self.stats['html_processed']}")
        print(f"  Text/MD files processed: {self.stats['text_processed']}")
        print(f"  New activities added: {self.stats['total_activities']}")
        print(f"  Processing time: {duration:.1f} seconds")
        
        print(f"\nFiles requiring Claude processing:")
        print(f"  PDF files: {self.stats['pdf_to_process']}")
        print(f"  DOC/DOCX files: {self.stats['doc_to_process']}")
        
        print("\n" + "="*60)
        print("NEXT STEPS:")
        print("1. Review the file 'pdf_doc_for_claude.txt' for manual processing")
        print("2. Use Claude to extract activities from PDF/DOC files")
        print("3. Focus on largest PDF files first (highest activity density)")
        print("="*60)
    
    def save_pdf_doc_list(self, base_path):
        """Salvează lista de PDF/DOC pentru procesare cu Claude"""
        base_path = Path(base_path)
        
        pdf_files = sorted(base_path.rglob("*.pdf"), key=lambda p: p.stat().st_size, reverse=True)
        doc_files = list(base_path.rglob("*.doc"))
        docx_files = list(base_path.rglob("*.docx"))
        
        with open('pdf_doc_for_claude.txt', 'w', encoding='utf-8') as f:
            f.write("PDF/DOC FILES FOR CLAUDE PROCESSING\n")
            f.write("="*60 + "\n")
            f.write("Files sorted by size (largest first = likely more activities)\n\n")
            
            f.write("TOP PRIORITY PDF FILES (process these first):\n")
            f.write("-"*40 + "\n")
            for i, pdf in enumerate(pdf_files[:20], 1):
                size_mb = pdf.stat().st_size / (1024*1024)
                f.write(f"{i}. {pdf.name} ({size_mb:.1f} MB)\n")
                f.write(f"   Path: {pdf}\n\n")
            
            if len(pdf_files) > 20:
                f.write(f"\n... and {len(pdf_files)-20} more PDF files\n\n")
            
            f.write("\nDOC/DOCX FILES:\n")
            f.write("-"*40 + "\n")
            for doc in doc_files + docx_files:
                size_kb = doc.stat().st_size / 1024
                f.write(f"- {doc.name} ({size_kb:.1f} KB)\n")
        
        print(f"\nPDF/DOC list saved to: pdf_doc_for_claude.txt")

if __name__ == "__main__":
    processor = UnifiedProcessor()
    processor.process_automated_formats()

FAZA 3: PROCESARE MANUALĂ CU CLAUDE (3-4 ore)

Pasul 3.1: Template pentru extracție cu Claude

Claude Code să creeze /scripts/claude_extraction_template.md:

# TEMPLATE PENTRU EXTRACȚIE ACTIVITĂȚI CU CLAUDE

## Instrucțiuni pentru Claude Code:

Pentru fiecare PDF/DOC, folosește următorul format de extracție:

### 1. Citește fișierul:

Claude, te rog citește fișierul: [CALE_FISIER]


### 2. Extrage activitățile folosind acest template JSON:
```json
{
  "source_file": "[NUME_FISIER]",
  "activities": [
    {
      "name": "Numele activității",
      "description": "Descrierea completă a activității",
      "rules": "Regulile jocului/activității",
      "variations": "Variante sau adaptări",
      "category": "[A-H] bazat pe tip",
      "age_group_min": 6,
      "age_group_max": 14,
      "participants_min": 4,
      "participants_max": 20,
      "duration_min": 10,
      "duration_max": 30,
      "materials_list": "Lista materialelor necesare",
      "skills_developed": "Competențe dezvoltate",
      "difficulty_level": "Ușor/Mediu/Dificil",
      "keywords": "cuvinte cheie separate prin virgulă",
      "tags": "taguri relevante"
    }
  ]
}

3. Salvează în fișier:

După extracție, salvează JSON-ul în: /scripts/extracted_activities/[NUME_FISIER].json

4. Priorități de procesare:

TOP PRIORITY (procesează primele):

  1. 1000 Fantastic Scout Games.pdf
  2. Cartea Mare a jocurilor.pdf
  3. 160-de-activitati-dinamice-jocuri-pentru-team-building.pdf
  4. 101 Ways to Create an Unforgettable Camp Experience.pdf
  5. 151 Awesome Summer Camp Nature Activities.pdf

Categorii de focus:

  • [A] Jocuri Cercetășești
  • [C] Camping & Activități Exterior
  • [G] Activități Educaționale

### Pasul 3.2: Script pentru import activități din JSON

**Claude Code să creeze `/scripts/import_claude_activities.py`:**

```python
#!/usr/bin/env python3
"""
Import activities extracted by Claude from JSON files
"""

import json
import sqlite3
from pathlib import Path
from datetime import datetime

class ClaudeActivityImporter:
    def __init__(self, db_path='data/activities.db'):
        self.db_path = db_path
        self.json_dir = Path('scripts/extracted_activities')
        self.json_dir.mkdir(exist_ok=True)
    
    def import_json_file(self, json_path):
        """Import activities from a single JSON file"""
        with open(json_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        source_file = data.get('source_file', str(json_path))
        activities = data.get('activities', [])
        
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        imported = 0
        for activity in activities:
            try:
                # Add source file and timestamp
                activity['source_file'] = source_file
                activity['created_at'] = datetime.now().isoformat()
                
                # Prepare insert
                columns = list(activity.keys())
                values = list(activity.values())
                placeholders = ['?' for _ in values]
                
                # Check for duplicate
                cursor.execute(
                    "SELECT id FROM activities WHERE name = ? AND source_file = ?",
                    (activity.get('name'), source_file)
                )
                
                if not cursor.fetchone():
                    query = f"INSERT INTO activities ({', '.join(columns)}) VALUES ({', '.join(placeholders)})"
                    cursor.execute(query, values)
                    imported += 1
                    
            except Exception as e:
                print(f"Error importing activity: {e}")
        
        conn.commit()
        conn.close()
        
        print(f"Imported {imported} activities from {json_path.name}")
        return imported
    
    def import_all_json_files(self):
        """Import all JSON files from the extracted_activities directory"""
        json_files = list(self.json_dir.glob("*.json"))
        
        if not json_files:
            print("No JSON files found in extracted_activities directory")
            return 0
        
        total_imported = 0
        for json_file in json_files:
            imported = self.import_json_file(json_file)
            total_imported += imported
        
        print(f"\nTotal imported: {total_imported} activities from {len(json_files)} files")
        return total_imported

if __name__ == "__main__":
    importer = ClaudeActivityImporter()
    importer.import_all_json_files()

FAZA 4: SCRIPT PRINCIPAL DE ORCHESTRARE

Claude Code să creeze /scripts/run_extraction.py:

#!/usr/bin/env python3
"""
Main extraction orchestrator
Rulează întregul proces de extracție
"""

import sys
import time
from pathlib import Path

# Add extractors to path
sys.path.append(str(Path(__file__).parent / 'extractors'))

from extractors.unified_processor import UnifiedProcessor
from import_claude_activities import ClaudeActivityImporter

def main():
    print("="*60)
    print("ACTIVITY EXTRACTION SYSTEM")
    print("Strategy S8: Hybrid Claude + Scripts")
    print("="*60)
    
    # Step 1: Run automated extraction
    print("\nSTEP 1: Automated Extraction")
    print("-"*40)
    processor = UnifiedProcessor()
    processor.process_automated_formats()
    
    # Step 2: Wait for Claude processing
    print("\n" + "="*60)
    print("STEP 2: Manual Claude Processing Required")
    print("-"*40)
    print("Please process PDF/DOC files with Claude using the template.")
    print("Files are listed in: pdf_doc_for_claude.txt")
    print("Save extracted activities as JSON in: scripts/extracted_activities/")
    print("="*60)
    
    response = input("\nHave you completed Claude processing? (y/n): ")
    
    if response.lower() == 'y':
        # Step 3: Import Claude-extracted activities
        print("\nSTEP 3: Importing Claude-extracted activities")
        print("-"*40)
        importer = ClaudeActivityImporter()
        importer.import_all_json_files()
    
    print("\n" + "="*60)
    print("EXTRACTION COMPLETE!")
    print("="*60)

if __name__ == "__main__":
    main()

INSTRUCȚIUNI DE EXECUȚIE PENTRU CLAUDE CODE

Executare automată (Claude Code să ruleze acestea):

# 1. Setup inițial
cd /mnt/d/GoogleDrive/Cercetasi/carti-camp-jocuri/INDEX-SISTEM-JOCURI
pip install beautifulsoup4 lxml pypdf2 python-docx chardet

# 2. Creează toate fișierele de mai sus
# [Claude Code creează toate fișierele Python descrise]

# 3. Backup database
cp data/activities.db data/activities_backup_$(date +%Y%m%d).db

# 4. Rulează extracția automată
python scripts/run_extraction.py

# 5. După procesare automată, afișează lista PDF pentru procesare manuală
cat pdf_doc_for_claude.txt | head -30

Pentru procesarea PDF/DOC cu Claude:

  1. Claude citește fiecare PDF din lista prioritară
  2. Extrage activitățile în format JSON
  3. Salvează în /scripts/extracted_activities/[nume_fisier].json
  4. După completare, rulează importul:
    python scripts/import_claude_activities.py
    

Verificare finală:

# Verifică câte activități au fost indexate
sqlite3 data/activities.db "SELECT COUNT(*) as total FROM activities;"

# Verifică distribuția pe categorii
sqlite3 data/activities.db "SELECT category, COUNT(*) as count FROM activities GROUP BY category;"

# Verifică sursele
sqlite3 data/activities.db "SELECT source_file, COUNT(*) as count FROM activities GROUP BY source_file ORDER BY count DESC LIMIT 10;"

REZULTATE ESTIMATE

După procesare automată (4 ore):

  • HTML: ~1200-1500 activități
  • TXT/MD: ~100-200 activități
  • Total automat: ~1300-1700 activități

După procesare Claude (3-4 ore):

  • PDF: ~300-500 activități (high quality)
  • DOC/DOCX: ~100-150 activități
  • Total Claude: ~400-650 activități

TOTAL FINAL: ~1700-2350 activități


TROUBLESHOOTING

Probleme comune și soluții:

  1. Encoding errors: Scripturile folosesc chardet pentru auto-detectare
  2. Memory issues: Procesare în batch-uri de 100 fișiere
  3. Duplicate detection: Verificare automată name+source_file
  4. PDF extraction fails: Fallback la Claude pentru procesare manuală
  5. Database locked: Închide aplicația Flask înainte de procesare

NOTE PENTRU IMPLEMENTARE

  1. Prioritizează PDF-urile mari - conțin cele mai multe activități
  2. Rulează noaptea dacă vrei să procesezi tot automatul
  3. Salvează progresul - scripturile salvează în batch-uri
  4. Verifică calitatea - spot-check pe câteva activități random
  5. Backup întotdeauna - ai backup automat în script

Acest plan este complet automatizat pentru 90% din muncă. Claude Code poate rula totul automat, tu doar supraveghezi și procesezi PDF-urile importante cu Claude.