#!/usr/bin/env python3 """ Script pour sélectionner les cartes quotidiennes du système Anki Tingting. Usage: python get_daily_cards.py [--num-cards N] [--json] Options: --num-cards N : Nombre de cartes à sélectionner (défaut: 3) --json : Output en JSON (défaut: texte lisible) """ import os import re import json import argparse from datetime import datetime, timedelta from pathlib import Path # Chemins relatifs au script SCRIPT_DIR = Path(__file__).parent BASE_DIR = SCRIPT_DIR.parent CARD_DB_PATH = BASE_DIR / "card_database.md" CARDS_DIR = BASE_DIR / "cards" def parse_date(date_str): """Parse une date au format DD/MM/YYYY ou 'Never'.""" if date_str == "Never" or date_str == "N/A": return None try: return datetime.strptime(date_str, "%d/%m/%Y") except ValueError: return None def parse_card_database(): """Parse le fichier card_database.md et retourne la liste des cartes.""" with open(CARD_DB_PATH, 'r', encoding='utf-8') as f: content = f.read() # Trouver la table markdown table_pattern = r'\| ID \| Card \| Difficulty.*?\n\|---.*?\n((?:\|.*?\n)+)' match = re.search(table_pattern, content, re.DOTALL) if not match: raise ValueError("Impossible de trouver la table dans card_database.md") table_rows = match.group(1).strip().split('\n') cards = [] for row in table_rows: # Parse chaque ligne de la table cols = [col.strip() for col in row.split('|')[1:-1]] # Ignore les | de début/fin if len(cols) < 8: continue card_id, card_file, difficulty, frequency, last_review, success_rate, times_failed, critical = cols cards.append({ 'id': card_id, 'file': card_file, 'difficulty': difficulty, 'frequency': frequency, 'last_review': parse_date(last_review), 'last_review_str': last_review, 'success_rate': success_rate, 'times_failed': int(times_failed) if times_failed.isdigit() else 0, 'critical': critical == '⚠️' }) return cards def load_card_content(card_file): """Charge le contenu d'une carte depuis son fichier.""" card_path = CARDS_DIR / card_file with open(card_path, 'r', encoding='utf-8') as f: content = f.read() # Extract question question_match = re.search(r'## Question\s*\n\s*\n(.*?)\n\s*\n---', content, re.DOTALL) question = question_match.group(1).strip() if question_match else "Question non trouvée" # Extract answer answer_match = re.search(r'## Answer\s*\n\s*\n(.*?)\n\s*\n---', content, re.DOTALL) answer = answer_match.group(1).strip() if answer_match else "Réponse non trouvée" # Extract notes notes_match = re.search(r'## Notes\s*\n\s*\n(.*?)\n\s*\n---', content, re.DOTALL) notes = notes_match.group(1).strip() if notes_match else "" return { 'question': question, 'answer': answer, 'notes': notes } def calculate_priority_score(card, today): """Calcule un score de priorité pour une carte.""" score = 0 # CRITICAL cards = haute priorité if card['critical']: score += 100 # Times failed = haute priorité score += card['times_failed'] * 50 # Difficulté difficulty_scores = {'Hard': 30, 'Medium': 20, 'Easy': 10} score += difficulty_scores.get(card['difficulty'], 0) # Temps depuis dernière review if card['last_review'] is None: score += 200 # Jamais révisée = très haute priorité else: days_since = (today - card['last_review']).days # Calculer le nombre de jours attendus selon frequency freq_days = { 'Daily': 1, 'Every 2-3 days': 2, 'Every 3-4 days': 3, 'Every 2 weeks': 14, 'Monthly': 30, 'Every conflict': 999 # Pas basé sur le temps } expected_days = freq_days.get(card['frequency'], 7) if card['frequency'] == 'Every conflict': # Cartes de conflit : priorité basse sauf si jamais révisées score += 5 elif days_since >= expected_days: # En retard sur la review score += 80 + (days_since - expected_days) * 10 else: # Pas encore le moment score += 5 # Success rate (si disponible) if card['success_rate'] not in ['N/A', 'Never']: try: rate = int(card['success_rate'].replace('%', '')) if rate < 70: score += 40 except ValueError: pass return score def select_daily_cards(num_cards=3): """Sélectionne les cartes pour la session quotidienne.""" cards = parse_card_database() today = datetime.now() # Calculer le score de priorité pour chaque carte for card in cards: card['priority_score'] = calculate_priority_score(card, today) # Trier par priorité décroissante cards.sort(key=lambda c: c['priority_score'], reverse=True) # Sélectionner les N premières cartes selected = cards[:num_cards] # Charger le contenu de chaque carte for card in selected: content = load_card_content(card['file']) card.update(content) return selected def format_output_text(cards): """Formate l'output en texte lisible.""" output = [] output.append("=" * 60) output.append(f"📚 CARTES SÉLECTIONNÉES - {datetime.now().strftime('%d/%m/%Y')}") output.append("=" * 60) output.append("") for i, card in enumerate(cards, 1): output.append(f"🎯 CARTE {i}/3") output.append(f"ID: {card['id']}") output.append(f"Fichier: {card['file']}") output.append(f"Difficulté: {card['difficulty']} | Critique: {'⚠️ OUI' if card['critical'] else 'Non'}") output.append(f"Dernière review: {card['last_review_str']}") output.append(f"Score priorité: {card['priority_score']}") output.append("") output.append(f"QUESTION:") output.append(card['question']) output.append("") output.append(f"RÉPONSE ATTENDUE:") output.append(card['answer']) output.append("") if card['notes']: output.append(f"NOTES:") output.append(card['notes']) output.append("") output.append("-" * 60) output.append("") return "\n".join(output) def format_output_json(cards): """Formate l'output en JSON.""" return json.dumps({ 'date': datetime.now().strftime('%d/%m/%Y'), 'cards': [ { 'id': card['id'], 'file': card['file'], 'difficulty': card['difficulty'], 'critical': card['critical'], 'last_review': card['last_review_str'], 'priority_score': card['priority_score'], 'question': card['question'], 'answer': card['answer'], 'notes': card['notes'] } for card in cards ] }, indent=2, ensure_ascii=False) def main(): parser = argparse.ArgumentParser(description='Sélectionne les cartes quotidiennes') parser.add_argument('--num-cards', type=int, default=3, help='Nombre de cartes à sélectionner') parser.add_argument('--json', action='store_true', help='Output en JSON') args = parser.parse_args() try: cards = select_daily_cards(args.num_cards) if args.json: print(format_output_json(cards)) else: print(format_output_text(cards)) except Exception as e: print(f"❌ ERREUR: {e}", file=sys.stderr) import sys sys.exit(1) if __name__ == '__main__': import sys main()