Files
yourpart3/backend/services/calendarService.js

298 lines
9.9 KiB
JavaScript

import CalendarEvent from '../models/community/calendar_event.js';
import User from '../models/community/user.js';
import Friendship from '../models/community/friendship.js';
import UserParam from '../models/community/user_param.js';
import UserParamType from '../models/type/user_param.js';
import UserParamVisibility from '../models/community/user_param_visibility.js';
import UserParamVisibilityType from '../models/type/user_param_visibility.js';
import { Op } from 'sequelize';
class CalendarService {
/**
* Get all calendar events for a user
* @param {string} hashedUserId - The user's hashed ID
* @param {object} options - Optional filters (startDate, endDate)
*/
async getEvents(hashedUserId, options = {}) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
throw new Error('User not found');
}
const where = { userId: user.id };
// Filter by date range if provided
if (options.startDate || options.endDate) {
where[Op.or] = [];
if (options.startDate && options.endDate) {
// Events that overlap with the requested range
where[Op.or].push({
startDate: { [Op.between]: [options.startDate, options.endDate] }
});
where[Op.or].push({
endDate: { [Op.between]: [options.startDate, options.endDate] }
});
where[Op.or].push({
[Op.and]: [
{ startDate: { [Op.lte]: options.startDate } },
{ endDate: { [Op.gte]: options.endDate } }
]
});
} else if (options.startDate) {
where[Op.or].push({ startDate: { [Op.gte]: options.startDate } });
where[Op.or].push({ endDate: { [Op.gte]: options.startDate } });
} else if (options.endDate) {
where[Op.or].push({ startDate: { [Op.lte]: options.endDate } });
}
}
const events = await CalendarEvent.findAll({
where,
order: [['startDate', 'ASC'], ['startTime', 'ASC']]
});
return events.map(e => this.formatEvent(e));
}
/**
* Get a single event by ID
*/
async getEvent(hashedUserId, eventId) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
throw new Error('User not found');
}
const event = await CalendarEvent.findOne({
where: { id: eventId, userId: user.id }
});
if (!event) {
throw new Error('Event not found');
}
return this.formatEvent(event);
}
/**
* Create a new calendar event
*/
async createEvent(hashedUserId, eventData) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
throw new Error('User not found');
}
const event = await CalendarEvent.create({
userId: user.id,
title: eventData.title,
description: eventData.description || null,
categoryId: eventData.categoryId || 'personal',
startDate: eventData.startDate,
endDate: eventData.endDate || eventData.startDate,
startTime: eventData.allDay ? null : eventData.startTime,
endTime: eventData.allDay ? null : eventData.endTime,
allDay: eventData.allDay || false
});
return this.formatEvent(event);
}
/**
* Update an existing calendar event
*/
async updateEvent(hashedUserId, eventId, eventData) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
throw new Error('User not found');
}
const event = await CalendarEvent.findOne({
where: { id: eventId, userId: user.id }
});
if (!event) {
throw new Error('Event not found');
}
await event.update({
title: eventData.title,
description: eventData.description || null,
categoryId: eventData.categoryId || 'personal',
startDate: eventData.startDate,
endDate: eventData.endDate || eventData.startDate,
startTime: eventData.allDay ? null : eventData.startTime,
endTime: eventData.allDay ? null : eventData.endTime,
allDay: eventData.allDay || false
});
return this.formatEvent(event);
}
/**
* Delete a calendar event
*/
async deleteEvent(hashedUserId, eventId) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
throw new Error('User not found');
}
const event = await CalendarEvent.findOne({
where: { id: eventId, userId: user.id }
});
if (!event) {
throw new Error('Event not found');
}
await event.destroy();
return { success: true };
}
/**
* Get friends' birthdays that are visible to the user
* @param {string} hashedUserId - The user's hashed ID
* @param {number} year - The year to get birthdays for
*/
async getFriendsBirthdays(hashedUserId, year) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) {
throw new Error('User not found');
}
// Get user's age for visibility check
const userAge = await this.getUserAge(user.id);
// Get all accepted friendships
const friendships = await Friendship.findAll({
where: {
accepted: true,
withdrawn: false,
denied: false,
[Op.or]: [
{ user1Id: user.id },
{ user2Id: user.id }
]
}
});
const birthdays = [];
for (const friendship of friendships) {
// Get the friend's user ID
const friendId = friendship.user1Id === user.id ? friendship.user2Id : friendship.user1Id;
// Get the friend's birthdate param with visibility
const birthdateParam = await UserParam.findOne({
where: { userId: friendId },
include: [
{
model: UserParamType,
as: 'paramType',
where: { description: 'birthdate' }
},
{
model: UserParamVisibility,
as: 'param_visibilities',
include: [{
model: UserParamVisibilityType,
as: 'visibility_type'
}]
}
]
});
if (!birthdateParam || !birthdateParam.value) continue;
// Check visibility
const visibility = birthdateParam.param_visibilities?.[0]?.visibility_type?.description || 'Invisible';
if (!this.isBirthdayVisibleToFriend(visibility, userAge)) continue;
// Get friend's username
const friend = await User.findOne({
where: { id: friendId },
attributes: ['username', 'hashedId']
});
if (!friend) continue;
// Parse birthdate and create birthday event for the requested year
const birthdate = new Date(birthdateParam.value);
if (isNaN(birthdate.getTime())) continue;
const birthdayDate = `${year}-${String(birthdate.getMonth() + 1).padStart(2, '0')}-${String(birthdate.getDate()).padStart(2, '0')}`;
birthdays.push({
id: `birthday-${friend.hashedId}-${year}`,
title: friend.username,
categoryId: 'birthday',
startDate: birthdayDate,
endDate: birthdayDate,
allDay: true,
isBirthday: true,
friendHashedId: friend.hashedId
});
}
return birthdays;
}
/**
* Check if birthdate is visible to a friend
*/
isBirthdayVisibleToFriend(visibility, requestingUserAge) {
// Visible to friends if visibility is 'All', 'Friends', or 'FriendsAndAdults' (if adult)
return visibility === 'All' ||
visibility === 'Friends' ||
(visibility === 'FriendsAndAdults' && requestingUserAge >= 18);
}
/**
* Get user's age from birthdate
*/
async getUserAge(userId) {
const birthdateParam = await UserParam.findOne({
where: { userId },
include: [{
model: UserParamType,
as: 'paramType',
where: { description: 'birthdate' }
}]
});
if (!birthdateParam || !birthdateParam.value) return 0;
const birthdate = new Date(birthdateParam.value);
const today = new Date();
let age = today.getFullYear() - birthdate.getFullYear();
const monthDiff = today.getMonth() - birthdate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthdate.getDate())) {
age--;
}
return age;
}
/**
* Format event for API response
*/
formatEvent(event) {
return {
id: event.id,
title: event.title,
description: event.description,
categoryId: event.categoryId,
startDate: event.startDate,
endDate: event.endDate,
startTime: event.startTime ? event.startTime.substring(0, 5) : null, // HH:MM format
endTime: event.endTime ? event.endTime.substring(0, 5) : null,
allDay: event.allDay,
createdAt: event.createdAt,
updatedAt: event.updatedAt
};
}
}
export default new CalendarService();