EduResourcesView Class

Technical Documentation - Educational Resource Management and Distribution

The EduResourcesView class is a PySide6 widget designed for managing, filtering, and distributing educational resources to students. It serves as the central hub for educators to browse educational content, assign activities to individual students or groups, and generate printable/exportable materials using customizable templates. The class implements a card-based grid interface with advanced filtering, template-based content generation, and student distribution capabilities.

This resource management system supports multiple template types (quiz, formal exam) with configurable layouts, internationalization support, and PDF generation capabilities. It integrates with the application's database to track resource assignments and student progress, creating a seamless workflow from resource selection to student distribution.

🔍 1. Overview and Purpose

The EduResourcesView class provides a comprehensive interface for managing educational resources within a teacher assistant application. It enables educators to:

📚

Resource Management

Browse, filter, and organize educational resources stored in the database with real-time search capabilities.

🎯

Student Distribution

Assign selected educational items to individual students or groups with customizable deadlines and distribution times.

📄

Template-Based Generation

Generate printable educational materials using configurable templates for different assessment types (quizzes, exams).

Primary Design Principle: All educational resources are formatted to A4 paper width (210mm/8.27 inches) for consistency in printed output. Template configurations are stored in JSON files within the /resources/templates/ directory.

Class Inheritance and Dependencies

class EduResourcesView(QWidget): # Primary dependencies: # - PySide6.QtWidgets (UI components) # - PySide6.QtCore (Threading, signals/slots) # - PySideAbdhUI (Custom UI components) # - data.loaders.DataLoaderWorker (Background data loading) # - processing.utils.pdf.PdfGeneratorApp (PDF generation) # - ui.widgets.widgets.EduItemWidget (Resource card widget) # - core.app_context.app_context (Application configuration)

System Architecture

[ARCHITECTURE DIAGRAM: Three-layer system showing:
1. Presentation Layer (Card Grid, Template Dialog, Distribution Menu)
2. Business Logic Layer (Filtering, Template Processing, Distribution Logic)
3. Data Layer (Database queries, Background loading, File I/O)
4. Output Layer (PDF Generation, Database updates)]

🏗️ 2. Architecture and Layout

3×2 Grid Layout Structure

# Main Grid Layout (3 rows × 2 columns): # Row 0: Header with title and filter controls (Column 0) # Row 1: Card grid view showing educational resources (Column 0) # Row 2: (Optional) Footer or additional controls # Column 1: Reserved for template settings and actions main_layout = QGridLayout(self) main_layout.addWidget(header_widget, 0, 0) # Cell [0,0] main_layout.addWidget(self.card_grid, 1, 0) # Cell [1,0] main_layout.setRowStretch(1, 1) # Make row 1 stretchable

Card Grid View Integration

The central component is a CardGridView that displays educational resources as interactive cards:

# Initialize card grid with configurable columns: self.card_grid = CardGridView(self.disply_columns, parent=self) # Connect signals for interactivity: self.card_grid.card_selected.connect(self.on_card_selected) self.card_grid.card_removed.connect(self.on_card_removed) # Default configuration: self.disply_columns = 2 # Number of columns in grid self.page_size = 50 # Items per page for pagination

Key Architectural Patterns

Pattern Implementation Purpose
Observer Pattern Signal/Slot connections Handle user interactions and data updates
Worker Pattern DataLoaderWorker + QThreadPool Background data loading without UI freeze
Template Method Configurable template system Generate different output formats
Composite Pattern CardGridView with EduItemWidget children Manage complex UI hierarchies

🎨 3. UI Components Structure

Header Section Components

# Header contains: header = QHBoxLayout(header_widget) # 1. Page title label page_title = QLabel('RESOURCE COLLECTION') page_title.setProperty('class', 'heading2') # 2. Filter input field self.source_input = QLineEdit() self.source_input.setPlaceholderText("Filter data using 'Source' ...") self.source_input.textChanged.connect(lambda text: self.load_data(filter=text)) # 3. Distribution button with dropdown menu btn3 = QPushButton('distribute') btn3.setMenu(self.create_distribution_options())

Card Grid Configuration

The CardGridView provides a responsive grid layout for educational resource cards:

Property Value Description
Default Columns 2 Number of cards per row
Page Size 50 Items loaded per batch
Card Width app_context.EDU_ITEM_PIXELS + 20 Card width based on A4 paper dimensions
Selection Mode Single/Multiple Supports selecting multiple resources

EduItemWidget Structure

Each card in the grid is an EduItemWidget containing:

# EduItemWidget displays: # 1. Resource ID and description # 2. Score/point value # 3. Source information # 4. Additional details # 5. Selection checkbox # 6. Titlebar with edit/remove options edu_item = EduItemWidget(record, app_context.EDU_ITEM_PIXELS + 20, cursor=app_context.database)

🎯 4. Student Distribution System

Distribution Menu Structure

The distribution system uses a custom QMenu with embedded widgets:

def create_distribution_options(self): menu = QMenu(self) widget_action = QWidgetAction(self) widget = QWidget() # Layout contains: dist_options_layout = QGridLayout(widget) # 1. Student list (QListView) # 2. Distribution time input (QLineEdit) # 3. Deadline input (QLineEdit) # 4. Distribute button (QPushButton)

Target Students Data Structure

The class accepts target students as a list of dictionaries:

# Constructor parameter: def __init__(self, parent=None, target_students=[dict]): self.target_students = target_students # Expected dictionary format: student = { 'Id': 'student_123', # Student identifier 'Name': 'John Doe' # Student display name }

Distribution Workflow

distribute() - Core Distribution Logic

This method handles the complete distribution process:

  1. Validation: Checks if target students exist and items are selected
  2. Confirmation: Shows confirmation dialog with item/student counts
  3. Database Insertion: Creates quests records for each student-item pair
  4. Notification: Provides user feedback on success/failure
# Database insertion for each student-item pair: cmd = 'INSERT INTO quests(qb_id, student_id, max_point_, earned_point_, assign_date_, deadline_, answer_, reply_date_, feedback_) ' cmd += 'VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s)' values = (item['Id'], stu['Id'], item['score'], 0, str(utc_time), str(future_time), None, None, None) app_context.database.execute(cmd, values)
Database Schema: The distribution system creates records in the quests table, which links educational resources (qb_id) to students (student_id) with assignment dates, deadlines, and scoring information.

Date Management

The system provides sensible defaults for distribution timing:

# Default distribution time: current date self.dist_time_input = QLineEdit(datetime.now().strftime("%Y-%m-%d")) # Default deadline: 7 days from current date self.deadline_input = QLineEdit((datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d'))

📄 5. Template Management

Template File System

Two primary template types are supported:

# Available template files (stored in /resources/templates/): Edu_Template_Files = ['01-Quiz', '02-Formal-Exam'] # Each template consists of: # 1. HTML template file: {template_name}-Template.html # 2. Config file: {template_name}-config.json # 3. Language-specific configurations

Template Configuration Structure

Configuration Key Purpose Example Values
Student name Placeholder for student name field "Student name", "نام دانش‌آموز"
Book Textbook and grade information "Mathematics Grade 10"
Date Exam/quiz date field "Date", "تاریخ"
Font family Typography for generated content "'B Nazanin', Tahoma"
Direction Text direction (RTL/LTR) "rtl", "ltr"
Text align Content alignment "right", "left", "center"

Template Selection Dialog

The show_template_selector_dlg() method creates a dynamic dialog based on selected template:

def template_changed(self, sender: QComboBox): index = sender.currentIndex() if index == 0: # Classroom quiz # Shows minimal fields: student name, book, date self.stu_info_input.setVisible(True) self.book_grade_input.setVisible(True) self.date_input.setVisible(True) elif index == 1: # Formal exam # Shows all fields including organization info, teacher, etc. self.stu_id_input.setVisible(True) self.teacher_input.setVisible(True) self.author_input.setVisible(True) # ... additional fields
Internationalization Support: Template configurations support multiple languages through the app_context.Language setting. Each configuration key has language-specific values for consistent localization.

Content Row Templates

Each template type has a specific HTML structure for content rows:

# Classroom quiz template (simplified): new_row_tmp = ' <tr>\n' new_row_tmp += f' <td style="border-left:none;border-top:none;border-right:none; vertical-align:top;">{{}})</td>\n' new_row_tmp += f' <td style="border-left:none;border-top:none;border-right:none; width:{app_context.EDU_ITEM_PIXELS}; text-align:{{}}">{{}}</td>\n' new_row_tmp += ' <td style="border-left:none;border-top:none;border-right:none; vertical-align:top">{{}}</td>\n' new_row_tmp += ' </tr>\n' # Format: row index, text_align, content, point # Formal exam template (more detailed): new_row_tmp = ' <tr>\n' new_row_tmp += ' <td>{{}}</td>\n' new_row_tmp += f' <td style="width:{app_context.EDU_ITEM_PIXELS}; text-align:{{}}">{{}}</td>\n' new_row_tmp += ' <td>{{}}</td>\n' new_row_tmp += ' </tr>\n'

🔄 6. Content Generation Engine

HTML Content Generation Workflow

generate_edu_contents() - Main Generation Method

This method orchestrates the complete content generation process:

  1. Template Validation: Checks template selection and file existence
  2. Content Selection: Retrieves selected educational resources
  3. HTML Generation: Creates HTML content with template placeholders
  4. PDF Preview: Opens PDF preview window for generated content

Placeholder Replacement System

The system replaces placeholders in template files with actual content:

# Dimension placeholders (converted from inches to pixels): dimensions = { '-- 2.43 inches --': f'{2.43*app_context.DPI}px', '-- 2.42 Inches --': f'{2.42*app_context.DPI}px', '-- 0.54 Inches --': f'{0.54*app_context.DPI}px', '-- 6.19 Inches --': f'{app_context.EDU_ITEM_PIXELS}px', } # Content placeholders: replacements = { '-- Stu-Name --': self.stu_info_input.text(), '-- Stu-Id --': self.stu_id_input.text(), '-- Organisation Info --': self.org_info_input.toPlainText().replace('\n', '<br>'), '-- SUM --': str(selected_content[-1]['total_points']), # ... additional placeholders }

PDF Generation Integration

The class integrates with PdfGeneratorApp for PDF preview and generation:

def ___show_pdf_preview__(self, html): # Create PDF preview window window = PdfGeneratorApp(html_content=html, parent=self.dialog) window.setWindowModality(Qt.WindowModality.WindowModal) # Position window relative to template dialog window.move(self.dialog.geometry().topRight() + QPoint(10, 0)) window.show()

Selected Content Processing

get_selected_contents() - Content Aggregation

This method collects and processes selected educational resources:

selected_content = [] total_points = 0 row = 0 for card in self.card_grid.get_cards(): data = card.get_data() if data['selected']: row += 1 content = { 'Id': data['Id'], 'row': row, 'content': data['content'], 'score': float(data['score']) } selected_content.append(content) total_points += content['score'] if selected_content: selected_content.append({'total_points': total_points})
Note: The method returns a list where the last element contains the total points sum for all selected items. This sum is used in the generated output's footer.

📥 7. Data Loading and Filtering

Background Data Loading

The class uses DataLoaderWorker with QThreadPool for responsive data loading:

def load_data(self, filter: str = ''): self.card_grid.reset() # clears grid, resets scroll self.current_page = 0 self.has_more = True self.current_filter = filter # Start background loading self.load_next_page(filter) def load_next_page(self, filter_text): if not self.has_more: return # Build search query search = f"%{filter_text}%".strip() base_query = "SELECT Id, content_description_, score_, source_, additional_details_ FROM educational_resources" # Create worker for background loading worker = DataLoaderWorker(query=full_query, page=self.current_page, page_size=self.page_size) worker.signals.batch_ready.connect(self.on_batch_received) worker.signals.finished.connect(self.on_load_finished) worker.signals.error.connect(self.on_load_error) QThreadPool.globalInstance().start(worker)

Query Construction and Filtering

Filter Field Search Logic Database Column
Source ILIKE (case-insensitive) source_
Content Description ILIKE (case-insensitive) content_description_
Additional Details ILIKE (case-insensitive) additional_details_
Resource ID TEXT conversion + ILIKE Id
Security Warning: The load_next_page() method constructs SQL queries with string concatenation for the WHERE clause (f"(source_ ILIKE '%{search}' OR ...)"), creating potential SQL injection vulnerabilities. This should be updated to use proper parameterized queries.

Pagination Implementation

The system implements server-side pagination with configurable page size:

# Default pagination settings: self.page_size = 50 # Items per page self.current_page = 0 # Current page index self.has_more = True # More data available # Sorting criteria: order_by = "ORDER BY score_ DESC NULLS LAST, Id DESC"

Signal Handling for Data Loading

Signal Slot Method Purpose
batch_ready on_batch_received() Process received data batch
finished on_load_finished() Handle loading completion
error on_load_error() Handle loading errors

⚙️ 8. Core Method Analysis

Initialization and Setup Methods

Method Purpose Complexity Key Operations
__init__(parent, target_students) Constructor with target students O(1) Store target students, initialize defaults
initUI() Main UI construction O(1) Create layout, header, card grid, signals
create_distribution_options() Build distribution menu O(n) for n students Create QMenu with embedded widgets

Core Functional Methods

Method Primary Function Performance Dependencies
distribute() Distribute resources to students O(n×m) for n items × m students Database, target_students, selected items
get_selected_contents() Collect selected resource data O(n) for n cards CardGridView, EduItemWidget
___generate_html_content__() Generate HTML from template O(n) for n placeholders Template files, app_context
load_data() Initialize data loading O(1) DataLoaderWorker, QThreadPool

Template Management Methods

template_changed() - Dynamic UI Updates

This method dynamically updates the template dialog based on selected template type:

  • Classroom Quiz: Shows minimal fields (student name, book, date)
  • Formal Exam: Shows comprehensive fields (organization info, teacher, author, etc.)
  • Config Loading: Loads language-specific configurations from JSON
  • UI Adjustment: Updates widget visibility and dialog size

___validate_template__() - Template Validation

Validates template selection and file existence before content generation:

def ___validate_template__(self) -> bool: if self.edu_template_file == '': PopupNotifier.Notify(self, 'Error', 'The output template has not selected.') return False template_file = app_context.resource_path + f'\\templates\\{self.edu_template_file}-Template.html' if not os.path.exists(template_file): PopupNotifier.Notify(self, 'Error', f'The template file not found in the specified path --> {template_file}') return False self.template_file = template_file return True

Event Handling Methods

Method Trigger Action
on_card_selected() Card selection in grid Update selected card, show/hide titlebars
on_card_removed() Card removal from grid Handle card removal (currently placeholder)
on_batch_received() DataLoaderWorker batch Add cards to grid for received data
on_load_finished() Data loading complete Update pagination state, show/hide indicators
on_load_error() Data loading error Display error message in grid

⚡ 9. Performance Considerations

Memory Usage Analysis

📚

Card Grid Memory

Issue: Each EduItemWidget contains HTML content
Impact: Linear memory growth with resource count
Solution: Pagination (50 items per page) reduces memory load

🔄

Background Loading

Benefit: DataLoaderWorker prevents UI freeze
Impact: Minimal main thread blocking
Optimization: Configurable page size (default: 50)

📄

Template Processing

Issue: HTML template loading and processing
Impact: File I/O and string manipulation overhead
Solution: Template caching could be implemented

Time Complexity Analysis

Operation Method Complexity Bottleneck Optimization Potential
Initial data load load_data() O(n) + background loading Database query, widget creation Medium - Virtual scrolling
Distribution to students distribute() O(n×m) Database inserts for each student-item pair High - Batch database operations
HTML generation ___generate_html_content__() O(p) for p placeholders String replacement operations Low - Already efficient
Template switching template_changed() O(1) + file I/O JSON config loading Medium - Config caching

Optimization Recommendations

Immediate Improvements

  1. Fix SQL Injection Vulnerability: Parameterize the filter query in load_next_page()
  2. Batch Database Operations: Use batch inserts in distribute() for better performance
  3. Template Caching: Cache loaded template files and configurations
  4. Virtual Scrolling: Implement virtual scrolling for large resource collections

Architectural Improvements

  1. Lazy Loading: Load HTML content in cards only when visible
  2. Connection Pooling: Optimize database connection management
  3. Memory Management: Implement proper cleanup of unused widgets
  4. Progressive Enhancement: Load basic card data first, enhance with details later
Critical Security Issue: The load_next_page() method uses string concatenation for SQL queries with user-provided filter text, creating a severe SQL injection vulnerability. This must be fixed immediately using parameterized queries.

📚 10. API Reference

Public Methods Reference

Method Parameters Return Description Thread Safety
__init__(parent, target_students) parent: QWidget
target_students: list[dict]
None Constructor with optional target students UI thread only
distribute() None None Distribute selected resources to target students UI thread only
show_template_selector_dlg() None None Display template selection dialog UI thread only
generate_edu_contents() None None Generate educational content using selected template UI thread only
load_data(filter) filter: str (optional) None Load educational resources with optional filter UI thread (spawns worker)
get_selected_contents() None list[dict] Get data for selected educational resources UI thread only
remove_card() None None Remove selected card from grid UI thread only
clear_cards() None None Clear all cards from grid UI thread only

Signal/Slot Connections

Signal Source Signal Slot/Method Purpose
Source filter input textChanged load_data(filter=text) Real-time filtering of resources
CardGridView card_selected on_card_selected Handle card selection
CardGridView card_removed on_card_removed Handle card removal
DataLoaderWorker batch_ready on_batch_received Process loaded data batches
DataLoaderWorker finished on_load_finished Handle loading completion
DataLoaderWorker error on_load_error Handle loading errors
Template selector combo currentIndexChanged template_changed Update template configuration

Dependencies and Requirements

Core Dependencies

  • PySide6: >= 6.4.0 (UI framework)
  • Python: >= 3.8 (datetime, typing, f-strings)
  • Database: PostgreSQL/MySQL with specific schema

Custom Module Dependencies

  • PySideAbdhUI.CardGridView: Card-based grid layout component
  • PySideAbdhUI.Notify: PopupNotifier for user notifications
  • data.loaders.DataLoaderWorker: Background data loading
  • ui.widgets.widgets.EduItemWidget: Resource card widget
  • processing.utils.pdf.PdfGeneratorApp: PDF generation and preview
  • core.app_context.app_context: Application configuration and database

File System Dependencies

  • Template Files: /resources/templates/{name}-Template.html
  • Config Files: /resources/templates/{name}-config.json
  • Resource Path: Configured via app_context.resource_path

Module Dependency Graph

[DEPENDENCY GRAPH: Visual representation showing:
Core PySide6 → Custom UI Framework (PySideAbdhUI) → Data Loading Layer →
Widget Components → Template Engine → PDF Generation → Database Layer
With bidirectional relationships and data flow directions]