This guide explains how to migrate from the monolithic notebook structure to the new modular architecture.
The GradeReporter has been restructured from a single Jupyter notebook into a scalable, modular application with:
- Separation of Concerns: Repository (data) → Service (logic) → UI (presentation)
- Feature Modules: Each feature is self-contained
- Streamlit Frontend: Modern web interface replacing notebook widgets
- Configuration Management: Centralized settings and environment variables
- Scalability: Ready to support 8+ features
GradeReporter/
├── config/ # Configuration management
│ ├── settings.py # Centralized settings
│ └── database.py # Database connections
├── src/
│ ├── core/ # Shared core functionality (auth, session, RBAC)
│ ├── models/ # Data models (to be populated)
│ ├── features/ # Feature modules
│ │ ├── authentication/
│ │ ├── announcements/
│ │ ├── parent_engagement/
│ │ ├── after_hours/
│ │ └── feature_5-8/ # Placeholders for future features
│ ├── utils/ # Utility functions
│ └── ui/ # Streamlit UI components
│ ├── components/ # Reusable UI components
│ └── pages/ # Page components
├── app.py # Main Streamlit entry point
├── scripts/ # Database initialization & utilities
├── tests/ # Test suite
└── data/ # Database files (gitignored)
# Navigate to the project directory
cd /Users/autumnerwin/GradeReporter/GradeReporter
# Create and activate virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install --upgrade pip
pip install -r requirements.txt# Copy the example environment file
cp .env.example .env
# Edit .env with your configuration
# - Set database paths
# - Configure feature flags
# - Set secret key for productionThe existing databases (school_system.db and after_hours.db) need to be placed in the data/ directory:
# Create data directory
mkdir -p data
# Copy existing databases (update paths as needed)
cp /path/to/school_system.db data/
cp /path/to/after_hours.db data/# Start the Streamlit app
streamlit run app.pyThe app will be available at http://localhost:8501
- Project structure
- Configuration management
- Session management
- Authentication scaffold
- Streamlit frontend scaffold
- Navigation system
- Role-based routing
Status: Scaffold created, needs notebook code migration
Files to populate:
src/features/authentication/service.py✓ (basic structure)src/features/authentication/repository.py✓ (basic structure)src/features/authentication/ui.py(needs work)src/core/rbac.py(needs creation)src/core/auth.py(needs creation)
Migration tasks:
- Extract
authenticate_user()logic →AuthenticationService.authenticate() - Extract RBAC filtering logic →
src/core/rbac.py - Migrate dashboard routing logic
- Implement role-based data filtering
Status: Placeholder created
Files to populate:
src/features/announcements/service.pysrc/features/announcements/repository.pysrc/features/announcements/ui.pysrc/models/announcement.py
Migration tasks:
- Extract
add_announcement()→AnnouncementService.create() - Extract
view_announcements()→AnnouncementService.get_visible_announcements() - Create Streamlit UI for announcement management
- Integrate with teacher/admin dashboards
Status: Placeholder created
Files to populate:
src/features/parent_engagement/service.pysrc/features/parent_engagement/repository.pysrc/features/parent_engagement/ui.pysrc/models/engagement.py
Migration tasks:
- Extract
parent_contact_teacher()→EngagementService.send_message() - Extract
parent_request_meeting()→EngagementService.request_meeting() - Extract
parent_view_requests()→EngagementService.get_requests() - Create Streamlit UI for parent-teacher communication
- Add teacher response interface
Status: Placeholder created
Files to populate:
src/features/after_hours/service.pysrc/features/after_hours/repository.pysrc/features/after_hours/ui.pysrc/models/after_hours.py
Migration tasks:
- Extract
AfterHoursSystemclass →AfterHoursService - Migrate teacher availability management
- Migrate ticket submission and scheduling
- Create Streamlit UI for question submission
- Add timezone selector and display
- Identify all database queries in the notebook
- Create methods in
repository.pyfor each query - Use parameterized queries to prevent SQL injection
- Return structured data (dictionaries or dataclasses)
- Identify business logic functions
- Create service methods that orchestrate repository calls
- Add validation and error handling
- Keep services independent of UI concerns
- Design Streamlit interface
- Use
@require_loginand@require_roledecorators - Call service methods (never repository directly)
- Display results using Streamlit components
Original (Notebook):
def view_grades(user_id, role):
conn = sqlite3.connect('school_system.db')
if role == 'student':
query = "SELECT * FROM Grades WHERE student_id = ?"
results = pd.read_sql(query, conn, params=(user_id,))
# ... more logic
display(results)New (Modular):
# repository.py
class GradeRepository:
def get_grades_by_student(self, student_id: int):
query = "SELECT * FROM Grades WHERE student_id = ?"
return db_manager.execute_query(query, (student_id,))
# service.py
class GradeService:
def __init__(self):
self.repo = GradeRepository()
def get_student_grades(self, student_id: int):
grades = self.repo.get_grades_by_student(student_id)
# Add business logic, formatting, etc.
return grades
# ui.py
@require_role('student')
def show_grades():
service = GradeService()
grades = service.get_student_grades(session.get_user()['student_id'])
st.dataframe(grades)Create tests for each layer:
# tests/test_authentication.py
def test_authenticate_valid_user():
service = AuthenticationService()
user = service.authenticate('test@example.com', 'password')
assert user is not None
assert user['email'] == 'test@example.com'Run tests:
pytest tests/-
Create feature module structure:
mkdir -p src/features/feature_name touch src/features/feature_name/{__init__.py,service.py,repository.py,ui.py} -
Define data models in
src/models/ -
Implement repository layer (database access)
-
Implement service layer (business logic)
-
Create UI components in
ui.py -
Add navigation in
src/ui/components/navigation.py -
Write tests
-
Update feature flag in
.env
- Never push directly to main - always use feature branches
- Branch naming:
feat/feature-nameorfix/issue-description - Create PRs for all changes
- Code reviews required before merge
- Keep PRs focused on single features or fixes
# Create feature branch
git checkout -b feat/migrate-announcements
# Make changes and commit
git add .
git commit -m "feat: migrate announcements from notebook to service layer"
# Push and create PR
git push -u origin feat/migrate-announcementsEnable/disable features without code changes:
# .env
FEATURE_ANNOUNCEMENTS=True
FEATURE_AFTER_HOURS=True
FEATURE_5=False # New feature not ready yet# .env
DB_PATH=data/school_system.db
AFTER_HOURS_DB_PATH=data/after_hours.dbMake sure you're running from the project root:
# Run from GradeReporter/GradeReporter/
streamlit run app.pyEnsure databases are in the data/ directory:
ls -la data/
# Should show school_system.db and after_hours.dbClear Streamlit cache:
# In browser: Settings → Clear Cache
# Or delete .streamlit/ directory
rm -rf .streamlit/- Set up development environment (see Setup Instructions above)
- Review the scaffolded code to understand the architecture
- Start migrating features one at a time (recommend starting with Authentication)
- Test each feature as you migrate it
- Plan new features (5-8) using the established patterns
- Document as you go - update this guide with learnings
- Check the original notebook:
notebooks/GradeReporter_Demo_1.ipynb - Review example code in
src/features/authentication/ - Consult with feature owners:
- Authentication: Autumn Erwin
- Announcements: Meetika Kanumukula
- Parent Engagement: Keith
- After-Hours: Jaikishan