diff --git a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt index ca760df965..ed55b4e3c0 100644 --- a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt +++ b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt @@ -4,6 +4,7 @@ import android.content.Context.MODE_PRIVATE import android.content.SharedPreferences import android.text.TextUtils import android.util.Base64 +import android.util.Log import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonObject @@ -18,12 +19,12 @@ import org.ole.planet.myplanet.MainApplication.Companion.context import org.ole.planet.myplanet.model.RealmMyLibrary.Companion.createStepResource import org.ole.planet.myplanet.model.RealmStepExam.Companion.insertCourseStepsExams import org.ole.planet.myplanet.utilities.Constants.PREFS_NAME -import org.ole.planet.myplanet.utilities.DownloadUtils.extractLinks import org.ole.planet.myplanet.utilities.JsonUtils import org.ole.planet.myplanet.utilities.Utilities import java.io.File import java.io.FileWriter import java.io.IOException +import java.util.regex.Pattern open class RealmMyCourse : RealmObject() { @PrimaryKey @@ -72,46 +73,76 @@ open class RealmMyCourse : RealmObject() { companion object { private val gson = Gson() private val concatenatedLinks = ArrayList() - val courseDataList: MutableList> = mutableListOf() + private val courseDataList: MutableList> = mutableListOf() @JvmStatic - fun insertMyCourses(userId: String?, myCoursesDoc: JsonObject?, mRealm: Realm) { - context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE) - if (!mRealm.isInTransaction) { - mRealm.beginTransaction() + fun insertMyCourses(userId: String?, myCousesDoc: JsonObject?, mRealm: Realm) { + try { + if (!mRealm.isInTransaction) { + mRealm.executeTransaction { realm -> + insertOrUpdateCourse(userId, myCousesDoc, realm) + } + } else { + insertOrUpdateCourse(userId, myCousesDoc, mRealm) + } + } catch (e: Exception) { + Log.e("RealmMyCourse1", "Error during insertion into courses: ${e.message}") + throw e } - val id = JsonUtils.getString("_id", myCoursesDoc) + } + + fun writeCsv(filePath: String, data: List>) { + try { + val file = File(filePath) + file.parentFile?.mkdirs() + val writer = CSVWriter(FileWriter(file)) + writer.writeNext(arrayOf("courseId", "course_rev", "languageOfInstruction", "courseTitle", "memberLimit", "description", "method", "gradeLevel", "subjectLevel", "createdDate", "steps")) + for (row in data) { + writer.writeNext(row) + } + writer.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + + fun courseWriteCsv() { + writeCsv("${context.getExternalFilesDir(null)}/ole/course.csv", courseDataList) + } + + private fun insertOrUpdateCourse(userId: String?, myCousesDoc: JsonObject?, mRealm: Realm) { + val id = JsonUtils.getString("_id", myCousesDoc) var myMyCoursesDB = mRealm.where(RealmMyCourse::class.java).equalTo("id", id).findFirst() if (myMyCoursesDB == null) { myMyCoursesDB = mRealm.createObject(RealmMyCourse::class.java, id) } myMyCoursesDB?.setUserId(userId) - myMyCoursesDB?.courseId = JsonUtils.getString("_id", myCoursesDoc) - myMyCoursesDB?.courseRev = JsonUtils.getString("_rev", myCoursesDoc) - myMyCoursesDB?.languageOfInstruction = JsonUtils.getString("languageOfInstruction", myCoursesDoc) - myMyCoursesDB?.courseTitle = JsonUtils.getString("courseTitle", myCoursesDoc) - myMyCoursesDB?.memberLimit = JsonUtils.getInt("memberLimit", myCoursesDoc) - myMyCoursesDB?.description = JsonUtils.getString("description", myCoursesDoc) - val description = JsonUtils.getString("description", myCoursesDoc) + myMyCoursesDB?.courseId = JsonUtils.getString("_id", myCousesDoc) + myMyCoursesDB?.courseRev = JsonUtils.getString("_rev", myCousesDoc) + myMyCoursesDB?.languageOfInstruction = JsonUtils.getString("languageOfInstruction", myCousesDoc) + myMyCoursesDB?.courseTitle = JsonUtils.getString("courseTitle", myCousesDoc) + myMyCoursesDB?.memberLimit = JsonUtils.getInt("memberLimit", myCousesDoc) + myMyCoursesDB?.description = JsonUtils.getString("description", myCousesDoc) + val description = JsonUtils.getString("description", myCousesDoc) val links = extractLinks(description) val baseUrl = Utilities.getUrl() for (link in links) { val concatenatedLink = "$baseUrl/$link" concatenatedLinks.add(concatenatedLink) } - myMyCoursesDB?.method = JsonUtils.getString("method", myCoursesDoc) - myMyCoursesDB?.gradeLevel = JsonUtils.getString("gradeLevel", myCoursesDoc) - myMyCoursesDB?.subjectLevel = JsonUtils.getString("subjectLevel", myCoursesDoc) - myMyCoursesDB?.createdDate = JsonUtils.getLong("createdDate", myCoursesDoc) - myMyCoursesDB?.setNumberOfSteps(JsonUtils.getJsonArray("steps", myCoursesDoc).size()) - val courseStepsJsonArray = JsonUtils.getJsonArray("steps", myCoursesDoc) + myMyCoursesDB?.method = JsonUtils.getString("method", myCousesDoc) + myMyCoursesDB?.gradeLevel = JsonUtils.getString("gradeLevel", myCousesDoc) + myMyCoursesDB?.subjectLevel = JsonUtils.getString("subjectLevel", myCousesDoc) + myMyCoursesDB?.createdDate = JsonUtils.getLong("createdDate", myCousesDoc) + myMyCoursesDB?.setNumberOfSteps(JsonUtils.getJsonArray("steps", myCousesDoc).size()) + val courseStepsJsonArray = JsonUtils.getJsonArray("steps", myCousesDoc) val courseStepsList = mutableListOf() for (i in 0 until courseStepsJsonArray.size()) { - val stepId = Base64.encodeToString(courseStepsJsonArray[i].toString().toByteArray(), Base64.NO_WRAP) + val step_id = Base64.encodeToString(courseStepsJsonArray[i].toString().toByteArray(), Base64.NO_WRAP) val stepJson = courseStepsJsonArray[i].asJsonObject val step = RealmCourseStep() - step.id = stepId + step.id = step_id step.stepTitle = JsonUtils.getString("stepTitle", stepJson) step.description = JsonUtils.getString("description", stepJson) val stepDescription = JsonUtils.getString("description", stepJson) @@ -120,58 +151,49 @@ open class RealmMyCourse : RealmObject() { val concatenatedLink = "$baseUrl/$stepLink" concatenatedLinks.add(concatenatedLink) } - insertCourseStepsAttachments(myMyCoursesDB?.courseId, stepId, JsonUtils.getJsonArray("resources", stepJson), mRealm) - insertExam(stepJson, mRealm, stepId, i + 1, myMyCoursesDB?.courseId) - insertSurvey(stepJson, mRealm, stepId, i + 1, myMyCoursesDB?.courseId, myMyCoursesDB?.createdDate) + insertCourseStepsAttachments(myMyCoursesDB?.courseId, step_id, JsonUtils.getJsonArray("resources", stepJson), mRealm) + insertExam(stepJson, mRealm, step_id, i + 1, myMyCoursesDB?.courseId) step.noOfResources = JsonUtils.getJsonArray("resources", stepJson).size() step.courseId = myMyCoursesDB?.courseId courseStepsList.add(step) } - if (mRealm.isInTransaction) { - mRealm.commitTransaction() + mRealm.executeTransaction { + myMyCoursesDB?.courseSteps?.clear() + myMyCoursesDB?.courseSteps?.addAll(courseStepsList) } - if (!mRealm.isInTransaction) { - mRealm.beginTransaction() - } - myMyCoursesDB?.courseSteps = RealmList() - myMyCoursesDB?.courseSteps?.addAll(courseStepsList) - mRealm.commitTransaction() - val csvRow = arrayOf( - JsonUtils.getString("_id", myCoursesDoc), - JsonUtils.getString("_rev", myCoursesDoc), - JsonUtils.getString("languageOfInstruction", myCoursesDoc), - JsonUtils.getString("courseTitle", myCoursesDoc), - JsonUtils.getInt("memberLimit", myCoursesDoc).toString(), - JsonUtils.getString("description", myCoursesDoc), - JsonUtils.getString("method", myCoursesDoc), - JsonUtils.getString("gradeLevel", myCoursesDoc), - JsonUtils.getString("subjectLevel", myCoursesDoc), - JsonUtils.getLong("createdDate", myCoursesDoc).toString(), - JsonUtils.getJsonArray("steps", myCoursesDoc).toString() + JsonUtils.getString("_id", myCousesDoc), + JsonUtils.getString("_rev", myCousesDoc), + JsonUtils.getString("languageOfInstruction", myCousesDoc), + JsonUtils.getString("courseTitle", myCousesDoc), + JsonUtils.getInt("memberLimit", myCousesDoc).toString(), + JsonUtils.getString("description", myCousesDoc), + JsonUtils.getString("method", myCousesDoc), + JsonUtils.getString("gradeLevel", myCousesDoc), + JsonUtils.getString("subjectLevel", myCousesDoc), + JsonUtils.getLong("createdDate", myCousesDoc).toString(), + JsonUtils.getJsonArray("steps", myCousesDoc).toString() ) courseDataList.add(csvRow) } - fun writeCsv(filePath: String, data: List>) { - try { - val file = File(filePath) - file.parentFile?.mkdirs() - val writer = CSVWriter(FileWriter(file)) - writer.writeNext(arrayOf("courseId", "course_rev", "languageOfInstruction", "courseTitle", "memberLimit", "description", "method", "gradeLevel", "subjectLevel", "createdDate", "steps")) - for (row in data) { - writer.writeNext(row) + private fun extractLinks(text: String?): ArrayList { + val links = ArrayList() + val pattern = Pattern.compile("!\\[.*?]\\((.*?)\\)") + val matcher = text?.let { pattern.matcher(it) } + if (matcher != null) { + while (matcher.find()) { + val link = matcher.group(1) + if (link != null) { + if (link.isNotEmpty()) { + links.add(link) + } + } } - writer.close() - } catch (e: IOException) { - e.printStackTrace() } - } - - fun courseWriteCsv() { - writeCsv("${context.getExternalFilesDir(null)}/ole/course.csv", courseDataList) + return links } @JvmStatic @@ -275,7 +297,11 @@ open class RealmMyCourse : RealmObject() { @JvmStatic fun insert(mRealm: Realm, myCoursesDoc: JsonObject?) { + if (!mRealm.isInTransaction) { + mRealm.beginTransaction() + } insertMyCourses("", myCoursesDoc, mRealm) + mRealm.commitTransaction() } @JvmStatic diff --git a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt index b360c194d3..9afa261757 100644 --- a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt +++ b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt @@ -2,6 +2,8 @@ package org.ole.planet.myplanet.model import android.content.Context import android.content.SharedPreferences +import android.text.TextUtils +import android.util.Log import com.google.gson.JsonArray import com.google.gson.JsonNull import com.google.gson.JsonObject @@ -241,7 +243,11 @@ open class RealmMyLibrary : RealmObject() { @JvmStatic fun insertMyLibrary(userId: String?, doc: JsonObject, mRealm: Realm) { + if (!mRealm.isInTransaction) { + mRealm.beginTransaction() + } insertMyLibrary(userId, "", "", doc, mRealm) + mRealm.commitTransaction() } @JvmStatic @@ -253,11 +259,23 @@ open class RealmMyLibrary : RealmObject() { mRealm.commitTransaction() } - @JvmStatic fun insertMyLibrary(userId: String?, stepId: String?, courseId: String?, doc: JsonObject, mRealm: Realm) { - if (!mRealm.isInTransaction) { - mRealm.beginTransaction() + try { + if (!mRealm.isInTransaction) { + mRealm.executeTransaction { realm -> + insertOrUpdateLibrary(userId, stepId, courseId, doc, realm) + } + } else { + insertOrUpdateLibrary(userId, stepId, courseId, doc, mRealm) + } + } catch (e: Exception) { + Log.e("RealmMyLibrary", "Error during insertion into library: ${e.message}") + throw e } + } + + @JvmStatic + fun insertOrUpdateLibrary(userId: String?, stepId: String?, courseId: String?, doc: JsonObject, mRealm: Realm) { val resourceId = JsonUtils.getString("_id", doc) val settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) var resource = mRealm.where(RealmMyLibrary::class.java).equalTo("id", resourceId).findFirst() diff --git a/app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt b/app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt index 4766a88e3f..9d228dfcd7 100644 --- a/app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt +++ b/app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt @@ -5,6 +5,7 @@ import android.content.SharedPreferences import android.net.wifi.SupplicantState import android.net.wifi.WifiManager import android.text.TextUtils +import android.util.Log import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonNull @@ -96,28 +97,33 @@ class SyncManager private constructor(private val context: Context) { isSyncing = true create(context, R.mipmap.ic_launcher, " Syncing data", "Please wait...") mRealm = dbService.realmInstance - TransactionSyncManager.syncDb(mRealm, "tablet_users") + Log.d("SyncManager", "Syncing tablet_users") + syncDb("tablet_users") + Log.d("SyncManager", "Syncing my library") myLibraryTransactionSync() - TransactionSyncManager.syncDb(mRealm, "courses") - TransactionSyncManager.syncDb(mRealm, "exams") - TransactionSyncManager.syncDb(mRealm, "ratings") - TransactionSyncManager.syncDb(mRealm, "courses_progress") - TransactionSyncManager.syncDb(mRealm, "achievements") - TransactionSyncManager.syncDb(mRealm, "tags") - TransactionSyncManager.syncDb(mRealm, "submissions") - TransactionSyncManager.syncDb(mRealm, "news") - TransactionSyncManager.syncDb(mRealm, "feedback") - TransactionSyncManager.syncDb(mRealm, "teams") - TransactionSyncManager.syncDb(mRealm, "tasks") - TransactionSyncManager.syncDb(mRealm, "login_activities") - TransactionSyncManager.syncDb(mRealm, "meetups") - TransactionSyncManager.syncDb(mRealm, "health") - TransactionSyncManager.syncDb(mRealm, "certifications") - TransactionSyncManager.syncDb(mRealm, "team_activities") - TransactionSyncManager.syncDb(mRealm, "chat_history") + syncDb("courses") + syncDb("exams") + syncDb("ratings") + syncDb("courses_progress") + syncDb("achievements") + syncDb("tags") + syncDb("submissions") + syncDb("news") + syncDb("feedback") + syncDb("teams") + syncDb("tasks") + syncDb("login_activities") + syncDb("meetups") + syncDb("health") + syncDb("certifications") + syncDb("team_activities") + syncDb("chat_history") ManagerSync.instance?.syncAdmin() + Log.d("SyncManager", "Syncing resources") resourceTransactionSync() + Log .d("SyncManager", "Before OnSynced") onSynced(mRealm, settings) + Log.d("SyncManager", "After OnSynced") mRealm.close() } catch (err: Exception) { err.printStackTrace() @@ -127,6 +133,19 @@ class SyncManager private constructor(private val context: Context) { } } + private fun syncDb(dbName: String) { + Log.d("SyncManager", "Starting transaction for $dbName") + mRealm.beginTransaction() + try { + TransactionSyncManager.syncDb(mRealm, dbName) + Log.d("SyncManager", "Transaction committed for $dbName") + mRealm.commitTransaction() + } catch (e: Exception) { + mRealm.cancelTransaction() + Log.e("SyncManager", "Error syncing $dbName: ${e.message}") + } + } + private fun handleException(message: String?) { if (listener != null) { isSyncing = false @@ -136,6 +155,7 @@ class SyncManager private constructor(private val context: Context) { } private fun resourceTransactionSync() { + Log.d("SyncManager", "Starting resource transaction") val apiInterface = client?.create(ApiInterface::class.java) try { syncResource(apiInterface) @@ -146,6 +166,7 @@ class SyncManager private constructor(private val context: Context) { @Throws(IOException::class) private fun syncResource(dbClient: ApiInterface?) { + Log.d("SyncManager", "Syncing resources") val newIds: MutableList = ArrayList() val allDocs = dbClient?.getJsonObject(Utilities.header, Utilities.getUrl() + "/resources/_all_docs?include_doc=false") val all = allDocs?.execute() @@ -169,32 +190,36 @@ class SyncManager private constructor(private val context: Context) { } private fun myLibraryTransactionSync() { + Log.d("SyncManager", "Starting my library transaction") val apiInterface = client?.create(ApiInterface::class.java) try { val res = apiInterface?.getDocuments(Utilities.header, Utilities.getUrl() + "/shelf/_all_docs")?.execute()?.body() - for (i in res?.rows!!.indices) { - shelfDoc = res.rows!![i] + res?.rows?.forEach { row -> + shelfDoc = row populateShelfItems(apiInterface) } } catch (e: IOException) { e.printStackTrace() + Log.e("SyncManager", "Error in myLibraryTransactionSync: ${e.message}") } } private fun populateShelfItems(apiInterface: ApiInterface) { + Log.d("SyncManager", "Populating shelf items") try { val jsonDoc = apiInterface.getJsonObject(Utilities.header, Utilities.getUrl() + "/shelf/" + shelfDoc?.id).execute().body() - for (i in Constants.shelfDataList.indices) { - val shelfData = Constants.shelfDataList[i] + Constants.shelfDataList.forEach { shelfData -> val array = getJsonArray(shelfData.key, jsonDoc) memberShelfData(array, shelfData) } } catch (err: Exception) { err.printStackTrace() + Log.e("SyncManager", "Error in populateShelfItems: ${err.message}") } } private fun memberShelfData(array: JsonArray, shelfData: ShelfData) { + Log.d("SyncManager", "Processing shelf data") if (array.size() > 0) { triggerInsert(shelfData.categoryKey, shelfData.type) check(array) @@ -223,33 +248,51 @@ class SyncManager private constructor(private val context: Context) { resourceDoc?.let { triggerInsert(stringArray, it) } } catch (e: IOException) { e.printStackTrace() + Log.e("SyncManager", "Error in validateDocument: ${e.message}") } } private fun triggerInsert(stringArray: Array, resourceDoc: JsonObject) { - when (stringArray[2]) { - "resources" -> insertMyLibrary(stringArray[0], resourceDoc, mRealm) - "meetups" -> insert(mRealm, resourceDoc) - "courses" -> { - if (!mRealm.isInTransaction){ - mRealm.beginTransaction() + val realm = Realm.getDefaultInstance() + var transactionStarted = false + + try { + if (!realm.isInTransaction) { + realm.beginTransaction() + transactionStarted = true + Log.d("SyncManager", "Transaction started for ${stringArray[2]}") + + when (stringArray[2]) { + "resources" -> insertMyLibrary(stringArray[0], resourceDoc, realm) + "meetups" -> insert(realm, resourceDoc) + "courses" -> insertMyCourses(stringArray[0], resourceDoc, realm) + "teams" -> insertMyTeams(resourceDoc, realm) } - insertMyCourses(stringArray[0], resourceDoc, mRealm) - if (mRealm.isInTransaction){ - mRealm.commitTransaction() + + if (realm.isInTransaction) { + realm.commitTransaction() + Log.d("SyncManager", "Transaction committed for ${stringArray[2]}") } + } else { + Log.e("SyncManager", "The Realm is already in a write transaction for ${stringArray[2]}") + } + } catch (e: Exception) { + if (transactionStarted && realm.isInTransaction) { + realm.cancelTransaction() + Log.e("SyncManager", "Transaction canceled for ${stringArray[2]}: ${e.message}") } - "teams" -> insertMyTeams(resourceDoc, mRealm) + Log.e("SyncManager", "Error inserting into ${stringArray[2]}: ${e.message}") + } finally { + realm.close() } saveConcatenatedLinksToPrefs() } companion object { private var ourInstance: SyncManager? = null - val instance: SyncManager? - get() { - ourInstance = SyncManager(MainApplication.context) - return ourInstance - } + val instance: SyncManager? get() { + ourInstance = SyncManager(MainApplication.context) + return ourInstance + } } } \ No newline at end of file diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/sync/SyncActivity.kt b/app/src/main/java/org/ole/planet/myplanet/ui/sync/SyncActivity.kt index 10b8f3f4db..e7f32eca12 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/sync/SyncActivity.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/sync/SyncActivity.kt @@ -185,9 +185,11 @@ abstract class SyncActivity : ProcessUserDataActivity(), SyncListener, CheckVers } suspend fun isServerReachable(processedUrl: String?): Boolean { + Log.d("SyncActivity", "isServerReachable") return withContext(Dispatchers.IO) { val apiInterface = client?.create(ApiInterface::class.java) try { + Log.d("SyncActivity", "isServerReachable onResponse") val response = apiInterface?.isPlanetAvailable("$processedUrl/_all_dbs")?.execute() when { @@ -305,6 +307,7 @@ abstract class SyncActivity : ProcessUserDataActivity(), SyncListener, CheckVers } fun startSync() { + Log.d("SyncActivity", "startSync") SyncManager.instance?.start(this@SyncActivity) }