#!/usr/bin/env python3 """ Professional PDF Generator for TimeTrack Pro Technical Reference Creates a comprehensive, beautifully formatted technical document """ from reportlab.lib import colors from reportlab.lib.pagesizes import letter, A4 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.units import inch from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY from reportlab.platypus import ( SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle, Image, KeepTogether, ListFlowable, ListItem, HRFlowable ) from reportlab.pdfgen import canvas from datetime import datetime # Professional color scheme PRIMARY_COLOR = colors.HexColor('#2C3E50') # Dark blue-grey ACCENT_COLOR = colors.HexColor('#2980B9') # Professional blue LIGHT_ACCENT = colors.HexColor('#3498DB') # Light blue SUCCESS_COLOR = colors.HexColor('#27AE60') # Green GREY_COLOR = colors.HexColor('#7F8C8D') # Grey LIGHT_GREY = colors.HexColor('#ECF0F1') # Light grey OUTPUT_FILE = r"C:\Users\alexi\Documents\projects\timetrack-pro\TimeTrack_Pro_Professional_Documentation.pdf" class NumberedCanvas(canvas.Canvas): """Custom canvas for page numbers and headers/footers""" def __init__(self, *args, **kwargs): canvas.Canvas.__init__(self, *args, **kwargs) self._saved_page_states = [] def showPage(self): self._saved_page_states.append(dict(self.__dict__)) self._startPage() def save(self): num_pages = len(self._saved_page_states) for state in self._saved_page_states: self.__dict__.update(state) self.draw_page_decorations(num_pages) canvas.Canvas.showPage(self) canvas.Canvas.save(self) def draw_page_decorations(self, page_count): self.saveState() self.setFont('Helvetica', 9) # Page number in footer page_num = f"Page {self._pageNumber} of {page_count}" self.setFillColor(GREY_COLOR) self.drawCentredString(letter[0] / 2, 0.5 * inch, page_num) # Document title in header (skip first page) if self._pageNumber > 1: self.setFillColor(GREY_COLOR) self.drawString(0.75 * inch, letter[1] - 0.5 * inch, "TimeTrack Pro - Technical Reference") self.drawRightString(letter[0] - 0.75 * inch, letter[1] - 0.5 * inch, "Alexis Trouvé | 2025") self.restoreState() def create_styles(): """Create custom paragraph styles""" styles = getSampleStyleSheet() # Cover title styles.add(ParagraphStyle( name='CoverTitle', parent=styles['Heading1'], fontSize=48, textColor=PRIMARY_COLOR, spaceAfter=20, alignment=TA_CENTER, fontName='Helvetica-Bold' )) # Cover subtitle styles.add(ParagraphStyle( name='CoverSubtitle', parent=styles['Normal'], fontSize=24, textColor=ACCENT_COLOR, spaceAfter=30, alignment=TA_CENTER, fontName='Helvetica' )) # Section header styles.add(ParagraphStyle( name='SectionHeader', parent=styles['Heading1'], fontSize=20, textColor=colors.white, backColor=PRIMARY_COLOR, spaceAfter=16, spaceBefore=12, leftIndent=10, fontName='Helvetica-Bold', leading=28 )) # Subsection header styles.add(ParagraphStyle( name='SubsectionHeader', parent=styles['Heading2'], fontSize=14, textColor=ACCENT_COLOR, spaceAfter=10, spaceBefore=12, fontName='Helvetica-Bold' )) # Body text styles.add(ParagraphStyle( name='BodyText', parent=styles['Normal'], fontSize=11, textColor=PRIMARY_COLOR, alignment=TA_JUSTIFY, spaceAfter=8, leading=14 )) # Bullet points styles.add(ParagraphStyle( name='Bullet', parent=styles['Normal'], fontSize=11, textColor=PRIMARY_COLOR, leftIndent=30, spaceAfter=6, leading=14 )) # Highlight box styles.add(ParagraphStyle( name='HighlightBox', parent=styles['Normal'], fontSize=12, textColor=colors.white, backColor=ACCENT_COLOR, alignment=TA_CENTER, spaceAfter=12, spaceBefore=12, leftIndent=20, rightIndent=20, borderPadding=10, fontName='Helvetica-Bold' )) # Code/Technical styles.add(ParagraphStyle( name='Code', parent=styles['Normal'], fontSize=9, textColor=PRIMARY_COLOR, fontName='Courier', leftIndent=20, backColor=LIGHT_GREY, spaceAfter=8 )) return styles def create_cover_page(story, styles): """Create professional cover page""" # Title story.append(Spacer(1, 1.5 * inch)) story.append(Paragraph("TimeTrack Pro", styles['CoverTitle'])) story.append(Spacer(1, 0.3 * inch)) # Subtitle story.append(Paragraph("Technical & Functional Reference", styles['CoverSubtitle'])) story.append(Spacer(1, 0.2 * inch)) # Horizontal line story.append(HRFlowable(width="70%", thickness=2, color=ACCENT_COLOR, spaceAfter=30, spaceBefore=10, hAlign='CENTER')) # Description desc_style = ParagraphStyle( 'CoverDesc', parent=styles['BodyText'], fontSize=13, alignment=TA_CENTER, textColor=PRIMARY_COLOR, fontName='Helvetica-Oblique' ) story.append(Paragraph("Professional Time Tracking Application", desc_style)) story.append(Paragraph("Built with Microsoft Access & VBA", desc_style)) story.append(Paragraph("Automated Development via MCP VBA Server", desc_style)) story.append(Spacer(1, 0.5 * inch)) # Key highlights box highlights_data = [ ['Project Highlights', ''], ['Total VBA Code', '915 Lines'], ['Modular Components', '7 Modules'], ['Database Tables', '3 Normalized Tables'], ['Public Functions', '45+ Functions'], ['Development Time Saved', '45% via Automation'], ['Code Quality', '25% Comment Ratio'], ] highlights_table = Table(highlights_data, colWidths=[3*inch, 2*inch]) highlights_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, 0), 'CENTER'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 14), ('BOTTOMPADDING', (0, 0), (-1, 0), 12), ('TOPPADDING', (0, 0), (-1, 0), 12), ('BACKGROUND', (0, 1), (0, -1), LIGHT_GREY), ('BACKGROUND', (1, 1), (1, -1), colors.white), ('TEXTCOLOR', (0, 1), (-1, -1), PRIMARY_COLOR), ('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'), ('FONTNAME', (1, 1), (1, -1), 'Helvetica'), ('FONTSIZE', (0, 1), (-1, -1), 11), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('ALIGN', (1, 1), (1, -1), 'RIGHT'), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 10), ('RIGHTPADDING', (0, 0), (-1, -1), 10), ('TOPPADDING', (0, 1), (-1, -1), 8), ('BOTTOMPADDING', (0, 1), (-1, -1), 8), ])) story.append(highlights_table) story.append(Spacer(1, 1 * inch)) # Author info author_style = ParagraphStyle( 'Author', parent=styles['BodyText'], fontSize=13, alignment=TA_CENTER, textColor=PRIMARY_COLOR, fontName='Helvetica-Bold' ) contact_style = ParagraphStyle( 'Contact', parent=styles['BodyText'], fontSize=11, alignment=TA_CENTER, textColor=ACCENT_COLOR ) story.append(Paragraph("Alexis Trouvé", author_style)) story.append(Spacer(1, 0.1 * inch)) story.append(Paragraph("alexistrouve.pro@gmail.com", contact_style)) story.append(Spacer(1, 0.3 * inch)) version_style = ParagraphStyle( 'Version', parent=styles['BodyText'], fontSize=10, alignment=TA_CENTER, textColor=GREY_COLOR ) story.append(Paragraph("Version 1.0 | January 2025", version_style)) story.append(PageBreak()) def create_section_1_executive_summary(story, styles): """Section 1: Executive Summary""" story.append(Paragraph("1. Executive Summary", styles['SectionHeader'])) story.append(Spacer(1, 0.15 * inch)) story.append(Paragraph( """TimeTrack Pro is a professional time tracking application built on Microsoft Access, showcasing advanced database design, VBA automation, and modern development practices. This project demonstrates the capability to deliver production-ready business applications through automated development workflows using the VBA MCP Server.""", styles['BodyText'] )) story.append(Spacer(1, 0.1 * inch)) story.append(Paragraph( """Key Achievement: Complete application development (database structure, business logic, queries, and VBA modules) automated via MCP (Model Context Protocol) integration, demonstrating cutting-edge AI-assisted development capabilities.""", styles['BodyText'] )) story.append(Spacer(1, 0.2 * inch)) # Statistics table story.append(Paragraph("1.1 Project Statistics", styles['SubsectionHeader'])) stats_data = [ ['Metric', 'Value', 'Description'], ['Total VBA Lines', '915', 'Professional, commented code'], ['VBA Modules', '7', 'Modular architecture with separation of concerns'], ['Database Tables', '3', 'Normalized relational structure'], ['Public Functions', '45+', 'Reusable business logic components'], ['Development Time Saved', '45%', 'Via MCP VBA automation'], ['Comment Ratio', '25%', 'Well-documented codebase'], ['Error Handling', '100%', 'All public functions include error handling'], ] stats_table = Table(stats_data, colWidths=[2*inch, 1.5*inch, 3*inch]) stats_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 11), ('BOTTOMPADDING', (0, 0), (-1, 0), 10), ('TOPPADDING', (0, 0), (-1, 0), 10), ('BACKGROUND', (0, 1), (-1, -1), colors.white), ('TEXTCOLOR', (0, 1), (-1, -1), PRIMARY_COLOR), ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'), ('FONTSIZE', (0, 1), (-1, -1), 10), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 8), ('RIGHTPADDING', (0, 0), (-1, -1), 8), ('TOPPADDING', (0, 1), (-1, -1), 6), ('BOTTOMPADDING', (0, 1), (-1, -1), 6), ])) story.append(stats_table) story.append(Spacer(1, 0.2 * inch)) # Current usage data story.append(Paragraph("1.2 Current Application Data", styles['SubsectionHeader'])) usage_data = [ ['Metric', 'Value'], ['Active Clients', '4'], ['Active Projects', '6'], ['Total Time Entries', '15+'], ['Total Hours Tracked', '58 hours'], ['Total Revenue Calculated', '€4,732.50'], ] usage_table = Table(usage_data, colWidths=[3*inch, 2*inch]) usage_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), ACCENT_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (0, -1), 'LEFT'), ('ALIGN', (1, 0), (1, -1), 'RIGHT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 11), ('FONTNAME', (1, 1), (1, -1), 'Helvetica-Bold'), ('FONTSIZE', (1, 1), (1, -1), 12), ('TEXTCOLOR', (1, 1), (1, -1), SUCCESS_COLOR), ('BOTTOMPADDING', (0, 0), (-1, 0), 10), ('TOPPADDING', (0, 0), (-1, 0), 10), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 10), ('RIGHTPADDING', (0, 0), (-1, -1), 10), ('TOPPADDING', (0, 1), (-1, -1), 8), ('BOTTOMPADDING', (0, 1), (-1, -1), 8), ])) story.append(usage_table) story.append(PageBreak()) def create_section_2_project_overview(story, styles): """Section 2: Project Overview""" story.append(Paragraph("2. Project Overview", styles['SectionHeader'])) story.append(Spacer(1, 0.15 * inch)) # Purpose story.append(Paragraph("2.1 Purpose", styles['SubsectionHeader'])) story.append(Paragraph( """TimeTrack Pro is a time management tool designed for freelancers, consultants, and small teams to:""", styles['BodyText'] )) bullets = [ "Track billable hours across multiple clients and projects", "Calculate revenue automatically based on hourly rates", "Generate professional reports for invoicing and analysis", "Maintain a complete audit trail of time entries" ] for bullet in bullets: story.append(Paragraph(f"• {bullet}", styles['Bullet'])) story.append(Spacer(1, 0.15 * inch)) # Target Audience story.append(Paragraph("2.2 Target Audience", styles['SubsectionHeader'])) audience_data = [ ['Audience', 'Use Case'], ['Freelancers', 'Independent consultants tracking multiple client projects and generating invoices'], ['Small Teams', 'Agencies managing client work, resource allocation, and team productivity'], ['Consultants', 'Professional services requiring detailed time records and client reporting'], ] audience_table = Table(audience_data, colWidths=[1.5*inch, 5*inch]) audience_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), ACCENT_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 11), ('BOTTOMPADDING', (0, 0), (-1, 0), 10), ('TOPPADDING', (0, 0), (-1, 0), 10), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'TOP'), ('LEFTPADDING', (0, 0), (-1, -1), 8), ('RIGHTPADDING', (0, 0), (-1, -1), 8), ('TOPPADDING', (0, 1), (-1, -1), 8), ('BOTTOMPADDING', (0, 1), (-1, -1), 8), ])) story.append(audience_table) story.append(Spacer(1, 0.2 * inch)) # Differentiators story.append(Paragraph("2.3 Key Differentiators", styles['SubsectionHeader'])) diff_bullets = [ "Automated Development: Built using VBA MCP Server v0.6.0+ for database automation", "Clean Architecture: Modular VBA design with clear separation of concerns", "Production-Ready: Complete with data validation, error handling, and user-friendly interfaces", "Extensible: Well-documented codebase ready for customization and enhancement", "AI-Assisted: Demonstrates cutting-edge development practices with MCP integration" ] for bullet in diff_bullets: story.append(Paragraph(f"• {bullet}", styles['Bullet'])) story.append(Spacer(1, 0.2 * inch)) # Technology Stack story.append(Paragraph("2.4 Technology Stack", styles['SubsectionHeader'])) tech_data = [ ['Component', 'Technology', 'Version/Details'], ['Database Engine', 'Microsoft Access', '2016+ / Office 365'], ['Programming Language', 'VBA', 'Visual Basic for Applications 7.1'], ['Development Automation', 'VBA MCP Server', 'v0.6.0+ (Model Context Protocol)'], ['Export Formats', 'PDF, Excel', 'Native Access/VBA integration'], ['Version Control', 'Git', '2.x with source file exports'], ] tech_table = Table(tech_data, colWidths=[2*inch, 2.2*inch, 2.3*inch]) tech_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 10), ('FONTSIZE', (0, 1), (-1, -1), 9), ('BOTTOMPADDING', (0, 0), (-1, 0), 10), ('TOPPADDING', (0, 0), (-1, 0), 10), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 8), ('RIGHTPADDING', (0, 0), (-1, -1), 8), ('TOPPADDING', (0, 1), (-1, -1), 6), ('BOTTOMPADDING', (0, 1), (-1, -1), 6), ])) story.append(tech_table) story.append(PageBreak()) def create_section_3_database(story, styles): """Section 3: Database Architecture""" story.append(Paragraph("3. Database Architecture", styles['SectionHeader'])) story.append(Spacer(1, 0.15 * inch)) story.append(Paragraph( """The application uses a normalized relational database structure following third normal form (3NF) principles. The schema consists of three core tables with enforced referential integrity and optimized indexes for query performance.""", styles['BodyText'] )) story.append(Spacer(1, 0.15 * inch)) # ERD description story.append(Paragraph("3.1 Entity Relationship Diagram", styles['SubsectionHeader'])) erd_code = """ tbl_Clients (1) ─────── (N) tbl_Projets │ │ tbl_Projets (1) ─────── (N) tbl_Temps Relationships: • One Client can have many Projects (1:N) • One Project can have many Time Entries (1:N) • Referential integrity enforced with CASCADE options """ story.append(Paragraph(erd_code, styles['Code'])) story.append(Spacer(1, 0.2 * inch)) # Table: tbl_Clients story.append(Paragraph("3.2 Table: tbl_Clients", styles['SubsectionHeader'])) story.append(Paragraph("Stores client information and contact details.", styles['BodyText'])) story.append(Spacer(1, 0.1 * inch)) clients_data = [ ['Field', 'Type', 'Size', 'Constraints', 'Description'], ['ClientID', 'AutoNumber', 'Long', 'PRIMARY KEY', 'Unique identifier'], ['Nom', 'Text', '100', 'NOT NULL', 'Client name'], ['Email', 'Text', '100', 'NULL, VALIDATED', 'Email address with format validation'], ['Telephone', 'Text', '20', 'NULL', 'Phone number'], ['Notes', 'Memo', '—', 'NULL', 'Additional notes and comments'], ['DateCreation', 'DateTime', '—', 'NOT NULL, DEFAULT Now()', 'Record creation timestamp'], ] clients_table = Table(clients_data, colWidths=[1.2*inch, 1*inch, 0.7*inch, 1.4*inch, 2.2*inch]) clients_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 9), ('FONTSIZE', (0, 1), (-1, -1), 8), ('BOTTOMPADDING', (0, 0), (-1, 0), 8), ('TOPPADDING', (0, 0), (-1, 0), 8), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 6), ('RIGHTPADDING', (0, 0), (-1, -1), 6), ('TOPPADDING', (0, 1), (-1, -1), 5), ('BOTTOMPADDING', (0, 1), (-1, -1), 5), ])) story.append(clients_table) story.append(Spacer(1, 0.15 * inch)) # Indexes index_style = ParagraphStyle('IndexNote', parent=styles['BodyText'], fontSize=9, textColor=GREY_COLOR, fontName='Helvetica-Oblique') story.append(Paragraph("Indexes: PRIMARY KEY on ClientID, Search Index on Nom", index_style)) story.append(Spacer(1, 0.2 * inch)) # Table: tbl_Projets story.append(Paragraph("3.3 Table: tbl_Projets", styles['SubsectionHeader'])) story.append(Paragraph("Stores project information linked to clients.", styles['BodyText'])) story.append(Spacer(1, 0.1 * inch)) projets_data = [ ['Field', 'Type', 'Size', 'Constraints', 'Description'], ['ProjetID', 'AutoNumber', 'Long', 'PRIMARY KEY', 'Unique identifier'], ['ClientID', 'Long', '—', 'FOREIGN KEY', 'Reference to tbl_Clients.ClientID'], ['Nom', 'Text', '100', 'NOT NULL', 'Project name'], ['Description', 'Memo', '—', 'NULL', 'Project description and notes'], ['TauxHoraire', 'Currency', '—', 'DEFAULT 0, >= 0', 'Hourly rate in EUR'], ['Actif', 'Yes/No', '—', 'NOT NULL, DEFAULT True', 'Active/archived status'], ['DateCreation', 'DateTime', '—', 'NOT NULL, DEFAULT Now()', 'Record creation timestamp'], ] projets_table = Table(projets_data, colWidths=[1.2*inch, 1*inch, 0.7*inch, 1.4*inch, 2.2*inch]) projets_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 9), ('FONTSIZE', (0, 1), (-1, -1), 8), ('BOTTOMPADDING', (0, 0), (-1, 0), 8), ('TOPPADDING', (0, 0), (-1, 0), 8), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 6), ('RIGHTPADDING', (0, 0), (-1, -1), 6), ('TOPPADDING', (0, 1), (-1, -1), 5), ('BOTTOMPADDING', (0, 1), (-1, -1), 5), ])) story.append(projets_table) story.append(Spacer(1, 0.15 * inch)) story.append(Paragraph("Indexes: PRIMARY KEY on ProjetID, FOREIGN KEY on ClientID (with referential integrity), Performance Index on Actif", index_style)) story.append(Spacer(1, 0.2 * inch)) # Table: tbl_Temps story.append(Paragraph("3.4 Table: tbl_Temps", styles['SubsectionHeader'])) story.append(Paragraph("Stores time entry records.", styles['BodyText'])) story.append(Spacer(1, 0.1 * inch)) temps_data = [ ['Field', 'Type', 'Size', 'Constraints', 'Description'], ['TempsID', 'AutoNumber', 'Long', 'PRIMARY KEY', 'Unique identifier'], ['ProjetID', 'Long', '—', 'FOREIGN KEY', 'Reference to tbl_Projets.ProjetID'], ['Date', 'DateTime', '—', 'NOT NULL', 'Entry date (no future dates allowed)'], ['Duree', 'Double', '—', 'NOT NULL, > 0', 'Duration in hours (decimal)'], ['Description', 'Memo', '—', 'NULL', 'Work description'], ['DateCreation', 'DateTime', '—', 'NOT NULL, DEFAULT Now()', 'Record creation timestamp'], ] temps_table = Table(temps_data, colWidths=[1.2*inch, 1*inch, 0.7*inch, 1.4*inch, 2.2*inch]) temps_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 9), ('FONTSIZE', (0, 1), (-1, -1), 8), ('BOTTOMPADDING', (0, 0), (-1, 0), 8), ('TOPPADDING', (0, 0), (-1, 0), 8), ('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('LEFTPADDING', (0, 0), (-1, -1), 6), ('RIGHTPADDING', (0, 0), (-1, -1), 6), ('TOPPADDING', (0, 1), (-1, -1), 5), ('BOTTOMPADDING', (0, 1), (-1, -1), 5), ])) story.append(temps_table) story.append(Spacer(1, 0.15 * inch)) story.append(Paragraph("Indexes: PRIMARY KEY on TempsID, FOREIGN KEY on ProjetID (with referential integrity), Performance Index on Date (for date range queries)", index_style)) story.append(Spacer(1, 0.2 * inch)) # Data Integrity story.append(Paragraph("3.5 Data Integrity Rules", styles['SubsectionHeader'])) integrity_bullets = [ "Referential Integrity: Foreign key constraints enforced with configurable CASCADE options", "Email Validation: Format validation on tbl_Clients.Email field", "Positive Values: CHECK constraints for TauxHoraire and Duree (must be >= 0)", "Date Validation: No future dates allowed beyond today's date", "Orphan Prevention: All foreign key constraints prevent orphaned records", "Audit Trail: All tables include DateCreation timestamps for tracking" ] for bullet in integrity_bullets: story.append(Paragraph(f"• {bullet}", styles['Bullet'])) story.append(PageBreak()) # Continue in next part... print("Creating professional PDF documentation...") print("This script will generate a complete technical reference document.") print("") # Create PDF doc = SimpleDocTemplate( OUTPUT_FILE, pagesize=letter, rightMargin=0.75*inch, leftMargin=0.75*inch, topMargin=1*inch, bottomMargin=0.75*inch, title="TimeTrack Pro - Technical Reference", author="Alexis Trouvé" ) # Create custom styles styles = create_styles() # Story container story = [] # Build document print("Creating cover page...") create_cover_page(story, styles) print("Creating Section 1: Executive Summary...") create_section_1_executive_summary(story, styles) print("Creating Section 2: Project Overview...") create_section_2_project_overview(story, styles) print("Creating Section 3: Database Architecture...") create_section_3_database(story, styles) print("\nNote: This is Part 1. Continue with additional sections...") print("Generating PDF (Part 1)...") # Build PDF with custom canvas doc.build(story, canvasmaker=NumberedCanvas) print(f"\n✓ PDF generated successfully!") print(f"Location: {OUTPUT_FILE}") print(f"\nNote: This is a partial document. Run Part 2 script to add remaining sections.")