Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(transcript): add weighted average on career screen and on teaching screen #578

Merged
merged 4 commits into from
Feb 6, 2025
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
7 changes: 6 additions & 1 deletion assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@
"averageLabel": "Average grade for admission to the Final Examination",
"finalAverageLabelDescription":{
"description": "It is the average grade, weighted according to the credits relating to each exam with a grade*, adjusted for the {{-excludedCreditsNumber}} worst credits",
"formula": "sum (grade * credits) / sum of credits,\nexcluding the {{-excludedCreditsNumber}} worst credits"
"formula": "(sum (grade * credits) / sum of credits) * (110/30) ,\nexcluding the {{-excludedCreditsNumber}} worst credits"
},
"laude": "*Honours does not count toward the calculation",
"masterAdmissionAverage": "Master admission average grade",
Expand All @@ -774,6 +774,11 @@
"title": "Career",
"weightedAverageLabelDescription":{
"description":"This is the average, weighted according to the credits relating to each exam with a grade*",
"formula": "(sum (grade * credits) / sum of credits) * (110/30)"
},
"weightedAverage": "Weighted average of grades",
"weightedAverageDescription":{
"description":"It is the average grade, weighted according to the credits relating to each exam with a grade*, expressed in thirtieths.",
"formula": "sum (grade * credits) / sum of credits"
},
"yourCareer": "Your career"
Expand Down
7 changes: 6 additions & 1 deletion assets/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@
"averageLabel": "Media di ammissione all’esame di laurea",
"finalAverageLabelDescription":{
"description": "E’ la media, pesata in funzione dei crediti relativi ad ogni esame con voto*, depurata dei {{-excludedCreditsNumber}} peggiori crediti",
"formula": "somma (voto * crediti) / somma crediti,\nescludendo i peggiori {{-excludedCreditsNumber}} crediti"
"formula": "(somma (voto * crediti) / somma crediti) * (110/30),\nescludendo i peggiori {{-excludedCreditsNumber}} crediti"
},
"laude": "*Le lodi non concorrono al calcolo",
"masterAdmissionAverage": "Media di ammissione al II liv",
Expand All @@ -801,6 +801,11 @@
"title": "Carriera",
"weightedAverageLabelDescription":{
"description":"E’ la media, pesata in funzione dei crediti relativi ad ogni esame con voto*",
"formula": "(somma (voto * crediti) / somma crediti) * (110/30)"
},
"weightedAverage": "Media pesata dei voti",
"weightedAverageDescription":{
"description":"E’ la media, pesata in funzione dei crediti relativi ad ogni esame con voto*, espressa in trentesimi",
"formula": "somma (voto * crediti) / somma crediti"
},
"yourCareer": "La tua carriera"
Expand Down
22 changes: 19 additions & 3 deletions lib/ui/components/ModalContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PropsWithChildren } from 'react';
import { PropsWithChildren, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { StyleSheet, View } from 'react-native';
import { ScrollView, StyleSheet, View } from 'react-native';

import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { HeaderAccessory } from '@lib/ui/components/HeaderAccessory';
Expand All @@ -12,16 +12,26 @@ import { Theme } from '@lib/ui/types/Theme';
type Props = {
title: string;
close: () => void;
scrollViewRef?: any;
setScrollOffset?: (value: number) => void;
};

export const ModalContent = ({
children,
close,
title,
scrollViewRef,
setScrollOffset,
}: PropsWithChildren<Props>) => {
const styles = useStylesheet(createStyles);
const { t } = useTranslation();

const handleOnScroll = useCallback((event: any) => {
if (setScrollOffset) {
setScrollOffset(event.nativeEvent.contentOffset.y);
}
}, []);

return (
<View style={styles.container}>
<HeaderAccessory
Expand All @@ -39,7 +49,13 @@ export const ModalContent = ({
adjustSpacing="left"
/>
</HeaderAccessory>
<View>{children}</View>
<ScrollView
onScroll={handleOnScroll}
scrollEventThrottle={120}
ref={scrollViewRef}
>
{children}
</ScrollView>
</View>
);
};
Expand Down
30 changes: 17 additions & 13 deletions src/core/components/BottomModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PropsWithChildren, useRef } from 'react';
import { ScrollView, View } from 'react-native';
import { PropsWithChildren, useCallback } from 'react';
import { View } from 'react-native';
import Modal from 'react-native-modal';

import { SCREEN_HEIGHT, SCREEN_WIDTH } from '@gorhom/bottom-sheet';
Expand All @@ -8,29 +8,30 @@ export type BottomModalProps = PropsWithChildren<{
visible: boolean;
onClose?: () => void;
dismissable?: boolean;
scrollOffset?: number;
scrollViewRef?: any;
}>;

export const BottomModal = ({
children,
visible,
onClose,
dismissable,
scrollOffset,
scrollViewRef,
}: BottomModalProps) => {
const handleCloseModal = () => {
dismissable && onClose?.();
};

const scrollViewRef = useRef<ScrollView>(null);

const handleScrollTo = (position: {
x?: number;
y?: number;
animated?: boolean;
}) => {
if (scrollViewRef.current) {
scrollViewRef.current.scrollTo(position);
}
};
const handleScrollTo = useCallback(
(p: any) => {
if (scrollViewRef && scrollViewRef.current) {
scrollViewRef.current.scrollTo(p);
}
},
[scrollViewRef],
);

return (
<Modal
Expand All @@ -51,9 +52,12 @@ export const BottomModal = ({
supportedOrientations={['landscape', 'portrait']}
onBackdropPress={handleCloseModal}
scrollTo={handleScrollTo}
propagateSwipe
useNativeDriver={false}
useNativeDriverForBackdrop
onSwipeComplete={handleCloseModal}
scrollOffset={scrollOffset}
scrollOffsetMax={100}
>
<View>{children}</View>
</Modal>
Expand Down
12 changes: 9 additions & 3 deletions src/features/teaching/screens/TeachingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { useGetExams } from '../../../core/queries/examHooks';
import { useGetStudent } from '../../../core/queries/studentHooks';
import { useGetSurveyCategories } from '../../../core/queries/surveysHooks';
import { GlobalStyles } from '../../../core/styles/GlobalStyles';
import { formatFinalGrade } from '../../../utils/grades';
import { formatFinalGrade, formatThirtiethsGrade } from '../../../utils/grades';
import { CourseListItem } from '../../courses/components/CourseListItem';
import { isCourseDetailed } from '../../courses/utils/courses';
import { ExamListItem } from '../components/ExamListItem';
Expand Down Expand Up @@ -209,7 +209,14 @@ export const TeachingScreen = ({ navigation }: Props) => {
underlayColor={colors.touchableHighlight}
>
<Row p={5} gap={5} align="center" justify="space-between">
<Col justify="center" flexShrink={1}>
<Col justify="center" flexShrink={1} gap={5}>
<Metric
title={t('transcriptMetricsScreen.weightedAverage')}
value={formatThirtiethsGrade(
!hideGrades ? studentQuery.data?.averageGrade : null,
)}
color={colors.title}
/>
<Metric
title={t('transcriptMetricsScreen.averageLabel')}
value={formatFinalGrade(
Expand All @@ -220,7 +227,6 @@ export const TeachingScreen = ({ navigation }: Props) => {
: null,
)}
color={colors.title}
valueStyle={{ paddingVertical: 10 }}
/>
</Col>
<Col style={styles.graph} flexShrink={1}>
Expand Down
17 changes: 13 additions & 4 deletions src/features/transcript/components/CareerScreenModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ScrollView, StyleSheet, View } from 'react-native';
import { StyleSheet, View } from 'react-native';

import { ModalContent } from '@lib/ui/components/ModalContent';
import { Text } from '@lib/ui/components/Text';
Expand All @@ -9,6 +9,8 @@ export const CareerScreenModal = ({
title,
itemList,
onDismiss,
scrollViewRef,
setScrollOffset,
}: {
title: string;
itemList: {
Expand All @@ -17,12 +19,19 @@ export const CareerScreenModal = ({
dot: boolean;
}[];
onDismiss: () => void;
scrollViewRef?: any;
setScrollOffset?: (value: number) => void;
}) => {
const styles = useStylesheet(createStyles);

return (
<ModalContent close={onDismiss} title={title}>
<ScrollView style={styles.content} accessible>
<ModalContent
close={onDismiss}
title={title}
scrollViewRef={scrollViewRef}
setScrollOffset={setScrollOffset}
>
<View style={styles.content} accessible>
{itemList.map((item, index) => {
if (item.content.description) {
return (
Expand Down Expand Up @@ -50,7 +59,7 @@ export const CareerScreenModal = ({
);
}
})}
</ScrollView>
</View>
</ModalContent>
);
};
Expand Down
40 changes: 39 additions & 1 deletion src/features/transcript/screens/CareerScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native';

Expand Down Expand Up @@ -58,11 +59,25 @@ export const CareerScreen = () => {
close: closeBottomModal,
} = useBottomModal();

const [scrollOffset, setScrollOffset] = useState<number>(0);
const scrollViewRef = useRef<ScrollView | null>(null);
const onPressAverageEvent = () => {
showBottomModal(
<CareerScreenModal
title={t('transcriptMetricsScreen.averagesAndGrades')}
itemList={[
{
title: t('transcriptMetricsScreen.weightedAverage'),
content: {
description: t(
'transcriptMetricsScreen.weightedAverageDescription.description',
),
formula: t(
'transcriptMetricsScreen.weightedAverageDescription.formula',
),
},
dot: true,
},
usePurgedAverageFinalGrade
? {
title: t('transcriptMetricsScreen.averageLabel'),
Expand Down Expand Up @@ -123,6 +138,8 @@ export const CareerScreen = () => {
},
]}
onDismiss={closeBottomModal}
scrollViewRef={scrollViewRef}
setScrollOffset={setScrollOffset}
/>,
);
};
Expand All @@ -147,7 +164,12 @@ export const CareerScreen = () => {

return (
<>
<BottomModal dismissable {...bottomModal} />
<BottomModal
dismissable
{...bottomModal}
scrollOffset={scrollOffset}
scrollViewRef={scrollViewRef}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
contentContainerStyle={styles.container}
Expand Down Expand Up @@ -253,6 +275,22 @@ export const CareerScreen = () => {
/>
<Card style={styles.metricsCard} accessible={true}>
<Col>
<>
<Row>
<Text style={styles.title}>
{t('transcriptMetricsScreen.weightedAverage')}
</Text>
</Row>
<Row style={styles.spaceBottom}>
<Metric
value={formatThirtiethsGrade(
studentQuery.data?.averageGrade,
)}
style={GlobalStyles.grow}
/>
</Row>
</>

<Row>
<Text style={styles.title}>
{t('transcriptMetricsScreen.averageLabel')}
Expand Down