Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 126 additions & 19 deletions app/courses/[courseId]/progress/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ export default function LearningProgress() {
skills {
skillName
skillCategory
skillValue
skillValue {
skillValue
}
skillAllUsersStats {
skillValueSum
participantCount
averageSkillValue
}
}
}
Expand Down Expand Up @@ -94,6 +95,76 @@ export default function LearningProgress() {

const [showAverageProgress, setAverageProgress] = useState<boolean>(false);

type skillItem = {
skillValue: number;
skillAverageValue: number;
maxParticipantCount: number;
};

const getCategorySkillKey = (category: string, skillName: string) => {
return `${category}_${skillName}`;
};

const progressBySkill = useMemo(() => {
const progressBySkillValues = new Map<
string,
{
progressSum: number;
averageProgressSum: number;
count: number;
maxParticipantCount: number;
}
>();

uniqueCategories.forEach((category) => {
skillsByCategory[category].forEach((skill) => {
const key = getCategorySkillKey(category, skill.skillName);

if (!progressBySkillValues.has(key)) {
progressBySkillValues.set(key, {
progressSum: 0,
averageProgressSum: 0,
count: 0,
maxParticipantCount: 0,
});
}

const progressItem = progressBySkillValues.get(key)!;

progressItem.progressSum += skill.skillValue.skillValue;
progressItem.averageProgressSum +=
skill.skillAllUsersStats.skillValueSum;
progressItem.count++;
progressItem.maxParticipantCount = Math.max(
progressItem.maxParticipantCount,
skill.skillAllUsersStats.participantCount
);
});
});

const result = new Map<string, skillItem>();
progressBySkillValues.forEach(
(
{
progressSum: sum,
averageProgressSum: averageSum,
count,
maxParticipantCount,
},
key
) => {
result.set(key, {
skillValue: sum / count,
skillAverageValue:
averageSum / count / course.numberOfCourseMemberships,
maxParticipantCount,
});
}
);

return result;
}, [course.numberOfCourseMemberships, skillsByCategory, uniqueCategories]);

const sortedCategories = useMemo(() => {
if (uniqueCategories.length === 0) return [];
return [...uniqueCategories].sort((a, b) => {
Expand All @@ -104,14 +175,16 @@ export default function LearningProgress() {
).values()
);
const progressSum = uniqueSkillsInCategory.reduce((sum, skill) => {
const progress = skill.skillValue;
const progress =
progressBySkill.get(getCategorySkillKey(category, skill.skillName))
?.skillValue ?? 0;
return sum + progress;
}, 0);
return (progressSum / uniqueSkillsInCategory.length) * 100;
};
return getTotalProgress(b) - getTotalProgress(a);
});
}, [skillsByCategory, uniqueCategories]);
}, [progressBySkill, skillsByCategory, uniqueCategories]);

const currentUniqueSkills = useMemo(() => {
if (sortedCategories.length === 0) return [];
Expand All @@ -123,9 +196,23 @@ export default function LearningProgress() {
return acc;
}, [] as (typeof skillsByCategory)[string])
.sort((skillA, skillB) => {
return skillB.skillValue - skillA.skillValue;
const progressA =
progressBySkill.get(
getCategorySkillKey(
sortedCategories[selectedCategory],
skillA.skillName
)
)?.skillValue ?? 0;
const progressB =
progressBySkill.get(
getCategorySkillKey(
sortedCategories[selectedCategory],
skillB.skillName
)
)?.skillValue ?? 0;
return progressB - progressA;
});
}, [selectedCategory, skillsByCategory, sortedCategories]);
}, [progressBySkill, selectedCategory, skillsByCategory, sortedCategories]);

const urgentChapters = useMemo(() => {
return course.chapters.elements.filter((chapter) => {
Expand Down Expand Up @@ -172,11 +259,12 @@ export default function LearningProgress() {
const tempMap = new Map<string, number>();

course.skills.forEach((skill) => {
tempMap.set(skill.skillName, skill.skillValue * 100);
const key = getCategorySkillKey(skill.skillCategory, skill.skillName);
tempMap.set(key, (progressBySkill.get(key)?.skillValue ?? 0) * 100);
});

sessionStorage.setItem("previousProgress", JSON.stringify([...tempMap]));
}, [course.skills, uniqueCategories.length]);
}, [course.skills, progressBySkill, uniqueCategories.length]);

const theme = useTheme();

Expand Down Expand Up @@ -257,33 +345,45 @@ export default function LearningProgress() {
);

const progressSum = uniqueSkillsInCategory.reduce((sum, skill) => {
const progress = skill.skillValue * 100;
const progress =
(progressBySkill.get(
getCategorySkillKey(category, skill.skillName)
)?.skillValue ?? 0) * 100;
return sum + progress;
}, 0);

const averageProgressSum = uniqueSkillsInCategory.reduce(
(sum, skill) => {
const averageProgress =
skill.skillAllUsersStats.averageSkillValue * 100;
(progressBySkill.get(
getCategorySkillKey(category, skill.skillName)
)?.skillAverageValue ?? 0) * 100;
return sum + averageProgress;
},
0
);

const maxParticipantCountForaSkill = Math.max(
...uniqueSkillsInCategory.map(
(skill) => skill.skillAllUsersStats.participantCount
(skill) =>
progressBySkill.get(
getCategorySkillKey(category, skill.skillName)
)?.maxParticipantCount ?? 0
)
);

const categoryProgressValue =
progressSum / uniqueSkillsInCategory.length;

const categoryAverageProgressValue =
averageProgressSum / uniqueSkillsInCategory.length;

const tempSumPreviousProgress = uniqueSkillsInCategory.reduce(
(sum, skill) =>
sum + (previousProgress.get(skill.skillName) ?? 0),
sum +
(previousProgress.get(
getCategorySkillKey(category, skill.skillName)
) ?? 0),
0
);

Expand Down Expand Up @@ -381,13 +481,22 @@ export default function LearningProgress() {
)
);

const skillProgressValue = currentSkill.skillValue * 100;
const key = getCategorySkillKey(
currentSkill.skillCategory,
currentSkill.skillName
);

const skillProgressValue =
(progressBySkill.get(key)?.skillValue ?? 0) * 100;

const skillAverageProgressValue =
currentSkill.skillAllUsersStats.averageSkillValue * 100;
(progressBySkill.get(key)?.skillAverageValue ?? 0) * 100;

const maxParticipantCount =
progressBySkill.get(key)?.maxParticipantCount ?? 0;

const previousSkillProgressValue =
previousProgress.get(currentSkill.skillName) ??
skillProgressValue;
previousProgress.get(key) ?? skillProgressValue;

return (
<Slide
Expand Down Expand Up @@ -420,9 +529,7 @@ export default function LearningProgress() {
}
isUrgent={urgent}
showAverageProgress={showAverageProgress}
participantCount={
currentSkill.skillAllUsersStats.participantCount
}
participantCount={maxParticipantCount}
courseMemberCount={course.numberOfCourseMemberships}
openTaskCount={
filteredSuggestionsBySkill(currentSkill.skillName).length
Expand Down
10 changes: 2 additions & 8 deletions src/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,7 @@ type Query {
inventoryForUser: Inventory!
isAccessTokenAvailable(provider: ExternalServiceProviderDto!): Boolean!
itemsByUserId(userId: UUID!): [UserItem!]!
latestProactiveFeedback: String
mediaRecords: [MediaRecord!]! @deprecated(reason: "In production there should probably be no way to get all media records of the system.")
mediaRecordsByContentIds(contentIds: [UUID!]!): [[MediaRecord!]!]!
mediaRecordsByIds(ids: [UUID!]!): [MediaRecord!]!
Expand All @@ -1360,12 +1361,6 @@ type Query {
thread(id: UUID!): Thread
threadsByContentId(id: UUID!): [Thread!]!
tutorImmersiveWidgetSpeechContent(courseId: UUID!): String!
"""
Retrieves the latest proactive feedback for the current user (any assessment).
The feedback is deleted after retrieval and must be less than 30 minutes old.
Returns the feedback text or null if no feedback is available.
"""
latestProactiveFeedback: String
userCourseRewardScores(courseId: UUID!): RewardScores!
userMediaRecords: [MediaRecord!]!
}
Expand Down Expand Up @@ -1614,11 +1609,10 @@ type Skill {
skillCategory: String!
skillLevels: SkillLevels
skillName: String!
skillValue: Float!
skillValue: SkillValue!
}

type SkillAllUsersStats {
averageSkillValue: Float!
participantCount: Int!
skillId: UUID!
skillValueSum: Float!
Expand Down
Loading