cards #8

Merged
faraphel merged 12 commits from cards into main 2024-06-13 15:12:13 +02:00
16 changed files with 234 additions and 57 deletions
Showing only changes of commit 7545e6aca9 - Show all commits

View file

@ -3,8 +3,8 @@ package com.faraphel.tasks_valider.connectivity.task
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.logging.HttpLoggingInterceptor
/** /**
@ -19,19 +19,21 @@ class TaskClient(
private val baseCookies: List<okhttp3.Cookie> = listOf() private val baseCookies: List<okhttp3.Cookie> = listOf()
) { ) {
private val baseUrl = "http://$address:$port" private val baseUrl = "http://$address:$port"
private val client = OkHttpClient().newBuilder().cookieJar( private val client = OkHttpClient().newBuilder()
// TODO(Faraphel): should be moved into another object .cookieJar(
object : okhttp3.CookieJar { // TODO(Faraphel): should be moved into another object
private val cookies = baseCookies.toMutableList() ///< list of cookies object : okhttp3.CookieJar {
private val cookies = baseCookies.toMutableList() ///< list of cookies
override fun loadForRequest(url: HttpUrl): List<okhttp3.Cookie> { override fun loadForRequest(url: HttpUrl): List<okhttp3.Cookie> {
return this.cookies return this.cookies
}
override fun saveFromResponse(url: HttpUrl, cookies: List<okhttp3.Cookie>) {
this.cookies.addAll(cookies)
}
} }
override fun saveFromResponse(url: HttpUrl, cookies: List<okhttp3.Cookie>) { )
this.cookies.addAll(cookies) .build()
}
}
).build()
// TODO(Faraphel): automatically convert content to the correct type ? // TODO(Faraphel): automatically convert content to the correct type ?
@ -83,7 +85,7 @@ class TaskClient(
this.client.newCall( this.client.newCall(
this.baseRequestBuilder(endpoint) this.baseRequestBuilder(endpoint)
.patch(content.toRequestBody(type.toMediaType())) .patch(content.toRequestBody(type.toMediaType()))
.build() .build()
).execute() ).execute()
/** /**
@ -96,6 +98,6 @@ class TaskClient(
this.client.newCall( this.client.newCall(
this.baseRequestBuilder(endpoint) this.baseRequestBuilder(endpoint)
.delete(content.toRequestBody(type.toMediaType())) .delete(content.toRequestBody(type.toMediaType()))
.build() .build()
).execute() ).execute()
} }

View file

@ -4,6 +4,11 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.ClassEntity import com.faraphel.tasks_valider.database.entities.ClassEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.google.gson.reflect.TypeToken
class ClassApi(dao: BaseTaskDao<ClassEntity>, session: SessionEntity) : class ClassApi(dao: BaseTaskDao<ClassEntity>, session: SessionEntity) :
BaseTaskApi<ClassEntity>(dao, session) BaseTaskApi<ClassEntity>(
dao,
object: TypeToken<ClassEntity>() {},
session
)

View file

@ -4,6 +4,11 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.PersonEntity import com.faraphel.tasks_valider.database.entities.PersonEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.google.gson.reflect.TypeToken
class PersonApi(dao: BaseTaskDao<PersonEntity>, session: SessionEntity) : class PersonApi(dao: BaseTaskDao<PersonEntity>, session: SessionEntity) :
BaseTaskApi<PersonEntity>(dao, session) BaseTaskApi<PersonEntity>(
dao,
object: TypeToken<PersonEntity>() {},
session
)

View file

@ -4,7 +4,12 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.RelationClassPersonEntity import com.faraphel.tasks_valider.database.entities.RelationClassPersonEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.google.gson.reflect.TypeToken
class RelationClassPersonApi(dao: BaseTaskDao<RelationClassPersonEntity>, session: SessionEntity) : class RelationClassPersonApi(dao: BaseTaskDao<RelationClassPersonEntity>, session: SessionEntity) :
BaseTaskApi<RelationClassPersonEntity>(dao, session) BaseTaskApi<RelationClassPersonEntity>(
dao,
object: TypeToken<RelationClassPersonEntity>() {},
session
)

View file

@ -4,7 +4,12 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.google.gson.reflect.TypeToken
class RelationPersonSessionSubjectApi(dao: BaseTaskDao<RelationPersonSessionSubjectEntity>, session: SessionEntity) : class RelationPersonSessionSubjectApi(dao: BaseTaskDao<RelationPersonSessionSubjectEntity>, session: SessionEntity) :
BaseTaskApi<RelationPersonSessionSubjectEntity>(dao, session) BaseTaskApi<RelationPersonSessionSubjectEntity>(
dao,
object: TypeToken<RelationPersonSessionSubjectEntity>() {},
session
)

View file

@ -3,6 +3,11 @@ package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.google.gson.reflect.TypeToken
class SessionApi(dao: BaseTaskDao<SessionEntity>, session: SessionEntity) : class SessionApi(dao: BaseTaskDao<SessionEntity>, session: SessionEntity) :
BaseTaskApi<SessionEntity>(dao, session) BaseTaskApi<SessionEntity>(
dao,
object: TypeToken<SessionEntity>() {},
session
)

View file

@ -4,7 +4,12 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.SubjectEntity import com.faraphel.tasks_valider.database.entities.SubjectEntity
import com.google.gson.reflect.TypeToken
class SubjectApi(dao: BaseTaskDao<SubjectEntity>, session: SessionEntity) : class SubjectApi(dao: BaseTaskDao<SubjectEntity>, session: SessionEntity) :
BaseTaskApi<SubjectEntity>(dao, session) BaseTaskApi<SubjectEntity>(
dao,
object: TypeToken<SubjectEntity>() {},
session
)

View file

@ -4,7 +4,12 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.TaskEntity import com.faraphel.tasks_valider.database.entities.TaskEntity
import com.google.gson.reflect.TypeToken
class TaskApi(dao: BaseTaskDao<TaskEntity>, session: SessionEntity) : class TaskApi(dao: BaseTaskDao<TaskEntity>, session: SessionEntity) :
BaseTaskApi<TaskEntity>(dao, session) BaseTaskApi<TaskEntity>(
dao,
object: TypeToken<TaskEntity>() {},
session
)

View file

@ -4,7 +4,12 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.ValidationEntity import com.faraphel.tasks_valider.database.entities.ValidationEntity
import com.google.gson.reflect.TypeToken
class ValidationApi(dao: BaseTaskDao<ValidationEntity>, session: SessionEntity) : class ValidationApi(dao: BaseTaskDao<ValidationEntity>, session: SessionEntity) :
BaseTaskApi<ValidationEntity>(dao, session) BaseTaskApi<ValidationEntity>(
dao,
object: TypeToken<ValidationEntity>() {},
session
)

View file

@ -1,5 +1,6 @@
package com.faraphel.tasks_valider.database.api.entities.base package com.faraphel.tasks_valider.database.api.entities.base
import android.util.Log
import com.faraphel.tasks_valider.database.dao.base.BaseCronDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.entities.base.BaseEntity import com.faraphel.tasks_valider.database.entities.base.BaseEntity
import com.google.gson.Gson import com.google.gson.Gson
@ -11,13 +12,14 @@ import fi.iki.elonen.NanoHTTPD
* This is preconfigured to handle JSON data. * This is preconfigured to handle JSON data.
* @param Entity the entity type to handle * @param Entity the entity type to handle
*/ */
abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseCronDao<Entity>) : BaseApi { abstract class BaseJsonApi<Entity: BaseEntity>(
private val dao: BaseCronDao<Entity>,
private val entityTypeToken: TypeToken<Entity>,
) : BaseApi {
companion object { companion object {
private val parser = Gson() ///< The JSON parser private val parser = Gson() ///< The JSON parser
} }
private val entityTypeToken: TypeToken<Entity> = object: TypeToken<Entity>() {} ///< the type of the managed entity
// Requests // Requests
override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
@ -43,10 +45,16 @@ abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseCronDao<Enti
} }
override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
val data = httpSession.inputStream.bufferedReader().readText()
Log.i("post data", "data raw : $data")
val obj = parser.fromJson<Entity>( val obj = parser.fromJson<Entity>(
httpSession.inputStream.bufferedReader().readText(), data,
this.entityTypeToken.type this.entityTypeToken.type
) )
Log.i("post data", "data parsed : $obj")
val id = this.dao.insert(obj) val id = this.dao.insert(obj)
return NanoHTTPD.newFixedLengthResponse( return NanoHTTPD.newFixedLengthResponse(

View file

@ -14,14 +14,16 @@ import fi.iki.elonen.NanoHTTPD
*/ */
abstract class BaseTaskApi<Entity: BaseEntity>( abstract class BaseTaskApi<Entity: BaseEntity>(
private val dao: BaseTaskDao<Entity>, private val dao: BaseTaskDao<Entity>,
private val entityTypeToken: TypeToken<Entity>,
private val session: SessionEntity, private val session: SessionEntity,
) : BaseJsonApi<Entity>(dao) { ) : BaseJsonApi<Entity>(
dao,
entityTypeToken,
) {
companion object { companion object {
private val parser = Gson() ///< The JSON parser private val parser = Gson() ///< The JSON parser
} }
private val entityTypeToken: TypeToken<Entity> = object: TypeToken<Entity>() {} ///< the type of the managed entity
// Requests // Requests
override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {

View file

@ -1,6 +1,8 @@
package com.faraphel.tasks_valider.ui.screen.communication.internet package com.faraphel.tasks_valider.ui.screen.communication.internet
import android.app.Activity import android.app.Activity
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -19,12 +21,14 @@ import com.faraphel.tasks_valider.ui.screen.communication.RANGE_SERVER_PORT
import com.faraphel.tasks_valider.ui.screen.task.TaskSessionScreen import com.faraphel.tasks_valider.ui.screen.task.TaskSessionScreen
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun CommunicationInternetClientScreen(activity: Activity) { fun CommunicationInternetClientScreen(activity: Activity) {
val client = remember { mutableStateOf<TaskClient?>(null) } val client = remember { mutableStateOf<TaskClient?>(null) }
if (client.value == null) CommunicationInternetClientContent(client) // TODO(Faraphel): fix and get a user
else TaskSessionScreen(activity, client.value!!) // if (client.value == null) CommunicationInternetClientContent(client)
// else TaskSessionScreen(activity, client.value!!, user)
} }

View file

@ -28,6 +28,7 @@ import com.faraphel.tasks_valider.ui.screen.authentification.AuthentificationSer
import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_PORT import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_PORT
import com.faraphel.tasks_valider.ui.screen.communication.RANGE_SERVER_PORT import com.faraphel.tasks_valider.ui.screen.communication.RANGE_SERVER_PORT
import com.faraphel.tasks_valider.ui.screen.task.TaskSessionScreen import com.faraphel.tasks_valider.ui.screen.task.TaskSessionScreen
import kotlinx.coroutines.flow.MutableStateFlow
import java.time.Instant import java.time.Instant
@ -42,26 +43,32 @@ fun CommunicationInternetServerScreen(
) { ) {
val controller = rememberNavController() val controller = rememberNavController()
val adminPersonEntityRaw = remember { mutableStateOf<PersonEntity?>(null) }
val adminPersonEntity = remember { mutableStateOf<PersonEntity?>(null) } val adminPersonEntity = remember { mutableStateOf<PersonEntity?>(null) }
val client = remember { mutableStateOf<TaskClient?>(null) } val client = remember { mutableStateOf<TaskClient?>(null) }
NavHost(navController = controller, startDestination = "authentication") { NavHost(navController = controller, startDestination = "authentication") {
composable("authentication") { composable("authentication") {
// if the admin person is not created, prompt the user for the admin information // if the admin person is not created, prompt the user for the admin information
if (adminPersonEntity.value == null) AuthentificationServerScreen(adminPersonEntity) if (adminPersonEntityRaw.value == null) AuthentificationServerScreen(adminPersonEntityRaw)
else controller.navigate("configuration") else controller.navigate("configuration")
} }
composable("configuration") { composable("configuration") {
if (client.value == null) if (client.value == null)
CommunicationInternetServerContent( CommunicationInternetServerContent(
database, database,
adminPersonEntity.value!!, adminPersonEntityRaw,
client adminPersonEntity,
client,
) )
else controller.navigate("session") else controller.navigate("session")
} }
composable("session") { composable("session") {
TaskSessionScreen(activity, client.value!!) TaskSessionScreen(
activity,
client.value!!,
adminPersonEntity.value!!
)
} }
} }
} }
@ -71,7 +78,8 @@ fun CommunicationInternetServerScreen(
@Composable @Composable
fun CommunicationInternetServerContent( fun CommunicationInternetServerContent(
database: TaskDatabase, database: TaskDatabase,
adminPersonEntity: PersonEntity, adminPersonEntityRaw: MutableState<PersonEntity?>,
adminPersonEntity: MutableState<PersonEntity?>,
client: MutableState<TaskClient?> client: MutableState<TaskClient?>
) { ) {
val classes = remember { mutableStateOf<List<ClassEntity>?>(null) } val classes = remember { mutableStateOf<List<ClassEntity>?>(null) }
@ -129,16 +137,17 @@ fun CommunicationInternetServerContent(
Button(onClick = { Button(onClick = {
Thread { // a thread is used for networking Thread { // a thread is used for networking
// Insert the admin in the database // Insert the admin in the database and get its id
database.personDao().insert(adminPersonEntity) val (adminPersonEntityId) = database.personDao().insert(adminPersonEntityRaw.value!!)
adminPersonEntity.value = database.personDao().getById(adminPersonEntityId)!!
// Create a new session // Create a new session
// TODO(Faraphel): name // TODO(Faraphel): name
val (sessionId) = database.sessionDao().insert( val (sessionId) = database.sessionDao().insert(
SessionEntity( SessionEntity(
name="NOM", name="NOM",
start=Instant.now(), start=Instant.now(),
classId=selectedClass.value!!.id, classId=selectedClass.value!!.id,
) )
) )
val session = database.sessionDao().getById(sessionId)!! val session = database.sessionDao().getById(sessionId)!!
@ -157,7 +166,7 @@ fun CommunicationInternetServerContent(
serverPort.intValue, serverPort.intValue,
database, database,
session, session,
adminPersonEntity, adminPersonEntity.value!!,
) )
server.start() server.start()

View file

@ -30,10 +30,11 @@ import com.faraphel.tasks_valider.ui.screen.task.TaskSessionScreen
fun CommunicationWifiP2pServerScreen(activity: Activity, bwfManager: BwfManager) { fun CommunicationWifiP2pServerScreen(activity: Activity, bwfManager: BwfManager) {
val client = remember { mutableStateOf<TaskClient?>(null) } val client = remember { mutableStateOf<TaskClient?>(null) }
// TODO(Faraphel): fix and get a user
// if the server is not created, prompt the user for the server configuration // if the server is not created, prompt the user for the server configuration
if (client.value == null) CommunicationWifiP2pServerContent(activity, bwfManager, client) // if (client.value == null) CommunicationWifiP2pServerContent(activity, bwfManager, client)
// else, go to the base tasks screen // else, go to the base tasks screen
else TaskSessionScreen(activity, client.value!!) // else TaskSessionScreen(activity, client.value!!)
} }

View file

@ -1,7 +1,9 @@
package com.faraphel.tasks_valider.ui.screen.task package com.faraphel.tasks_valider.ui.screen.task
import android.app.Activity import android.app.Activity
import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -20,8 +22,13 @@ import com.google.gson.reflect.TypeToken
* @param activity the android activity * @param activity the android activity
* @param client an HTTP client that can communicate with the server * @param client an HTTP client that can communicate with the server
*/ */
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun TaskSessionScreen(activity: Activity, client: TaskClient) { fun TaskSessionScreen(
activity: Activity,
client: TaskClient,
user: PersonEntity,
) {
val students = remember { mutableStateOf<List<PersonEntity>?>(null) } val students = remember { mutableStateOf<List<PersonEntity>?>(null) }
val selectedStudent = remember { mutableStateOf<PersonEntity?>(null) } val selectedStudent = remember { mutableStateOf<PersonEntity?>(null) }
@ -30,7 +37,12 @@ fun TaskSessionScreen(activity: Activity, client: TaskClient) {
return Thread { refreshStudents(activity, client, students) }.start() return Thread { refreshStudents(activity, client, students) }.start()
if (selectedStudent.value != null) if (selectedStudent.value != null)
return TaskStudentScreen(activity, client, selectedStudent.value!!) return TaskStudentScreen(
activity,
client,
user,
selectedStudent.value!!
)
Column { Column {
// if the groups have already been defined, display them // if the groups have already been defined, display them
@ -44,7 +56,11 @@ fun TaskSessionScreen(activity: Activity, client: TaskClient) {
// TODO(Faraphel): template this function ? // TODO(Faraphel): template this function ?
fun refreshStudents(activity: Activity, client: TaskClient, students: MutableState<List<PersonEntity>?>) { fun refreshStudents(
activity: Activity,
client: TaskClient,
students: MutableState<List<PersonEntity>?>
) {
// TODO(Faraphel): global variable ? // TODO(Faraphel): global variable ?
val jsonParser = Gson() val jsonParser = Gson()

View file

@ -1,49 +1,103 @@
package com.faraphel.tasks_valider.ui.screen.task package com.faraphel.tasks_valider.ui.screen.task
import android.app.Activity import android.app.Activity
import android.os.Build
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import com.faraphel.tasks_valider.connectivity.task.TaskClient import com.faraphel.tasks_valider.connectivity.task.TaskClient
import com.faraphel.tasks_valider.connectivity.task.session.TaskPermission
import com.faraphel.tasks_valider.database.entities.* import com.faraphel.tasks_valider.database.entities.*
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import java.time.Instant
/** /**
* This screen represent a student * This screen represent a student
* @param student the student object * @param student the student object
*/ */
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun TaskStudentScreen(activity: Activity, client: TaskClient, student: PersonEntity) { fun TaskStudentScreen(
activity: Activity,
client: TaskClient,
user: PersonEntity,
student: PersonEntity,
) {
val tasks = remember { mutableStateOf<List<TaskEntity>?>(null) } val tasks = remember { mutableStateOf<List<TaskEntity>?>(null) }
val validations = remember { mutableStateOf<List<ValidationEntity>?>(null) }
if (tasks.value == null) if (tasks.value == null || validations.value == null)
Thread { refreshTasks(activity, client, student, tasks) }.start() Thread {
refreshTasksValidations(
activity,
client,
student,
tasks,
validations
)
}.start()
Column { Column {
Text(text = student.fullName()) Text(text = student.fullName())
tasks.value?.forEach { task -> // if both the list of tasks and validations are loaded
Button(onClick = {}) { if (!(tasks.value == null || validations.value == null)) {
Column { tasks.value?.forEach { task ->
Text(task.title) // get the validation
task.description?.let { description -> Text(description) } val validation = validations.value!!.firstOrNull { validation -> validation.taskId == task.id }
Button(onClick = {}) {
Row {
Column {
// task title
Text(task.title)
// task description
task.description?.let { description -> Text(description) }
// if the task have been validated, show the date
if (validation != null) Text(validation.date.toString())
}
// the validation state
Checkbox(
checked = validation != null,
enabled = user.role.permissions.contains(TaskPermission.WRITE),
onCheckedChange = { state ->
Thread {
updateValidation(
client,
state,
validation ?: ValidationEntity(
Instant.now(),
user.id,
student.id,
task.id,
)
)
}.start()
}
)
}
} }
} }
} }
} }
} }
fun refreshTasks( fun refreshTasksValidations(
activity: Activity, activity: Activity,
client: TaskClient, client: TaskClient,
student: PersonEntity, student: PersonEntity,
tasks: MutableState<List<TaskEntity>?> tasks: MutableState<List<TaskEntity>?>,
validations: MutableState<List<ValidationEntity>?>,
) { ) {
val jsonParser = Gson() val jsonParser = Gson()
@ -86,4 +140,45 @@ fun refreshTasks(
}.sortedBy { task -> }.sortedBy { task ->
task.order task.order
} }
// try to obtain the list of validations
val responseValidations = client.get("entities/" + ValidationEntity.TABLE_NAME)
// in case of error, notify it
if (!responseValidations.isSuccessful)
return activity.runOnUiThread { Toast.makeText(activity, responseTasks.message, Toast.LENGTH_LONG).show() }
// parse the list of validations
val allValidations = jsonParser.fromJson<List<ValidationEntity>>(
responseValidations.body.string(),
object : TypeToken<List<ValidationEntity>>(){}.type
)
// filter only the interesting validations
validations.value = allValidations.filter { validation ->
validation.studentId == student.id &&
validation.taskId in allTasks.map { task -> task.id }
}
} }
fun updateValidation(client: TaskClient, checked: Boolean, validation: ValidationEntity) {
val jsonParser = Gson()
if (checked) {
// if the validation is not set, create it
client.post(
"entities/" + ValidationEntity.TABLE_NAME,
jsonParser.toJson(validation),
"application/json"
)
}
else {
// if the validation is set, delete it
client.delete(
"entities/" + ValidationEntity.TABLE_NAME,
jsonParser.toJson(validation),
"application/json"
)
}
}