adpa-enterprise-framework-automation
Version:
Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe
357 lines • 16.3 kB
JavaScript
import { DocumentReviewIntegration } from '../../services/DocumentReviewIntegration.js';
import { logger } from '../../utils/logger.js';
export class DocumentGenerationController {
static integrationService = new DocumentReviewIntegration();
/**
* Generate documents with automatic review creation
*/
static async generateWithReview(req, res, next) {
try {
const options = {
projectId: req.body.projectId,
projectName: req.body.projectName,
context: req.body.context,
enableReview: req.body.enableReview ?? true,
reviewPriority: req.body.reviewPriority ?? 'medium',
requiredRoles: req.body.requiredRoles,
specificReviewers: req.body.specificReviewers,
workflowId: req.body.workflowId,
autoSubmitForReview: req.body.autoSubmitForReview ?? true,
generateAll: req.body.generateAll ?? false,
documentKeys: req.body.documentKeys,
notifyOnCompletion: req.body.notifyOnCompletion ?? false,
notificationRecipients: req.body.notificationRecipients
};
// Validate required fields
if (!options.projectId || !options.projectName || !options.context) {
return res.status(400).json({
error: 'Missing required fields: projectId, projectName, context'
});
}
if (!options.generateAll && (!options.documentKeys || options.documentKeys.length === 0)) {
return res.status(400).json({
error: 'Either generateAll must be true or documentKeys must be provided'
});
}
logger.info(`Starting document generation with review for project: ${options.projectName}`);
const result = await DocumentGenerationController.integrationService.generateDocumentsWithReview(options);
res.status(200).json({
success: result.generationResult.success,
message: result.generationResult.message,
generation: {
documentsGenerated: result.summary.documentsGenerated,
generatedFiles: result.generationResult.generatedFiles,
duration: result.generationResult.duration,
errors: result.generationResult.errors
},
reviews: {
reviewsCreated: result.summary.reviewsCreated,
documentsSubmittedForReview: result.summary.documentsSubmittedForReview,
documentsSkippedReview: result.summary.documentsSkippedReview,
createdReviews: result.reviewsCreated.map(review => ({
id: review.id,
documentName: review.documentName,
status: review.status,
priority: review.priority,
dueDate: review.dueDate
}))
},
summary: result.summary,
errors: result.errors
});
}
catch (error) {
logger.error('Error in generate with review:', error);
next(error);
}
}
/**
* Get project review status
*/
static async getProjectReviewStatus(req, res, next) {
try {
const { projectId } = req.params;
if (!projectId) {
return res.status(400).json({ error: 'Project ID is required' });
}
const status = await DocumentGenerationController.integrationService.getProjectReviewStatus(projectId);
res.json({
projectId,
reviewStatus: status,
completionPercentage: status.totalDocuments > 0
? Math.round((status.completedReviews / status.totalDocuments) * 100)
: 0,
approvalRate: status.completedReviews > 0
? Math.round((status.approvedDocuments / status.completedReviews) * 100)
: 0
});
}
catch (error) {
logger.error('Error getting project review status:', error);
next(error);
}
}
/**
* Regenerate document with feedback
*/
static async regenerateWithFeedback(req, res, next) {
try {
const { reviewId } = req.params;
const { documentKey, context } = req.body;
if (!reviewId || !documentKey || !context) {
return res.status(400).json({
error: 'Missing required fields: reviewId (in params), documentKey, context'
});
}
logger.info(`Regenerating document with feedback for review: ${reviewId}`);
const result = await DocumentGenerationController.integrationService.regenerateDocumentWithFeedback(reviewId, documentKey, context);
if (result.success) {
res.json({
success: true,
message: 'Document regenerated successfully',
newReviewId: result.newReviewId
});
}
else {
res.status(400).json({
success: false,
error: result.error
});
}
}
catch (error) {
logger.error('Error regenerating document with feedback:', error);
next(error);
}
}
/**
* Bulk approve documents
*/
static async bulkApproveDocuments(req, res, next) {
try {
const { projectId } = req.params;
const { documentIds } = req.body;
if (!projectId || !documentIds || !Array.isArray(documentIds)) {
return res.status(400).json({
error: 'Missing required fields: projectId (in params), documentIds (array)'
});
}
logger.info(`Bulk approving ${documentIds.length} documents for project: ${projectId}`);
const result = await DocumentGenerationController.integrationService.bulkApproveDocuments(projectId, documentIds);
res.json({
success: result.approved.length > 0,
message: `Approved ${result.approved.length} documents, ${result.failed.length} failed`,
approved: result.approved,
failed: result.failed,
errors: result.errors,
summary: {
totalRequested: documentIds.length,
approved: result.approved.length,
failed: result.failed.length
}
});
}
catch (error) {
logger.error('Error in bulk approve documents:', error);
next(error);
}
}
/**
* Generate documents only (without review)
*/
static async generateDocumentsOnly(req, res, next) {
try {
const { context, generateAll, documentKeys } = req.body;
if (!context) {
return res.status(400).json({ error: 'Context is required' });
}
if (!generateAll && (!documentKeys || documentKeys.length === 0)) {
return res.status(400).json({
error: 'Either generateAll must be true or documentKeys must be provided'
});
}
// Use the existing DocumentGenerator directly
const { DocumentGenerator } = await import('../../modules/documentGenerator/DocumentGenerator.js');
const generator = new DocumentGenerator(context);
let result;
if (generateAll) {
result = await generator.generateAll();
}
else {
// Generate specific documents
result = {
success: true,
successCount: 0,
failureCount: 0,
generatedFiles: [],
errors: [],
duration: 0,
message: ''
};
const startTime = Date.now();
for (const documentKey of documentKeys) {
const success = await generator.generateOne(documentKey);
if (success) {
result.successCount++;
result.generatedFiles.push(`${documentKey}.md`);
}
else {
result.failureCount++;
result.errors.push({ task: documentKey, error: 'Generation failed' });
}
}
result.duration = Date.now() - startTime;
result.success = result.successCount > 0;
result.message = `Generated ${result.successCount} documents`;
}
res.json({
success: result.success,
message: result.message,
documentsGenerated: result.successCount,
documentsFailed: result.failureCount,
generatedFiles: result.generatedFiles,
duration: result.duration,
errors: result.errors
});
}
catch (error) {
logger.error('Error generating documents only:', error);
next(error);
}
}
/**
* Generate documents with PMBOK validation and review
*/
static async generateWithValidationAndReview(req, res, next) {
try {
const options = {
projectId: req.body.projectId,
projectName: req.body.projectName,
context: req.body.context,
enableReview: true, // Always enable review for validation workflow
reviewPriority: req.body.reviewPriority ?? 'high', // Higher priority for validated documents
requiredRoles: req.body.requiredRoles ?? ['compliance_officer', 'subject_matter_expert'],
specificReviewers: req.body.specificReviewers,
workflowId: req.body.workflowId,
autoSubmitForReview: true,
generateAll: req.body.generateAll ?? true,
documentKeys: req.body.documentKeys,
notifyOnCompletion: req.body.notifyOnCompletion ?? true,
notificationRecipients: req.body.notificationRecipients
};
// Validate required fields
if (!options.projectId || !options.projectName || !options.context) {
return res.status(400).json({
error: 'Missing required fields: projectId, projectName, context'
});
}
logger.info(`Starting document generation with PMBOK validation and review for project: ${options.projectName}`);
// Step 1: Generate documents with review
const result = await DocumentGenerationController.integrationService.generateDocumentsWithReview(options);
// Step 2: Run PMBOK validation on generated documents
let validationResult = null;
if (result.generationResult.success) {
try {
const { DocumentGenerator } = await import('../../modules/documentGenerator/DocumentGenerator.js');
const generator = new DocumentGenerator(options.context);
validationResult = await generator.validatePMBOKCompliance();
}
catch (validationError) {
logger.error('Error running PMBOK validation:', validationError);
result.errors.push(`PMBOK validation failed: ${validationError instanceof Error ? validationError.message : 'Unknown error'}`);
}
}
res.status(200).json({
success: result.generationResult.success,
message: result.generationResult.message,
generation: {
documentsGenerated: result.summary.documentsGenerated,
generatedFiles: result.generationResult.generatedFiles,
duration: result.generationResult.duration,
errors: result.generationResult.errors
},
validation: validationResult ? {
compliance: validationResult.compliance,
consistencyScore: validationResult.consistencyScore,
documentQuality: validationResult.documentQuality
} : null,
reviews: {
reviewsCreated: result.summary.reviewsCreated,
documentsSubmittedForReview: result.summary.documentsSubmittedForReview,
documentsSkippedReview: result.summary.documentsSkippedReview,
createdReviews: result.reviewsCreated.map(review => ({
id: review.id,
documentName: review.documentName,
status: review.status,
priority: review.priority,
dueDate: review.dueDate,
complianceScore: review.metadata?.complianceScore
}))
},
summary: {
...result.summary,
validationCompleted: validationResult !== null,
overallCompliance: validationResult?.compliance ?? false
},
errors: result.errors
});
}
catch (error) {
logger.error('Error in generate with validation and review:', error);
next(error);
}
}
/**
* Get workflow status for a project
*/
static async getWorkflowStatus(req, res, next) {
try {
const { projectId } = req.params;
if (!projectId) {
return res.status(400).json({ error: 'Project ID is required' });
}
// Get review status
const reviewStatus = await DocumentGenerationController.integrationService.getProjectReviewStatus(projectId);
// Calculate workflow progress
const workflowStatus = {
projectId,
phase: 'unknown',
progress: 0,
reviewStatus,
nextActions: [],
blockers: []
};
// Determine current phase and progress
if (reviewStatus.totalDocuments === 0) {
workflowStatus.phase = 'generation';
workflowStatus.progress = 0;
workflowStatus.nextActions.push('Generate project documents');
}
else if (reviewStatus.pendingReviews > 0) {
workflowStatus.phase = 'review';
workflowStatus.progress = Math.round((reviewStatus.completedReviews / reviewStatus.totalDocuments) * 100);
workflowStatus.nextActions.push(`Complete ${reviewStatus.pendingReviews} pending reviews`);
}
else if (reviewStatus.completedReviews === reviewStatus.totalDocuments) {
workflowStatus.phase = 'completed';
workflowStatus.progress = 100;
workflowStatus.nextActions.push('All documents reviewed and approved');
}
// Identify blockers
if (reviewStatus.rejectedDocuments > 0) {
workflowStatus.blockers.push(`${reviewStatus.rejectedDocuments} documents rejected - require revision`);
}
// Check for overdue reviews (would need additional data)
// This is a placeholder - in a real implementation, you'd check due dates
if (reviewStatus.pendingReviews > 0) {
workflowStatus.blockers.push('Some reviews may be overdue');
}
res.json(workflowStatus);
}
catch (error) {
logger.error('Error getting workflow status:', error);
next(error);
}
}
}
//# sourceMappingURL=DocumentGenerationController.js.map