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
232 lines • 6.08 kB
JavaScript
// Document Feedback Model
// filepath: src/models/DocumentFeedback.ts
import mongoose, { Schema } from 'mongoose';
const DocumentFeedbackSchema = new Schema({
projectId: {
type: String,
required: true,
index: true
},
documentType: {
type: String,
required: true,
index: true
},
documentPath: {
type: String,
required: true
},
feedbackType: {
type: String,
enum: ['quality', 'accuracy', 'completeness', 'clarity', 'compliance', 'suggestion'],
required: true,
index: true
},
rating: {
type: Number,
min: 1,
max: 5,
required: true
},
title: {
type: String,
required: true,
trim: true,
maxlength: 200
},
description: {
type: String,
required: true,
trim: true,
maxlength: 2000
},
suggestedImprovement: {
type: String,
trim: true,
maxlength: 2000
},
priority: {
type: String,
enum: ['low', 'medium', 'high', 'critical'],
default: 'medium',
index: true
},
status: {
type: String,
enum: ['open', 'in-review', 'implemented', 'rejected', 'closed'],
default: 'open',
index: true
},
submittedBy: {
type: String,
required: true,
index: true
},
submittedByName: {
type: String,
required: true,
trim: true
},
submittedAt: {
type: Date,
default: Date.now,
index: true
},
reviewedBy: {
type: String,
index: true
},
reviewedAt: {
type: Date
},
implementedAt: {
type: Date
},
tags: [{
type: String,
trim: true,
maxlength: 50
}],
category: {
type: String,
required: true,
index: true
},
aiPromptImpact: {
affectedPrompts: [{
type: String
}],
suggestedPromptChanges: [{
type: String
}]
},
qualityMetrics: {
beforeScore: {
type: Number,
min: 0,
max: 100
},
afterScore: {
type: Number,
min: 0,
max: 100
},
improvementMeasured: {
type: Boolean,
default: false
}
},
relatedFeedback: [{
type: Schema.Types.ObjectId,
ref: 'DocumentFeedback'
}],
attachments: [{
fileName: {
type: String,
required: true
},
filePath: {
type: String,
required: true
},
fileType: {
type: String,
required: true
},
uploadedAt: {
type: Date,
default: Date.now
}
}]
}, {
timestamps: true,
toJSON: {
transform: function (doc, ret) {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
return ret;
}
}
});
// Indexes for better query performance
DocumentFeedbackSchema.index({ projectId: 1, documentType: 1 });
DocumentFeedbackSchema.index({ submittedBy: 1, submittedAt: -1 });
DocumentFeedbackSchema.index({ status: 1, priority: 1 });
DocumentFeedbackSchema.index({ feedbackType: 1, category: 1 });
DocumentFeedbackSchema.index({ rating: 1 });
DocumentFeedbackSchema.index({ tags: 1 });
// Text search index
DocumentFeedbackSchema.index({
title: 'text',
description: 'text',
suggestedImprovement: 'text'
});
// Virtual for feedback age
DocumentFeedbackSchema.virtual('ageInDays').get(function () {
return Math.ceil((Date.now() - this.submittedAt.getTime()) / (1000 * 60 * 60 * 24));
});
// Virtual for resolution time
DocumentFeedbackSchema.virtual('resolutionTimeInDays').get(function () {
if (this.implementedAt) {
return Math.ceil((this.implementedAt.getTime() - this.submittedAt.getTime()) / (1000 * 60 * 60 * 24));
}
return null;
});
// Pre-save middleware to update status timestamps
DocumentFeedbackSchema.pre('save', function (next) {
if (this.isModified('status')) {
if (this.status === 'in-review' && !this.reviewedAt) {
this.reviewedAt = new Date();
}
if (this.status === 'implemented' && !this.implementedAt) {
this.implementedAt = new Date();
}
}
next();
});
// Static methods for analytics
DocumentFeedbackSchema.statics.getProjectFeedbackStats = function (projectId) {
return this.aggregate([
{ $match: { projectId } },
{
$group: {
_id: null,
totalFeedback: { $sum: 1 },
averageRating: { $avg: '$rating' },
byType: {
$push: {
type: '$feedbackType',
rating: '$rating'
}
},
byStatus: {
$push: {
status: '$status',
priority: '$priority'
}
}
}
}
]);
};
DocumentFeedbackSchema.statics.getFeedbackTrends = function (days = 30) {
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
return this.aggregate([
{ $match: { submittedAt: { $gte: startDate } } },
{
$group: {
_id: {
date: { $dateToString: { format: '%Y-%m-%d', date: '$submittedAt' } },
type: '$feedbackType'
},
count: { $sum: 1 },
averageRating: { $avg: '$rating' }
}
},
{ $sort: { '_id.date': 1 } }
]);
};
export const DocumentFeedback = mongoose.model('DocumentFeedback', DocumentFeedbackSchema);
//# sourceMappingURL=DocumentFeedback.js.map