diff --git a/Graduator/Graduator/Model/Subject.swift b/Graduator/Graduator/Model/Subject.swift
index 76451d9..4530ca7 100644
--- a/Graduator/Graduator/Model/Subject.swift
+++ b/Graduator/Graduator/Model/Subject.swift
@@ -12,7 +12,6 @@ struct Subject : Identifiable {
var name: String
var weight: Int
var grade: Double?
- var isCalled: Bool
func gradeIsValid(_ grade: Double?) -> Bool {
return grade == nil || (grade! >= 0 && grade! <= 1)
diff --git a/Graduator/Graduator/Stub.swift b/Graduator/Graduator/Stub.swift
index 6ea6956..4cd9121 100644
--- a/Graduator/Graduator/Stub.swift
+++ b/Graduator/Graduator/Stub.swift
@@ -20,29 +20,25 @@ struct Stub {
id: UUID(),
name: "Processus de développement",
weight: 4,
- grade: 13.17/20.0,
- isCalled: false
+ grade: 13.17/20.0
),
Subject(
id: UUID(),
name: "Programmation Orientée Objet",
weight: 9,
- grade: 13.63/20.0,
- isCalled: false
+ grade: 13.63/20.0
),
Subject(
id: UUID(),
name: "Qualité de développement",
weight: 5,
- grade: 12.4/20.0,
- isCalled: true
+ grade: 12.4/20.0
),
Subject(
id: UUID(),
name: "Remise à niveau objets",
weight: 4,
- grade: 20.0/20.0,
- isCalled: true
+ grade: 20.0/20.0
),
]
),
@@ -56,28 +52,24 @@ struct Stub {
Subject(
id: UUID(),
name: "Internet des Objets",
- weight: 4,
- isCalled: false
+ weight: 4
),
Subject(
id: UUID(),
name: "Réseaux",
weight: 4,
- grade: 14.5/20.0,
- isCalled: true
+ grade: 14.5/20.0
),
Subject(
id: UUID(),
name: "Services mobiles",
- weight: 4,
- isCalled: false
+ weight: 4
),
Subject(
id: UUID(),
name: "Système",
weight: 5,
- grade: 13.88/20.0,
- isCalled: true
+ grade: 13.88/20.0
),
]
),
@@ -92,29 +84,25 @@ struct Stub {
id: UUID(),
name: "Anglais",
weight: 5,
- grade: 18.65/20.0,
- isCalled: false
+ grade: 18.65/20.0
),
Subject(
id: UUID(),
name: "Communication",
weight: 4,
- grade: 17.13/20.0,
- isCalled: false
+ grade: 17.13/20.0
),
Subject(
id: UUID(),
name: "Économie",
weight: 4,
- grade: 9.5/20.0,
- isCalled: false
+ grade: 9.5/20.0
),
Subject(
id: UUID(),
name: "Gestion",
weight: 3,
- grade: 9.5/20.0,
- isCalled: false
+ grade: 9.5/20.0
),
]
),
@@ -129,29 +117,25 @@ struct Stub {
id: UUID(),
name: "Android",
weight: 6,
- grade: 4.0/20.0,
- isCalled: true
+ grade: 4.0/20.0
),
Subject(
id: UUID(),
name: "Architecture de projetc C# .NET (1)",
weight: 5,
- grade: 14.5/20.0,
- isCalled: true
+ grade: 14.5/20.0
),
Subject(
id: UUID(),
name: "C++",
weight: 4,
- grade: 10.2/20.0,
- isCalled: true
+ grade: 10.2/20.0
),
Subject(
id: UUID(),
name: "Swift",
weight: 5,
- grade: 14.93/20.0,
- isCalled: true
+ grade: 14.93/20.0
),
]
),
@@ -166,38 +150,32 @@ struct Stub {
id: UUID(),
name: "Architecture de projetc C# .NET (2)",
weight: 4,
- grade: 12.17/20.0,
- isCalled: false
+ grade: 12.17/20.0
),
Subject(
id: UUID(),
name: "Client/Serveur",
- weight: 4,
- isCalled: false
+ weight: 4
),
Subject(
id: UUID(),
name: "iOS",
- weight: 5,
- isCalled: false
+ weight: 5
),
Subject(
id: UUID(),
name: "Multiplateformes",
- weight: 3,
- isCalled: false
+ weight: 3
),
Subject(
id: UUID(),
name: "Qt Quick",
- weight: 5,
- isCalled: false
+ weight: 5
),
Subject(
id: UUID(),
name: "Xamarin",
- weight: 5,
- isCalled: false
+ weight: 5
),
]
),
@@ -212,8 +190,7 @@ struct Stub {
id: UUID(),
name: "Projet",
weight: 1,
- grade: 13.66/20.0,
- isCalled: true
+ grade: 13.66/20.0
)
]
),
@@ -227,8 +204,7 @@ struct Stub {
Subject(
id: UUID(),
name: "Stage",
- weight: 1,
- isCalled: false
+ weight: 1
)
]
)
diff --git a/Graduator/Graduator/View/Cells/SubjectViewCell.swift b/Graduator/Graduator/View/Cells/SubjectViewCell.swift
index 137edb3..99cfb74 100644
--- a/Graduator/Graduator/View/Cells/SubjectViewCell.swift
+++ b/Graduator/Graduator/View/Cells/SubjectViewCell.swift
@@ -49,7 +49,7 @@ struct SubjectViewCell: View {
}
), in: 0...1, step: 0.001)
.accentColor(grade < 0.5 ? .red : .green)
- .disabled(!isGradeEditable || subjectVM.model.isCalled)
+ .disabled(!isGradeEditable)
TextField("", value: Binding(
get: { grade * 20.0 },
@@ -60,16 +60,7 @@ struct SubjectViewCell: View {
}
), formatter: Formatters.gradeFormatter)
.frame(width: 50)
- .disabled(!isGradeEditable || subjectVM.model.isCalled)
-
- VStack {
- Toggle("", isOn: $subjectVM.model.isCalled)
- .frame(width: 40)
- Image(systemName: "snowflake.circle.fill")
- .foregroundColor(subjectVM.model.isCalled ? .primary : .gray)
- }
-
-
+ .disabled(!isGradeEditable)
} else {
NoGradesInfo()
Button(action: {
diff --git a/Graduator/Graduator/View/Cells/UnitViewCell.swift b/Graduator/Graduator/View/Cells/UnitViewCell.swift
index b6c0f01..e132f11 100644
--- a/Graduator/Graduator/View/Cells/UnitViewCell.swift
+++ b/Graduator/Graduator/View/Cells/UnitViewCell.swift
@@ -35,10 +35,6 @@ struct UnitViewCell: View {
Text(String(format: "%.2f", average * 20.0))
Spacer()
-
- Image(systemName: "snowflake.circle.fill")
- .foregroundColor(unitVM.IsCalled ? .primary : .gray)
-
} else {
NoGradesInfo()
}
diff --git a/Graduator/Graduator/View/Forms/SubjectFormView.swift b/Graduator/Graduator/View/Forms/SubjectFormView.swift
index 91e9ca4..b025356 100644
--- a/Graduator/Graduator/View/Forms/SubjectFormView.swift
+++ b/Graduator/Graduator/View/Forms/SubjectFormView.swift
@@ -27,7 +27,6 @@ struct SubjectFormView: View {
Spacer()
TextField("Note", text: $formVM.gradeString)
}
- Toggle("Note définitive?", isOn: $formVM.isCalled)
}
}
}
diff --git a/Graduator/Graduator/ViewModel/SubjectFormVM.swift b/Graduator/Graduator/ViewModel/SubjectFormVM.swift
index db260a6..1bf2a75 100644
--- a/Graduator/Graduator/ViewModel/SubjectFormVM.swift
+++ b/Graduator/Graduator/ViewModel/SubjectFormVM.swift
@@ -11,7 +11,6 @@ class SubjectFormVM: ObservableObject {
@Published var name: String = ""
@Published var weight: Int = 1
@Published var gradeString: String = ""
- @Published var isCalled: Bool = false
var grade: Double? {
if let gradeOverTwenty = Double(gradeString) {
@@ -22,7 +21,7 @@ class SubjectFormVM: ObservableObject {
}
func isValid(_ subject: Subject) -> Bool {
- return !(name.isEmpty || weight <= 0 || !subject.gradeIsValid(grade) || (isCalled && grade == nil))
+ return !(name.isEmpty || weight <= 0 || !subject.gradeIsValid(grade))
}
func createSubject() -> Subject? {
@@ -30,8 +29,7 @@ class SubjectFormVM: ObservableObject {
id: UUID(),
name: name,
weight: weight,
- grade: grade,
- isCalled: isCalled)
+ grade: grade)
guard isValid(subject) else { return nil }
return subject
}
diff --git a/Graduator/Graduator/ViewModel/SubjectVM.swift b/Graduator/Graduator/ViewModel/SubjectVM.swift
index 50a8da5..405f2d3 100644
--- a/Graduator/Graduator/ViewModel/SubjectVM.swift
+++ b/Graduator/Graduator/ViewModel/SubjectVM.swift
@@ -13,7 +13,6 @@ extension Subject {
var name: String
var weight: Int
var grade: Double?
- var isCalled: Bool
}
var data: Data {
@@ -21,19 +20,12 @@ extension Subject {
id: self.id,
name: self.name,
weight: self.weight,
- grade: self.grade,
- isCalled: self.isCalled
+ grade: self.grade
)
}
mutating func update(from data: Data) {
- // papers please
guard data.id == self.data.id else { return }
- // can't update grade if this subject is called, unless the update is to 'un-call' the subject
- guard !(self.isCalled && data.isCalled && self.grade != data.grade) else { return }
- // can't update a subject to become called and have a nil grade at the same time
- guard !(data.grade == nil && data.isCalled) else { return }
-
if (!data.name.isEmpty) {
self.name = data.name
}
@@ -43,9 +35,7 @@ extension Subject {
} else {
self.grade = nil
}
- self.isCalled = data.isCalled
}
-
}
class SubjectVM : ObservableObject, Identifiable {
@@ -63,8 +53,7 @@ class SubjectVM : ObservableObject, Identifiable {
self.init(subject: Subject(
id: UUID(),
name: "",
- weight: 1,
- isCalled: false
+ weight: 1
))
}
diff --git a/Graduator/Graduator/ViewModel/UnitVM.swift b/Graduator/Graduator/ViewModel/UnitVM.swift
index 9762baf..703b2fc 100644
--- a/Graduator/Graduator/ViewModel/UnitVM.swift
+++ b/Graduator/Graduator/ViewModel/UnitVM.swift
@@ -41,8 +41,7 @@ extension Unit {
id: $0.id,
name: $0.name,
weight: $0.weight,
- grade: $0.grade,
- isCalled: $0.isCalled
+ grade: $0.grade
)
}
}
@@ -117,8 +116,4 @@ class UnitVM : ObservableObject, Identifiable {
var Average: Double? {
return original.getAverage()
}
-
- var IsCalled: Bool {
- return original.subjects.allSatisfy { $0.isCalled }
- }
}
diff --git a/Graduator/Graduator/ViewModel/UnitsManagerVM.swift b/Graduator/Graduator/ViewModel/UnitsManagerVM.swift
index fdfd6bc..dc496ce 100644
--- a/Graduator/Graduator/ViewModel/UnitsManagerVM.swift
+++ b/Graduator/Graduator/ViewModel/UnitsManagerVM.swift
@@ -31,8 +31,7 @@ extension UnitsManager {
id: $0.id,
name: $0.name,
weight: $0.weight,
- grade: $0.grade,
- isCalled: $0.isCalled
+ grade: $0.grade
)
}
)
diff --git a/README.md b/README.md
index 1b76c71..0c337da 100644
--- a/README.md
+++ b/README.md
@@ -2,29 +2,46 @@
Graduator is an iOS application developed with SwiftUI that helps users manage their academic `units`, `subjects`, and grades. Users can add and delete `subjects`, edit the weight and name of `subjects` and `units`, and input grades. The app displays weighted averages and explains the conditions for graduating from the Clermont Auvergne Tech Institute's mobile development BSc in 2023.
+
+
+
## Features
Beyond those basic features, some details need to be specified here.
-### isCalled Feature
+### Weighted average
-The isCalled property, available for each subject, denotes whether the grade of a specific subject has been called or not -- i.e., whether it is a definitive grade or a temporary one.
+A weighted average means that a `subject` or `unit`'s weight plays a part in calculating the average. Users can observe that increasing the weight of a `subject`, for instance, will make the average of the parent `unit` tend more towards that `subject`'s grade.
-From the user's point of view, they can see an image of a snowflake that turns from gray to primary color when a subject grade has been called. A Toggle control allows them to set or unset the isCalled status. If they attempt to change the grade of a subject that has been called, they will not be able to -- the corresponding slider and textfield will remain disabled.
+
+
-In the model and view model, the `isCalled` property of `subjects` affects the state of their `unit`. If all of a `unit`'s `subject` grades are called, the `unit` is considered called too. Note that if a `subject`'s grade is not set (nil), the `subject` can't be called.
+### Deleting a `Subject`
-### Deleting a Subject
+In the app, users can delete a `subject` by swiping it off the list, right-to-left.
-In the app, users can delete a subject by swiping it off the list, right-to-left.
+
+
-Note that when a subject is deleted, it is permanently removed from the system. If a user is in the process of editing and deletes a subject, the deletion occurs immediately upon swiping, not when they save (click 'OK'). If the user chooses to cancel their edits (click 'Annuler'), all other unsaved changes will be discarded, but the deletion of the subject remains.
+Note that when a `subject` is deleted, it is permanently removed from the system. If a user is in the process of editing and deletes a `subject`, the deletion occurs immediately upon swiping, not when they save (click 'OK'). If the user chooses to cancel their edits (click *'Annuler'*), all other unsaved changes will be discarded, but the deletion of the `subject` remains.
### Changing a grade
-Before a user changes a grade, they first need to activate the `lock.open` toggle. If the grade is *called*, they also need to use the `isCalled` toggle.
+Before a user changes a grade, they first need to activate the `lock.open` toggle.
-After a grade was changed, in order to save the change and to see it reflected iun the weighted average, users need to use the (previously `lock.open`, now) `checkmark` toggle.
+
+
+After a grade was changed, in order to save the change and to see it reflected in the `unit`'s weighted average, users need to use the (`lock.open` previously) `checkmark` toggle.
+
+
+
+### Creating a `Subject`
+
+Finally, users can create a `subject` when in edit mode. After clicking on *'Modifier'*, look for a `+` in the top navigation bar.
+
+
+
+
## Architecture
@@ -55,7 +72,6 @@ classDiagram
+deleteSubject(subjectVM: SubjectVM): Void
+addSubject(subject: Subject): Void
+Average: Double?
- +IsCalled: Bool
}
class UnitView {
@@ -68,7 +84,6 @@ classDiagram
+name: String
+weight: Int
+grade: Double?
- +isCalled: Bool
+gradeIsValid(grade: Double?): Bool
+data: Data
+update(from: Data): Void
@@ -123,10 +138,15 @@ classDiagram
MainView -- UnitsManagerVM : Observes
```
+
+
It might be useful to note that, just like `UnitVM`s aggregate `SubjectVM`s, `Unit`s aggregate `Subject`s, but these relationship between `Model` entities were removed from the diagram above for clarity.
Here is the diagram with those relationships depicted.
+
+
+
```mermaid
classDiagram
class Unit {
@@ -152,7 +172,6 @@ classDiagram
+deleteSubject(subjectVM: SubjectVM): Void
+addSubject(subject: Subject): Void
+Average: Double?
- +IsCalled: Bool
}
class UnitView {
@@ -165,7 +184,6 @@ classDiagram
+name: String
+weight: Int
+grade: Double?
- +isCalled: Bool
+gradeIsValid(grade: Double?): Bool
+data: Data
+update(from: Data): Void
diff --git a/docs/create_1.png b/docs/create_1.png
new file mode 100644
index 0000000..899822b
Binary files /dev/null and b/docs/create_1.png differ
diff --git a/docs/create_2.png b/docs/create_2.png
new file mode 100644
index 0000000..a95d7ba
Binary files /dev/null and b/docs/create_2.png differ
diff --git a/docs/delete_1.png b/docs/delete_1.png
new file mode 100644
index 0000000..75482f3
Binary files /dev/null and b/docs/delete_1.png differ
diff --git a/docs/delete_2.png b/docs/delete_2.png
new file mode 100644
index 0000000..4b099fd
Binary files /dev/null and b/docs/delete_2.png differ
diff --git a/docs/grade_1.png b/docs/grade_1.png
new file mode 100644
index 0000000..06ddf14
Binary files /dev/null and b/docs/grade_1.png differ
diff --git a/docs/grade_2.png b/docs/grade_2.png
new file mode 100644
index 0000000..1ab59a6
Binary files /dev/null and b/docs/grade_2.png differ
diff --git a/docs/home.png b/docs/home.png
new file mode 100644
index 0000000..167e093
Binary files /dev/null and b/docs/home.png differ
diff --git a/docs/unit.png b/docs/unit.png
new file mode 100644
index 0000000..a15862a
Binary files /dev/null and b/docs/unit.png differ
diff --git a/docs/weight_1.png b/docs/weight_1.png
new file mode 100644
index 0000000..aa5a237
Binary files /dev/null and b/docs/weight_1.png differ
diff --git a/docs/weight_2.png b/docs/weight_2.png
new file mode 100644
index 0000000..e9d815f
Binary files /dev/null and b/docs/weight_2.png differ