Software: Apache. PHP/8.1.30 uname -a: Linux server1.tuhinhossain.com 5.15.0-163-generic #173-Ubuntu SMP Tue Oct 14 17:51:00 UTC uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root) Safe-mode: OFF (not secure) /home/picotech/domains/note.picotech.app/public_html/src/models/ drwxr-xr-x | |
| Viewing file: Select action/file-type: const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Summary = sequelize.define('Summary', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
meeting_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'meetings',
key: 'id',
},
onDelete: 'CASCADE',
},
overview: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
notEmpty: true,
len: [10, 5000],
},
comment: 'High-level summary of the meeting',
},
decisions: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: [],
validate: {
isArray(value) {
if (value && !Array.isArray(value)) {
throw new Error('Decisions must be an array');
}
},
},
comment: 'Array of decisions made during the meeting',
},
action_items: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: [],
validate: {
isValidActionItems(value) {
if (value && !Array.isArray(value)) {
throw new Error('Action items must be an array');
}
if (value) {
for (const item of value) {
if (!item.title || typeof item.title !== 'string') {
throw new Error('Each action item must have a title');
}
}
}
},
},
comment: 'Array of action items with assignees and due dates',
},
risks: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: [],
validate: {
isArray(value) {
if (value && !Array.isArray(value)) {
throw new Error('Risks must be an array');
}
},
},
comment: 'Array of identified risks and concerns',
},
questions: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: [],
validate: {
isArray(value) {
if (value && !Array.isArray(value)) {
throw new Error('Questions must be an array');
}
},
},
comment: 'Array of unresolved questions and follow-ups',
},
key_topics: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: [],
comment: 'Main topics discussed in the meeting',
},
participants_summary: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: {},
comment: 'Summary of each participant\'s contributions',
},
sentiment_analysis: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: {},
comment: 'Overall sentiment and mood analysis',
},
confidence_score: {
type: DataTypes.DECIMAL(3, 2),
allowNull: false,
defaultValue: 0.00,
validate: {
min: 0.00,
max: 1.00,
},
comment: 'AI confidence in the summary accuracy (0.00 to 1.00)',
},
word_count: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
processing_time: {
type: DataTypes.INTEGER,
allowNull: true,
comment: 'Time taken to generate summary in seconds',
},
ai_model: {
type: DataTypes.STRING(100),
allowNull: false,
defaultValue: 'gpt-4',
comment: 'AI model used for summary generation',
},
model_version: {
type: DataTypes.STRING(50),
allowNull: true,
comment: 'Version of the AI model used',
},
prompt_version: {
type: DataTypes.STRING(20),
allowNull: true,
comment: 'Version of the prompt template used',
},
source_data: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: {},
comment: 'Metadata about source data used (notes, transcripts, etc.)',
},
status: {
type: DataTypes.ENUM('generating', 'completed', 'failed', 'reviewed', 'approved'),
allowNull: false,
defaultValue: 'generating',
},
error_message: {
type: DataTypes.TEXT,
allowNull: true,
comment: 'Error message if generation failed',
},
is_edited: {
type: DataTypes.BOOLEAN,
defaultValue: false,
comment: 'Whether the summary has been manually edited',
},
edited_by: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'users',
key: 'id',
},
comment: 'User who last edited the summary',
},
approved_by: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'users',
key: 'id',
},
comment: 'User who approved the summary',
},
approved_at: {
type: DataTypes.DATE,
allowNull: true,
comment: 'When the summary was approved',
},
tags: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: [],
comment: 'Tags for categorizing the summary',
},
priority: {
type: DataTypes.ENUM('low', 'medium', 'high', 'urgent'),
defaultValue: 'medium',
allowNull: false,
},
visibility: {
type: DataTypes.ENUM('private', 'team', 'organization', 'public'),
defaultValue: 'private',
allowNull: false,
},
}, {
tableName: 'summaries',
timestamps: true,
indexes: [
{
fields: ['meeting_id'],
unique: true,
},
{
fields: ['status'],
},
{
fields: ['priority'],
},
{
fields: ['visibility'],
},
{
fields: ['approved_by'],
},
],
hooks: {
beforeSave: (summary) => {
// Calculate word count from overview
if (summary.overview) {
const words = summary.overview.trim().split(/\s+/).filter(word => word.length > 0);
summary.word_count = words.length;
}
},
},
});
// Instance methods
Summary.prototype.getActionItemsCount = function() {
return this.action_items ? this.action_items.length : 0;
};
Summary.prototype.getDecisionsCount = function() {
return this.decisions ? this.decisions.length : 0;
};
Summary.prototype.getRisksCount = function() {
return this.risks ? this.risks.length : 0;
};
Summary.prototype.getQuestionsCount = function() {
return this.questions ? this.questions.length : 0;
};
Summary.prototype.getPendingActionItems = function() {
if (!this.action_items || !Array.isArray(this.action_items)) {
return [];
}
return this.action_items.filter(item =>
!item.status || item.status === 'pending' || item.status === 'in_progress'
);
};
Summary.prototype.getOverdueActionItems = function() {
if (!this.action_items || !Array.isArray(this.action_items)) {
return [];
}
const now = new Date();
return this.action_items.filter(item => {
if (!item.due_date) return false;
const dueDate = new Date(item.due_date);
return dueDate < now && (!item.status || item.status !== 'completed');
});
};
Summary.prototype.getHighPriorityItems = function() {
const items = [];
// High priority action items
if (this.action_items && Array.isArray(this.action_items)) {
items.push(...this.action_items.filter(item =>
item.priority === 'high' || item.priority === 'urgent'
));
}
// All risks are considered high priority
if (this.risks && Array.isArray(this.risks)) {
items.push(...this.risks.map(risk => ({ ...risk, type: 'risk' })));
}
return items;
};
Summary.prototype.addActionItem = function(actionItem) {
if (!this.action_items) {
this.action_items = [];
}
const newItem = {
id: Date.now().toString(),
title: actionItem.title,
description: actionItem.description || '',
assignee: actionItem.assignee || null,
due_date: actionItem.due_date || null,
priority: actionItem.priority || 'medium',
status: actionItem.status || 'pending',
created_at: new Date().toISOString(),
};
this.action_items.push(newItem);
return this;
};
Summary.prototype.updateActionItem = function(itemId, updates) {
if (!this.action_items || !Array.isArray(this.action_items)) {
return false;
}
const itemIndex = this.action_items.findIndex(item => item.id === itemId);
if (itemIndex === -1) {
return false;
}
this.action_items[itemIndex] = {
...this.action_items[itemIndex],
...updates,
updated_at: new Date().toISOString(),
};
return true;
};
Summary.prototype.markAsCompleted = async function() {
this.status = 'completed';
return this.save();
};
Summary.prototype.markAsFailed = async function(errorMessage) {
this.status = 'failed';
this.error_message = errorMessage;
return this.save();
};
Summary.prototype.approve = async function(userId) {
this.status = 'approved';
this.approved_by = userId;
this.approved_at = new Date();
return this.save();
};
// Class methods
Summary.findByMeeting = function(meetingId) {
return this.findOne({
where: { meeting_id: meetingId },
});
};
Summary.findPendingGeneration = function(limit = 10) {
return this.findAll({
where: {
status: 'generating',
},
order: [['created_at', 'ASC']],
limit,
});
};
Summary.findByStatus = function(status, options = {}) {
return this.findAll({
where: {
status,
...options.where,
},
order: options.order || [['created_at', 'DESC']],
limit: options.limit,
});
};
Summary.findRecentByUser = function(userId, limit = 10) {
return this.findAll({
include: [{
model: sequelize.models.Meeting,
where: { user_id: userId },
attributes: [],
}],
order: [['created_at', 'DESC']],
limit,
});
};
Summary.getStatsByUser = async function(userId) {
const summaries = await this.findAll({
include: [{
model: sequelize.models.Meeting,
where: { user_id: userId },
attributes: [],
}],
});
const stats = {
total_summaries: summaries.length,
completed: summaries.filter(s => s.status === 'completed').length,
pending: summaries.filter(s => s.status === 'generating').length,
total_action_items: summaries.reduce((sum, s) => sum + s.getActionItemsCount(), 0),
pending_action_items: summaries.reduce((sum, s) => sum + s.getPendingActionItems().length, 0),
overdue_action_items: summaries.reduce((sum, s) => sum + s.getOverdueActionItems().length, 0),
total_decisions: summaries.reduce((sum, s) => sum + s.getDecisionsCount(), 0),
total_risks: summaries.reduce((sum, s) => sum + s.getRisksCount(), 0),
average_confidence: summaries.length > 0
? summaries.reduce((sum, s) => sum + parseFloat(s.confidence_score), 0) / summaries.length
: 0,
};
return stats;
};
module.exports = Summary; |
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0042 ]-- |