class ControlledDocApp
A standalone Panel web application class that provides a complete controlled document management system with user authentication, navigation, and document lifecycle management features.
/tf/active/vicechatdev/panel_app.py
45 - 390
complex
Purpose
ControlledDocApp serves as the main application container for a controlled document management system. It manages user authentication, navigation between different views (dashboard, document creation/editing, reviews, administration), and integrates with the underlying ControlledDocSystem. The class handles the complete application lifecycle from login to logout, manages UI state, and provides role-based access control for different features. It uses Panel's MaterialTemplate for a modern web interface with sidebar navigation.
Source Code
class ControlledDocApp:
"""
Standalone Panel application for the Controlled Document System.
This class provides a complete Panel application with navigation,
user authentication, and all document management features.
"""
def __init__(self):
"""Initialize the controlled document application."""
# State variables
self.current_view = None
self.current_document_id = None
self.doc_system = None
self.user = None
self.is_authenticated = False
# Build login UI first
self._build_login_ui()
# Create placeholder for main UI (will be built after login)
self.main_ui = pn.pane.Markdown("Please log in to access the system")
# Create application layout with template
self.template = pn.template.MaterialTemplate(
title="Controlled Document Management System",
header_background="#0072B5",
sidebar_width=300
)
# Add login panel to template
self.main_window = pn.Column()
self.sidebar_window = pn.Column()
self.template.main.append(self.main_window)
self.template.sidebar.append(self.sidebar_window)
self.main_window.append(self.login_panel)
# Setup event handlers for navigation
self._setup_navigation_handlers()
def _build_login_ui(self):
"""Build the login user interface."""
# Create login form
self.username_input = pn.widgets.TextInput(
name="Username",
placeholder="Enter your username"
)
self.password_input = pn.widgets.PasswordInput(
name="Password",
placeholder="Enter your password"
)
login_button = pn.widgets.Button(
name="Login",
button_type="primary"
)
login_button.on_click(self._handle_login)
self.login_message = pn.pane.Alert(
"Please log in with your credentials",
alert_type="info"
)
# Assemble login panel
self.login_panel = pn.Card(
pn.Column(
pn.pane.Markdown("## Login"),
self.login_message,
self.username_input,
self.password_input,
login_button,
width=400
),
title="Controlled Document System",
width=500
)
def _handle_login(self, event):
"""Handle login button click."""
username = self.username_input.value
password = self.password_input.value
if not username or not password:
self.login_message.object = "Please enter both username and password"
self.login_message.alert_type = "danger"
return
# For demo purposes, accept any login
# In a real app, this would verify credentials against a database
self._login_success({
"id": username,
"name": f"Demo User ({username})",
"email": f"{username}@example.com",
"department": "QA",
"roles": ["admin", "Quality Manager"] # Use Role enum names for full access
})
def _login_success(self, user_data):
"""Handle successful login."""
# Store user data
self.user = user_data
self.is_authenticated = True
try:
# Ensure document types are created in the database before initializing the document system
self._ensure_document_types_exist()
# Initialize the document system with the authenticated user
# Force schema initialization to ensure database is properly set up
self.doc_system = ControlledDocSystem(
user=self.user,
initialize_schema=True,
force_schema_init=True
)
# Build the main UI
self._build_main_ui()
# Switch to main UI
self.main_window.clear()
self.main_window.append(self.main_ui)
# Update sidebar
self._update_sidebar()
print(f"User {self.user['name']} logged in successfully")
except Exception as e:
# If document system initialization fails, show error message
self.login_message.object = f"Error initializing document system: {str(e)}"
self.login_message.alert_type = "danger"
logger.error(f"Login error: {str(e)}")
# Reset user state
self.user = None
self.is_authenticated = False
def _ensure_document_types_exist(self):
"""Ensure document types from configuration exist in the database."""
try:
# Call the new function to ensure document types exist
ensure_document_types(
DOCUMENT_DB["uri"],
DOCUMENT_DB["user"],
DOCUMENT_DB["password"]
)
logger.info("Document types updated successfully")
except Exception as e:
logger.error(f"Error ensuring document types: {str(e)}")
raise
def _build_main_ui(self):
"""Build the main UI after successful login."""
# Get the UI from the document system
#print(f"getting main UI")
self.main_ui = self.doc_system.get_ui()
# Set current view to the document dashboard
self.current_view = "dashboard"
def _update_sidebar(self):
"""Update the sidebar with user information and navigation."""
# Clear existing sidebar
self.sidebar_window.clear()
# Add user information
self.sidebar_window.append(pn.pane.Markdown(f"## Welcome, {self.user['name']}"))
self.sidebar_window.append(pn.pane.Markdown(f"Department: {self.user['department']}"))
self.sidebar_window.append(pn.layout.Divider())
# Add navigation
self.sidebar_window.append(pn.pane.Markdown("### Navigation"))
# Dashboard button
dashboard_btn = pn.widgets.Button(
name="Document Dashboard",
button_type="primary" if self.current_view == "dashboard" else "default",
width=250
)
dashboard_btn.on_click(self._navigate_to_dashboard)
self.sidebar_window.append(dashboard_btn)
# Import permission classes
from controlled_doc_system.utils.permissions import Permission, check_permission, check_any_permission
# New document button
new_doc_btn = pn.widgets.Button(
name="Create New Document",
button_type="success",
width=250,
disabled=not check_permission(self.user.get('id'), Permission.CREATE_DOCUMENT)
)
new_doc_btn.on_click(self._navigate_to_new_document)
self.sidebar_window.append(new_doc_btn)
# Reviews button
reviews_btn = pn.widgets.Button(
name="My Reviews",
button_type="primary" if self.current_view == "reviews" else "default",
width=250
)
reviews_btn.on_click(self._navigate_to_reviews)
self.sidebar_window.append(reviews_btn)
# Admin button (only for admin users)
# Check for any admin permissions
admin_permissions = [
Permission.MANAGE_USERS,
Permission.CONFIGURE_SYSTEM,
Permission.CREATE_TEMPLATE,
Permission.EDIT_TEMPLATE,
Permission.DELETE_TEMPLATE
]
if check_any_permission(self.user.get('id'), admin_permissions):
admin_btn = pn.widgets.Button(
name="Administration",
button_type="primary" if self.current_view == "admin" else "default",
width=250
)
admin_btn.on_click(self._navigate_to_admin)
self.sidebar_window.append(admin_btn)
self.sidebar_window.append(pn.layout.Divider())
# Logout button
logout_btn = pn.widgets.Button(
name="Logout",
button_type="danger",
width=250
)
logout_btn.on_click(self._handle_logout)
self.sidebar_window.append(logout_btn)
def _setup_navigation_handlers(self):
"""Set up handlers for navigation buttons."""
# These handlers will be assigned to buttons in the sidebar
pass
def _navigate_to_dashboard(self, event=None):
"""Navigate to the document dashboard."""
if self.current_view == "dashboard":
return
self.current_view = "dashboard"
# Update UI
self.main_window.clear()
self.main_window.append(self.doc_system.get_ui())
# Update sidebar
self._update_sidebar()
def _navigate_to_new_document(self, event=None):
"""Navigate to the new document form."""
self.current_view = "document_form"
self.current_document_id = None
# Create document form
doc_form = DocumentForm(self.doc_system)
# Update UI
self.main_window.clear()
self.main_window.append(doc_form.get_panel())
# Update sidebar
self._update_sidebar()
def _navigate_to_edit_document(self, document_id):
"""
Navigate to the document edit form.
Args:
document_id: ID of the document to edit
"""
self.current_view = "document_form"
self.current_document_id = document_id
# Create document form for existing document
doc_form = DocumentForm(self.doc_system, document_id=document_id)
# Update UI
self.main_window.clear()
self.main_window.append(doc_form.get_panel())
# Update sidebar
self._update_sidebar()
def _navigate_to_reviews(self, event=None):
"""Navigate to the reviews dashboard."""
if self.current_view == "reviews":
return
self.current_view = "reviews"
# Update UI - show the reviews tab
self.main_window.clear()
self.doc_system.main_tabs.active = 1 # Select the Reviews tab
self.main_window.append(self.doc_system.get_ui())
# Update sidebar
self._update_sidebar()
def _navigate_to_admin(self, event=None):
"""Navigate to the admin dashboard."""
if self.current_view == "admin":
return
self.current_view = "admin"
# Update UI - show the admin tab
self.main_window.clear()
self.doc_system.main_tabs.active = 2 # Select the Admin tab
self.main_window.append(self.doc_system.get_ui())
# Update sidebar
self._update_sidebar()
def _handle_logout(self, event):
"""Handle logout button click."""
# Clear user data
self.user = None
self.is_authenticated = False
# Clean up document system
if self.doc_system:
self.doc_system.close()
self.doc_system = None
# Reset UI to login screen
self.main_window.clear()
self.main_window.append(self.login_panel)
# Clear sidebar
self.sidebar_window.clear()
# Reset login form
self.username_input.value = ""
self.password_input.value = ""
self.login_message.object = "You have been logged out successfully"
self.login_message.alert_type = "success"
logger.info("User logged out")
def get_app(self):
"""Get the Panel application for serving."""
return self.template
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
- | - |
Parameter Details
__init__: No parameters required. The constructor initializes all state variables, builds the login UI, creates the Panel template with sidebar, and sets up navigation handlers. The application starts in an unauthenticated state showing the login panel.
Return Value
The class constructor returns a ControlledDocApp instance. The get_app() method returns a Panel MaterialTemplate object that can be served as a web application. Navigation methods return None but update the UI state as side effects. The _handle_login method processes authentication and initializes the document system on success.
Class Interface
Methods
__init__(self)
Purpose: Initialize the controlled document application with login UI, template, and state variables
Returns: None - initializes the ControlledDocApp instance
_build_login_ui(self)
Purpose: Build the login user interface with username, password inputs and login button
Returns: None - creates and stores login_panel as instance attribute
_handle_login(self, event)
Purpose: Handle login button click event, validate credentials, and initialize the document system
Parameters:
event: Panel button click event object
Returns: None - calls _login_success on valid credentials or updates login_message on error
_login_success(self, user_data: dict)
Purpose: Handle successful login by storing user data, initializing document system, and building main UI
Parameters:
user_data: Dictionary containing user information with keys: id, name, email, department, roles
Returns: None - updates application state and UI on success, shows error message on failure
_ensure_document_types_exist(self)
Purpose: Ensure document types from configuration exist in the database before system initialization
Returns: None - raises exception if document types cannot be created
_build_main_ui(self)
Purpose: Build the main UI after successful login by getting UI from document system
Returns: None - updates main_ui attribute and sets current_view to 'dashboard'
_update_sidebar(self)
Purpose: Update the sidebar with user information, navigation buttons, and logout option based on permissions
Returns: None - rebuilds sidebar_window with current user context and navigation state
_setup_navigation_handlers(self)
Purpose: Set up handlers for navigation buttons (currently a placeholder method)
Returns: None - navigation handlers are assigned directly to buttons in _update_sidebar
_navigate_to_dashboard(self, event=None)
Purpose: Navigate to the document dashboard view
Parameters:
event: Optional Panel button click event object
Returns: None - updates main_window with dashboard UI and refreshes sidebar
_navigate_to_new_document(self, event=None)
Purpose: Navigate to the new document creation form
Parameters:
event: Optional Panel button click event object
Returns: None - creates DocumentForm and updates main_window with form UI
_navigate_to_edit_document(self, document_id: str)
Purpose: Navigate to the document edit form for an existing document
Parameters:
document_id: ID of the document to edit
Returns: None - creates DocumentForm with document_id and updates main_window
_navigate_to_reviews(self, event=None)
Purpose: Navigate to the reviews dashboard showing documents pending review
Parameters:
event: Optional Panel button click event object
Returns: None - switches to reviews tab and updates UI
_navigate_to_admin(self, event=None)
Purpose: Navigate to the administration dashboard for system configuration
Parameters:
event: Optional Panel button click event object
Returns: None - switches to admin tab and updates UI
_handle_logout(self, event)
Purpose: Handle logout button click by cleaning up resources and returning to login screen
Parameters:
event: Panel button click event object
Returns: None - clears user data, closes document system, and resets UI to login panel
get_app(self) -> pn.template.MaterialTemplate
Purpose: Get the Panel application template for serving
Returns: Panel MaterialTemplate object that can be served as a web application
Attributes
| Name | Type | Description | Scope |
|---|---|---|---|
current_view |
str | None | Tracks the currently active view ('dashboard', 'document_form', 'reviews', 'admin', or None) | instance |
current_document_id |
str | None | ID of the document currently being edited, or None if creating new document | instance |
doc_system |
ControlledDocSystem | None | Instance of the document management system, initialized after successful login | instance |
user |
dict | None | Dictionary containing authenticated user data (id, name, email, department, roles) | instance |
is_authenticated |
bool | Flag indicating whether a user is currently authenticated | instance |
username_input |
pn.widgets.TextInput | Panel text input widget for username entry | instance |
password_input |
pn.widgets.PasswordInput | Panel password input widget for password entry | instance |
login_message |
pn.pane.Alert | Panel alert widget for displaying login status messages | instance |
login_panel |
pn.Card | Panel card containing the complete login form UI | instance |
main_ui |
pn.pane.Markdown | Panel component | The main UI component displayed after login, initially a placeholder | instance |
template |
pn.template.MaterialTemplate | Panel MaterialTemplate providing the overall application layout with header and sidebar | instance |
main_window |
pn.Column | Panel column container for the main content area | instance |
sidebar_window |
pn.Column | Panel column container for the sidebar navigation area | instance |
Dependencies
panelparampandasloggingpathlibargparsewarningscontrolled_doc_system.panel_integrationcontrolled_doc_system.panel_ui.document_formcontrolled_doc_system.utils.permissionscontrolled_doc_system.core.db_schema_initcontrolled_doc_system.cdoc_configCDocs.utils.sharing_validator
Required Imports
import panel as pn
import param
import pandas as pd
import os
import sys
import logging
from pathlib import Path
import argparse
import warnings
from controlled_doc_system.panel_integration import ControlledDocSystem
from controlled_doc_system.panel_ui.document_form import DocumentForm
from controlled_doc_system.utils.permissions import Permission, Role, check_permission, check_any_permission
from controlled_doc_system.core.db_schema_init import initialize_db_schema, ensure_document_types
from controlled_doc_system.cdoc_config import DOCUMENT_DB
from CDocs.utils.sharing_validator import check_document_permissions_on_startup
Conditional/Optional Imports
These imports are only needed under specific conditions:
from controlled_doc_system.utils.permissions import Permission, check_permission, check_any_permission
Condition: imported within _update_sidebar method for permission checking
Required (conditional)Usage Example
# Create and serve the application
import panel as pn
from controlled_doc_app import ControlledDocApp
# Initialize Panel extension
pn.extension('tabulator', notifications=True)
# Create the application
app = ControlledDocApp()
# Get the Panel template for serving
template = app.get_app()
# Serve the application
# Option 1: In a script
template.servable()
# Option 2: Programmatically
pn.serve(template, port=5006, show=True)
# The application will show a login screen
# After login with any username/password (demo mode),
# users can navigate between:
# - Document Dashboard (view and manage documents)
# - Create New Document (if user has CREATE_DOCUMENT permission)
# - My Reviews (review assigned documents)
# - Administration (if user has admin permissions)
# - Logout (return to login screen)
Best Practices
- Always call get_app() to retrieve the Panel template for serving, not the class instance directly
- The application manages its own state through instance variables - avoid external state manipulation
- Login is currently in demo mode (accepts any credentials) - implement proper authentication for production
- The document system is initialized only after successful login to ensure proper user context
- Always handle logout properly to clean up resources (doc_system.close() is called automatically)
- Navigation methods update both main_window and sidebar_window - don't modify these directly
- The current_view attribute tracks the active view - use navigation methods to change views
- Permission checks are performed when building the sidebar - ensure user object has 'id' and 'roles' fields
- Database connection errors during login are caught and displayed to the user
- The template uses MaterialTemplate with a fixed sidebar width of 300px
- Document types must exist in the database before the system can be used - _ensure_document_types_exist() handles this
- The application maintains a single ControlledDocSystem instance per session - it's recreated on each login
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class CDocsApp 85.2% similar
-
function main_v10 60.2% similar
-
function controlled_docs_navigation 60.1% similar
-
function create_document 59.1% similar
-
class User 56.9% similar