diff --git a/app/src/main/java/de/sebse/fuplanner/fragments/moddetails/ModDetailGradebookAdapter.java b/app/src/main/java/de/sebse/fuplanner/fragments/moddetails/ModDetailGradebookAdapter.java index 8a1e3c9..19bcb96 100644 --- a/app/src/main/java/de/sebse/fuplanner/fragments/moddetails/ModDetailGradebookAdapter.java +++ b/app/src/main/java/de/sebse/fuplanner/fragments/moddetails/ModDetailGradebookAdapter.java @@ -13,6 +13,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import de.sebse.fuplanner.R; import de.sebse.fuplanner.services.kvv.types.Grade; +import de.sebse.fuplanner.services.kvv.types.Gradebook; import de.sebse.fuplanner.services.kvv.types.Modules; import de.sebse.fuplanner.tools.ui.StringViewHolder; @@ -20,9 +21,12 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter> mPositionalData; ModDetailGradebookAdapter() { @@ -38,9 +42,32 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter(TYPE_TOTAL, SECTION_GRADE)); - for (int i = 0; i < getGradesCount(); i++) { - mPositionalData.add(new Pair<>(TYPE_GRADE, SECTION_GRADE +1024*i)); + if (mValue != null && mValue.gradebook != null) { + mGrades = mValue.gradebook.getAutoCategorizedGrades(); + int size; + size = mGrades.getExams().grades.size(); + if (size > 0) { + mPositionalData.add(new Pair<>(setViewType(TYPE_TOTAL, SECTION_EXAM), 0)); + for (int i = 0; i < size; i++) { + mPositionalData.add(new Pair<>(setViewType(TYPE_GRADE, SECTION_EXAM), i)); + } + } + size = mGrades.getAssignments().grades.size(); + if (size > 0) { + mPositionalData.add(new Pair<>(setViewType(TYPE_TOTAL, SECTION_ASSIGNMENT), 0)); + for (int i = 0; i < size; i++) { + mPositionalData.add(new Pair<>(setViewType(TYPE_GRADE, SECTION_ASSIGNMENT), i)); + } + } + size = mGrades.getOthers().grades.size(); + if (size > 0) { + mPositionalData.add(new Pair<>(setViewType(TYPE_TOTAL, SECTION_OTHER), 0)); + for (int i = 0; i < size; i++) { + mPositionalData.add(new Pair<>(setViewType(TYPE_GRADE, SECTION_OTHER), i)); + } + } + } else { + mGrades = null; } this.notifyDataSetChanged(); @@ -53,8 +80,8 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter mPositionalData.size()) return; Pair data = mPositionalData.get(position); - switch (data.first) { + int viewType = getViewType(data.first); + int viewSection = getViewSection(data.first); + Gradebook.GradebookOutput gradebook = getViewSectionGradebook(viewSection); + switch (viewType) { case TYPE_TOTAL: - StringViewHolder h = (StringViewHolder) holder; - h.mString.setText(h.mView.getResources().getString(R.string.current_percentage, mValue.getGradebookPercent()*100)); - break; - case TYPE_GRADE: - int index = data.second / 1024; - GradeViewHolder i = (GradeViewHolder) holder; - Grade gradebook = mValue.gradebook.get(index); + GradeTitleViewHolder h = (GradeTitleViewHolder) holder; - i.mTitle.setText(gradebook.getItemName()); - i.mGrade.setText(String.valueOf(gradebook.getPoints())); - i.mGradeMax.setText(String.valueOf(gradebook.getMaxPoints())); + if (gradebook != null) { + String title = ""; + switch (viewSection) { + case SECTION_EXAM: title = h.mView.getResources().getString(R.string.exam); break; + case SECTION_ASSIGNMENT: title = h.mView.getResources().getString(R.string.assignments); break; + case SECTION_OTHER: title = h.mView.getResources().getString(R.string.others); break; + } + h.mTitle.setText(title); + Grade bestGrade = gradebook.getBestGrade(); + h.mString.setText(h.mView.getResources().getString( + R.string.current_percentage, + gradebook.getUserPointSum(), + gradebook.getMaxPointSum(), + gradebook.getPercentage() * 100, + bestGrade.getPoints(), + bestGrade.getMaxPoints(), + bestGrade.getPoints() / bestGrade.getMaxPoints() * 100, + bestGrade.getItemName() + )); + } + break; + case TYPE_GRADE: + int index = data.second; + GradeViewHolder i = (GradeViewHolder) holder; + + if (gradebook != null) { + Grade grade = gradebook.grades.get(index); + + i.mTitle.setText(grade.getItemName()); + i.mGrade.setText(String.valueOf(grade.getPoints())); + i.mGradeMax.setText(String.valueOf(grade.getMaxPoints())); + } break; } } @@ -101,10 +154,27 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter> { +public class ModulesGradebook extends PartModules { ModulesGradebook(Login login, ModulesList list, Context context) { super(login, list, context); } @Override - protected ArrayList getPart(Modules.Module module) { + protected Gradebook getPart(Modules.Module module) { return module.gradebook; } @Override - protected boolean setPart(Modules.Module module, ArrayList part) { + protected boolean setPart(Modules.Module module, Gradebook part) { boolean changed = module.gradebook == null || module.gradebook.hashCode() != part.hashCode(); module.gradebook = part; return changed; } @Override - protected void upgradeKVV(final String ID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + protected void upgradeKVV(final String ID, final NetworkCallback callback, final NetworkErrorCallback errorCallback) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null || !mLogin.getLoginTokenKVV().isAvailable()) { errorCallback.onError(new NetworkError(101504, 500, "Currently running in offline mode!")); return; @@ -44,7 +45,7 @@ public class ModulesGradebook extends PartModules> { errorCallback.onError(new NetworkError(101501, 403, "No gradebook retrieved!")); return; } - ArrayList gradebook = new ArrayList<>(); + Gradebook gradebook = new Gradebook(); JSONArray sites; try { JSONObject json = new JSONObject(body); @@ -62,7 +63,7 @@ public class ModulesGradebook extends PartModules> { String itemName = site.optString("itemName", null); double maxPoints = site.optDouble("points", -1); - gradebook.add(0, new Grade(itemName, grade, maxPoints)); + gradebook.addGrade(new Grade(itemName, grade, maxPoints)); } catch (JSONException e) { log.e(new NetworkError(101505, 403, "Cannot parse gradebook!")); log.e("ID:", i, "JSON:", sites); @@ -73,14 +74,14 @@ public class ModulesGradebook extends PartModules> { callback.onResponse(gradebook); }, error -> { if (error.networkResponse.statusCode == 400) - callback.onResponse(new ArrayList<>()); + callback.onResponse(new Gradebook()); else errorCallback.onError(new NetworkError(101503, error.networkResponse.statusCode, "Cannot get gradebook!")); }); } @Override - protected void upgradeBB(String ID, NetworkCallback> callback, NetworkErrorCallback errorCallback) { + protected void upgradeBB(String ID, NetworkCallback callback, NetworkErrorCallback errorCallback) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenBB() == null || !mLogin.getLoginTokenBB().isAvailable()) { errorCallback.onError(new NetworkError(101510, 500, "Currently running in offline mode!")); return; @@ -117,7 +118,7 @@ public class ModulesGradebook extends PartModules> { return; } - ArrayList result = new ArrayList<>(); + Gradebook result = new Gradebook(); for (int i = 0; i < grades.length(); i++) { for (int j = 0; j < gradeColumns.length(); j++) { try { @@ -133,7 +134,7 @@ public class ModulesGradebook extends PartModules> { JSONObject score = column.optJSONObject("score"); double maxPoints = score != null ? score.optDouble("possible", 0) : 0; - result.add(new Grade(name, points, maxPoints)); + result.addGrade(new Grade(name, points, maxPoints)); } catch (JSONException e) { log.e(new NetworkError(101515, 400, "Cannot parse grades!")); log.e("ID:", i, "JSON-grades:", grades); @@ -147,7 +148,7 @@ public class ModulesGradebook extends PartModules> { }, error -> errorCallback.onError(new NetworkError(101516, error.networkResponse.statusCode, "Cannot get gradebook columns!"))); }, error -> { if (error.networkResponse.statusCode == 403) - callback.onResponse(new ArrayList<>()); + callback.onResponse(new Gradebook()); else errorCallback.onError(new NetworkError(101517, error.networkResponse.statusCode, "Cannot get gradebook entries!")); }); diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java index b9727c1..2ee94cb 100644 --- a/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java +++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java @@ -23,6 +23,7 @@ import de.sebse.fuplanner.services.kvv.types.Assignment; import de.sebse.fuplanner.services.kvv.types.AssignmentList; import de.sebse.fuplanner.services.kvv.types.EventList; import de.sebse.fuplanner.services.kvv.types.Grade; +import de.sebse.fuplanner.services.kvv.types.Gradebook; import de.sebse.fuplanner.services.kvv.types.Modules; import de.sebse.fuplanner.services.kvv.types.Resource; import de.sebse.fuplanner.tools.CustomNotificationManager; @@ -126,7 +127,7 @@ public class KVVSyncAdapter extends AbstractThreadedSyncAdapter { final ArrayList announcements = module.announcements; final AssignmentList assignments = module.assignments; final EventList events = module.events; - final ArrayList gradebook = module.gradebook; + final Gradebook gradebook = module.gradebook; final ArrayList resources = module.resources; mKVV.modules().details().recv(module, success1 -> { if (success1.second) { diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Gradebook.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Gradebook.java new file mode 100644 index 0000000..fa4436f --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Gradebook.java @@ -0,0 +1,138 @@ +package de.sebse.fuplanner.services.kvv.types; + +import androidx.annotation.NonNull; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public class Gradebook implements Serializable, Iterable { + private ArrayList gradebook = new ArrayList<>(); + private final static String[] EXAM_KEYWORDS = new String[] {"exam", "klausur", "prüfung"}; + private final static String[] ASSIGNMENT_KEYWORDS = new String[] {"übung", "assignment", "blatt", "sheet", "exercise"}; + + public void addGrade(Grade grade) { + gradebook.add(0, grade); + } + + public CategorizedGrades getAutoCategorizedGrades() { + ArrayList exams = new ArrayList<>(); + ArrayList assignments = new ArrayList<>(); + ArrayList others = new ArrayList<>(); + for (Grade grade : gradebook) { + boolean found = false; + String name = grade.getItemName().toLowerCase(); + for (String keyword : EXAM_KEYWORDS) { + if (name.contains(keyword)) { + exams.add(grade); + found = true; + break; + } + } + if (found) continue; + for (String keyword : ASSIGNMENT_KEYWORDS) { + if (name.contains(keyword)) { + assignments.add(grade); + found = true; + break; + } + } + if (found) continue; + others.add(grade); + } + return new CategorizedGrades(exams, assignments, others); + } + + public GradebookOutput getUncategorizedGrades() { + return new GradebookOutput(gradebook); + } + + @NonNull + @Override + public Iterator iterator() { + return gradebook.iterator(); + } + + public class CategorizedGrades { + private GradebookOutput exams; + private GradebookOutput assignments; + private GradebookOutput others; + + private CategorizedGrades(List exams, List assignments, List others) { + this.exams = new GradebookOutput(exams); + this.assignments = new GradebookOutput(assignments); + this.others = new GradebookOutput(others); + } + + public GradebookOutput getExams() { + return exams; + } + + public GradebookOutput getAssignments() { + return assignments; + } + + public GradebookOutput getOthers() { + return others; + } + } + + public class GradebookOutput { + public List grades; + + private GradebookOutput(List grades) { + this.grades = Collections.unmodifiableList(grades); + } + + public float getPercentage() { + float maxPoints = 0; + float userPoints = 0; + if (grades != null) { + for (Grade g : grades){ + maxPoints += g.getMaxPoints(); + userPoints += g.getPoints(); + } + } + if (maxPoints == 0) + return 0; + return userPoints / maxPoints; + } + + public float getMaxPointSum() { + float maxPoints = 0; + if (grades != null) { + for (Grade g : grades){ + maxPoints += g.getMaxPoints(); + } + } + return maxPoints; + } + + public float getUserPointSum() { + float userPoints = 0; + if (grades != null) { + for (Grade g : grades){ + userPoints += g.getPoints(); + } + } + return userPoints; + } + + public Grade getBestGrade() { + Grade bestGrade = null; + double bestValue = -1; + if (grades != null) { + for (Grade g : grades){ + double perc = g.getPoints() / g.getMaxPoints(); + if (perc > bestValue) { + bestValue = perc; + bestGrade = g; + } + } + } + return bestGrade; + } + } +} diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Modules.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Modules.java index 90ff6a6..c8aa233 100644 --- a/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Modules.java +++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/types/Modules.java @@ -178,23 +178,9 @@ public class Modules implements Iterable, Serializable { @Nullable public ArrayList announcements; @Nullable public AssignmentList assignments; @Nullable public EventList events; - @Nullable public ArrayList gradebook; + @Nullable public Gradebook gradebook; @Nullable public ArrayList resources; - public float getGradebookPercent(){ - float maxPoint = 0; - float userPoint = 0; - if (gradebook != null) { - for (Grade g : gradebook){ - maxPoint += g.getMaxPoints(); - userPoint += g.getPoints(); - } - } - if (maxPoint == 0) - return 0; - return userPoint/maxPoint; - } - private Module(@Nullable Semester semester, @NotNull HashSet lvNumber, @NotNull String title, @NotNull LinkedHashSet lecturer, @Nullable String type, @Nullable String description, @NotNull String ID, int moduleType) { title = title.replaceAll("(.*?) (S[0-9]{2}|W[0-9/]{5})", "$1"); diff --git a/app/src/main/res/layout/list_moddetails_gradebook.xml b/app/src/main/res/layout/list_moddetails_gradebook.xml index 7932bcf..6c79674 100644 --- a/app/src/main/res/layout/list_moddetails_gradebook.xml +++ b/app/src/main/res/layout/list_moddetails_gradebook.xml @@ -1,49 +1,56 @@ - - - + + android:orientation="horizontal" + android:padding="5dip" > - + - + - - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_moddetails_gradebook_title.xml b/app/src/main/res/layout/list_moddetails_gradebook_title.xml new file mode 100644 index 0000000..e6eaf55 --- /dev/null +++ b/app/src/main/res/layout/list_moddetails_gradebook_title.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c6f80f6..dfa61fa 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -28,7 +28,7 @@ Keine Einträge vorhanden! Veranstaltungen Noten - Aktuelle Prozentzahl: %1$.2f \%% + Punkte: %1$.1f / %2$.1f\nProzentsatz: %3$.1f \%%\nBeste Note: %4$.1f / %5$.1f - %6$.1f \%% (%7$s) Offline-Modus Aktualisieren fehlgeschlagen… Hey, schau\' dir die neue KVV App an: %1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 519311f..f0b5456 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,7 +30,7 @@ No items available! Events Gradebook - Current Percentage: %1$.2f \%% + Points: %1$.1f / %2$.1f\nPercentage: %3$.1f \%%\nBest Grade: %4$.1f / %5$.1f - %6$.1f \%% (%7$s) Offline Mode Refresh failed… Hey, check out the new KVV app: %1$s