fix: map exact Brightwheel video_info, state (1=start/in, 0=end, 2=out), and actor metadata
This commit is contained in:
+46
-30
@@ -642,6 +642,20 @@ function extractMediaUrls(act) {
|
||||
let videoUrl = null;
|
||||
let thumbUrl = null;
|
||||
|
||||
// Direct check for Brightwheel video_info object
|
||||
if (act.video_info && typeof act.video_info === 'object') {
|
||||
if (act.video_info.downloadable_url) videoUrl = act.video_info.downloadable_url;
|
||||
else if (act.video_info.streamable_url) videoUrl = act.video_info.streamable_url;
|
||||
if (act.video_info.thumbnail_url) thumbUrl = act.video_info.thumbnail_url;
|
||||
}
|
||||
|
||||
// Direct check for Brightwheel media object
|
||||
if (act.media && typeof act.media === 'object') {
|
||||
if (act.media.image_url) imageUrl = act.media.image_url;
|
||||
if (act.media.video_url && !videoUrl) videoUrl = act.media.video_url;
|
||||
if (act.media.thumbnail_url && !thumbUrl) thumbUrl = act.media.thumbnail_url;
|
||||
}
|
||||
|
||||
const isVideoStr = (url) => {
|
||||
if (!url || typeof url !== 'string') return false;
|
||||
const lower = url.toLowerCase();
|
||||
@@ -662,7 +676,7 @@ function extractMediaUrls(act) {
|
||||
if (isVideoStr(obj) && !videoUrl) videoUrl = obj;
|
||||
else if (isImageStr(obj) && !imageUrl) imageUrl = obj;
|
||||
else if (!imageUrl && !videoUrl) {
|
||||
if (act.activity_type === 'ac_video' || act.type === 'video') videoUrl = obj;
|
||||
if (act.activity_type === 'ac_video' || act.action_type === 'ac_video' || act.type === 'video') videoUrl = obj;
|
||||
else imageUrl = obj;
|
||||
}
|
||||
}
|
||||
@@ -677,7 +691,7 @@ function extractMediaUrls(act) {
|
||||
if (typeof obj === 'object') {
|
||||
// Check video keys
|
||||
const possibleVideos = [
|
||||
obj.video_url, obj.video_stream_url, obj.video_hd_url, obj.video_transcode_url,
|
||||
obj.downloadable_url, obj.streamable_url, obj.video_url, obj.video_stream_url, obj.video_hd_url, obj.video_transcode_url,
|
||||
obj.transcoded_video_url, obj.original_video_url, obj.stream_url, obj.mp4_url,
|
||||
obj.video, obj.source_url, obj.download_url
|
||||
];
|
||||
@@ -702,7 +716,7 @@ function extractMediaUrls(act) {
|
||||
|
||||
// Check generic url
|
||||
if (typeof obj.url === 'string' && obj.url.startsWith('http')) {
|
||||
if (isVideoStr(obj.url) || act.activity_type === 'ac_video' || act.type === 'video') {
|
||||
if (isVideoStr(obj.url) || act.activity_type === 'ac_video' || act.action_type === 'ac_video' || act.type === 'video') {
|
||||
if (!videoUrl) videoUrl = obj.url;
|
||||
} else if (!imageUrl) {
|
||||
imageUrl = obj.url;
|
||||
@@ -719,6 +733,7 @@ function extractMediaUrls(act) {
|
||||
}
|
||||
|
||||
// Recurse into nested common containers
|
||||
if (obj.video_info) search(obj.video_info, depth + 1);
|
||||
if (obj.media) search(obj.media, depth + 1);
|
||||
if (obj.video_data) search(obj.video_data, depth + 1);
|
||||
if (obj.attachments) search(obj.attachments, depth + 1);
|
||||
@@ -733,7 +748,7 @@ function extractMediaUrls(act) {
|
||||
search(act);
|
||||
|
||||
// If it's an ac_video activity and imageUrl was found but points to video, fix assignment
|
||||
if ((act.activity_type === 'ac_video' || act.type === 'video') && !videoUrl && imageUrl) {
|
||||
if ((act.activity_type === 'ac_video' || act.action_type === 'ac_video' || act.type === 'video') && !videoUrl && imageUrl) {
|
||||
videoUrl = imageUrl;
|
||||
imageUrl = null;
|
||||
}
|
||||
@@ -743,11 +758,13 @@ function extractMediaUrls(act) {
|
||||
|
||||
function normalizeActivity(act) {
|
||||
const { imageUrl, videoUrl, thumbUrl } = extractMediaUrls(act);
|
||||
const actType = (act.activity_type || act.action_type || act.type || 'activity').toLowerCase();
|
||||
const rawSubtype = (act.action_type || act.event_type || act.subtype || act.sub_type || act.action || act.state || act.status || '').toLowerCase();
|
||||
const actType = (act.action_type || act.activity_type || act.type || 'activity').toLowerCase();
|
||||
const rawSubtype = (act.action_type || act.event_type || act.subtype || act.sub_type || act.action || '').toLowerCase();
|
||||
const stateVal = String(act.state ?? '');
|
||||
|
||||
// Extract staff / actor name
|
||||
const teacherName = act.actor_name || act.created_by?.name || act.teacher_name || act.staff_name || (act.actor ? `${act.actor.first_name || ''} ${act.actor.last_name || ''}`.trim() : '') || '';
|
||||
// Extract actor / staff name
|
||||
const actor = act.actor ? `${act.actor.first_name || ''} ${act.actor.last_name || ''}`.trim() : '';
|
||||
const teacherName = actor || act.actor_name || act.created_by?.name || act.teacher_name || act.staff_name || '';
|
||||
|
||||
// Parse Subtypes and Human-readable Summaries for each category
|
||||
let actionSummary = '';
|
||||
@@ -761,12 +778,12 @@ function normalizeActivity(act) {
|
||||
let milestoneName = act.milestone_name || act.tag_name || act.skill_name || act.milestone || null;
|
||||
let attendanceInfo = '';
|
||||
|
||||
// 1. NAP
|
||||
// 1. NAP (state 1 = start, state 0 = end/wake)
|
||||
if (actType.includes('nap')) {
|
||||
if (rawSubtype.includes('start') || (napStart && !napEnd && !durationMinutes)) {
|
||||
if (stateVal === '1' || rawSubtype.includes('start') || (napStart && !napEnd && !durationMinutes)) {
|
||||
actionSubtype = 'start_nap';
|
||||
actionSummary = 'Nap Started';
|
||||
} else if (rawSubtype.includes('end') || rawSubtype.includes('wake') || napEnd || durationMinutes) {
|
||||
} else if (stateVal === '0' || rawSubtype.includes('end') || rawSubtype.includes('wake') || napEnd || durationMinutes) {
|
||||
actionSubtype = 'end_nap';
|
||||
const durText = durationMinutes ? `${durationMinutes} mins` : '';
|
||||
actionSummary = durText ? `Nap Ended (${durText})` : 'Nap Ended';
|
||||
@@ -775,20 +792,27 @@ function normalizeActivity(act) {
|
||||
actionSummary = durationMinutes ? `Nap (${durationMinutes} mins)` : 'Nap Log';
|
||||
}
|
||||
}
|
||||
// 2. CHECK-IN / ATTENDANCE
|
||||
// 2. CHECK-IN / ATTENDANCE (state 1 = signed in, state 2 = signed out)
|
||||
else if (actType.includes('checkin') || actType.includes('checkout') || actType.includes('attendance')) {
|
||||
const isOut = rawSubtype.includes('out') || rawSubtype.includes('away') || rawSubtype.includes('pickup') || actType.includes('checkout');
|
||||
const signedBy = act.signed_in_by || act.signed_out_by || act.guardian_name || act.actor_name || teacherName || '';
|
||||
const isOut = stateVal === '2' || rawSubtype.includes('out') || rawSubtype.includes('away') || rawSubtype.includes('pickup') || actType.includes('checkout');
|
||||
if (isOut) {
|
||||
actionSubtype = 'signed_out';
|
||||
actionSummary = signedBy ? `Checked Out / Signed Out (by ${signedBy})` : 'Checked Out / Signed Out';
|
||||
actionSummary = actor ? `Checked Out / Signed Out (by ${actor})` : 'Checked Out / Signed Out';
|
||||
} else {
|
||||
actionSubtype = 'signed_in';
|
||||
actionSummary = signedBy ? `Checked In / Signed In (by ${signedBy})` : 'Checked In / Signed In';
|
||||
actionSummary = actor ? `Checked In / Signed In (by ${actor})` : 'Checked In / Signed In';
|
||||
}
|
||||
attendanceInfo = signedBy ? `Signed by: ${signedBy}` : '';
|
||||
attendanceInfo = actor ? `Signed by: ${actor}` : '';
|
||||
}
|
||||
// 3. FOOD / MEALS
|
||||
// 3. PHOTOS & VIDEOS
|
||||
else if (actType.includes('video') || videoUrl) {
|
||||
actionSubtype = 'video';
|
||||
actionSummary = act.note ? `Video: "${act.note}"` : 'Video Clip';
|
||||
} else if (actType.includes('photo') || imageUrl) {
|
||||
actionSubtype = 'photo';
|
||||
actionSummary = act.note ? `Photo: "${act.note}"` : 'Photo';
|
||||
}
|
||||
// 4. FOOD / MEALS
|
||||
else if (actType.includes('food') || actType.includes('meal')) {
|
||||
if (!foodType && rawSubtype) foodType = rawSubtype;
|
||||
const mealLabel = foodType ? capitalize(foodType) : 'Meal';
|
||||
@@ -796,21 +820,13 @@ function normalizeActivity(act) {
|
||||
actionSubtype = foodType || 'meal';
|
||||
actionSummary = eatLabel ? `${mealLabel} (${eatLabel})` : mealLabel;
|
||||
}
|
||||
// 4. POTTY / DIAPER
|
||||
// 5. POTTY / DIAPER
|
||||
else if (actType.includes('potty') || actType.includes('diaper')) {
|
||||
if (!pottyType && rawSubtype) pottyType = rawSubtype;
|
||||
const pLabel = pottyType ? capitalize(pottyType) : 'Check';
|
||||
actionSubtype = pottyType || 'potty';
|
||||
actionSummary = `Potty / Diaper (${pLabel})`;
|
||||
}
|
||||
// 5. PHOTOS & VIDEOS
|
||||
else if (actType.includes('video') || videoUrl) {
|
||||
actionSubtype = 'video';
|
||||
actionSummary = act.note ? `Video: "${act.note}"` : 'Video Clip';
|
||||
} else if (actType.includes('photo') || imageUrl) {
|
||||
actionSubtype = 'photo';
|
||||
actionSummary = act.note ? `Photo: "${act.note}"` : 'Photo';
|
||||
}
|
||||
// 6. MILESTONES & OBSERVATIONS
|
||||
else if (actType.includes('milestone') || actType.includes('observation') || actType.includes('learn') || actType.includes('kudos')) {
|
||||
actionSubtype = 'milestone';
|
||||
@@ -839,9 +855,9 @@ function normalizeActivity(act) {
|
||||
}
|
||||
|
||||
return {
|
||||
id: act.id || act.object_id || Math.random().toString(36).substring(2),
|
||||
created_at: act.created_at || act.event_date || new Date().toISOString(),
|
||||
activity_type: act.activity_type || actType,
|
||||
id: act.object_id || act.id || Math.random().toString(36).substring(2),
|
||||
created_at: act.event_date || act.created_at || new Date().toISOString(),
|
||||
activity_type: act.action_type || act.activity_type || actType,
|
||||
action_subtype: actionSubtype,
|
||||
action_summary: actionSummary,
|
||||
title: act.title || '',
|
||||
|
||||
Reference in New Issue
Block a user