Extension Development.md - himent12/FlashGenie GitHub Wiki

๐Ÿ”Œ FlashGenie Extension Development Guide

Learn how to extend FlashGenie with custom plugins, algorithms, and integrations. This guide covers the plugin architecture, extension points, and best practices for building powerful extensions.

๐ŸŽฏ Overview

FlashGenie is designed with extensibility in mind. The plugin architecture allows you to:

  • Custom Learning Algorithms: Implement your own spaced repetition or difficulty analysis algorithms
  • Data Importers/Exporters: Add support for new file formats and data sources
  • User Interface Extensions: Create custom CLI commands or GUI components
  • Analytics and Reporting: Build custom analytics and progress tracking features
  • Integration Plugins: Connect FlashGenie with external services and APIs

๐Ÿ—๏ธ Plugin Architecture

Core Concepts

FlashGenie's plugin system is built around several key concepts:

  1. Extension Points: Predefined interfaces where plugins can hook into the system
  2. Plugin Registry: Central system for discovering and managing plugins
  3. Event System: Publish-subscribe pattern for loose coupling between components
  4. Configuration Management: Standardized way to handle plugin settings

Plugin Types

1. Algorithm Plugins

Extend FlashGenie's learning algorithms:

from flashgenie.core.difficulty_analyzer import DifficultyAnalyzer
from flashgenie.plugins import AlgorithmPlugin

class CustomDifficultyAnalyzer(DifficultyAnalyzer, AlgorithmPlugin):
    """Custom difficulty analysis algorithm."""
    
    plugin_name = "custom_difficulty"
    plugin_version = "1.0.0"
    plugin_description = "Advanced difficulty analysis with machine learning"
    
    def analyze_performance(self, card, responses):
        """Implement custom performance analysis."""
        # Your custom algorithm here
        performance_score = self._calculate_ml_score(responses)
        return {
            'accuracy': performance_score,
            'response_time': self._analyze_timing(responses),
            'confidence': self._calculate_confidence(responses)
        }
    
    def suggest_difficulty_adjustment(self, performance):
        """Suggest difficulty changes based on performance."""
        if performance['accuracy'] > 0.9 and performance['response_time'] < 3.0:
            return -0.2  # Make it easier
        elif performance['accuracy'] < 0.6:
            return 0.3   # Make it harder
        return 0.0       # No change

2. Data Plugins

Add support for new import/export formats:

from flashgenie.data.importers.base_importer import BaseImporter
from flashgenie.plugins import DataPlugin

class AnkiImporter(BaseImporter, DataPlugin):
    """Import flashcards from Anki export files."""
    
    plugin_name = "anki_importer"
    supported_extensions = ['.apkg', '.txt']
    
    def import_from_file(self, file_path, config=None):
        """Import cards from Anki format."""
        cards = []
        
        if file_path.suffix == '.apkg':
            cards = self._import_apkg(file_path)
        elif file_path.suffix == '.txt':
            cards = self._import_anki_txt(file_path)
            
        return cards
    
    def _import_apkg(self, file_path):
        """Handle .apkg files (Anki package format)."""
        # Implementation for Anki package format
        pass
    
    def _import_anki_txt(self, file_path):
        """Handle Anki text export format."""
        # Implementation for Anki text format
        pass

3. Interface Plugins

Extend the user interface:

from flashgenie.interfaces.cli.commands import BaseCommand
from flashgenie.plugins import InterfacePlugin

class StudyStreakCommand(BaseCommand, InterfacePlugin):
    """Add study streak tracking command."""
    
    plugin_name = "study_streak"
    command_name = "streak"
    
    def add_arguments(self, parser):
        """Add command-line arguments."""
        parser.add_argument('--reset', action='store_true',
                          help='Reset the current streak')
        parser.add_argument('--goal', type=int, default=30,
                          help='Set streak goal in days')
    
    def handle(self, args):
        """Handle the streak command."""
        if args.reset:
            self._reset_streak()
            print("Study streak reset!")
        else:
            streak_data = self._get_streak_data()
            self._display_streak(streak_data, args.goal)

๐Ÿ”ง Creating Your First Plugin

Step 1: Plugin Structure

Create a new directory for your plugin:

my_flashgenie_plugin/
โ”œโ”€โ”€ __init__.py
โ”œโ”€โ”€ plugin.py
โ”œโ”€โ”€ setup.py
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ tests/
    โ””โ”€โ”€ test_plugin.py

Step 2: Implement the Plugin

# plugin.py
from flashgenie.plugins import BasePlugin
from flashgenie.core.flashcard import Flashcard

class StudyReminderPlugin(BasePlugin):
    """Plugin to send study reminders."""
    
    # Plugin metadata
    name = "study_reminder"
    version = "1.0.0"
    description = "Sends notifications for study sessions"
    author = "Your Name"
    
    def __init__(self):
        super().__init__()
        self.reminder_settings = self.get_config('reminders', {})
    
    def on_plugin_load(self):
        """Called when plugin is loaded."""
        self.logger.info("Study reminder plugin loaded")
        self._schedule_reminders()
    
    def on_quiz_complete(self, session_data):
        """Called when a quiz session completes."""
        self._update_study_streak(session_data)
        self._schedule_next_reminder(session_data)
    
    def on_card_reviewed(self, card, result):
        """Called when a card is reviewed."""
        if result['correct']:
            self._celebrate_success(card)
    
    def _schedule_reminders(self):
        """Schedule study reminders based on settings."""
        # Implementation here
        pass

Step 3: Plugin Configuration

# __init__.py
from .plugin import StudyReminderPlugin

# Plugin entry point
def get_plugin():
    return StudyReminderPlugin()

# Plugin configuration
PLUGIN_CONFIG = {
    'name': 'study_reminder',
    'version': '1.0.0',
    'entry_point': 'get_plugin',
    'dependencies': ['flashgenie>=1.5.0'],
    'settings': {
        'reminder_interval': 24,  # hours
        'notification_method': 'desktop',
        'quiet_hours': {'start': '22:00', 'end': '08:00'}
    }
}

Step 4: Setup Script

# setup.py
from setuptools import setup, find_packages

setup(
    name="flashgenie-study-reminder",
    version="1.0.0",
    description="Study reminder plugin for FlashGenie",
    author="Your Name",
    author_email="[email protected]",
    packages=find_packages(),
    install_requires=[
        "flashgenie>=1.5.0",
        "plyer>=2.0.0",  # For desktop notifications
    ],
    entry_points={
        'flashgenie.plugins': [
            'study_reminder = my_flashgenie_plugin:get_plugin',
        ],
    },
    classifiers=[
        "Development Status :: 4 - Beta",
        "Intended Audience :: Education",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3.8",
    ],
)

๐ŸŽจ Extension Points

Core Extension Points

1. Learning Algorithm Extensions

from flashgenie.core.spaced_repetition import SpacedRepetitionAlgorithm

class SuperMemoAlgorithm(SpacedRepetitionAlgorithm):
    """Implementation of SuperMemo SM-17 algorithm."""
    
    def calculate_next_review(self, card, quality):
        """Calculate next review date using SM-17."""
        # Advanced SuperMemo implementation
        pass

2. Data Processing Extensions

from flashgenie.data.validators import BaseValidator

class AdvancedCardValidator(BaseValidator):
    """Advanced validation for flashcard content."""
    
    def validate_card(self, card_data):
        """Validate card with advanced rules."""
        errors = []
        
        # Check for duplicate content
        if self._is_duplicate(card_data):
            errors.append("Duplicate card detected")
        
        # Validate question complexity
        if self._is_too_simple(card_data['question']):
            errors.append("Question may be too simple")
        
        return errors

3. Analytics Extensions

from flashgenie.core.performance_tracker import PerformanceTracker

class AdvancedAnalytics(PerformanceTracker):
    """Advanced learning analytics."""
    
    def generate_learning_insights(self, user_data):
        """Generate personalized learning insights."""
        insights = {
            'optimal_study_time': self._find_optimal_time(user_data),
            'difficulty_preferences': self._analyze_preferences(user_data),
            'learning_velocity': self._calculate_velocity(user_data),
            'knowledge_gaps': self._identify_gaps(user_data)
        }
        return insights

Event System

FlashGenie uses an event-driven architecture for plugin communication:

from flashgenie.events import EventManager

class MyPlugin(BasePlugin):
    def on_plugin_load(self):
        # Subscribe to events
        EventManager.subscribe('card.reviewed', self.on_card_reviewed)
        EventManager.subscribe('session.started', self.on_session_started)
        EventManager.subscribe('deck.created', self.on_deck_created)
    
    def on_card_reviewed(self, event_data):
        """Handle card review events."""
        card = event_data['card']
        result = event_data['result']
        # Process the event
    
    def trigger_custom_event(self):
        """Trigger custom events."""
        EventManager.publish('plugin.custom_event', {
            'plugin': self.name,
            'data': {'message': 'Custom event triggered'}
        })

๐Ÿงช Testing Plugins

Unit Testing

# tests/test_plugin.py
import pytest
from unittest.mock import Mock, patch
from my_flashgenie_plugin import StudyReminderPlugin

class TestStudyReminderPlugin:
    def setup_method(self):
        """Set up test fixtures."""
        self.plugin = StudyReminderPlugin()
        self.mock_session = Mock()
    
    def test_plugin_initialization(self):
        """Test plugin initializes correctly."""
        assert self.plugin.name == "study_reminder"
        assert self.plugin.version == "1.0.0"
    
    @patch('my_flashgenie_plugin.plugin.schedule_notification')
    def test_reminder_scheduling(self, mock_schedule):
        """Test reminder scheduling functionality."""
        self.plugin._schedule_reminders()
        mock_schedule.assert_called_once()
    
    def test_quiz_completion_handler(self):
        """Test quiz completion event handling."""
        session_data = {
            'score': 8,
            'total': 10,
            'duration': 300
        }
        
        # Should not raise any exceptions
        self.plugin.on_quiz_complete(session_data)

Integration Testing

def test_plugin_integration():
    """Test plugin integration with FlashGenie core."""
    from flashgenie.plugins import PluginManager
    from flashgenie.core.quiz_engine import QuizEngine
    
    # Load plugin
    plugin_manager = PluginManager()
    plugin_manager.load_plugin('study_reminder')
    
    # Test plugin hooks into quiz engine
    quiz_engine = QuizEngine()
    session = quiz_engine.start_session(test_deck)
    
    # Plugin should receive events
    assert plugin_manager.get_plugin('study_reminder').event_count > 0

๐Ÿ“ฆ Plugin Distribution

Publishing to PyPI

  1. Prepare your package:

    python setup.py sdist bdist_wheel
    
  2. Upload to PyPI:

    pip install twine
    twine upload dist/*
    

FlashGenie Plugin Registry

Submit your plugin to the official registry:

  1. Create plugin manifest:

    {
      "name": "study_reminder",
      "version": "1.0.0",
      "description": "Study reminder plugin",
      "author": "Your Name",
      "repository": "https://github.com/yourusername/flashgenie-study-reminder",
      "pypi_package": "flashgenie-study-reminder",
      "tags": ["notifications", "productivity", "reminders"],
      "screenshots": ["screenshot1.png", "screenshot2.png"],
      "compatibility": {
        "min_flashgenie_version": "1.5.0",
        "python_versions": ["3.8", "3.9", "3.10", "3.11"]
      }
    }
    
  2. Submit for review:

    • Fork the FlashGenie repository
    • Add your plugin manifest to plugins/registry/
    • Submit a pull request

๐ŸŽฏ Best Practices

Plugin Design

  1. Single Responsibility: Each plugin should have a clear, focused purpose
  2. Minimal Dependencies: Keep external dependencies to a minimum
  3. Graceful Degradation: Handle missing dependencies gracefully
  4. Configuration: Make plugins configurable through settings
  5. Documentation: Provide clear documentation and examples

Performance

  1. Lazy Loading: Load resources only when needed
  2. Caching: Cache expensive computations
  3. Async Operations: Use async for I/O operations when possible
  4. Memory Management: Clean up resources properly

Security

  1. Input Validation: Validate all user inputs
  2. Safe File Operations: Use safe file handling practices
  3. Permission Checks: Respect user permissions and privacy
  4. Error Handling: Handle errors gracefully without exposing sensitive data

๐Ÿ”— Resources

Community


Ready to build amazing extensions? Start with our plugin template and join the FlashGenie developer community! ๐Ÿงžโ€โ™‚๏ธโœจ