diff --git a/backend/controllers/memberActivityController.js b/backend/controllers/memberActivityController.js index d91791d..b2c5c49 100644 --- a/backend/controllers/memberActivityController.js +++ b/backend/controllers/memberActivityController.js @@ -49,13 +49,19 @@ export const getMemberActivities = async (req, res) => { const participantIds = participants.map(p => p.id); - // Get all diary member activities for this member - const whereClause = { - participantId: participantIds - }; + // Sammle alle Gruppen-IDs, zu denen der Member gehört + const memberGroupIds = new Set(); + participants.forEach(p => { + if (p.groupId !== null && p.groupId !== undefined) { + memberGroupIds.add(p.groupId); + } + }); + // 1. Get all diary member activities explicitly assigned to this member const memberActivities = await DiaryMemberActivity.findAll({ - where: whereClause, + where: { + participantId: participantIds + }, include: [ { model: Participant, @@ -90,28 +96,90 @@ export const getMemberActivities = async (req, res) => { order: [[{ model: DiaryDateActivity, as: 'activity' }, { model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']] }); - // Group activities by name and count occurrences, considering group assignment + // 2. Get all group activities for groups the member belongs to + const groupActivities = []; + if (memberGroupIds.size > 0) { + const groupActivitiesData = await DiaryDateActivity.findAll({ + include: [ + { + model: DiaryDates, + as: 'diaryDate', + where: startDate ? { + date: { + [Op.gte]: startDate + } + } : {} + }, + { + model: PredefinedActivity, + as: 'predefinedActivity', + required: true + }, + { + model: GroupActivity, + as: 'groupActivities', + where: { + groupId: { + [Op.in]: Array.from(memberGroupIds) + } + }, + required: true + } + ] + }); + + // Erstelle virtuelle DiaryMemberActivity-Objekte für Gruppen-Aktivitäten + for (const activity of groupActivitiesData) { + // Finde den entsprechenden Participant für diese Aktivität + const diaryDateId = activity.diaryDateId; + const relevantParticipants = participants.filter(p => p.diaryDateId === diaryDateId); + + for (const participant of relevantParticipants) { + // Prüfe, ob die Aktivität für die Gruppe des Participants ist + const activityGroupIds = activity.groupActivities?.map(ga => ga.groupId) || []; + if (participant.groupId !== null && activityGroupIds.includes(participant.groupId)) { + // Erstelle ein virtuelles DiaryMemberActivity-Objekt + groupActivities.push({ + activity: activity, + participant: participant, + id: null // Virtuell, nicht in DB + }); + } + } + } + } + + // 3. Kombiniere beide Listen und entferne Duplikate + // Ein Duplikat liegt vor, wenn dieselbe Aktivität für denselben Participant bereits explizit zugeordnet ist + const explicitActivityKeys = new Set(); + memberActivities.forEach(ma => { + if (ma.activity && ma.activity.id && ma.participant && ma.participant.id) { + // Erstelle einen eindeutigen Schlüssel: activityId-participantId + const key = `${ma.activity.id}-${ma.participant.id}`; + explicitActivityKeys.add(key); + } + }); + + // Filtere Gruppen-Aktivitäten, die bereits explizit zugeordnet sind + const uniqueGroupActivities = groupActivities.filter(ga => { + if (!ga.activity || !ga.activity.id || !ga.participant || !ga.participant.id) { + return false; + } + const key = `${ga.activity.id}-${ga.participant.id}`; + return !explicitActivityKeys.has(key); + }); + + // Kombiniere beide Listen + const allActivities = [...memberActivities, ...uniqueGroupActivities]; + + // Group activities by name and count occurrences const activityMap = new Map(); - for (const ma of memberActivities) { + for (const ma of allActivities) { if (!ma.activity || !ma.activity.predefinedActivity || !ma.participant) { continue; } - // Check group assignment - const participantGroupId = ma.participant.groupId; - const activityGroupIds = ma.activity.groupActivities?.map(ga => ga.groupId) || []; - - // Filter: Only count if: - // 1. Activity has no group assignment (empty activityGroupIds) - activity is for all groups OR - // 2. Participant's group matches one of the activity's groups - const shouldCount = activityGroupIds.length === 0 || - (participantGroupId !== null && activityGroupIds.includes(participantGroupId)); - - if (!shouldCount) { - continue; - } - const activity = ma.activity.predefinedActivity; const activityName = activity.name; const date = ma.activity.diaryDate?.date; @@ -162,7 +230,15 @@ export const getMemberLastParticipations = async (req, res) => { const participantIds = participants.map(p => p.id); - // Get last participations for this member + // Sammle alle Gruppen-IDs, zu denen der Member gehört + const memberGroupIds = new Set(); + participants.forEach(p => { + if (p.groupId !== null && p.groupId !== undefined) { + memberGroupIds.add(p.groupId); + } + }); + + // 1. Get last participations explicitly assigned to this member const memberActivities = await DiaryMemberActivity.findAll({ where: { participantId: participantIds @@ -197,22 +273,92 @@ export const getMemberLastParticipations = async (req, res) => { limit: parseInt(limit) * 10 // Get more to filter by group }); - // Format the results, considering group assignment - const participations = memberActivities + // 2. Get all group activities for groups the member belongs to + const groupActivities = []; + if (memberGroupIds.size > 0) { + const groupActivitiesData = await DiaryDateActivity.findAll({ + include: [ + { + model: DiaryDates, + as: 'diaryDate' + }, + { + model: PredefinedActivity, + as: 'predefinedActivity', + required: true + }, + { + model: GroupActivity, + as: 'groupActivities', + where: { + groupId: { + [Op.in]: Array.from(memberGroupIds) + } + }, + required: true + } + ], + order: [[{ model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']], + limit: parseInt(limit) * 10 // Get more to filter + }); + + // Erstelle virtuelle DiaryMemberActivity-Objekte für Gruppen-Aktivitäten + for (const activity of groupActivitiesData) { + // Finde den entsprechenden Participant für diese Aktivität + const diaryDateId = activity.diaryDateId; + const relevantParticipants = participants.filter(p => p.diaryDateId === diaryDateId); + + for (const participant of relevantParticipants) { + // Prüfe, ob die Aktivität für die Gruppe des Participants ist + const activityGroupIds = activity.groupActivities?.map(ga => ga.groupId) || []; + if (participant.groupId !== null && activityGroupIds.includes(participant.groupId)) { + // Erstelle ein virtuelles DiaryMemberActivity-Objekt + groupActivities.push({ + activity: activity, + participant: participant, + id: null // Virtuell, nicht in DB + }); + } + } + } + } + + // 3. Kombiniere beide Listen und entferne Duplikate + // Ein Duplikat liegt vor, wenn dieselbe Aktivität für denselben Participant bereits explizit zugeordnet ist + const explicitActivityKeys = new Set(); + memberActivities.forEach(ma => { + if (ma.activity && ma.activity.id && ma.participant && ma.participant.id) { + // Erstelle einen eindeutigen Schlüssel: activityId-participantId + const key = `${ma.activity.id}-${ma.participant.id}`; + explicitActivityKeys.add(key); + } + }); + + // Filtere Gruppen-Aktivitäten, die bereits explizit zugeordnet sind + const uniqueGroupActivities = groupActivities.filter(ga => { + if (!ga.activity || !ga.activity.id || !ga.participant || !ga.participant.id) { + return false; + } + const key = `${ga.activity.id}-${ga.participant.id}`; + return !explicitActivityKeys.has(key); + }); + + // Kombiniere beide Listen + const allActivities = [...memberActivities, ...uniqueGroupActivities]; + + // Format the results + const participations = allActivities .filter(ma => { if (!ma.activity || !ma.activity.predefinedActivity || !ma.activity.diaryDate || !ma.participant) { return false; } - - // Check group assignment - const participantGroupId = ma.participant.groupId; - const activityGroupIds = ma.activity.groupActivities?.map(ga => ga.groupId) || []; - - // Filter: Only count if: - // 1. Activity has no group assignment (empty activityGroupIds) - activity is for all groups OR - // 2. Participant's group matches one of the activity's groups - return activityGroupIds.length === 0 || - (participantGroupId !== null && activityGroupIds.includes(participantGroupId)); + return true; + }) + .sort((a, b) => { + // Sortiere nach Datum (neueste zuerst) + const dateA = new Date(a.activity.diaryDate.date); + const dateB = new Date(b.activity.diaryDate.date); + return dateB - dateA; }) .slice(0, parseInt(limit)) // Limit after filtering .map(ma => ({