Instructions references/21cfr11-compliance-guide.md references/document-control-procedures.md scripts/document_validator.py scripts/document_version_control.py
name: “quality-documentation-manager”
description: Document control system management for medical device QMS. Covers document numbering, version control, change management, and 21 CFR Part 11 compliance. Use for document control procedures, change control workflow, document numbering, version management, electronic signature compliance, or regulatory documentation review.
triggers:
document control
document numbering
version control
change control
document approval
electronic signature
21 CFR Part 11
audit trail
document lifecycle
controlled document
document master list
record retention
Quality Documentation Manager
Document control system design and management for ISO 13485-compliant quality management systems, including numbering conventions, approval workflows, change control, and electronic record compliance.
Table of Contents
Document Control Workflow
Implement document control from creation through obsolescence:
Assign document number per numbering procedure
Create document using controlled template
Route for review to required reviewers
Address review comments and document responses
Obtain required approval signatures
Assign effective date and distribute
Update Document Master List
Validation: Document accessible at point of use; obsolete versions removed
Document Lifecycle Stages
Stage Definition Actions Required Draft Under creation or revision Author editing, not for use Review Circulated for review Reviewers provide feedback Approved All signatures obtained Ready for training/distribution Effective Training complete, released Available for use Superseded Replaced by newer revision Remove from active use Obsolete No longer applicable Archive per retention schedule
Document Types and Prefixes
Prefix Document Type Typical Content QM Quality Manual QMS overview, scope, policy SOP Standard Operating Procedure Process-level procedures WI Work Instruction Task-level step-by-step TF Template/Form Controlled forms SPEC Specification Product/process specs PLN Plan Quality/project plans
Required Reviewers by Document Type
Document Type Required Reviewers Required Approvers SOP Process Owner, QA QA Manager, Process Owner WI Area Supervisor, QA Area Manager SPEC Engineering, QA Engineering Manager, QA TF Process Owner QA Design Documents Design Team, QA Design Control Authority
Document Numbering System
Assign consistent document numbers for identification and retrieval.
Standard format: PREFIX-CATEGORY-SEQUENCE[-REVISION]
Example: SOP-02-001-A
SOP = Document type (Standard Operating Procedure)
02 = Category code (Document Control)
001 = Sequential number
A = Revision indicator
Category Codes
Code Functional Area Description 01 Quality Management QMS procedures, management review 02 Document Control This area 03 Human Resources Training, competency 04 Design & Development Design control processes 05 Purchasing Supplier management 06 Production Manufacturing procedures 07 Quality Control Inspection, testing 08 CAPA Corrective/preventive actions 09 Risk Management ISO 14971 processes 10 Regulatory Affairs Submissions, compliance
Numbering Workflow
Author requests document number from Document Control
Document Control verifies category assignment
Document Control assigns next available sequence number
Number recorded in Document Master List
Author creates document using assigned number
Validation: Number format matches standard; no duplicates in Master List
Revision Designation
Change Type Revision Increment Example Major revision Increment number Rev 01 → Rev 02 Minor revision Increment sub-revision Rev 01 → Rev 01.1 Administrative No change or letter suffix Rev 01 → Rev 01a
See references/document-control-procedures.md for complete numbering guidance.
Approval and Review Process
Obtain required reviews and approvals before document release.
Review Workflow
Author completes document draft
Author submits for review via routing form or DMS
Reviewers assigned based on document type
Reviewers provide comments within review period (5-10 business days)
Author addresses comments and documents responses
Author resubmits revised document
Approvers sign and date
Validation: All required reviewers completed; all comments addressed with documented disposition
Disposition Action Required Accept Incorporate comment as written Accept with modification Incorporate with changes, document rationale Reject Do not incorporate, document justification Defer Address in future revision, document reason
Approval Matrix
Document Level 1 (Policy/QM): CEO or delegate + QA Manager
Document Level 2 (SOP): Department Manager + QA Manager
Document Level 3 (WI/TF): Area Supervisor + QA Representative
Signature Requirements
Element Requirement Name Printed name of signer Signature Handwritten or electronic signature Date Date signature applied Role Function/role of signer
Change Control Process
Manage document changes systematically through review and approval.
Change Control Workflow
Identify need for document change
Complete Change Request Form with justification
Document Control assigns change number and logs request
Route to reviewers for impact assessment
Obtain approvals based on change classification
Author implements approved changes
Update revision number and change history
Validation: Changes match approved scope; change history complete
Change Classification
Class Definition Approval Level Examples Administrative No content impact Document Control Typos, formatting Minor Limited content change Process Owner + QA Clarifications Major Significant content change Full review cycle New requirements Emergency Urgent safety/compliance Expedited + retrospective Safety issues
Impact Assessment Checklist
Impact Area Assessment Questions Training Does change require retraining? Equipment Does change affect equipment or systems? Validation Does change require revalidation? Regulatory Does change affect regulatory filings? Other Documents Which related documents need updating? Records What records are affected?
Change History Documentation
Each document must include change history:
| Revision | Date | Description | Author | Approver |
|----------|------|-------------|--------|----------|
| 01 | 2023-01-15 | Initial release | J. Smith | M. Jones |
| 02 | 2024-03-01 | Updated workflow | J. Smith | M. Jones |
21 CFR Part 11 Compliance
Implement electronic record and signature controls for FDA compliance.
Part 11 Scope
Applies To Does Not Apply To Records required by FDA regulations Paper records Records submitted to FDA Internal non-regulated documents Electronic signatures on required records General email communication
Electronic Record Controls
Validate system for accuracy and reliability
Implement secure audit trail for all changes
Restrict system access to authorized individuals
Generate accurate copies in human-readable format
Protect records throughout retention period
Validation: Audit trail captures who, what, when for all changes
Audit Trail Requirements
Requirement Implementation Secure Cannot be modified by users Computer-generated System creates automatically Time-stamped Date and time of each action Original values Previous values retained User identity Who made each change
Electronic Signature Requirements
Requirement Implementation Unique to individual Not shared between persons At least 2 components User ID + password minimum Signature manifestation Name, date/time, meaning displayed Linked to record Cannot be excised or copied
Signature Manifestation
Every electronic signature must display:
Element Example Printed name John Smith Date and time 2024-03-15 14:32:05 EST Meaning Approved for Release
System Controls Checklist
Access Controls:
Audit Trail:
Security:
See references/21cfr11-compliance-guide.md for detailed compliance requirements.
Reference Documentation
Document Control Procedures
references/document-control-procedures.md contains:
Document numbering system and format
Document lifecycle stages and transitions
Review and approval workflow details
Change control process with classification criteria
Distribution and access control methods
Record retention periods and disposal procedures
Document Master List requirements
21 CFR Part 11 Compliance Guide
references/21cfr11-compliance-guide.md contains:
Part 11 scope and applicability
Electronic record requirements (§11.10)
Electronic signature requirements (§11.50, 11.100, 11.200)
System control specifications
Validation approach and documentation
Compliance checklist and gap assessment template
Common FDA deficiencies and prevention
Document Validator
# Validate document metadata
python scripts/document_validator.py --doc document.json
# Interactive validation mode
python scripts/document_validator.py --interactive
# JSON output for integration
python scripts/document_validator.py --doc document.json --output json
# Generate sample document JSON
python scripts/document_validator.py --sample > sample_doc.json
Validates:
Document numbering convention compliance
Title and status requirements
Date validation (effective, review due)
Approval requirements by document type
Change history completeness
21 CFR Part 11 controls (audit trail, signatures)
{
"number" : "SOP-02-001" ,
"title" : "Document Control Procedure" ,
"doc_type" : "SOP" ,
"revision" : "03" ,
"status" : "Effective" ,
"effective_date" : "2024-01-15" ,
"review_date" : "2025-01-15" ,
"author" : "J. Smith" ,
"approver" : "M. Jones" ,
"change_history" : [
{ "revision" : "01" , "date" : "2022-01-01" , "description" : "Initial release" },
{ "revision" : "02" , "date" : "2023-01-15" , "description" : "Updated workflow" },
{ "revision" : "03" , "date" : "2024-01-15" , "description" : "Added e-signature requirements" }
],
"has_audit_trail" : true ,
"has_electronic_signature" : true ,
"signature_components" : 2
}
Document Control Metrics
Track document control system performance.
Metric Target Calculation Document cycle time <30 days Average days from draft to effective Review completion rate >95% Reviews completed on time / Total reviews Change request backlog <10 Open change requests at month end Overdue review rate <5% Documents past review date / Total effective Audit finding rate <2 per audit Document control findings per internal audit
Periodic Review Schedule
Document Type Review Frequency Policy Every 3 years SOP Every 2 years WI Every 2 years Specifications As needed or with product changes Forms/Templates Every 3 years
Regulatory Requirements
ISO 13485:2016 Clause 4.2
Sub-clause Requirement 4.2.1 Quality management system documentation 4.2.2 Quality manual 4.2.3 Medical device file (technical documentation) 4.2.4 Control of documents 4.2.5 Control of records
FDA 21 CFR 820
Section Requirement 820.40 Document controls 820.180 General record requirements 820.181 Device master record 820.184 Device history record 820.186 Quality system record
Common Audit Findings
Finding Prevention Obsolete documents in use Implement distribution control Missing approval signatures Enforce workflow before release Incomplete change history Require history update with each revision No periodic review schedule Establish and enforce review calendar Inadequate audit trail Validate DMS for Part 11 compliance
21 CFR Part 11 Compliance Guide
Electronic records and electronic signatures compliance for FDA-regulated systems.
Table of Contents
Part 11 Overview
Scope and Applicability
21 CFR Part 11 applies to electronic records and signatures used to meet FDA predicate rule requirements.
Applies To
Does Not Apply To
Records required by FDA regulations
Paper records
Records submitted to FDA
Internal documents not required by regulation
Electronic signatures on required records
Digital communication (email) for general purposes
Systems creating/maintaining regulated records
Non-regulated systems
Key Terms
Term
Definition
Electronic Record
Any combination of text, graphics, data in digital form
Electronic Signature
Computer data compilation intended as legally binding signature
Digital Signature
Electronic signature based on cryptographic methods
Closed System
Environment with controlled access by responsible persons
Open System
Environment with uncontrolled access
Audit Trail
Secure, computer-generated, time-stamped record
Predicate Rules
Part 11 does not create new record requirements. It governs HOW records are maintained when electronic:
Predicate Rule
Record Type
21 CFR 820 (QSR)
Device Master Records, Device History Records
21 CFR 211 (cGMP)
Batch records, laboratory records
21 CFR 58 (GLP)
Study records, raw data
21 CFR 11.10(e)
Records required to be maintained
Electronic Record Requirements
General Requirements (§11.10)
Closed systems must implement controls including:
System Validation - Accuracy, reliability, consistent intended performance
Record Generation - Accurate and complete copies in human-readable form
Record Protection - Throughout retention period
Access Control - Limit system access to authorized individuals
Audit Trail - Secure, computer-generated, time-stamped record
Operational Checks - Enforce permitted sequencing of steps
Authority Checks - Restrict functions to authorized individuals
Device Checks - Determine validity of input/output devices
Training - Personnel education and experience
Documentation - Written policies and accountability
Audit Trail Requirements
Requirement
Implementation
Secure
Cannot be modified or deleted by users
Computer-generated
System creates automatically, not manually entered
Time-stamped
Date and time of each action recorded
Independent
Stored separately from application data
Original values
Previous values retained when modified
Who, what, when
User identity, action taken, date/time
Reason for change
Where required by predicate rule
Audit Trail Entries
Event Type
Data Captured
Record Creation
User, date/time, initial values
Record Modification
User, date/time, old value, new value, reason
Record Deletion
User, date/time, reason (if permitted)
Login/Logout
User, date/time, success/failure
Signature Application
User, date/time, signature meaning
Failed Access
User attempted, date/time, reason
Record Copy Requirements
Must be able to generate accurate and complete copies:
Format
Requirement
Electronic
Export in standard format (PDF, XML)
Paper
Human-readable printout
FDA Inspection
Provide copies upon request
Audit Trail
Include with record or separately
Electronic Signature Requirements
General Requirements (§11.50, 11.100)
Requirement
Implementation
Unique to individual
Not shared between persons
Not reused
Identifier not assigned to another person
Identity verification
Verify identity before assignment
Certification
Certify to FDA that signatures are binding
Signature Components (§11.200)
Type
Components Required
Non-biometric
At least two distinct identification components
- First signing
Both components (user ID + password)
- Subsequent signings
At least one component within controlled session
Biometric
Biometric designed for individual identification
Signature Manifestations (§11.50)
Electronic signatures must include:
Element
Requirement
Printed name
Full name of signer
Date and time
When signature was applied
Meaning
Purpose of signature (e.g., review, approval, responsibility)
Signature/Record Linking (§11.70)
Requirement
Implementation
Linked to record
Signature cannot be excised, copied, or transferred
Cannot falsify
Technical controls prevent counterfeiting
Cannot repudiate
Signer cannot deny signing
Signature Certification
Organizations must submit certification to FDA (§11.100(c)):
SAMPLE CERTIFICATION LETTER
[Date]
Food and Drug Administration
[Appropriate Center Address]
Subject: Electronic Signature Certification
[Company Name] hereby certifies that all electronic signatures
used in our FDA-regulated systems are the legally binding
equivalent of traditional handwritten signatures.
This certification is made in accordance with 21 CFR Part 11,
Section 11.100(c).
Sincerely,
[Authorized Representative]
[Title]
System Controls
Administrative Controls
Control
Implementation
Written policies
SOPs for electronic records and signatures
Roles and responsibilities
Defined system access roles
Training program
Initial and periodic training
Periodic review
Regular assessment of controls
Accountability
Individual responsibility for actions
Operational Controls
Control
Implementation
Sequence enforcement
System enforces step order
Time limits
Session timeout after inactivity
Event logging
All significant events recorded
Error handling
System prevents invalid operations
Backup/recovery
Regular backup and tested recovery
Technical Controls
Control
Implementation
User authentication
Unique ID + password minimum
Password complexity
Minimum length, character requirements
Password expiration
Periodic change requirement
Account lockout
Lock after failed attempts
Access control
Role-based permissions
Encryption
Data in transit and at rest
Password Requirements
Requirement
Specification
Minimum length
8 characters minimum
Complexity
Upper, lower, number, special character
History
Cannot reuse last 12 passwords
Expiration
Maximum 90 days
Lockout
5 failed attempts, 30-minute lockout
Initial password
Must change on first login
Session Controls
Control
Specification
Inactivity timeout
Maximum 15 minutes
Session duration
Maximum 8 hours
Concurrent sessions
Limit or prevent
Re-authentication
Required for sensitive operations
Validation Requirements
Validation Approach
Phase
Activities
Planning
Validation plan, requirements, risk assessment
Specification
User requirements, functional specifications
Configuration
System setup, security configuration
Testing
IQ, OQ, PQ protocols and execution
Release
Validation summary report, release approval
Maintenance
Change control, periodic review
Validation Documentation
Document
Purpose
Validation Plan
Scope, approach, responsibilities, schedule
User Requirements
What system must do (business requirements)
Functional Specification
How system will meet requirements
Design Specification
Technical implementation details
Test Protocols
IQ, OQ, PQ test procedures
Test Results
Executed protocols with evidence
Traceability Matrix
Requirements to test coverage
Validation Summary Report
Overall validation conclusion
Testing Categories
Installation Qualification (IQ):
System installed per specifications
Hardware and software inventory
Configuration documentation
Operational Qualification (OQ):
Functions operate as specified
Audit trail verification
Security control testing
Error handling verification
Performance Qualification (PQ):
System performs in production environment
User acceptance testing
Integration testing
Load/stress testing (if applicable)
Part 11 Specific Testing
Test Area
Verification
Audit trail
All CRUD operations recorded correctly
Access control
Role permissions enforced
Electronic signatures
Signature components and linking
Record integrity
Data cannot be altered without detection
Backup/restore
Records restored accurately
Session controls
Timeout and lockout function
Password controls
Complexity and expiration enforced
Compliance Checklist
System Assessment Checklist
Administrative Controls:
Access Controls:
Audit Trail:
Electronic Signatures:
Record Management:
System Controls:
Validation:
Gap Assessment Template
PART 11 GAP ASSESSMENT
System: [System Name]
Assessment Date: [Date]
Assessor: [Name]
| Requirement | §11 Reference | Current State | Gap | Remediation | Priority |
|-------------|---------------|---------------|-----|-------------|----------|
| Audit trail | 11.10(e) | [Description] | [Y/N] | [Action] | [H/M/L] |
| Access control | 11.10(d) | [Description] | [Y/N] | [Action] | [H/M/L] |
| E-signatures | 11.50 | [Description] | [Y/N] | [Action] | [H/M/L] |
Summary:
- Total requirements assessed: [Number]
- Requirements met: [Number]
- Gaps identified: [Number]
- Remediation timeline: [Date] Periodic Review Schedule
Review Type
Frequency
Scope
Access review
Quarterly
User access appropriateness
Audit trail review
Monthly
Sample review of audit entries
Security review
Annually
Controls effectiveness
Validation review
Annually or on change
System still validated
Policy review
Annually
SOPs current and followed
Common Deficiencies
FDA Warning Letter Themes
Deficiency
Root Cause
Prevention
Shared user accounts
Convenience over compliance
Enforce unique accounts
Inadequate audit trail
System limitation
Validate audit trail
Missing signatures
Process gap
Enforce signature workflow
Incomplete validation
Time/resource constraints
Plan adequate resources
No change control
Process not followed
Enforce change control
Password sharing
Culture issue
Training and enforcement
Remediation Priorities
Priority
Deficiency Type
Timeline
Critical
Audit trail missing/modifiable
Immediate
Critical
Signatures can be falsified
Immediate
High
Shared accounts in production
30 days
High
Validation gaps
60 days
Medium
Training gaps
90 days
Low
Documentation gaps
120 days
Document Control Procedures
Implementation guide for ISO 13485-compliant document control systems.
Table of Contents
Document Numbering System
Numbering Format
Standard format: [PREFIX]-[CATEGORY]-[SEQUENCE]-[REVISION]
Component
Format
Example
Description
PREFIX
2-3 letters
SOP, WI, TF
Document type identifier
CATEGORY
2-3 digits
01, 02, 10
Functional area code
SEQUENCE
3-4 digits
001, 0001
Sequential number within category
REVISION
Letter or number
A, 01
Revision indicator
Document Type Prefixes
Prefix
Document Type
Description
QM
Quality Manual
Top-level QMS description
SOP
Standard Operating Procedure
Process procedures
WI
Work Instruction
Task-level instructions
TF
Template/Form
Controlled forms and templates
POL
Policy
Policy statements
SPEC
Specification
Product/process specifications
PLN
Plan
Project and quality plans
RPT
Report
Technical and quality reports
Category Codes
Code
Functional Area
Examples
01
Quality Management
QMS procedures, audits
02
Document Control
This area
03
Human Resources
Training, competency
04
Design & Development
Design control
05
Purchasing
Supplier management
06
Production
Manufacturing
07
Quality Control
Inspection, testing
08
CAPA
Corrective/preventive actions
09
Risk Management
ISO 14971 processes
10
Regulatory Affairs
Submissions, compliance
Numbering Workflow
Author requests document number from Document Control
Document Control verifies category and assigns next sequence number
Document number recorded in Document Master List
Author creates document using assigned number
Validation: Number format matches standard; no duplicates exist
Document Lifecycle
Lifecycle Stages
DRAFT → REVIEW → APPROVED → EFFECTIVE → SUPERSEDED → OBSOLETE
│ │ │ │ │ │
│ │ │ │ │ └── Archived/Destroyed
│ │ │ │ └── New revision effective
│ │ │ └── Training complete, distribution done
│ │ └── All approvals obtained
│ └── Under review/revision
└── Initial creation Stage Definitions
Stage
Definition
Actions Required
Draft
Document under creation or revision
Author editing, not for use
Review
Circulated for review and comment
Reviewers provide feedback
Approved
All required signatures obtained
Ready for training/distribution
Effective
Training complete, document released
Available for use
Superseded
Replaced by newer revision
Remove from active use
Obsolete
No longer applicable
Archive per retention schedule
Document Status Indicators
Status
Indicator
Location
Draft
"DRAFT" watermark
Header or footer
Approved
Approval signatures with dates
Signature page
Effective
Effective date
Header
Obsolete
"OBSOLETE" stamp
Across all pages
Review and Approval Workflow
Document Review Workflow
Author completes document draft
Author submits for review via DMS or routing form
Reviewers assigned based on document type and content
Reviewers provide comments within review period (typically 5-10 business days)
Author addresses comments and documents responses
Author resubmits for approval
Approvers sign and date
Validation: All required reviewers completed; all comments addressed
Required Reviewers by Document Type
Document Type
Required Reviewers
Required Approvers
SOP
Process Owner, QA
QA Manager, Process Owner
WI
Area Supervisor, QA
Area Manager
SPEC
Engineering, QA
Engineering Manager, QA
TF
Process Owner
QA
POL
Department Heads
Management Representative
Design Documents
Design Team, QA
Design Control Authority
Approval Matrix
APPROVAL AUTHORITY MATRIX
Document Level 1 (Policy): CEO or delegate + QA Manager
Document Level 2 (SOP): Department Manager + QA Manager
Document Level 3 (WI/TF): Area Supervisor + QA Representative
Regulatory Submissions: RA Manager + QA Manager + Technical Expert
Design Documents: Design Authority + QA Manager Review Comment Template
REVIEW COMMENT LOG
Document: [Document Number and Title]
Reviewer: [Name, Role]
Review Date: [Date]
| Section | Line/Para | Comment | Disposition | Response |
|---------|-----------|---------|-------------|----------|
| [Ref] | [Location] | [Issue/suggestion] | Accept/Reject/Modify | [Explanation] |
Change Control Process
Change Request Workflow
Identify need for document change
Complete Change Request Form (CRF)
Submit CRF to Document Control
Document Control assigns change number
Route to reviewers for impact assessment
Obtain approvals based on change classification
Author implements approved changes
Validation: Changes match approved scope; version number incremented
Change Classification
Class
Definition
Approval Level
Examples
Administrative
No impact on content meaning
Document Control
Typos, formatting, references
Minor
Limited content change, no process impact
Process Owner + QA
Clarifications, minor additions
Major
Significant content change, process impact
Full review cycle
New requirements, process changes
Emergency
Urgent change required for safety/compliance
Expedited approval + retrospective review
Safety issues, regulatory mandates
Change Impact Assessment
Impact Area
Assessment Questions
Training
Does change require retraining? Who?
Equipment
Does change affect equipment or systems?
Validation
Does change require revalidation?
Regulatory
Does change affect regulatory filings?
Other Documents
Which related documents need updating?
Records
What records are affected?
Version Control Rules
Change Type
Version Increment
Example
Major revision
Increment revision number
Rev 01 → Rev 02
Minor revision
Increment sub-revision
Rev 01 → Rev 01.1
Administrative
No version change (or sub-increment)
Rev 01 → Rev 01a
Draft iterations
Use draft version
Draft 1, Draft 2
Change History Template
DOCUMENT CHANGE HISTORY
| Revision | Date | Description of Change | Author | Approver |
|----------|------|----------------------|--------|----------|
| 01 | YYYY-MM-DD | Initial release | [Name] | [Name] |
| 02 | YYYY-MM-DD | [Change description] | [Name] | [Name] |
Distribution and Access Control
Distribution Methods
Method
Use Case
Control Mechanism
Electronic (DMS)
Primary method
Access permissions
Controlled Print
Manufacturing floor
Signature log
Uncontrolled Copy
External distribution
Watermark "UNCONTROLLED"
Reference Copy
Training/archive
Watermark "REFERENCE ONLY"
Access Permission Levels
Level
Permissions
Typical Roles
Read
View documents only
General users
Print
View and print controlled copies
Area supervisors
Review
View, print, add comments
Reviewers
Author
Create, edit drafts
Document authors
Approve
Approve documents
Approvers
Admin
Full system access
Document Control
Controlled Print Log
CONTROLLED PRINT LOG
Document: [Document Number]
Revision: [Revision Number]
| Copy # | Location | Issued To | Date Issued | Date Returned | Signature |
|--------|----------|-----------|-------------|---------------|-----------|
| 001 | Production Area 1 | [Name] | [Date] | [Date] | [Sig] |
| 002 | QC Lab | [Name] | [Date] | [Date] | [Sig] | Obsolete Document Control
Mark document as "OBSOLETE" in DMS
Notify copy holders of obsolescence
Collect and destroy controlled prints
Update Document Master List
Archive master copy per retention schedule
Validation: No obsolete copies remain in active use areas
Record Retention
Retention Periods
Record Type
Retention Period
Basis
Device Master Record (DMR)
Life of device + 2 years
21 CFR 820.181
Device History Record (DHR)
Life of device + 2 years
21 CFR 820.184
Design History File (DHF)
Life of device + 2 years
21 CFR 820.30
Quality Records
2 years beyond device discontinuation
ISO 13485
Training Records
Duration of employment + 3 years
Best practice
Audit Records
7 years
Best practice
Complaint Records
Life of device + 2 years
21 CFR 820.198
CAPA Records
7 years
Best practice
Calibration Records
2 years beyond equipment disposal
Best practice
Supplier Records
Life of relationship + 3 years
Best practice
Archive Requirements
Requirement
Specification
Storage Conditions
Temperature 15-25°C, RH 30-60%
Access Control
Restricted to authorized personnel
Indexing
Searchable by document number, date, type
Media
Original format or validated conversion
Backup
Offsite backup for electronic records
Integrity Checks
Periodic verification of record legibility
Disposal Procedure
Verify retention period has expired
Check for legal holds or ongoing litigation
Obtain disposal authorization
Execute secure destruction (shred paper, wipe electronic)
Document disposal in Disposal Log
Validation: No premature disposal; disposal documented
Disposal Log Template
RECORD DISPOSAL LOG
| Document/Record ID | Description | Retention Expired | Disposal Date | Method | Witness |
|--------------------|-------------|-------------------|---------------|--------|---------|
| [ID] | [Description] | [Date] | [Date] | Shred/Wipe | [Name] |
Document Master List
Master List Content
Field
Description
Required
Document Number
Unique identifier
Yes
Title
Document title
Yes
Current Revision
Active revision number
Yes
Effective Date
Date document became effective
Yes
Status
Draft/Effective/Obsolete
Yes
Process Owner
Responsible party
Yes
Review Date
Next scheduled review
Yes
Category
Functional area
Yes
Storage Location
Physical or electronic location
Yes
Master List Maintenance
Update within 24 hours of document status change
Review quarterly for accuracy
Audit annually for completeness
Archive historical versions
Sample Master List Entry
| Doc # | Title | Rev | Eff Date | Status | Owner | Review Date |
|-------|-------|-----|----------|--------|-------|-------------|
| SOP-02-001 | Document Control | 03 | 2024-01-15 | Effective | QA Mgr | 2025-01-15 |
| WI-06-012 | Assembly Line Setup | 02 | 2024-03-01 | Effective | Prod Mgr | 2025-03-01 | #!/usr/bin/env python3
"""
Document Validator - Quality Documentation Compliance Checker
Validates document metadata, numbering conventions, and control requirements
for ISO 13485 and 21 CFR Part 11 compliance.
Usage:
python document_validator.py --doc document.json
python document_validator.py --interactive
python document_validator.py --doc document.json --output json
"""
import argparse
import json
import re
import sys
from dataclasses import dataclass, field, asdict
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Tuple
from enum import Enum
class DocumentType ( Enum ):
QM = "Quality Manual"
SOP = "Standard Operating Procedure"
WI = "Work Instruction"
TF = "Template/Form"
POL = "Policy"
SPEC = "Specification"
PLN = "Plan"
RPT = "Report"
class DocumentStatus ( Enum ):
DRAFT = "Draft"
REVIEW = "Under Review"
APPROVED = "Approved"
EFFECTIVE = "Effective"
SUPERSEDED = "Superseded"
OBSOLETE = "Obsolete"
class Severity ( Enum ):
CRITICAL = "Critical"
MAJOR = "Major"
MINOR = "Minor"
INFO = "Info"
@dataclass
class ValidationFinding :
rule: str
severity: Severity
message: str
recommendation: str
@dataclass
class Document :
number: str
title: str
doc_type: str
revision: str
status: str
effective_date: Optional[ str ] = None
review_date: Optional[ str ] = None
author: Optional[ str ] = None
approver: Optional[ str ] = None
approval_date: Optional[ str ] = None
change_history: List[Dict] = field( default_factory = list )
has_audit_trail: bool = False
has_electronic_signature: bool = False
signature_components: int = 0
@dataclass
class ValidationResult :
document_number: str
validation_date: str
total_findings: int
critical_findings: int
major_findings: int
minor_findings: int
compliance_score: float
findings: List[Dict]
recommendations: List[ str ]
class DocumentValidator :
"""Validator for quality documentation compliance."""
# Document number pattern: PREFIX-CATEGORY-SEQUENCE-REVISION
DOC_NUMBER_PATTERN = r ' ^([A-Z] {2,4} ) - (\d {2,3} ) - (\d {3,4} )(?: - ([A-Z] | \d {2} )) ? $ '
# Valid document type prefixes
VALID_PREFIXES = [ 'QM' , 'SOP' , 'WI' , 'TF' , 'POL' , 'SPEC' , 'PLN' , 'RPT' ]
# Category codes
VALID_CATEGORIES = [ '01' , '02' , '03' , '04' , '05' , '06' , '07' , '08' , '09' , '10' ]
def __init__ (self, document: Document):
self .document = document
self .today = datetime.now()
self .findings: List[ValidationFinding] = []
def validate (self) -> ValidationResult:
"""Run all validation checks."""
self ._validate_document_number()
self ._validate_title()
self ._validate_status_lifecycle()
self ._validate_dates()
self ._validate_approvals()
self ._validate_change_history()
self ._validate_electronic_controls()
# Calculate compliance score
score = self ._calculate_compliance_score()
# Generate recommendations
recommendations = self ._generate_recommendations()
# Count findings by severity
critical = len ([f for f in self .findings if f.severity == Severity. CRITICAL ])
major = len ([f for f in self .findings if f.severity == Severity. MAJOR ])
minor = len ([f for f in self .findings if f.severity == Severity. MINOR ])
return ValidationResult(
document_number = self .document.number,
validation_date = self .today.strftime( "%Y-%m- %d " ),
total_findings = len ( self .findings),
critical_findings = critical,
major_findings = major,
minor_findings = minor,
compliance_score = round (score, 1 ),
findings = [asdict(f) for f in self .findings],
recommendations = recommendations
)
def _validate_document_number (self):
"""Validate document numbering convention."""
number = self .document.number
if not number:
self .findings.append(ValidationFinding(
rule = "DOC-NUM-001" ,
severity = Severity. CRITICAL ,
message = "Document number is missing" ,
recommendation = "Assign document number per numbering procedure"
))
return
match = re.match( self . DOC_NUMBER_PATTERN , number)
if not match:
self .findings.append(ValidationFinding(
rule = "DOC-NUM-002" ,
severity = Severity. MAJOR ,
message = f "Document number ' { number } ' does not match standard format" ,
recommendation = "Use format: PREFIX-CATEGORY-SEQUENCE[-REVISION] (e.g., SOP-02-001-A)"
))
return
prefix, category, sequence, revision = match.groups()
if prefix not in self . VALID_PREFIXES :
self .findings.append(ValidationFinding(
rule = "DOC-NUM-003" ,
severity = Severity. MAJOR ,
message = f "Invalid document type prefix: { prefix } " ,
recommendation = f "Use one of: { ', ' .join( self . VALID_PREFIXES ) } "
))
if category not in self . VALID_CATEGORIES :
self .findings.append(ValidationFinding(
rule = "DOC-NUM-004" ,
severity = Severity. MINOR ,
message = f "Non-standard category code: { category } " ,
recommendation = f "Standard categories are: { ', ' .join( self . VALID_CATEGORIES ) } "
))
def _validate_title (self):
"""Validate document title."""
title = self .document.title
if not title:
self .findings.append(ValidationFinding(
rule = "DOC-TTL-001" ,
severity = Severity. MAJOR ,
message = "Document title is missing" ,
recommendation = "Provide descriptive document title"
))
return
if len (title) < 10 :
self .findings.append(ValidationFinding(
rule = "DOC-TTL-002" ,
severity = Severity. MINOR ,
message = "Document title is very short" ,
recommendation = "Use descriptive title that clearly identifies content"
))
if len (title) > 100 :
self .findings.append(ValidationFinding(
rule = "DOC-TTL-003" ,
severity = Severity. MINOR ,
message = "Document title exceeds recommended length" ,
recommendation = "Keep title under 100 characters"
))
def _validate_status_lifecycle (self):
"""Validate document status and lifecycle."""
status = self .document.status
if not status:
self .findings.append(ValidationFinding(
rule = "DOC-STS-001" ,
severity = Severity. MAJOR ,
message = "Document status is missing" ,
recommendation = "Assign appropriate document status"
))
return
valid_statuses = [s.value for s in DocumentStatus]
if status not in valid_statuses:
self .findings.append(ValidationFinding(
rule = "DOC-STS-002" ,
severity = Severity. MAJOR ,
message = f "Invalid document status: { status } " ,
recommendation = f "Use one of: { ', ' .join(valid_statuses) } "
))
# Check status-specific requirements
if status == DocumentStatus. EFFECTIVE .value:
if not self .document.effective_date:
self .findings.append(ValidationFinding(
rule = "DOC-STS-003" ,
severity = Severity. MAJOR ,
message = "Effective document missing effective date" ,
recommendation = "Add effective date for effective documents"
))
if status == DocumentStatus. APPROVED .value:
if not self .document.approval_date:
self .findings.append(ValidationFinding(
rule = "DOC-STS-004" ,
severity = Severity. MAJOR ,
message = "Approved document missing approval date" ,
recommendation = "Add approval date for approved documents"
))
def _validate_dates (self):
"""Validate document dates."""
# Check effective date
if self .document.effective_date:
try :
eff_date = datetime.strptime( self .document.effective_date, "%Y-%m- %d " )
if eff_date > self .today:
self .findings.append(ValidationFinding(
rule = "DOC-DTE-001" ,
severity = Severity. INFO ,
message = "Effective date is in the future" ,
recommendation = "Verify planned effective date is correct"
))
except ValueError :
self .findings.append(ValidationFinding(
rule = "DOC-DTE-002" ,
severity = Severity. MINOR ,
message = "Invalid effective date format" ,
recommendation = "Use YYYY-MM-DD format for dates"
))
# Check review date
if self .document.review_date:
try :
review_date = datetime.strptime( self .document.review_date, "%Y-%m- %d " )
if review_date < self .today:
self .findings.append(ValidationFinding(
rule = "DOC-DTE-003" ,
severity = Severity. MAJOR ,
message = "Document is overdue for review" ,
recommendation = "Initiate periodic review process"
))
elif review_date < self .today + timedelta( days = 30 ):
self .findings.append(ValidationFinding(
rule = "DOC-DTE-004" ,
severity = Severity. MINOR ,
message = "Document review due within 30 days" ,
recommendation = "Plan for upcoming review"
))
except ValueError :
self .findings.append(ValidationFinding(
rule = "DOC-DTE-005" ,
severity = Severity. MINOR ,
message = "Invalid review date format" ,
recommendation = "Use YYYY-MM-DD format for dates"
))
else :
if self .document.status == DocumentStatus. EFFECTIVE .value:
self .findings.append(ValidationFinding(
rule = "DOC-DTE-006" ,
severity = Severity. MINOR ,
message = "Effective document missing review date" ,
recommendation = "Add next review date (typically 1-3 years from effective)"
))
def _validate_approvals (self):
"""Validate document approval information."""
if self .document.status in [DocumentStatus. APPROVED .value, DocumentStatus. EFFECTIVE .value]:
if not self .document.author:
self .findings.append(ValidationFinding(
rule = "DOC-APR-001" ,
severity = Severity. MAJOR ,
message = "Document author not identified" ,
recommendation = "Document author on signature page"
))
if not self .document.approver:
self .findings.append(ValidationFinding(
rule = "DOC-APR-002" ,
severity = Severity. CRITICAL ,
message = "Document approver not identified" ,
recommendation = "Obtain required approval signatures"
))
def _validate_change_history (self):
"""Validate change history completeness."""
history = self .document.change_history
if not history:
self .findings.append(ValidationFinding(
rule = "DOC-CHG-001" ,
severity = Severity. MAJOR ,
message = "Document change history is missing" ,
recommendation = "Include change history table with revision descriptions"
))
return
for i, entry in enumerate (history):
if not entry.get( 'revision' ):
self .findings.append(ValidationFinding(
rule = "DOC-CHG-002" ,
severity = Severity. MINOR ,
message = f "Change history entry { i + 1} missing revision number" ,
recommendation = "Include revision number for each history entry"
))
if not entry.get( 'description' ):
self .findings.append(ValidationFinding(
rule = "DOC-CHG-003" ,
severity = Severity. MINOR ,
message = f "Change history entry { i + 1} missing description" ,
recommendation = "Include description of changes for each revision"
))
if not entry.get( 'date' ):
self .findings.append(ValidationFinding(
rule = "DOC-CHG-004" ,
severity = Severity. MINOR ,
message = f "Change history entry { i + 1} missing date" ,
recommendation = "Include date for each history entry"
))
def _validate_electronic_controls (self):
"""Validate 21 CFR Part 11 requirements for electronic documents."""
# Audit trail check
if not self .document.has_audit_trail:
self .findings.append(ValidationFinding(
rule = "P11-AUD-001" ,
severity = Severity. MAJOR ,
message = "Electronic document lacks audit trail" ,
recommendation = "Enable audit trail for 21 CFR Part 11 compliance"
))
# Electronic signature check
if self .document.has_electronic_signature:
if self .document.signature_components < 2 :
self .findings.append(ValidationFinding(
rule = "P11-SIG-001" ,
severity = Severity. CRITICAL ,
message = "Electronic signature uses fewer than 2 identification components" ,
recommendation = "Use at least 2 components (e.g., user ID + password)"
))
else :
if self .document.status in [DocumentStatus. APPROVED .value, DocumentStatus. EFFECTIVE .value]:
self .findings.append(ValidationFinding(
rule = "P11-SIG-002" ,
severity = Severity. INFO ,
message = "Document uses handwritten signatures" ,
recommendation = "Consider electronic signatures for efficiency"
))
def _calculate_compliance_score (self) -> float :
"""Calculate compliance score based on findings."""
if not self .findings:
return 100.0
# Weight by severity
deductions = {
Severity. CRITICAL : 25 ,
Severity. MAJOR : 10 ,
Severity. MINOR : 3 ,
Severity. INFO : 0
}
total_deduction = sum (deductions[f.severity] for f in self .findings)
score = max ( 0 , 100 - total_deduction)
return score
def _generate_recommendations (self) -> List[ str ]:
"""Generate prioritized recommendations."""
recommendations = []
# Critical findings
critical = [f for f in self .findings if f.severity == Severity. CRITICAL ]
if critical:
recommendations.append(
f "URGENT: {len (critical) } critical finding(s) require immediate attention"
)
# Major findings
major = [f for f in self .findings if f.severity == Severity. MAJOR ]
if major:
recommendations.append(
f "ACTION: {len (major) } major finding(s) should be addressed within 30 days"
)
# Review overdue
review_overdue = [f for f in self .findings if f.rule == "DOC-DTE-003" ]
if review_overdue:
recommendations.append(
"REVIEW: Document is overdue for periodic review. Initiate review process."
)
# Part 11 gaps
p11_findings = [f for f in self .findings if f.rule.startswith( "P11" )]
if p11_findings:
recommendations.append(
f "COMPLIANCE: {len (p11_findings) } 21 CFR Part 11 gap(s) identified"
)
if not recommendations:
recommendations.append( "Document passes validation checks" )
return recommendations
def format_text_output (result: ValidationResult) -> str :
"""Format validation result as text report."""
lines = [
"=" * 70 ,
"DOCUMENT VALIDATION REPORT" ,
"=" * 70 ,
f "Document: { result.document_number } " ,
f "Validation Date: { result.validation_date } " ,
f "Compliance Score: { result.compliance_score } %" ,
"" ,
"FINDINGS SUMMARY" ,
"-" * 40 ,
f " Critical: { result.critical_findings } " ,
f " Major: { result.major_findings } " ,
f " Minor: { result.minor_findings } " ,
f " Total: { result.total_findings } " ,
]
if result.findings:
lines.extend([
"" ,
"DETAILED FINDINGS" ,
"-" * 40 ,
])
for finding in result.findings:
severity = finding[ 'severity' ]
lines.append( f " \n [ { severity } ] { finding[ 'rule' ] } " )
lines.append( f " Issue: { finding[ 'message' ] } " )
lines.append( f " Action: { finding[ 'recommendation' ] } " )
lines.extend([
"" ,
"RECOMMENDATIONS" ,
"-" * 40 ,
])
for i, rec in enumerate (result.recommendations, 1 ):
lines.append( f " { i } . { rec } " )
lines.append( "=" * 70 )
return " \n " .join(lines)
def interactive_mode ():
"""Run interactive document validation."""
print ( "=" * 60 )
print ( "Document Validator - Interactive Mode" )
print ( "=" * 60 )
print ( " \n Enter document information: \n " )
number = input ( "Document Number (e.g., SOP-02-001): " ).strip()
title = input ( "Document Title: " ).strip()
print ( " \n Document Types: QM, SOP, WI, TF, POL, SPEC, PLN, RPT" )
doc_type = input ( "Document Type: " ).strip().upper()
revision = input ( "Revision (e.g., 01 or A): " ).strip()
print ( " \n Statuses: Draft, Under Review, Approved, Effective, Superseded, Obsolete" )
status = input ( "Status: " ).strip()
effective_date = input ( "Effective Date (YYYY-MM-DD, or Enter to skip): " ).strip() or None
review_date = input ( "Next Review Date (YYYY-MM-DD, or Enter to skip): " ).strip() or None
author = input ( "Author Name (or Enter to skip): " ).strip() or None
approver = input ( "Approver Name (or Enter to skip): " ).strip() or None
has_audit = input ( "Has Audit Trail? (y/n): " ).strip().lower() == 'y'
has_esig = input ( "Uses Electronic Signatures? (y/n): " ).strip().lower() == 'y'
sig_components = 0
if has_esig:
sig_input = input ( "Number of signature components (e.g., 2): " ).strip()
sig_components = int (sig_input) if sig_input.isdigit() else 0
doc = Document(
number = number,
title = title,
doc_type = doc_type,
revision = revision,
status = status,
effective_date = effective_date,
review_date = review_date,
author = author,
approver = approver,
has_audit_trail = has_audit,
has_electronic_signature = has_esig,
signature_components = sig_components
)
validator = DocumentValidator(doc)
result = validator.validate()
print ( " \n " + format_text_output(result))
def main ():
parser = argparse.ArgumentParser(
description = "Quality Documentation Validator"
)
parser.add_argument(
"--doc" ,
type = str ,
help = "JSON file with document metadata"
)
parser.add_argument(
"--output" ,
choices = [ "text" , "json" ],
default = "text" ,
help = "Output format"
)
parser.add_argument(
"--interactive" ,
action = "store_true" ,
help = "Run in interactive mode"
)
parser.add_argument(
"--sample" ,
action = "store_true" ,
help = "Generate sample document JSON"
)
args = parser.parse_args()
if args.interactive:
interactive_mode()
return
if args.sample:
sample = {
"number" : "SOP-02-001" ,
"title" : "Document Control Procedure" ,
"doc_type" : "SOP" ,
"revision" : "03" ,
"status" : "Effective" ,
"effective_date" : "2024-01-15" ,
"review_date" : "2025-01-15" ,
"author" : "J. Smith" ,
"approver" : "M. Jones" ,
"approval_date" : "2024-01-10" ,
"change_history" : [
{ "revision" : "01" , "date" : "2022-01-01" , "description" : "Initial release" },
{ "revision" : "02" , "date" : "2023-01-15" , "description" : "Updated approval workflow" },
{ "revision" : "03" , "date" : "2024-01-15" , "description" : "Added electronic signature requirements" }
],
"has_audit_trail" : True ,
"has_electronic_signature" : True ,
"signature_components" : 2
}
print (json.dumps(sample, indent = 2 ))
return
if args.doc:
with open (args.doc, "r" ) as f:
data = json.load(f)
doc = Document(
number = data.get( "number" , "" ),
title = data.get( "title" , "" ),
doc_type = data.get( "doc_type" , "" ),
revision = data.get( "revision" , "" ),
status = data.get( "status" , "" ),
effective_date = data.get( "effective_date" ),
review_date = data.get( "review_date" ),
author = data.get( "author" ),
approver = data.get( "approver" ),
approval_date = data.get( "approval_date" ),
change_history = data.get( "change_history" , []),
has_audit_trail = data.get( "has_audit_trail" , False ),
has_electronic_signature = data.get( "has_electronic_signature" , False ),
signature_components = data.get( "signature_components" , 0 )
)
else :
# Demo document
doc = Document(
number = "SOP-02-001" ,
title = "Document Control" ,
doc_type = "SOP" ,
revision = "01" ,
status = "Effective" ,
effective_date = "2024-01-15" ,
author = "J. Smith" ,
has_audit_trail = True ,
has_electronic_signature = True ,
signature_components = 2
)
validator = DocumentValidator(doc)
result = validator.validate()
if args.output == "json" :
print (json.dumps(asdict(result), indent = 2 ))
else :
print (format_text_output(result))
if __name__ == "__main__" :
main()
#!/usr/bin/env python3
"""
Document Version Control for Quality Documentation
Manages document lifecycle for quality manuals, SOPs, work instructions, and forms.
Tracks versions, approvals, revisions, change history, electronic signatures per 21 CFR Part 11.
Features:
- Version numbering (Major.Minor.Edit, e.g., 2.1.3)
- Change control with impact assessment
- Review/approval workflows
- Electronic signature capture
- Document distribution tracking
- Training record integration
- Expiry/obsolete management
Usage:
python document_version_control.py --create new_sop.md
python document_version_control.py --revise existing_sop.md --reason "Regulatory update"
python document_version_control.py --status
python document_version_control.py --matrix --output json
"""
import argparse
import json
import os
import hashlib
from dataclasses import dataclass, field, asdict
from typing import List, Dict, Optional, Tuple
from datetime import datetime, timedelta
from pathlib import Path
import re
@dataclass
class DocumentVersion :
"""A single document version."""
doc_id: str
title: str
version: str
revision_date: str
author: str
status: str # "Draft", "Under Review", "Approved", "Obsolete"
change_summary: str = ""
next_review_date: str = ""
approved_by: List[ str ] = field( default_factory = list )
signed_by: List[Dict] = field( default_factory = list ) # electronic signatures
attachments: List[ str ] = field( default_factory = list )
checksum: str = ""
template_version: str = "1.0"
@dataclass
class ChangeControl :
"""Change control record."""
change_id: str
document_id: str
change_type: str # "New", "Revision", "Withdrawal"
reason: str
impact_assessment: Dict # Quality, Regulatory, Training, etc.
risk_assessment: str
notifications: List[ str ]
effective_date: str
change_author: str
class DocumentVersionControl :
"""Manages quality document lifecycle and version control."""
VERSION_PATTERN = re.compile( r ' ^(\d + ) \. (\d + ) \. (\d + )$ ' )
DOCUMENT_TYPES = {
'QMSM' : 'Quality Management System Manual' ,
'SOP' : 'Standard Operating Procedure' ,
'WI' : 'Work Instruction' ,
'FORM' : 'Form/Template' ,
'REC' : 'Record' ,
'POL' : 'Policy'
}
def __init__ (self, doc_store_path: str = "./doc_store" ):
self .doc_store = Path(doc_store_path)
self .doc_store.mkdir( parents = True , exist_ok = True )
self .metadata_file = self .doc_store / "metadata.json"
self .documents = self ._load_metadata()
def _load_metadata (self) -> Dict[ str , DocumentVersion]:
"""Load document metadata from storage."""
if self .metadata_file.exists():
with open ( self .metadata_file, 'r' , encoding = 'utf-8' ) as f:
data = json.load(f)
return {
doc_id: DocumentVersion( ** doc_data)
for doc_id, doc_data in data.items()
}
return {}
def _save_metadata (self):
"""Save document metadata to storage."""
with open ( self .metadata_file, 'w' , encoding = 'utf-8' ) as f:
json.dump({
doc_id: asdict(doc)
for doc_id, doc in self .documents.items()
}, f, indent = 2 , ensure_ascii = False )
def _generate_doc_id (self, title: str , doc_type: str ) -> str :
"""Generate unique document ID."""
# Extract first letters of words, append type code
words = re.findall( r ' \b\w ' , title.upper())
prefix = '' .join(words[: 3 ]) if words else 'DOC'
timestamp = datetime.now().strftime( '%y%m %d %H%M' )
return f " { prefix } - { doc_type } - { timestamp } "
def _parse_version (self, version: str ) -> Tuple[ int , int , int ]:
"""Parse semantic version string."""
match = self . VERSION_PATTERN .match(version)
if match:
return tuple ( int (x) for x in match.groups())
raise ValueError ( f "Invalid version format: { version } " )
def _increment_version (self, current: str , change_type: str ) -> str :
"""Increment version based on change type."""
major, minor, edit = self ._parse_version(current)
if change_type == "Major" :
return f " { major + 1} .0.0"
elif change_type == "Minor" :
return f " { major } . { minor + 1} .0"
else : # Edit
return f " { major } . { minor } . { edit + 1} "
def _calculate_checksum (self, filepath: Path) -> str :
"""Calculate SHA256 checksum of document file."""
with open (filepath, 'rb' ) as f:
return hashlib.sha256(f.read()).hexdigest()
def create_document (
self,
title: str ,
content: str ,
author: str ,
doc_type: str ,
change_summary: str = "Initial release" ,
attachments: List[ str ] = None
) -> DocumentVersion:
"""Create a new document version."""
if doc_type not in self . DOCUMENT_TYPES :
raise ValueError ( f "Invalid document type. Choose from: {list ( self . DOCUMENT_TYPES .keys()) } " )
doc_id = self ._generate_doc_id(title, doc_type)
version = "1.0.0"
revision_date = datetime.now().strftime( '%Y-%m- %d ' )
next_review = (datetime.now() + timedelta( days = 365 )).strftime( '%Y-%m- %d ' )
# Save document content
doc_path = self .doc_store / f " { doc_id } _v { version } .md"
with open (doc_path, 'w' , encoding = 'utf-8' ) as f:
f.write(content)
doc = DocumentVersion(
doc_id = doc_id,
title = title,
version = version,
revision_date = revision_date,
author = author,
status = "Approved" , # Initially approved for simplicity
change_summary = change_summary,
next_review_date = next_review,
attachments = attachments or [],
checksum = self ._calculate_checksum(doc_path)
)
self .documents[doc_id] = doc
self ._save_metadata()
return doc
def revise_document (
self,
doc_id: str ,
new_content: str ,
change_author: str ,
change_type: str = "Edit" ,
change_summary: str = "" ,
attachments: List[ str ] = None
) -> Optional[DocumentVersion]:
"""Create a new revision of an existing document."""
if doc_id not in self .documents:
return None
old_doc = self .documents[doc_id]
new_version = self ._increment_version(old_doc.version, change_type)
revision_date = datetime.now().strftime( '%Y-%m- %d ' )
# Archive old version
old_path = self .doc_store / f " { doc_id } _v { old_doc.version } .md"
archive_path = self .doc_store / "archive" / f " { doc_id } _v { old_doc.version } _ { revision_date } .md"
archive_path.parent.mkdir( exist_ok = True )
if old_path.exists():
os.rename(old_path, archive_path)
# Save new content
doc_path = self .doc_store / f " { doc_id } _v { new_version } .md"
with open (doc_path, 'w' , encoding = 'utf-8' ) as f:
f.write(new_content)
# Create new document record
new_doc = DocumentVersion(
doc_id = doc_id,
title = old_doc.title,
version = new_version,
revision_date = revision_date,
author = change_author,
status = "Draft" , # Needs re-approval
change_summary = change_summary or f "Revision { new_version } " ,
next_review_date = (datetime.now() + timedelta( days = 365 )).strftime( '%Y-%m- %d ' ),
attachments = attachments or old_doc.attachments,
checksum = self ._calculate_checksum(doc_path)
)
self .documents[doc_id] = new_doc
self ._save_metadata()
return new_doc
def approve_document (
self,
doc_id: str ,
approver_name: str ,
approver_title: str ,
comments: str = ""
) -> bool :
"""Approve a document with electronic signature."""
if doc_id not in self .documents:
return False
doc = self .documents[doc_id]
if doc.status != "Draft" :
return False
signature = {
"name" : approver_name,
"title" : approver_title,
"date" : datetime.now().strftime( '%Y-%m- %d %H:%M' ),
"comments" : comments,
"signature_hash" : hashlib.sha256( f " { doc_id }{ doc.version }{ approver_name } " .encode()).hexdigest()[: 16 ]
}
doc.approved_by.append(approver_name)
doc.signed_by.append(signature)
# Approve if enough approvers (simplified: 1 is enough for demo)
doc.status = "Approved"
self ._save_metadata()
return True
def withdraw_document (self, doc_id: str , reason: str , withdrawn_by: str ) -> bool :
"""Withdraw/obsolete a document."""
if doc_id not in self .documents:
return False
doc = self .documents[doc_id]
doc.status = "Obsolete"
doc.change_summary = f "OBsolete: { reason } "
# Add withdrawal signature
signature = {
"name" : withdrawn_by,
"title" : "QMS Manager" ,
"date" : datetime.now().strftime( '%Y-%m- %d %H:%M' ),
"comments" : reason,
"signature_hash" : hashlib.sha256( f " { doc_id } OB { withdrawn_by } " .encode()).hexdigest()[: 16 ]
}
doc.signed_by.append(signature)
self ._save_metadata()
return True
def get_document_history (self, doc_id: str ) -> List[Dict]:
"""Get version history for a document."""
history = []
pattern = f " { doc_id } _v*.md"
for file in self .doc_store.glob(pattern):
match = re.search( r ' _v (\d + \. \d + \. \d + ) \. md $ ' , file .name)
if match:
version = match.group( 1 )
stat = file .stat()
history.append({
"version" : version,
"filename" : file .name,
"size" : stat.st_size,
"modified" : datetime.fromtimestamp(stat.st_mtime).strftime( '%Y-%m- %d %H:%M' )
})
# Check archive
for file in ( self .doc_store / "archive" ).glob( f " { doc_id } _v*.md" ):
match = re.search( r ' _v (\d + \. \d + \. \d + ) _ (\d {4} - \d {2} - \d {2} ) \. md $ ' , file .name)
if match:
version, date = match.groups()
history.append({
"version" : version,
"filename" : file .name,
"status" : "archived" ,
"archived_date" : date
})
return sorted (history, key =lambda x: x[ "version" ])
def generate_document_matrix (self) -> Dict:
"""Generate document matrix report."""
matrix = {
"total_documents" : len ( self .documents),
"by_status" : {},
"by_type" : {},
"documents" : []
}
for doc in self .documents.values():
# By status
matrix[ "by_status" ][doc.status] = matrix[ "by_status" ].get(doc.status, 0 ) + 1
# By type (from doc_id)
doc_type = doc.doc_id.split( '-' )[ 1 ] if '-' in doc.doc_id else "Unknown"
matrix[ "by_type" ][doc_type] = matrix[ "by_type" ].get(doc_type, 0 ) + 1
matrix[ "documents" ].append({
"doc_id" : doc.doc_id,
"title" : doc.title,
"type" : doc_type,
"version" : doc.version,
"status" : doc.status,
"author" : doc.author,
"last_modified" : doc.revision_date,
"next_review" : doc.next_review_date,
"approved_by" : doc.approved_by
})
matrix[ "documents" ].sort( key =lambda x: (x[ "type" ], x[ "title" ]))
return matrix
def format_matrix_text (matrix: Dict) -> str :
"""Format document matrix as text."""
lines = [
"=" * 80 ,
"QUALITY DOCUMENTATION MATRIX" ,
"=" * 80 ,
f "Total Documents: { matrix[ 'total_documents' ] } " ,
"" ,
"BY STATUS" ,
"-" * 40 ,
]
for status, count in matrix[ "by_status" ].items():
lines.append( f " { status } : { count } " )
lines.extend([
"" ,
"BY TYPE" ,
"-" * 40 ,
])
for dtype, count in matrix[ "by_type" ].items():
lines.append( f " { dtype } : { count } " )
lines.extend([
"" ,
"DOCUMENT LIST" ,
"-" * 40 ,
f " { 'ID' :<20 } { 'Type' :<8 } { 'Version' :<10 } { 'Status' :<12 } { 'Title' :<30 } " ,
"-" * 80 ,
])
for doc in matrix[ "documents" ]:
lines.append( f " { doc[ 'doc_id' ][: 19 ] :<20 } { doc[ 'type' ] :<8 } { doc[ 'version' ] :<10 } { doc[ 'status' ] :<12 } { doc[ 'title' ][: 29 ] :<30 } " )
lines.append( "=" * 80 )
return " \n " .join(lines)
def main ():
parser = argparse.ArgumentParser( description = "Document Version Control for Quality Documentation" )
parser.add_argument( "--create" , type = str , help = "Create new document from template" )
parser.add_argument( "--title" , type = str , help = "Document title (required with --create)" )
parser.add_argument( "--type" , choices = list (DocumentVersionControl. DOCUMENT_TYPES .keys()), help = "Document type" )
parser.add_argument( "--author" , type = str , default = "QMS Manager" , help = "Document author" )
parser.add_argument( "--revise" , type = str , help = "Revise existing document (doc_id)" )
parser.add_argument( "--reason" , type = str , help = "Reason for revision" )
parser.add_argument( "--approve" , type = str , help = "Approve document (doc_id)" )
parser.add_argument( "--approver" , type = str , help = "Approver name" )
parser.add_argument( "--withdraw" , type = str , help = "Withdraw document (doc_id)" )
parser.add_argument( "--reason" , type = str , help = "Withdrawal reason" )
parser.add_argument( "--status" , action = "store_true" , help = "Show document status" )
parser.add_argument( "--matrix" , action = "store_true" , help = "Generate document matrix" )
parser.add_argument( "--output" , choices = [ "text" , "json" ], default = "text" )
parser.add_argument( "--interactive" , action = "store_true" , help = "Interactive mode" )
args = parser.parse_args()
dvc = DocumentVersionControl()
if args.create and args.title and args.type:
# Create new document with default content
template = f """# { args.title }
**Document ID:** [auto-generated]
**Version:** 1.0.0
**Date:** { datetime.now().strftime( '%Y-%m- %d ' ) }
**Author:** { args.author }
## Purpose
[Describe the purpose and scope of this document]
## Responsibility
[List roles and responsibilities]
## Procedure
[Detailed procedure steps]
## References
[List referenced documents]
## Revision History
| Version | Date | Author | Change Summary |
|---------|------|--------|----------------|
| 1.0.0 | { datetime.now().strftime( '%Y-%m- %d ' ) } | { args.author } | Initial release |
"""
doc = dvc.create_document(
title = args.title,
content = template,
author = args.author,
doc_type = args.type,
change_summary = args.reason or "Initial release"
)
print ( f "✅ Created document { doc.doc_id } v { doc.version } " )
print ( f " File: doc_store/ { doc.doc_id } _v { doc.version } .md" )
elif args.revise and args.reason:
# Add revision reason to the content (would normally modify the file)
print ( f "📝 Would revise document { args.revise } - reason: { args.reason } " )
print ( " Note: In production, this would load existing content, make changes, and create new revision" )
elif args.approve and args.approver:
success = dvc.approve_document(args.approve, args.approver, "QMS Manager" )
print ( f " { '✅ Approved' if success else '❌ Failed' } document { args.approve } " )
elif args.withdraw and args.reason:
success = dvc.withdraw_document(args.withdraw, args.reason, "QMS Manager" )
print ( f " { '✅ Withdrawn' if success else '❌ Failed' } document { args.withdraw } " )
elif args.matrix:
matrix = dvc.generate_document_matrix()
if args.output == "json" :
print (json.dumps(matrix, indent = 2 ))
else :
print (format_matrix_text(matrix))
elif args.status:
print ( "📋 Document Status:" )
for doc_id, doc in dvc.documents.items():
print ( f " { doc_id } v { doc.version } - { doc.title } [ { doc.status } ]" )
else :
# Demo
print ( "📁 Document Version Control System Demo" )
print ( " Repository contains" , len (dvc.documents), "documents" )
if dvc.documents:
print ( " \n Existing documents:" )
for doc in dvc.documents.values():
print ( f " { doc.doc_id } v { doc.version } - { doc.title } ( { doc.status } )" )
print ( " \n 💡 Usage:" )
print ( " --create \" SOP-001 \" --title \" Document Title \" --type SOP --author \" Your Name \" " )
print ( " --revise DOC-001 --reason \" Regulatory update \" " )
print ( " --approve DOC-001 --approver \" Approver Name \" " )
print ( " --matrix --output text/json" )
if __name__ == "__main__" :
main()