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

Add editing grades #1618

Merged
merged 10 commits into from
May 6, 2024
28 changes: 26 additions & 2 deletions app/lib/grades/grades_service/grades_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,28 @@ class GradesService {
_updateTerm(newTerm);
}

bool _hasGradeWithId(GradeId id) {
return _terms.any((term) => term.hasGrade(id));
/// Replaces an existing grade with [Grade.id] with the [newGrade].
///
/// Throws [GradeNotFoundException] if no grade with the given [Grade.id]
/// of [newGrade] exists.
///
/// Throws [GradeTypeNotFoundException] if the grade type of [newGrade] does
/// not exist.
///
/// Throws [InvalidGradeValueException] if the [Grade.value] of the [newGrade]
/// is not valid for the [Grade.gradingSystem] of the [newGrade].
void editGrade(Grade newGrade) {
if (!_hasGradeWithId(newGrade.id)) {
throw GradeNotFoundException(newGrade.id);
}
if (!_hasGradeTypeWithId(newGrade.type)) {
throw GradeTypeNotFoundException(newGrade.type);
}

final term = _terms.firstWhere((term) => term.containsGrade(newGrade.id));
final newTerm = term.replaceGrade(newGrade);

_updateTerm(newTerm);
}

void deleteGrade(GradeId gradeId) {
Expand All @@ -306,6 +326,10 @@ class GradesService {
throw GradeNotFoundException(gradeId);
}

bool _hasGradeWithId(GradeId id) {
return _terms.any((term) => term.hasGrade(id));
}

void changeGradeWeight({
required GradeId id,
required TermId termId,
Expand Down
62 changes: 44 additions & 18 deletions app/lib/grades/grades_service/src/term.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,24 +180,7 @@ class TermModel extends Equatable {
orElse: () => throw SubjectNotFoundException(toSubject),
);

final originalInput = grade.value;
final gradingSystem = grade.gradingSystem.toGradingSystemModel();
final gradeVal = gradingSystem.toNumOrThrow(grade.value);

subject = subject.addGrade(GradeModel(
id: grade.id,
subjectId: toSubject,
termId: id,
date: grade.date,
originalInput: originalInput,
value: gradingSystem.toGradeResult(gradeVal),
gradingSystem: gradingSystem,
takenIntoAccount: grade.takeIntoAccount,
gradeType: grade.type,
weight: const Weight.factor(1),
title: grade.title,
details: grade.details,
));
subject = subject.addGrade(_toGradeModel(grade, subjectId: subject.id));

return subjects.where((element) => element.id == toSubject).isNotEmpty
? _copyWith(
Expand All @@ -206,6 +189,17 @@ class TermModel extends Equatable {
: _copyWith(subjects: subjects.add(subject));
}

TermModel replaceGrade(Grade grade) {
final subject = subjects.firstWhere((s) => s.hasGrade(grade.id));
final gradeModel = _toGradeModel(grade, subjectId: subject.id);

final newSubject = subject.replaceGrade(gradeModel);

final newSubjects =
subjects.replaceAllWhere((s) => s.id == subject.id, newSubject);
return _copyWith(subjects: newSubjects);
}

TermModel removeGrade(GradeId gradeId) {
final subject = subjects.firstWhere(
(s) => s.hasGrade(gradeId),
Expand All @@ -219,6 +213,27 @@ class TermModel extends Equatable {
);
}

GradeModel _toGradeModel(Grade grade, {required SubjectId subjectId}) {
final originalInput = grade.value;
final gradingSystem = grade.gradingSystem.toGradingSystemModel();
final gradeVal = gradingSystem.toNumOrThrow(grade.value);

return GradeModel(
id: grade.id,
subjectId: subjectId,
termId: id,
date: grade.date,
originalInput: originalInput,
value: gradingSystem.toGradeResult(gradeVal),
gradingSystem: gradingSystem,
takenIntoAccount: grade.takeIntoAccount,
gradeType: grade.type,
weight: const Weight.factor(1),
title: grade.title,
details: grade.details,
);
}

bool hasGrade(GradeId gradeId) {
return subjects.any((s) => s.hasGrade(gradeId));
}
Expand Down Expand Up @@ -301,6 +316,10 @@ class TermModel extends Equatable {
TermModel setGradingSystem(GradingSystemModel gradingSystem) {
return _copyWith(gradingSystem: gradingSystem);
}

bool containsGrade(GradeId id) {
return subjects.any((s) => s.hasGrade(id));
}
}

class SubjectModel extends Equatable {
Expand Down Expand Up @@ -420,6 +439,13 @@ class SubjectModel extends Equatable {
return grades.firstWhere((element) => element.id == id);
}

SubjectModel replaceGrade(GradeModel grade) {
final newGrades = grades
.replaceAllWhere((subjGrade) => subjGrade.id == grade.id, grade)
.toIList();
return copyWith(grades: newGrades);
}

SubjectModel copyWith({
TermId? termId,
SubjectId? id,
Expand Down
119 changes: 119 additions & 0 deletions app/test/grades/grades_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,125 @@ void main() {
.id,
const GradeId('grade2'));
});
test(
'Trying to edit a grade with an unknown Id will throw $GradeNotFoundException',
() {
final controller = GradesTestController();

edit() => controller.editGrade(gradeWith(id: const GradeId('foo')));

expect(edit, throwsA(const GradeNotFoundException(GradeId('foo'))));
});
test('A grade can be edited', () {
final controller = GradesTestController();

final term = termWith(
subjects: [
subjectWith(
id: const SubjectId('Philosophie'),
grades: [
gradeWith(id: const GradeId('grade1')),
],
),
],
);
controller.createTerm(term);

controller.editGrade(
gradeWith(
id: const GradeId('grade1'),
date: Date('2024-03-22'),
details: 'foo details',
includeInGradeCalculations: false,
title: 'foo title',
type: GradeType.other.id,
gradingSystem: GradingSystem.zeroToHundredPercentWithDecimals,
value: '23',
),
);

final actualGrade = controller
.term(term.id)
.subject(const SubjectId('Philosophie'))
.grade(const GradeId('grade1'));

expect(
actualGrade,
GradeResult(
id: const GradeId('grade1'),
isTakenIntoAccount: false,
value: const GradeValue(
asNum: 23,
displayableGrade: null,
gradingSystem: GradingSystem.zeroToHundredPercentWithDecimals,
suffix: '%',
),
date: Date('2024-03-22'),
title: 'foo title',
gradeTypeId: GradeType.other.id,
details: 'foo details',
originalInput: '23',
),
);
});
test(
'If the edited grade has an unknown grade type a $GradeTypeNotFoundException is thrown',
() {
final controller = GradesTestController();

final term = termWith(
subjects: [
subjectWith(
id: const SubjectId('Philosophie'),
grades: [
gradeWith(id: const GradeId('grade1')),
],
),
],
);
controller.createTerm(term);

expect(
() => controller.editGrade(
gradeWith(
id: const GradeId('grade1'),
type: const GradeTypeId('foo'),
),
),
throwsA(const GradeTypeNotFoundException(GradeTypeId('foo'))),
);
});
test(
'If the edited grade has an invalid grade value a $InvalidGradeValueException is thrown',
() {
final controller = GradesTestController();

final term = termWith(
subjects: [
subjectWith(
id: const SubjectId('Philosophie'),
grades: [
gradeWith(id: const GradeId('grade1')),
],
),
],
);
controller.createTerm(term);

expect(
() => controller.editGrade(
gradeWith(
id: const GradeId('grade1'),
gradingSystem: GradingSystem.oneToSixWithPlusAndMinus,
value: '8+',
),
),
throwsA(const InvalidGradeValueException(
gradeInput: '8+',
gradingSystem: GradingSystem.oneToSixWithPlusAndMinus,
)),
);
});
test(
'When trying to add a grade with the same id as an existing grade then a $DuplicateGradeIdException is thrown',
() {
Expand Down
11 changes: 9 additions & 2 deletions app/test/grades/grades_test_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ class GradesTestController {
service.deleteTerm(id);
}

void editGrade(TestGrade grade) {
if (grade.weight != null) {
throw UnimplementedError();
}
service.editGrade(_toGrade(grade));
}

void deleteGrade({required GradeId gradeId}) {
service.deleteGrade(gradeId);
}
Expand Down Expand Up @@ -424,7 +431,7 @@ class TestSubject {
}

TestGrade gradeWith({
required Object value,
Object? value,
bool includeInGradeCalculations = true,
GradeTypeId? type,
Weight? weight,
Expand All @@ -436,7 +443,7 @@ TestGrade gradeWith({
}) {
return TestGrade(
id: id ?? GradeId(randomAlpha(5)),
value: value,
value: value ?? randomBetween(0, 15),
includeInGradeCalculations: includeInGradeCalculations,
date: date ?? Date('2024-02-22'),
gradingSystem: gradingSystem ?? GradingSystem.zeroToFifteenPoints,
Expand Down
Loading