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
168 lines • 7.25 kB
JavaScript
import mongoose, { Schema } from 'mongoose';
const ReviewerAvailabilitySchema = new Schema({
hoursPerWeek: { type: Number, required: true, min: 1, max: 168 },
timeZone: { type: String, required: true },
workingHours: {
start: { type: String, required: true }, // HH:MM format
end: { type: String, required: true } // HH:MM format
},
workingDays: [{ type: Number, min: 0, max: 6 }], // 0-6, Sunday = 0
unavailableDates: [Date],
maxConcurrentReviews: { type: Number, default: 3, min: 1 }
});
const ReviewerPreferencesSchema = new Schema({
preferredDocumentTypes: [String],
preferredProjectTypes: [String],
notificationPreferences: {
email: { type: Boolean, default: true },
inApp: { type: Boolean, default: true },
sms: { type: Boolean, default: false }
},
reminderFrequency: {
type: String,
enum: ['none', 'daily', 'weekly'],
default: 'daily'
}
});
const ReviewerMetricsSchema = new Schema({
totalReviews: { type: Number, default: 0 },
completedReviews: { type: Number, default: 0 },
averageReviewTime: { type: Number, default: 0 }, // in hours
averageQualityScore: { type: Number, default: 0, min: 0, max: 100 },
onTimeCompletionRate: { type: Number, default: 0, min: 0, max: 100 },
// Recent performance
last30DaysReviews: { type: Number, default: 0 },
last30DaysAvgTime: { type: Number, default: 0 },
// Quality metrics
feedbackQualityScore: { type: Number, default: 0, min: 0, max: 100 },
thoroughnessScore: { type: Number, default: 0, min: 0, max: 100 },
// Updated timestamp
lastUpdated: { type: Date, default: Date.now }
});
const ReviewerProfileSchema = new Schema({
userId: { type: String, required: true, unique: true, index: true },
name: { type: String, required: true },
email: { type: String, required: true, index: true },
// Professional information
title: { type: String, required: true },
department: { type: String, required: true },
organization: { type: String, required: true },
// Expertise and qualifications
roles: [{
type: String,
enum: ['subject_matter_expert', 'technical_reviewer', 'compliance_officer', 'project_manager', 'business_analyst', 'quality_assurance', 'stakeholder'],
required: true
}],
expertise: [{ type: String, required: true }],
certifications: [String],
experienceYears: { type: Number, required: true, min: 0 },
// Availability and preferences
availability: { type: ReviewerAvailabilitySchema, required: true },
preferences: { type: ReviewerPreferencesSchema, required: true },
// Performance metrics
metrics: { type: ReviewerMetricsSchema, required: true },
// Status
isActive: { type: Boolean, default: true, index: true }
}, {
timestamps: true,
toJSON: {
transform: function (doc, ret) {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
return ret;
}
}
});
// Indexes for better query performance
ReviewerProfileSchema.index({ roles: 1, isActive: 1 });
ReviewerProfileSchema.index({ expertise: 1, isActive: 1 });
ReviewerProfileSchema.index({ 'availability.hoursPerWeek': 1 });
ReviewerProfileSchema.index({ 'metrics.averageQualityScore': -1 });
ReviewerProfileSchema.index({ 'metrics.onTimeCompletionRate': -1 });
// Virtual for current workload
ReviewerProfileSchema.virtual('currentWorkload').get(function () {
// This would be calculated based on active reviews
// Implementation would query DocumentReview collection
return 0; // Placeholder
});
// Virtual for availability status
ReviewerProfileSchema.virtual('availabilityStatus').get(function () {
const now = new Date();
const currentDay = now.getDay();
const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
// Check if today is a working day
if (!this.availability.workingDays.includes(currentDay)) {
return 'unavailable';
}
// Check if current time is within working hours
if (currentTime < this.availability.workingHours.start || currentTime > this.availability.workingHours.end) {
return 'outside_hours';
}
// Check for unavailable dates
const today = new Date();
today.setHours(0, 0, 0, 0);
const isUnavailable = this.availability.unavailableDates.some(date => {
const unavailableDate = new Date(date);
unavailableDate.setHours(0, 0, 0, 0);
return unavailableDate.getTime() === today.getTime();
});
if (isUnavailable) {
return 'unavailable';
}
return 'available';
});
// Virtual for performance rating
ReviewerProfileSchema.virtual('performanceRating').get(function () {
const qualityWeight = 0.4;
const timelinessWeight = 0.3;
const thoroughnessWeight = 0.3;
const rating = (this.metrics.averageQualityScore * qualityWeight +
this.metrics.onTimeCompletionRate * timelinessWeight +
this.metrics.thoroughnessScore * thoroughnessWeight);
if (rating >= 90)
return 'excellent';
if (rating >= 80)
return 'good';
if (rating >= 70)
return 'satisfactory';
if (rating >= 60)
return 'needs_improvement';
return 'poor';
});
// Method to check if reviewer can take on new review
ReviewerProfileSchema.methods.canTakeReview = function (estimatedHours = 8) {
if (!this.isActive)
return false;
// Check availability status
if (this.availabilityStatus !== 'available')
return false;
// This would check current workload against max concurrent reviews
// Implementation would query active reviews for this reviewer
return true; // Placeholder
};
// Method to update metrics
ReviewerProfileSchema.methods.updateMetrics = async function (reviewData) {
const metrics = this.metrics;
// Update totals
metrics.totalReviews += 1;
metrics.completedReviews += 1;
// Update averages
metrics.averageReviewTime = ((metrics.averageReviewTime * (metrics.completedReviews - 1) + reviewData.reviewTime) /
metrics.completedReviews);
metrics.averageQualityScore = ((metrics.averageQualityScore * (metrics.completedReviews - 1) + reviewData.qualityScore) /
metrics.completedReviews);
// Update on-time completion rate
const onTimeReviews = Math.round(metrics.onTimeCompletionRate * (metrics.completedReviews - 1) / 100);
const newOnTimeReviews = onTimeReviews + (reviewData.onTime ? 1 : 0);
metrics.onTimeCompletionRate = (newOnTimeReviews / metrics.completedReviews) * 100;
// Update quality metrics
metrics.feedbackQualityScore = ((metrics.feedbackQualityScore * (metrics.completedReviews - 1) + reviewData.feedbackQuality) /
metrics.completedReviews);
metrics.thoroughnessScore = ((metrics.thoroughnessScore * (metrics.completedReviews - 1) + reviewData.thoroughness) /
metrics.completedReviews);
metrics.lastUpdated = new Date();
await this.save();
};
export const ReviewerProfileModel = mongoose.model('ReviewerProfile', ReviewerProfileSchema);
//# sourceMappingURL=ReviewerProfile.js.map