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:
- Extension Points: Predefined interfaces where plugins can hook into the system
- Plugin Registry: Central system for discovering and managing plugins
- Event System: Publish-subscribe pattern for loose coupling between components
- 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
-
Prepare your package:
python setup.py sdist bdist_wheel
-
Upload to PyPI:
pip install twine twine upload dist/*
FlashGenie Plugin Registry
Submit your plugin to the official registry:
-
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"] } }
-
Submit for review:
- Fork the FlashGenie repository
- Add your plugin manifest to
plugins/registry/
- Submit a pull request
๐ฏ Best Practices
Plugin Design
- Single Responsibility: Each plugin should have a clear, focused purpose
- Minimal Dependencies: Keep external dependencies to a minimum
- Graceful Degradation: Handle missing dependencies gracefully
- Configuration: Make plugins configurable through settings
- Documentation: Provide clear documentation and examples
Performance
- Lazy Loading: Load resources only when needed
- Caching: Cache expensive computations
- Async Operations: Use async for I/O operations when possible
- Memory Management: Clean up resources properly
Security
- Input Validation: Validate all user inputs
- Safe File Operations: Use safe file handling practices
- Permission Checks: Respect user permissions and privacy
- Error Handling: Handle errors gracefully without exposing sensitive data
๐ Resources
Community
- GitHub Discussions - Ask questions and share ideas
Ready to build amazing extensions? Start with our plugin template and join the FlashGenie developer community! ๐งโโ๏ธโจ