timetrack-pro/generate_professional_pdf.py
StillHammer 7c3dd3fb31 Add VBS scripts, documentation, and HTML form templates
- Test and helper VBS scripts for VBA MCP development
- Technical reference documentation and PDFs
- HTML form templates for all 5 forms
- PowerShell and Python scripts for PDF/documentation generation

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-21 11:53:09 +07:00

704 lines
26 KiB
Python

#!/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(
"""<b><font color="#2980B9">Key Achievement:</font></b> 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 = [
"<b>Automated Development:</b> Built using VBA MCP Server v0.6.0+ for database automation",
"<b>Clean Architecture:</b> Modular VBA design with clear separation of concerns",
"<b>Production-Ready:</b> Complete with data validation, error handling, and user-friendly interfaces",
"<b>Extensible:</b> Well-documented codebase ready for customization and enhancement",
"<b>AI-Assisted:</b> 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("<b>Indexes:</b> 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("<b>Indexes:</b> 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("<b>Indexes:</b> 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 = [
"<b>Referential Integrity:</b> Foreign key constraints enforced with configurable CASCADE options",
"<b>Email Validation:</b> Format validation on tbl_Clients.Email field",
"<b>Positive Values:</b> CHECK constraints for TauxHoraire and Duree (must be >= 0)",
"<b>Date Validation:</b> No future dates allowed beyond today's date",
"<b>Orphan Prevention:</b> All foreign key constraints prevent orphaned records",
"<b>Audit Trail:</b> 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.")