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 including login/logout, UI state management, and permission-based access control. 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 instance with login panel displayed
_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 initiate login process
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 initializing document system and building main UI
Parameters:
user_data: Dictionary containing user information with keys: id, name, email, department, roles
Returns: None - updates authentication state and switches to main UI, or shows error on failure
_ensure_document_types_exist(self)
Purpose: Ensure document types from configuration exist in the database before system initialization
Returns: None - calls ensure_document_types function or raises exception on error
_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 permission-based access controls
Returns: None - clears and repopulates sidebar_window with navigation elements
_setup_navigation_handlers(self)
Purpose: Set up handlers for navigation buttons (currently a placeholder method)
Returns: None - reserved for future navigation handler setup
_navigate_to_dashboard(self, event=None)
Purpose: Navigate to the document dashboard view
Parameters:
event: Optional Panel button click event object
Returns: None - updates current_view, main_window, and 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 UI to show creation form
_navigate_to_edit_document(self, document_id: str)
Purpose: Navigate to the document edit form for a specific document
Parameters:
document_id: ID of the document to edit
Returns: None - creates DocumentForm with document_id and updates UI to show edit form
_navigate_to_reviews(self, event=None)
Purpose: Navigate to the reviews dashboard showing pending document reviews
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 management
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 clearing user data and returning to login screen
Parameters:
event: Panel button click event object
Returns: None - resets authentication state, closes doc_system, and shows login panel
get_app(self) -> pn.template.MaterialTemplate
Purpose: Get the Panel application template for serving as a web application
Returns: Panel MaterialTemplate object that can be served with pn.serve() or made servable
Attributes
| Name | Type | Description | Scope |
|---|---|---|---|
current_view |
str or None | Tracks the currently active view ('dashboard', 'document_form', 'reviews', 'admin', or None) | instance |
current_document_id |
str or None | Stores the ID of the document currently being edited, None for new documents | instance |
doc_system |
ControlledDocSystem or None | The main document management system instance, initialized after successful login | instance |
user |
dict or 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 in login form | instance |
password_input |
pn.widgets.PasswordInput | Panel password input widget for password entry in login form | instance |
login_message |
pn.pane.Alert | Panel alert pane for displaying login status messages and errors | instance |
login_panel |
pn.Card | Panel card containing the complete login form UI | instance |
main_ui |
Panel component | The main UI component displayed after login, initially a placeholder markdown pane | instance |
template |
pn.template.MaterialTemplate | The Panel MaterialTemplate that serves as the application container with header and sidebar | instance |
main_window |
pn.Column | Panel column container for the main content area, appended to template.main | instance |
sidebar_window |
pn.Column | Panel column container for the sidebar navigation, appended to template.sidebar | 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
Usage Example
import panel as pn
from controlled_doc_system.cdoc_config import DOCUMENT_DB
# Initialize Panel
pn.extension()
# Create the application
app = ControlledDocApp()
# Get the Panel template for serving
template = app.get_app()
# Serve the application
# pn.serve(template, port=5006, show=True)
# Or in a Jupyter notebook:
# template.servable()
# The application will show a login screen
# After login with any username/password (demo mode),
# users can navigate through:
# - Document Dashboard (view and manage documents)
# - Create New Document (if user has CREATE_DOCUMENT permission)
# - My Reviews (review pending documents)
# - Administration (if user has admin permissions)
# - Logout (clear session and return to login)
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 doc_system is initialized only after successful login and should be accessed only when is_authenticated is True
- Always call the close() method on doc_system during logout to properly clean up resources
- Navigation methods update both main_window and sidebar_window - don't modify these directly
- The current_view attribute tracks the active view and prevents redundant navigation
- Document types must exist in the database before document system initialization
- Permission checks are performed in _update_sidebar to show/hide navigation buttons based on user roles
- Error handling during login will reset authentication state and display error messages
- The template uses MaterialTemplate with a fixed sidebar width of 300px and custom header color
- Method call order: __init__ -> user logs in -> _handle_login -> _login_success -> _ensure_document_types_exist -> _build_main_ui -> navigation methods
- State management: current_view, current_document_id, doc_system, user, and is_authenticated track application state
- Side effects: All navigation methods clear and repopulate main_window and update sidebar_window
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class CDocsApp 89.1% similar
-
class ControlledDocumentApp 80.3% similar
-
class ControlledDocumentFlaskApp 69.0% similar
-
class DocumentAccessControls 66.3% similar
-
class DocumentDashboard 64.8% similar