🔍 Code Extractor

class TrainingManagement

Maturity: 27

UI component for managing document training.

File:
/tf/active/vicechatdev/CDocs/ui/training_management.py
Lines:
23 - 754
Complexity:
moderate

Purpose

UI component for managing document training.

Source Code

class TrainingManagement(BaseUIComponent):
    """UI component for managing document training."""
    
    def __init__(self, parent_app=None, document_uid: str = None, **params):
        super().__init__(parent_app, **params)
        self.current_user = None
        self.document_uid = document_uid
        self.document = None
        self.training_config = None
        
        # UI components
        self.main_content = pn.Column(sizing_mode='stretch_width')
        self.config_panel = None
        self.assignment_panel = None
        
        self._build_ui()
    
    def _build_ui(self):
        """Build the training management UI."""
        try:
            # Create header
            self.header = pn.pane.Markdown("# Training Management", sizing_mode='stretch_width')
            
            # Create status area for messages
            self.status_area = pn.pane.HTML("", sizing_mode='stretch_width')
            
            # Create loading message
            self.loading_panel = pn.pane.HTML(
                "<div class='alert alert-info'>Loading training management...</div>",
                sizing_mode='stretch_width'
            )
            
            # Add components to main content
            self.main_content.extend([
                self.header,
                self.status_area,
                self.loading_panel
            ])
            
        except Exception as e:
            logger.error(f"Error building training management UI: {e}")
            self.main_content.append(pn.pane.Markdown(f"**Error:** {str(e)}"))
    
    def set_user_and_document(self, user: DocUser, document_uid: str):
        """Set the current user and document."""
        try:
            self.current_user = user
            self.document_uid = document_uid
            self._load_training_management()
        except Exception as e:
            logger.error(f"Error setting user and document: {e}")
            self.show_error(f"Error loading training management: {str(e)}")
    
    def _load_training_management(self):
        """Load training management interface."""
        try:
            if not self.current_user or not self.document_uid:
                self.show_error("Missing user or document information")
                return
            
            # Check permissions
            if not permissions.user_has_permission(self.current_user, "MANAGE_TRAINING"):
                self.show_error("You do not have permission to manage training")
                return
            
            # Load document
            self.document = ControlledDocument(uid=self.document_uid)
            if not self.document or not self.document.uid:
                self.show_error("Document not found")
                return
            
            # Load training configuration
            self.training_config = DocumentTraining(document_uid=self.document_uid)
            
            # Ensure training config data has proper defaults
            if not hasattr(self.training_config, '_data') or self.training_config._data is None:
                self.training_config._data = {}
            
            # Set safe defaults for boolean fields
            training_data = self.training_config._data
            if 'training_required' not in training_data:
                training_data['training_required'] = False
            if 'quiz_required' not in training_data:
                training_data['quiz_required'] = False
            if 'validity_days' not in training_data:
                training_data['validity_days'] = 365
            if 'instructions' not in training_data:
                training_data['instructions'] = ''
            
            # Build the management interface with error handling
            try:
                self._build_management_interface()
            except Exception as ui_error:
                logger.error(f"Error building UI: {ui_error}")
                # Fallback to simple error display
                self.main_content.clear()
                self.main_content.append(pn.pane.Markdown(f"**Error building interface:** {str(ui_error)}"))
            
        except Exception as e:
            logger.error(f"Error loading training management: {e}")
            self.show_error(f"Error loading training management: {str(e)}")
    
    def _build_management_interface(self):
        """Build the main training management interface."""
        try:
            # Clear main content
            self.main_content.clear()
            
            # Update header with document info
            header_text = f"# Training Management: {self.document.doc_number} - {self.document.title}"
            self.header = pn.pane.Markdown(header_text, sizing_mode='stretch_width')
            self.main_content.append(self.header)
            
            # Create tabs for different management functions
            self.tabs = pn.Tabs(
                ("Training Configuration", self._build_config_tab()),
                ("User Assignments", self._build_assignments_tab()),
                ("Training Reports", self._build_reports_tab()),
                sizing_mode='stretch_width'
            )
            
            self.main_content.append(self.tabs)
            
        except Exception as e:
            logger.error(f"Error building management interface: {e}")
            self.show_error(f"Error building management interface: {str(e)}")
    
    def _build_config_tab(self) -> pn.viewable.Viewable:
        """Build the training configuration tab."""
        try:
            config_tab = pn.Column(sizing_mode='stretch_width')
            
            # Helper function to ensure boolean values
            def ensure_boolean(value):
                """Convert value to boolean, handling None and other types"""
                if value is None:
                    return False
                if isinstance(value, bool):
                    return value
                if isinstance(value, str):
                    return value.lower() in ('true', '1', 'yes', 'on')
                return bool(value)
            
            # Training enabled toggle - ensure boolean value
            training_enabled = self.training_config._data.get('training_required', False)
            training_enabled = ensure_boolean(training_enabled)
            self.enable_checkbox = pn.widgets.Checkbox(
                name="Enable Training Requirement",
                value=training_enabled,
                sizing_mode='stretch_width'
            )
            
            # Training validity period - ensure integer value
            validity_days = self.training_config._data.get('validity_days', 365)
            try:
                validity_days = int(validity_days) if validity_days is not None else 365
            except (ValueError, TypeError):
                validity_days = 365
                
            self.validity_input = pn.widgets.IntInput(
                name="Training Validity (days)",
                value=validity_days,
                start=1,
                end=3650,
                sizing_mode='stretch_width'
            )
            
            # Quiz requirement - ensure boolean value
            quiz_required = self.training_config._data.get('quiz_required', False)
            quiz_required = ensure_boolean(quiz_required)
            self.quiz_checkbox = pn.widgets.Checkbox(
                name="Require Quiz Completion",
                value=quiz_required,
                sizing_mode='stretch_width'
            )
            
            # Training instructions - ensure string value
            instructions = self.training_config._data.get('instructions', '')
            instructions = str(instructions) if instructions is not None else ''
            self.instructions_input = pn.widgets.TextAreaInput(
                name="Training Instructions",
                value=instructions,
                placeholder="Enter instructions for trainees...",
                rows=5,
                sizing_mode='stretch_width'
            )
            
            # Save button
            self.save_config_btn = pn.widgets.Button(
                name="Save Configuration",
                button_type="primary",
                width=150
            )
            self.save_config_btn.on_click(self._save_training_config)
            
            # Add components to config tab
            config_tab.extend([
                pn.pane.Markdown("## Training Configuration"),
                self.enable_checkbox,
                self.validity_input,
                self.quiz_checkbox,
                self.instructions_input,
                pn.Row(pn.layout.HSpacer(), self.save_config_btn)
            ])
            
            return config_tab
            
        except Exception as e:
            logger.error(f"Error building config tab: {e}")
            return pn.pane.Markdown(f"**Error building configuration tab:** {str(e)}")
    
    def _build_assignments_tab(self) -> pn.viewable.Viewable:
        """Build the user assignments tab."""
        try:
            assignments_tab = pn.Column(sizing_mode='stretch_width')
            
            # Get assigned users
            assigned_users = self.training_config.get_assigned_users()
            
            # User selection for new assignments
            user_options = self._get_user_options()
            logger.info(f"User options for MultiSelect: {user_options}")
            
            self.user_select = pn.widgets.MultiSelect(
                name="Select Users to Assign",
                options=user_options,  # This should be a dict now
                size=10,
                sizing_mode='stretch_width'
            )
            
            # Assign button
            self.assign_btn = pn.widgets.Button(
                name="Assign Training",
                button_type="success",
                width=150
            )
            self.assign_btn.on_click(self._assign_training)
            
            # Current assignments table
            if assigned_users and len(assigned_users) > 0:
                assignments_data = []
                for user in assigned_users:
                    assignments_data.append({
                        'User': user.get('user_name', 'Unknown'),
                        'Email': user.get('user_email', 'Unknown'),
                        'Status': user.get('status', 'Unknown'),
                        'Assigned Date': self._format_date(user.get('assigned_date')),
                        'Trained Date': self._format_date(user.get('trained_on')),
                        'Expires Date': self._format_date(user.get('expires_date')),
                        'UID': user.get('user_uid', '')  # Hidden for actions
                    })
                
                # Create DataFrame for Tabulator if pandas is available
                try:
                    import pandas as pd
                    df = pd.DataFrame(assignments_data)
                    
                    # Create Tabulator with DataFrame
                    self.assignments_table = pn.widgets.Tabulator(
                        df,
                        pagination='remote',
                        page_size=10,
                        sizing_mode='stretch_width',
                        hidden_columns=['UID'],  # Hide UID column
                        configuration={
                            'selectable': True,
                            'layout': 'fitData'
                        }
                    )
                    
                    # Add remove button functionality
                    def create_remove_button(user_uid):
                        btn = pn.widgets.Button(
                            name="Remove",
                            button_type="danger",
                            width=80,
                            height=30
                        )
                        btn.on_click(lambda event: self._remove_training_assignment(user_uid))
                        return btn
                    
                    # Create remove buttons for each assignment
                    remove_buttons = pn.Column()
                    for assignment in assignments_data:
                        user_uid = assignment['UID']
                        if user_uid:
                            btn = create_remove_button(user_uid)
                            remove_buttons.append(btn)
                    
                    # Combine table and buttons in a row layout
                    table_section = pn.Row(
                        self.assignments_table,
                        pn.Column(
                            pn.pane.Markdown("**Actions**"),
                            remove_buttons
                        ),
                        sizing_mode='stretch_width'
                    )
                    
                except ImportError:
                    # Fallback to simple HTML table if pandas not available
                    table_html = "<table class='table table-striped'>"
                    table_html += "<thead><tr><th>User</th><th>Email</th><th>Status</th><th>Assigned Date</th><th>Trained Date</th><th>Expires Date</th><th>Actions</th></tr></thead>"
                    table_html += "<tbody>"
                    
                    for assignment in assignments_data:
                        table_html += f"""
                        <tr>
                            <td>{assignment['User']}</td>
                            <td>{assignment['Email']}</td>
                            <td>{assignment['Status']}</td>
                            <td>{assignment['Assigned Date']}</td>
                            <td>{assignment['Trained Date']}</td>
                            <td>{assignment['Expires Date']}</td>
                            <td><button class='btn btn-danger btn-sm' onclick='removeAssignment(\"{assignment['UID']}\")'>Remove</button></td>
                        </tr>
                        """
                    
                    table_html += "</tbody></table>"
                    
                    table_section = pn.pane.HTML(table_html, sizing_mode='stretch_width')
                
            else:
                table_section = pn.pane.Markdown("No users currently assigned to this training.")
        
            # Add components to assignments tab
            assignments_tab.extend([
                pn.pane.Markdown("## Assign Training to Users"),
                self.user_select,
                pn.Row(pn.layout.HSpacer(), self.assign_btn),
                pn.pane.Markdown("## Current Assignments"),
                table_section
            ])
            
            return assignments_tab
            
        except Exception as e:
            logger.error(f"Error building assignments tab: {e}")
            import traceback
            logger.error(f"Traceback: {traceback.format_exc()}")
            return pn.pane.Markdown(f"**Error building assignments tab:** {str(e)}")
    
    def _build_reports_tab(self) -> pn.viewable.Viewable:
        """Build the training reports tab."""
        try:
            reports_tab = pn.Column(sizing_mode='stretch_width')
            
            # Get training statistics
            assigned_users = self.training_config.get_assigned_users()
            
            if assigned_users:
                total_assigned = len(assigned_users)
                completed = len([u for u in assigned_users if u.get('status') == 'TRAINED'])
                pending = len([u for u in assigned_users if u.get('status') == 'REQUIRED'])
                
                completion_rate = (completed / total_assigned * 100) if total_assigned > 0 else 0
                
                # Statistics summary
                stats_html = f"""
                <div class="row">
                    <div class="col-md-3">
                        <div class="card text-white bg-info">
                            <div class="card-body">
                                <h5 class="card-title">Total Assigned</h5>
                                <h2>{total_assigned}</h2>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-white bg-success">
                            <div class="card-body">
                                <h5 class="card-title">Completed</h5>
                                <h2>{completed}</h2>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-white bg-warning">
                            <div class="card-body">
                                <h5 class="card-title">Pending</h5>
                                <h2>{pending}</h2>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-white bg-primary">
                            <div class="card-body">
                                <h5 class="card-title">Completion Rate</h5>
                                <h2>{completion_rate:.1f}%</h2>
                            </div>
                        </div>
                    </div>
                </div>
                """
                
                reports_tab.extend([
                    pn.pane.Markdown("## Training Statistics"),
                    pn.pane.HTML(stats_html)
                ])
            else:
                reports_tab.append(pn.pane.Markdown("No training assignments to report on."))
            
            return reports_tab
            
        except Exception as e:
            logger.error(f"Error building reports tab: {e}")
            return pn.pane.Markdown(f"**Error building reports tab:** {str(e)}")
    
    def _get_user_options(self) -> List[str]:
        """Get list of users for assignment selection."""
        try:
            # Get all users from database
            from CDocs.db import db_operations
            
            result = db_operations.run_query(
                "MATCH (u:User) WHERE u.active = true RETURN u.UID as uid, u.name as name, u.Mail as email ORDER BY u.name"
            )
            
            options = {}  # Use dict to map display names to UIDs
            for row in result:
                name = row.get('name', 'Unknown')
                email = row.get('email', '')
                uid = row.get('uid', '')
                
                # Create display name
                if name and name != 'Unknown':
                    display_name = f"{name} ({email})" if email else name
                else:
                    display_name = email if email else 'Unknown User'
                
                options[display_name] = uid
            
            logger.info(f"Found {len(options)} users for assignment")
            return options
        
        except Exception as e:
            logger.error(f"Error getting user options: {e}")
            return {}
    
    def _save_training_config(self, event):
        """Save training configuration."""
        try:
            # Disable the save button to prevent double-clicks
            self.save_config_btn.disabled = True
            
            if self.enable_checkbox.value:
                # Enable training
                result = training_controller.enable_document_training(
                    user=self.current_user,
                    document_uid=self.document_uid,
                    validity_days=self.validity_input.value,
                    quiz_required=self.quiz_checkbox.value,
                    instructions=self.instructions_input.value
                )
                
                if result.get('success'):
                    self.show_success("Training configuration saved successfully")
                    # Simple reload without tornado dependency
                    self._reload_interface_safe()
                else:
                    error_msg = result.get('message', 'Unknown error')
                    self.show_error(f"Error saving configuration: {error_msg}")
                    self.save_config_btn.disabled = False
            else:
                # Disable training
                success = self.training_config.disable_training()
                if success:
                    self.show_success("Training disabled successfully")
                    self._reload_interface_safe()
                else:
                    self.show_error("Error disabling training")
                    self.save_config_btn.disabled = False
                    
        except Exception as e:
            logger.error(f"Error saving training config: {e}")
            self.show_error(f"Error saving configuration: {str(e)}")
            self.save_config_btn.disabled = False

    def _reload_interface_safe(self):
        """Safely reload the interface without state conflicts."""
        try:
            # Re-enable save button
            self.save_config_btn.disabled = False
            
            # Instead of full reload, just update the current tab
            current_tab = getattr(self.tabs, 'active', 0)
            
            # Update training config data
            self.training_config = DocumentTraining(document_uid=self.document_uid)
            if not hasattr(self.training_config, '_data') or self.training_config._data is None:
                self.training_config._data = {}
            
            # Update widget values based on new data
            training_data = self.training_config._data
            self.enable_checkbox.value = bool(training_data.get('training_required', False))
            self.validity_input.value = int(training_data.get('validity_days', 365))
            self.quiz_checkbox.value = bool(training_data.get('quiz_required', False))
            self.instructions_input.value = str(training_data.get('instructions', ''))
            
        except Exception as e:
            logger.error(f"Error in safe reload: {e}")
            # Fallback: just re-enable the button
            self.save_config_btn.disabled = False
    
    def _assign_training(self, event):
        """Assign training to selected users."""
        try:
            # Disable assign button
            self.assign_btn.disabled = True
            
            selected_uids = self.user_select.value
            logger.info(f"Raw selected values from MultiSelect: {selected_uids}")
            logger.info(f"Type of selected_uids: {type(selected_uids)}")
            
            # Ensure we have a list of UIDs
            if isinstance(selected_uids, (list, tuple)):
                # If it's a list/tuple, each item should be a UID
                user_uids = list(selected_uids)
            else:
                # If it's a single value, make it a list
                user_uids = [selected_uids] if selected_uids else []
        
            logger.info(f"Processed user UIDs for assignment: {user_uids}")
            logger.info(f"Document UID: {self.document_uid}")
            logger.info(f"Current user: {self.current_user.name if self.current_user else 'None'}")
            
            if not user_uids:
                self.show_error("Please select users to assign")
                self.assign_btn.disabled = False
                return
            
            # Validate that all UIDs look like proper UUIDs
            import re
            uuid_pattern = re.compile(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', re.IGNORECASE)
            valid_uids = []
            invalid_uids = []
            
            for uid in user_uids:
                uid_str = str(uid).strip()
                if uuid_pattern.match(uid_str):
                    valid_uids.append(uid_str)
                else:
                    invalid_uids.append(uid_str)
            
            if invalid_uids:
                logger.error(f"Invalid UIDs detected: {invalid_uids}")
                self.show_error(f"Invalid user IDs detected: {', '.join(invalid_uids[:3])}...")
                self.assign_btn.disabled = False
                return
            
            logger.info(f"Validated UIDs for assignment: {valid_uids}")
            
            # Check if training is enabled first
            if not self.training_config._data.get('training_required', False):
                self.show_error("Training must be enabled before assigning users")
                self.assign_btn.disabled = False
                return
            
            logger.info(f"Calling training_controller.assign_user_training...")
            result = training_controller.assign_user_training(
                user=self.current_user,
                document_uid=self.document_uid,
                user_uids=valid_uids
            )
            
            logger.info(f"Training assignment result: {result}")
            
            if result.get('success'):
                assigned_count = result.get('assigned_count', 0)
                failed_assignments = result.get('failed_assignments', [])
                
                success_msg = f"Training assigned to {assigned_count} user(s)"
                if failed_assignments:
                    success_msg += f". Failed assignments: {'; '.join(failed_assignments)}"
                
                self.show_success(success_msg)
                
                # Clear selection
                self.user_select.value = []
                
                # Update assignments tab
                self._update_assignments_tab()
            else:
                error_msg = result.get('message', 'Unknown error')
                failed_assignments = result.get('failed_assignments', [])
                full_error = error_msg
                if failed_assignments:
                    full_error += f". Details: {'; '.join(failed_assignments)}"
                
                logger.error(f"Training assignment failed: {full_error}")
                self.show_error(f"Error assigning training: {full_error}")
                
        except Exception as e:
            logger.error(f"Error in _assign_training: {e}")
            import traceback
            logger.error(f"Traceback: {traceback.format_exc()}")
            self.show_error(f"Error assigning training: {str(e)}")
        finally:
            # Always re-enable the button
            self.assign_btn.disabled = False

    def _update_assignments_tab(self):
        """Update the assignments tab without full interface reload."""
        try:
            logger.info("Updating assignments tab")
            
            # Force reload of training config to get fresh data
            self.training_config = DocumentTraining(document_uid=self.document_uid)
            
            # Get fresh assignment data
            assigned_users = self.training_config.get_assigned_users()
            logger.info(f"Found {len(assigned_users) if assigned_users else 0} assigned users")
            
            if assigned_users and len(assigned_users) > 0:
                assignments_data = []
                for user in assigned_users:
                    logger.info(f"Processing assigned user: {user}")
                    assignments_data.append({
                        'User': user.get('user_name', user.get('name', 'Unknown')),
                        'Email': user.get('user_email', user.get('email', 'Unknown')),
                        'Status': user.get('status', 'Unknown'),
                        'Assigned Date': self._format_date(user.get('assigned_date')),
                        'Trained Date': self._format_date(user.get('trained_on')),
                        'Expires Date': self._format_date(user.get('expires_on', user.get('expires_date'))),
                        'UID': user.get('user_uid', user.get('uid', ''))
                    })
                
                # Update the table data if it exists and has proper structure
                if hasattr(self, 'assignments_table'):
                    try:
                        import pandas as pd
                        df = pd.DataFrame(assignments_data)
                        
                        # Update table value with DataFrame
                        if hasattr(self.assignments_table, 'value'):
                            self.assignments_table.value = df
                            logger.info(f"Updated table with {len(assignments_data)} assignments")
                        else:
                            logger.warning("assignments_table doesn't have value attribute")
                            
                    except ImportError:
                        # If pandas not available, rebuild the assignments tab
                        logger.info("Pandas not available, rebuilding assignments tab")
                        # Find assignments tab and rebuild it
                        if hasattr(self, 'tabs') and self.tabs:
                            try:
                                # Replace the assignments tab
                                new_assignments_tab = self._build_assignments_tab()
                                self.tabs[1] = ("User Assignments", new_assignments_tab)
                            except Exception as rebuild_error:
                                logger.error(f"Error rebuilding assignments tab: {rebuild_error}")
                                
                    except Exception as update_error:
                        logger.error(f"Error updating table data: {update_error}")
                        
                else:
                    logger.warning("assignments_table not found")
            else:
                logger.info("No assigned users found")
                if hasattr(self, 'assignments_table') and hasattr(self.assignments_table, 'value'):
                    try:
                        import pandas as pd
                        empty_df = pd.DataFrame(columns=['User', 'Email', 'Status', 'Assigned Date', 'Trained Date', 'Expires Date', 'UID'])
                        self.assignments_table.value = empty_df
                    except ImportError:
                        # If pandas not available, show empty message
                        pass
        
        except Exception as e:
            logger.error(f"Error updating assignments tab: {e}")
            import traceback
            logger.error(f"Traceback: {traceback.format_exc()}")
    
    def _remove_training_assignment(self, user_uid: str):
        """Remove training assignment for a user."""
        try:
            success = self.training_config.remove_user_training(user_uid)
            if success:
                self.show_success("Training assignment removed")
                # Update assignments without full reload
                self._update_assignments_tab()
            else:
                self.show_error("Error removing training assignment")
            
        except Exception as e:
            logger.error(f"Error removing training assignment: {e}")
            self.show_error(f"Error removing assignment: {str(e)}")
    
    def _format_date(self, date_value) -> str:
        """Format a date value for display."""
        if not date_value:
            return "N/A"
        
        try:
            if isinstance(date_value, str):
                # Parse string date
                from datetime import datetime
                date_obj = datetime.fromisoformat(date_value.replace('Z', '+00:00'))
            elif hasattr(date_value, 'strftime'):
                date_obj = date_value
            else:
                return str(date_value)
            
            return date_obj.strftime('%Y-%m-%d %H:%M')
        except Exception:
            return str(date_value)
    
    def get_view(self) -> pn.viewable.Viewable:
        """Get the main view component."""
        return self.main_content

    def show_success(self, message: str):
        """Show success message safely."""
        try:
            if hasattr(self, 'status_area') and self.status_area:
                self.status_area.object = f"<div class='alert alert-success'>{message}</div>"
            else:
                logger.info(f"Success: {message}")
        except Exception as e:
            logger.error(f"Error showing success message: {e}")
            logger.info(f"Success: {message}")

    def show_error(self, message: str):
        """Show error message safely."""
        try:
            if hasattr(self, 'status_area') and self.status_area:
                self.status_area.object = f"<div class='alert alert-danger'>{message}</div>"
            else:
                logger.error(f"Error: {message}")
        except Exception as e:
            logger.error(f"Error showing error message: {e}")
            logger.error(f"Error: {message}")

Parameters

Name Type Default Kind
bases BaseUIComponent -

Parameter Details

bases: Parameter of type BaseUIComponent

Return Value

Returns unspecified type

Class Interface

Methods

__init__(self, parent_app, document_uid)

Purpose: Internal method: init

Parameters:

  • parent_app: Parameter
  • document_uid: Type: str

Returns: None

_build_ui(self)

Purpose: Build the training management UI.

Returns: None

set_user_and_document(self, user, document_uid)

Purpose: Set the current user and document.

Parameters:

  • user: Type: DocUser
  • document_uid: Type: str

Returns: None

_load_training_management(self)

Purpose: Load training management interface.

Returns: None

_build_management_interface(self)

Purpose: Build the main training management interface.

Returns: None

_build_config_tab(self) -> pn.viewable.Viewable

Purpose: Build the training configuration tab.

Returns: Returns pn.viewable.Viewable

_build_assignments_tab(self) -> pn.viewable.Viewable

Purpose: Build the user assignments tab.

Returns: Returns pn.viewable.Viewable

_build_reports_tab(self) -> pn.viewable.Viewable

Purpose: Build the training reports tab.

Returns: Returns pn.viewable.Viewable

_get_user_options(self) -> List[str]

Purpose: Get list of users for assignment selection.

Returns: Returns List[str]

_save_training_config(self, event)

Purpose: Save training configuration.

Parameters:

  • event: Parameter

Returns: None

_reload_interface_safe(self)

Purpose: Safely reload the interface without state conflicts.

Returns: None

_assign_training(self, event)

Purpose: Assign training to selected users.

Parameters:

  • event: Parameter

Returns: None

_update_assignments_tab(self)

Purpose: Update the assignments tab without full interface reload.

Returns: None

_remove_training_assignment(self, user_uid)

Purpose: Remove training assignment for a user.

Parameters:

  • user_uid: Type: str

Returns: None

_format_date(self, date_value) -> str

Purpose: Format a date value for display.

Parameters:

  • date_value: Parameter

Returns: Returns str

get_view(self) -> pn.viewable.Viewable

Purpose: Get the main view component.

Returns: Returns pn.viewable.Viewable

show_success(self, message)

Purpose: Show success message safely.

Parameters:

  • message: Type: str

Returns: None

show_error(self, message)

Purpose: Show error message safely.

Parameters:

  • message: Type: str

Returns: None

Required Imports

import logging
from typing import Dict
from typing import List
from typing import Any
from typing import Optional

Usage Example

# Example usage:
# result = TrainingManagement(bases)

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class TrainingCompletion 72.7% similar

    UI component for completing training requirements.

    From: /tf/active/vicechatdev/CDocs/ui/training_completion.py
  • class DocumentTraining 70.9% similar

    A model class that manages training requirements and assignments for controlled documents, including enabling/disabling training, assigning training to users, and tracking training status.

    From: /tf/active/vicechatdev/CDocs/models/training.py
  • function create_training_management 70.4% similar

    Factory function that instantiates and returns a TrainingManagement object with optional parent application and document UID parameters.

    From: /tf/active/vicechatdev/CDocs/ui/training_management.py
  • class UserTraining 65.9% similar

    A model class representing a user's training status for a specific controlled document, managing training assignments, completion tracking, and expiration dates.

    From: /tf/active/vicechatdev/CDocs/models/training.py
  • class TrainingDashboard 61.7% similar

    Training dashboard for users to view and complete training requirements.

    From: /tf/active/vicechatdev/CDocs/ui/training_dashboard.py
← Back to Browse