diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java index 767cbc1..ff97331 100644 --- a/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java +++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java @@ -16,6 +16,7 @@ import java.util.regex.Matcher; import androidx.arch.core.util.Function; import de.sebse.fuplanner.services.kvv.types.CacheBBCourse; +import de.sebse.fuplanner.services.kvv.types.CacheKVVCourse; import de.sebse.fuplanner.services.kvv.types.Lecturer; import de.sebse.fuplanner.services.kvv.types.Modules; import de.sebse.fuplanner.services.kvv.types.Semester; @@ -35,6 +36,7 @@ public class ModulesList extends HTTPService { private ModulesListLecturer mLecturer; private CacheBBCourse mBBCache; private final NewAsyncQueue mQueue = new NewAsyncQueue(); + private CacheKVVCourse mKVVCache; ModulesList(Login login, KVVListener listener, Context context) { super(context); @@ -167,69 +169,109 @@ public class ModulesList extends HTTPService { } private void upgradeKVV(final NetworkCallback callback, final NetworkErrorCallback errorCallback) { + NetworkCallback successCallback = (modules -> { + try { + cacheKVVCourse().save(getContext()); + } catch (IOException e) { + e.printStackTrace(); + } + callback.onResponse(modules); + }); + if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) { errorCallback.onError(new NetworkError(101110, 500, "Currently running in offline mode!")); return; } + Modules modules = new Modules(mLogin.getLoginTokenKVV().getUsername()); if (!mLogin.getLoginTokenKVV().isAvailable()) { - callback.onResponse(new Modules(mLogin.getLoginTokenKVV().getUsername())); + callback.onResponse(modules); return; } - get("https://kvv.imp.fu-berlin.de/direct/site.json?_validateSession=", mLogin.getLoginTokenKVV().getCookies(), response -> { + get("https://kvv.imp.fu-berlin.de/direct/membership.json?_validateSession=", mLogin.getLoginTokenKVV().getCookies(), response -> { String body = response.getParsed(); if (body == null) { - errorCallback.onError(new NetworkError(101111, 403, "No module list retrieved!")); + errorCallback.onError(new NetworkError(101111, 403, "No membership list retrieved!")); return; } - Modules modules = new Modules(mLogin.getLoginTokenKVV().getUsername()); - JSONArray sites; + JSONArray memberships; try { JSONObject json = new JSONObject(body); - sites = json.getJSONArray("site_collection"); + memberships = json.getJSONArray("membership_collection"); } catch (JSONException e) { e.printStackTrace(); - errorCallback.onError(new NetworkError(101112, 403, "Cannot parse module list!")); + errorCallback.onError(new NetworkError(101112, 403, "Cannot parse membership list!")); return; } - for (int i = 0; i < sites.length(); i++) { + final int[] latch = {memberships.length()}; + for (int i = 0; i < memberships.length(); i++) { try { - JSONObject site = sites.getJSONObject(i); - String semester_string = site.getJSONObject("props").optString("term_eid", null); - Semester semester; - if (semester_string == null) - semester = null; - else - semester = new Semester(semester_string); - HashSet lvNumbers = new HashSet<>(); - String kvv_lvnumbers = site.getJSONObject("props").optString("kvv_lvnumbers", null); - if (kvv_lvnumbers != null) - for (MatchResult matchResult : Regex.allMatches("[0-9]+", kvv_lvnumbers)) { - lvNumbers.add(matchResult.group()); - } - String title = site.getString("entityTitle"); - LinkedHashSet lecturers = new LinkedHashSet<>(); - String kvv_lecturers = site.getJSONObject("props").optString("kvv_lecturers", null); - if (kvv_lecturers != null) for (String lecturer : kvv_lecturers.split("#")) { - if (lecturer.length() > 2) - lecturers.add(new Lecturer(lecturer)); + JSONObject membership = memberships.getJSONObject(i); + String locationReference = membership.getString("locationReference"); + String courseId = Regex.regex("/site/([0-9a-f-]+)", locationReference); + Modules.Module kvvCourse = cacheKVVCourse().getKVVCourse(courseId); + if (kvvCourse != null) { + kvvCourse = kvvCourse.clone(); + modules.addModule(kvvCourse); + if (--latch[0] == 0) successCallback.onResponse(modules); + continue; } - String type = site.getJSONObject("props").optString("kvv_coursetype", "Projekt"); - String description = site.optString("description", ""); - description = String.valueOf(PartModules.fromHtml(description)); - String id = site.getString("id"); - modules.addModule(semester, lvNumbers, title, lecturers, type, description, id, Modules.TYPE_KVV); + get(String.format("https://kvv.imp.fu-berlin.de/direct/site/%s.json?_validateSession=", courseId), mLogin.getLoginTokenKVV().getCookies(), response1 -> { + String body1 = response1.getParsed(); + if (body1 == null) { + errorCallback.onError(new NetworkError(101113, 403, "No site retrieved!")); + return; + } + try { + JSONObject site = new JSONObject(body1); + + String semester_string = site.getJSONObject("props").optString("term_eid", null); + Semester semester; + if (semester_string == null) + semester = null; + else + semester = new Semester(semester_string); + HashSet lvNumbers = new HashSet<>(); + String kvv_lvnumbers = site.getJSONObject("props").optString("kvv_lvnumbers", null); + if (kvv_lvnumbers != null) + for (MatchResult matchResult : Regex.allMatches("[0-9]+", kvv_lvnumbers)) { + lvNumbers.add(matchResult.group()); + } + String title = site.getString("entityTitle"); + LinkedHashSet lecturers = new LinkedHashSet<>(); + String kvv_lecturers = site.getJSONObject("props").optString("kvv_lecturers", null); + if (kvv_lecturers != null) for (String lecturer : kvv_lecturers.split("#")) { + if (lecturer.length() > 2) + lecturers.add(new Lecturer(lecturer)); + } + String type = site.getJSONObject("props").optString("kvv_coursetype", "Projekt"); + String description = site.optString("description", ""); + description = String.valueOf(PartModules.fromHtml(description)); + String id = site.getString("id"); + Modules.Module module = modules.addModule(semester, lvNumbers, title, lecturers, type, description, id, Modules.TYPE_KVV); + cacheKVVCourse().setKVVCourse(courseId, module.clone()); + if (--latch[0] == 0) successCallback.onResponse(modules); + } catch (JSONException e) { + log.e(new NetworkError(101114, 403, "Cannot parse site!")); + log.e("JSON:", body1); + e.printStackTrace(); + } catch (NoSuchFieldException e) { + log.e(new NetworkError(101115, 403, "Cannot parse site!")); + e.printStackTrace(); + } + }, error -> errorCallback.onError(new NetworkError(101116, error.networkResponse.statusCode, "Cannot get membership list!"))); } catch (JSONException e) { - log.e(new NetworkError(101113, 403, "Cannot parse module list!")); - log.e("ID:", i, "JSON:", sites); + log.e("ID:", i, "JSON:", memberships); e.printStackTrace(); + errorCallback.onError(new NetworkError(101117, 403, "Cannot parse membership list!")); + return; } catch (NoSuchFieldException e) { - log.e(new NetworkError(101114, 403, "Cannot parse module list!")); - log.e("ID:", i, "JSON:", sites); + log.e("ID:", i, "JSON:", memberships); e.printStackTrace(); + errorCallback.onError(new NetworkError(101118, 403, "Cannot parse membership list!")); + return; } } - callback.onResponse(modules); - }, error -> errorCallback.onError(new NetworkError(101115, error.networkResponse.statusCode, "Cannot get module list!"))); + }, error -> errorCallback.onError(new NetworkError(101119, error.networkResponse.statusCode, "Cannot get membership list!"))); } private void upgradeBB(final Modules modulesKVV, final NetworkCallback callback, final NetworkErrorCallback errorCallback) { @@ -363,4 +405,19 @@ public class ModulesList extends HTTPService { mBBCache = new CacheBBCourse(); return mBBCache; } + + private CacheKVVCourse cacheKVVCourse() { + if (mKVVCache == null) { + try { + mKVVCache = CacheKVVCourse.load(getContext()); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + if (mKVVCache == null) + mKVVCache = new CacheKVVCourse(); + return mKVVCache; + } } diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/types/CacheKVVCourse.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/types/CacheKVVCourse.java new file mode 100644 index 0000000..10dd993 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/types/CacheKVVCourse.java @@ -0,0 +1,79 @@ +package de.sebse.fuplanner.services.kvv.types; + +import android.content.Context; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; + +public class CacheKVVCourse implements Serializable { + private transient static final long RESAVE_TIMER = 1000L * 60 * 60 * 24 * 30; + private static final String FILE_NAME = "KVVCourseStorageSaving"; + private static final String FILE_NAME_TIMESTAMP = "KVVCourseStorageSavingTimestamp"; + private transient long mLastTimestamp = 0; + + private HashMap mKVVCourseList = new HashMap<>(); + private HashMap mKVVCourseListRefresh = new HashMap<>(); + + public static CacheKVVCourse load(Context context) throws IOException, ClassNotFoundException { + FileInputStream fis = context.openFileInput(FILE_NAME); + ObjectInputStream is = new ObjectInputStream(fis); + Object readObject = is.readObject(); + if (!(readObject instanceof CacheKVVCourse)) + return null; + CacheKVVCourse storage = (CacheKVVCourse) readObject; + is.close(); + fis.close(); + + fis = context.openFileInput(FILE_NAME_TIMESTAMP); + is = new ObjectInputStream(fis); + storage.mLastTimestamp = is.readLong(); + is.close(); + fis.close(); + + return storage; + } + + public boolean isNewerVersionInStorage(Context context) throws IOException { + FileInputStream fis = context.openFileInput(FILE_NAME_TIMESTAMP); + ObjectInputStream is = new ObjectInputStream(fis); + boolean result = this.mLastTimestamp < is.readLong(); + is.close(); + fis.close(); + return result; + } + + public void save(Context context) throws IOException { + FileOutputStream fos = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE); + ObjectOutputStream os = new ObjectOutputStream(fos); + os.writeObject(this); + os.close(); + fos.close(); + + fos = context.openFileOutput(FILE_NAME_TIMESTAMP, Context.MODE_PRIVATE); + os = new ObjectOutputStream(fos); + this.mLastTimestamp = System.currentTimeMillis(); + os.writeLong(this.mLastTimestamp); + os.close(); + fos.close(); + } + + public void setKVVCourse(String courseID, Modules.Module lecturer) { + mKVVCourseList.put(courseID, lecturer); + mKVVCourseListRefresh.put(courseID, System.currentTimeMillis()); + } + + public Modules.Module getKVVCourse(String courseID) { + if (!mKVVCourseListRefresh.containsKey(courseID)) + return null; + //noinspection ConstantConditions + if (mKVVCourseListRefresh.get(courseID) + RESAVE_TIMER < System.currentTimeMillis()) + return null; + return mKVVCourseList.get(courseID); + } +}