Prashikshak / API /src /util /eventDay.util.ts
Abhisingh-18's picture
Initial commit: Prashikshak - disaster management training platform
9a92a42
Raw
History Blame Contribute Delete
6.31 kB
import EventDay from '../model/eventDay.model';
import { IEvent } from '../model/event.model';
import cloudinary from './cloudinary.util';
/**
* Generate EventDay records for each day between event's startDate and endDate
*/
export const generateEventDays = async (event: IEvent): Promise<void> => {
const startDate = new Date(event.startDate);
const endDate = new Date(event.endDate);
// Set time to midnight for consistent date comparison
startDate.setHours(0, 0, 0, 0);
endDate.setHours(0, 0, 0, 0);
const days: any[] = [];
let currentDate = new Date(startDate);
let dayNumber = 1;
// Parse default time slots
const [startHour, startMinute] = event.defaultStartTime.split(':').map(Number);
const [endHour, endMinute] = event.defaultEndTime.split(':').map(Number);
while (currentDate <= endDate) {
// Create start and end times for this specific day
const dayStartTime = new Date(currentDate);
dayStartTime.setHours(startHour, startMinute, 0, 0);
const dayEndTime = new Date(currentDate);
dayEndTime.setHours(endHour, endMinute, 0, 0);
days.push({
event: event._id,
date: new Date(currentDate),
dayNumber: dayNumber,
startTime: dayStartTime,
endTime: dayEndTime,
photos: [],
attendance: [],
hasStarted: false,
isCompleted: false
});
currentDate.setDate(currentDate.getDate() + 1);
dayNumber++;
}
// Bulk insert all days
if (days.length > 0) {
await EventDay.insertMany(days);
}
};
/**
* Upload event photo to Cloudinary with proper folder structure
* Folder: events/{eventId}/day-{dayNumber}/{timestamp}-{random}
*/
export const uploadEventPhoto = async (
file: any,
eventId: string,
dayNumber: number
): Promise<{ url: string; publicId: string }> => {
return new Promise((resolve, reject) => {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(7);
const folder = `events/${eventId}/day-${dayNumber}`;
const publicId = `${folder}/${timestamp}-${random}`;
const uploadStream = cloudinary.uploader.upload_stream(
{
folder: folder,
public_id: `${timestamp}-${random}`,
resource_type: 'auto'
},
(error, result) => {
if (error || !result) {
reject(error || new Error('Upload failed'));
} else {
resolve({
url: result.secure_url,
publicId: result.public_id
});
}
}
);
uploadStream.end(file.buffer);
});
};
/**
* Delete event photo from Cloudinary
*/
export const deleteEventPhoto = async (publicId: string): Promise<void> => {
await cloudinary.uploader.destroy(publicId);
};
/**
* Calculate attendance statistics for an event
*/
export const calculateAttendanceStats = async (eventId: string): Promise<{
totalDays: number;
completedDays: number;
averageAttendance: number;
dailyStats: Array<{
date: Date;
dayNumber: number;
attendanceCount: number;
isCompleted: boolean;
}>;
}> => {
const eventDays = await EventDay.find({ event: eventId }).sort({ dayNumber: 1 });
const totalDays = eventDays.length;
const completedDays = eventDays.filter(day => day.isCompleted).length;
const dailyStats = eventDays.map(day => ({
date: day.date,
dayNumber: day.dayNumber,
attendanceCount: day.attendance.length,
isCompleted: day.isCompleted
}));
const totalAttendance = dailyStats.reduce((sum, day) => sum + day.attendanceCount, 0);
const averageAttendance = totalDays > 0 ? totalAttendance / totalDays : 0;
return {
totalDays,
completedDays,
averageAttendance: Math.round(averageAttendance * 100) / 100,
dailyStats
};
};
/**
* Delete all EventDay records for an event (used when event is deleted)
*/
export const deleteEventDays = async (eventId: string): Promise<void> => {
// Get all event days to delete their photos from Cloudinary
const eventDays = await EventDay.find({ event: eventId });
// Delete all photos from Cloudinary
for (const day of eventDays) {
for (const photo of day.photos) {
try {
await deleteEventPhoto(photo.publicId);
} catch (err) {
console.error(`Failed to delete photo ${photo.publicId}:`, err);
}
}
}
// Delete all EventDay records
await EventDay.deleteMany({ event: eventId });
};
/**
* Regenerate EventDay records if event dates are updated
*/
export const regenerateEventDays = async (event: IEvent): Promise<void> => {
// Delete existing days (but preserve photos and attendance if possible)
const existingDays = await EventDay.find({ event: event._id }).sort({ date: 1 });
// Delete all existing days
await EventDay.deleteMany({ event: event._id });
// Generate new days
await generateEventDays(event);
// Try to restore data to matching dates
const newDays = await EventDay.find({ event: event._id }).sort({ date: 1 });
for (const existingDay of existingDays) {
const matchingNewDay = newDays.find(
newDay => newDay.date.toISOString().split('T')[0] === existingDay.date.toISOString().split('T')[0]
);
if (matchingNewDay) {
matchingNewDay.photos = existingDay.photos;
matchingNewDay.attendance = existingDay.attendance;
matchingNewDay.notes = existingDay.notes;
matchingNewDay.weatherCondition = existingDay.weatherCondition;
matchingNewDay.activitiesConducted = existingDay.activitiesConducted;
matchingNewDay.incidentsReported = existingDay.incidentsReported;
matchingNewDay.equipmentUsed = existingDay.equipmentUsed;
matchingNewDay.isCompleted = existingDay.isCompleted;
matchingNewDay.completedAt = existingDay.completedAt;
await matchingNewDay.save();
}
}
};