rewrote the database, added the entire session system, updated the api with it, continued the UI

This commit is contained in:
Faraphel 2024-05-30 21:09:57 +02:00
parent be801d5b31
commit f821406578
35 changed files with 583 additions and 151 deletions

View file

@ -76,4 +76,5 @@ dependencies {
debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest") debugImplementation("androidx.compose.ui:ui-test-manifest")
ksp("androidx.room:room-compiler:2.6.1") ksp("androidx.room:room-compiler:2.6.1")
implementation(kotlin("reflect"))
} }

View file

@ -6,24 +6,42 @@ import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.room.Room
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
import com.faraphel.tasks_valider.database.TaskDatabase import com.faraphel.tasks_valider.database.TaskDatabase
import com.faraphel.tasks_valider.database.populateTaskDatabaseTest
import com.faraphel.tasks_valider.ui.screen.communication.CommunicationModeSelectionScreen import com.faraphel.tasks_valider.ui.screen.communication.CommunicationModeSelectionScreen
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private var bwfManager: BwfManager? = null ///< the WiFi-Direct helper private var bwfManager: BwfManager? = null ///< the WiFi-Direct helper
companion object {
private lateinit var database: TaskDatabase ///< the database manager private lateinit var database: TaskDatabase ///< the database manager
}
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// Reset the database | TODO(Faraphel): only for testing purpose
this.deleteDatabase("local")
// Create the database
this.database = Room.databaseBuilder(
this,
TaskDatabase::class.java,
"local"
).build()
// Populate the database with test data
// TODO(Faraphel): remove test data
Thread {
populateTaskDatabaseTest(database)
}.let { thread ->
thread.start()
thread.join()
}
this.setContent { this.setContent {
CommunicationModeSelectionScreen(this) CommunicationModeSelectionScreen(this, database)
} }
} }

View file

@ -3,6 +3,7 @@ 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

View file

@ -5,21 +5,25 @@ import com.faraphel.tasks_valider.connectivity.task.session.TaskSessionManager
import com.faraphel.tasks_valider.database.TaskDatabase import com.faraphel.tasks_valider.database.TaskDatabase
import com.faraphel.tasks_valider.database.api.TaskDatabaseApi import com.faraphel.tasks_valider.database.api.TaskDatabaseApi
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 fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD
/** /**
* A server to handle the task API to allow clients to interact with the database. * A server to handle the task API to allow clients to interact with the database.
* @param port the port of the server * @param port the port of the server.
* @param database the database to interact with * @param database the database to interact with.
* @param session the current session.
* @param adminPersonEntity the person that represent the host of the session.
*/ */
class TaskServer( class TaskServer(
private val port: Int, private val port: Int,
private val database: TaskDatabase, private val database: TaskDatabase,
adminPersonEntity: PersonEntity, private val session: SessionEntity,
private val adminPersonEntity: PersonEntity,
) : NanoHTTPD(port) { ) : NanoHTTPD(port) {
private val sessionManager = TaskSessionManager(adminPersonEntity) ///< the session manager private val sessionManager = TaskSessionManager(adminPersonEntity) ///< the session manager
private val databaseApi = TaskDatabaseApi(this.database) ///< the api of the database private val databaseApi = TaskDatabaseApi(this.database, session) ///< the api of the database
private val sessionManagerApi = TaskSessionManagerApi(this.sessionManager, this.database) ///< the api of the session manager private val sessionManagerApi = TaskSessionManagerApi(this.sessionManager, this.database) ///< the api of the session manager
/** /**

View file

@ -22,6 +22,7 @@ import com.faraphel.tasks_valider.database.entities.*
ValidationEntity::class, ValidationEntity::class,
RelationClassPersonEntity::class, RelationClassPersonEntity::class,
RelationPersonSessionSubjectEntity::class,
], ],
version = 1 version = 1
) )
@ -37,4 +38,5 @@ abstract class TaskDatabase: RoomDatabase() {
abstract fun validationDao(): ValidationDao abstract fun validationDao(): ValidationDao
abstract fun relationClassPersonDao(): RelationClassPersonDao abstract fun relationClassPersonDao(): RelationClassPersonDao
abstract fun relationPersonSessionSubjectDao(): RelationPersonSessionSubjectDao
} }

View file

@ -8,16 +8,20 @@ import com.faraphel.tasks_valider.database.api.entities.base.BaseApi
import com.faraphel.tasks_valider.database.entities.* import com.faraphel.tasks_valider.database.entities.*
import fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD
class TaskDatabaseApi(private val database: TaskDatabase) { class TaskDatabaseApi(
private val database: TaskDatabase,
private val session: SessionEntity,
) {
private val api: Map<String, BaseApi> = mapOf( private val api: Map<String, BaseApi> = mapOf(
ClassEntity.TABLE_NAME to ClassApi(this.database.classDao()), ClassEntity.TABLE_NAME to ClassApi(this.database.classDao(), session),
PersonEntity.TABLE_NAME to PersonApi(this.database.personDao()), PersonEntity.TABLE_NAME to PersonApi(this.database.personDao(), session),
SessionEntity.TABLE_NAME to SessionApi(this.database.sessionDao()), SessionEntity.TABLE_NAME to SessionApi(this.database.sessionDao(), session),
SubjectEntity.TABLE_NAME to SubjectApi(this.database.subjectDao()), SubjectEntity.TABLE_NAME to SubjectApi(this.database.subjectDao(), session),
TaskEntity.TABLE_NAME to TaskApi(this.database.taskDao()), TaskEntity.TABLE_NAME to TaskApi(this.database.taskDao(), session),
ValidationEntity.TABLE_NAME to ValidationApi(this.database.validationDao()), ValidationEntity.TABLE_NAME to ValidationApi(this.database.validationDao(), session),
RelationClassPersonEntity.TABLE_NAME to RelationClassPersonApi(this.database.relationClassPersonDao()), RelationClassPersonEntity.TABLE_NAME to RelationClassPersonApi(this.database.relationClassPersonDao(), session),
RelationPersonSessionSubjectEntity.TABLE_NAME to RelationPersonSessionSubjectApi(this.database.relationPersonSessionSubjectDao(), session),
) )
/** /**

View file

@ -1,7 +1,9 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao 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
class ClassApi(dao: BaseDao<ClassEntity>) : BaseJsonApi<ClassEntity>(dao) class ClassApi(dao: BaseTaskDao<ClassEntity>, session: SessionEntity) :
BaseTaskApi<ClassEntity>(dao, session)

View file

@ -1,7 +1,9 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao 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
class PersonApi(dao: BaseDao<PersonEntity>) : BaseJsonApi<PersonEntity>(dao) class PersonApi(dao: BaseTaskDao<PersonEntity>, session: SessionEntity) :
BaseTaskApi<PersonEntity>(dao, session)

View file

@ -1,7 +1,10 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao 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
class RelationClassPersonApi(dao: BaseDao<RelationClassPersonEntity>) : BaseJsonApi<RelationClassPersonEntity>(dao)
class RelationClassPersonApi(dao: BaseTaskDao<RelationClassPersonEntity>, session: SessionEntity) :
BaseTaskApi<RelationClassPersonEntity>(dao, session)

View file

@ -0,0 +1,10 @@
package com.faraphel.tasks_valider.database.api.entities
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.entities.RelationPersonSessionSubjectEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity
class RelationPersonSessionSubjectApi(dao: BaseTaskDao<RelationPersonSessionSubjectEntity>, session: SessionEntity) :
BaseTaskApi<RelationPersonSessionSubjectEntity>(dao, session)

View file

@ -1,7 +1,8 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao 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
class SessionApi(dao: BaseDao<SessionEntity>) : BaseJsonApi<SessionEntity>(dao) class SessionApi(dao: BaseTaskDao<SessionEntity>, session: SessionEntity) :
BaseTaskApi<SessionEntity>(dao, session)

View file

@ -1,7 +1,10 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.SubjectEntity import com.faraphel.tasks_valider.database.entities.SubjectEntity
class SubjectApi(dao: BaseDao<SubjectEntity>) : BaseJsonApi<SubjectEntity>(dao)
class SubjectApi(dao: BaseTaskDao<SubjectEntity>, session: SessionEntity) :
BaseTaskApi<SubjectEntity>(dao, session)

View file

@ -1,7 +1,10 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.TaskEntity import com.faraphel.tasks_valider.database.entities.TaskEntity
class TaskApi(dao: BaseDao<TaskEntity>) : BaseJsonApi<TaskEntity>(dao)
class TaskApi(dao: BaseTaskDao<TaskEntity>, session: SessionEntity) :
BaseTaskApi<TaskEntity>(dao, session)

View file

@ -1,7 +1,10 @@
package com.faraphel.tasks_valider.database.api.entities package com.faraphel.tasks_valider.database.api.entities
import com.faraphel.tasks_valider.database.api.entities.base.BaseJsonApi import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.ValidationEntity import com.faraphel.tasks_valider.database.entities.ValidationEntity
class ValidationApi(dao: BaseDao<ValidationEntity>) : BaseJsonApi<ValidationEntity>(dao)
class ValidationApi(dao: BaseTaskDao<ValidationEntity>, session: SessionEntity) :
BaseTaskApi<ValidationEntity>(dao, session)

View file

@ -10,23 +10,23 @@ interface BaseApi {
* Handle the HEAD request * Handle the HEAD request
* This is used to check if a data exists in the database * This is used to check if a data exists in the database
*/ */
fun head(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response
/** /**
* Handle the GET request * Handle the GET request
* This is used to get data from the database * This is used to get all the data of a table from the database
*/ */
fun get(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response
/** /**
* Handle the POST request * Handle the POST request
* This is used to insert data into the database * This is used to insert data into the database
*/ */
fun post(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response
/** /**
* Handle the PUT request * Handle the PUT request
* This is used to delete data from the database * This is used to delete data from the database
*/ */
fun delete(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response
} }

View file

@ -1,6 +1,6 @@
package com.faraphel.tasks_valider.database.api.entities.base package com.faraphel.tasks_valider.database.api.entities.base
import com.faraphel.tasks_valider.database.dao.base.BaseDao 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
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
@ -11,7 +11,7 @@ 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: BaseDao<Entity>) : BaseApi { abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseCronDao<Entity>) : BaseApi {
companion object { companion object {
private val parser = Gson() ///< The JSON parser private val parser = Gson() ///< The JSON parser
} }
@ -20,9 +20,9 @@ abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseDao<Entity>)
// Requests // Requests
override fun head(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
val obj = parser.fromJson<Entity>( val obj = parser.fromJson<Entity>(
session.inputStream.bufferedReader().readText(), httpSession.inputStream.bufferedReader().readText(),
this.entityTypeToken.type this.entityTypeToken.type
) )
val exists = this.dao.exists(obj) val exists = this.dao.exists(obj)
@ -34,7 +34,7 @@ abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseDao<Entity>)
) )
} }
override fun get(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
return NanoHTTPD.newFixedLengthResponse( return NanoHTTPD.newFixedLengthResponse(
NanoHTTPD.Response.Status.OK, NanoHTTPD.Response.Status.OK,
"application/json", "application/json",
@ -42,9 +42,9 @@ abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseDao<Entity>)
) )
} }
override fun post(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
val obj = parser.fromJson<Entity>( val obj = parser.fromJson<Entity>(
session.inputStream.bufferedReader().readText(), httpSession.inputStream.bufferedReader().readText(),
this.entityTypeToken.type this.entityTypeToken.type
) )
val id = this.dao.insert(obj) val id = this.dao.insert(obj)
@ -56,9 +56,9 @@ abstract class BaseJsonApi<Entity: BaseEntity>(private val dao: BaseDao<Entity>)
) )
} }
override fun delete(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
val obj = parser.fromJson<Entity>( val obj = parser.fromJson<Entity>(
session.inputStream.bufferedReader().readText(), httpSession.inputStream.bufferedReader().readText(),
this.entityTypeToken.type this.entityTypeToken.type
) )
val count = this.dao.delete(obj) val count = this.dao.delete(obj)

View file

@ -0,0 +1,49 @@
package com.faraphel.tasks_valider.database.api.entities.base
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import fi.iki.elonen.NanoHTTPD
/**
* A base for the API to handle the database operations.
* This is preconfigured to handle data for Task objects.
* @param Entity the entity type to handle
*/
abstract class BaseTaskApi<Entity: BaseEntity>(
private val dao: BaseTaskDao<Entity>,
private val session: SessionEntity,
) : BaseJsonApi<Entity>(dao) {
companion object {
private val parser = Gson() ///< The JSON parser
}
private val entityTypeToken: TypeToken<Entity> = object: TypeToken<Entity>() {} ///< the type of the managed entity
// Requests
override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
val obj = parser.fromJson<Entity>(
httpSession.inputStream.bufferedReader().readText(),
this.entityTypeToken.type
)
// check if the object is in the object accessible from the session
val exists = this.dao.getAllBySession(session.id).contains(obj)
return NanoHTTPD.newFixedLengthResponse(
if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND,
"text/plain",
if (exists) "Exists" else "Not found"
)
}
override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response {
return NanoHTTPD.newFixedLengthResponse(
NanoHTTPD.Response.Status.OK,
"application/json",
parser.toJson(this.dao.getAllBySession(session.id))
)
}
}

View file

@ -2,17 +2,27 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
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.PersonEntity import com.faraphel.tasks_valider.database.entities.PersonEntity
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
@Dao @Dao
interface ClassDao : BaseDao<ClassEntity> { interface ClassDao : BaseTaskDao<ClassEntity> {
@Query("SELECT * FROM ${ClassEntity.TABLE_NAME}") @Query("SELECT * FROM ${ClassEntity.TABLE_NAME}")
override fun getAll(): List<ClassEntity> override fun getAll(): List<ClassEntity>
@Query(
"SELECT * FROM ${ClassEntity.TABLE_NAME} " +
"JOIN ${SessionEntity.TABLE_NAME} " +
"ON ${SessionEntity.TABLE_NAME}.class_id = ${ClassEntity.TABLE_NAME}.id " +
"WHERE ${SessionEntity.TABLE_NAME}.id = :sessionId"
)
override fun getAllBySession(sessionId: Long): List<ClassEntity>
/** /**
* Get the object from its identifier * Get the object from its identifier
*/ */
@ -31,7 +41,7 @@ interface ClassDao : BaseDao<ClassEntity> {
* @param id the id of the class * @param id the id of the class
*/ */
@Query( @Query(
"SELECT * FROM ${PersonEntity.TABLE_NAME} " + "SELECT ${PersonEntity.TABLE_NAME}.* FROM ${PersonEntity.TABLE_NAME} " +
"JOIN ${RelationClassPersonEntity.TABLE_NAME} " + "JOIN ${RelationClassPersonEntity.TABLE_NAME} " +
"ON ${PersonEntity.TABLE_NAME}.id = ${RelationClassPersonEntity.TABLE_NAME}.student_id " + "ON ${PersonEntity.TABLE_NAME}.id = ${RelationClassPersonEntity.TABLE_NAME}.student_id " +
"WHERE ${RelationClassPersonEntity.TABLE_NAME}.class_id = :id" "WHERE ${RelationClassPersonEntity.TABLE_NAME}.class_id = :id"

View file

@ -2,11 +2,13 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.* import com.faraphel.tasks_valider.database.entities.*
@Dao @Dao
interface PersonDao : BaseDao<PersonEntity> { interface PersonDao : BaseTaskDao<PersonEntity> {
@Query("SELECT * FROM ${PersonEntity.TABLE_NAME}") @Query("SELECT * FROM ${PersonEntity.TABLE_NAME}")
override fun getAll(): List<PersonEntity> override fun getAll(): List<PersonEntity>
@ -16,6 +18,19 @@ interface PersonDao : BaseDao<PersonEntity> {
@Query("SELECT * FROM ${PersonEntity.TABLE_NAME} WHERE id = :id") @Query("SELECT * FROM ${PersonEntity.TABLE_NAME} WHERE id = :id")
fun getById(id: Long): PersonEntity? fun getById(id: Long): PersonEntity?
@Query(
"SELECT * FROM ${PersonEntity.TABLE_NAME} " +
"WHERE id IN (" +
"SELECT student_id FROM ${RelationClassPersonEntity.TABLE_NAME} " +
"JOIN ${ClassEntity.TABLE_NAME} " +
"ON ${RelationClassPersonEntity.TABLE_NAME}.class_id = ${ClassEntity.TABLE_NAME}.id " +
"JOIN ${SessionEntity.TABLE_NAME} " +
"ON ${SessionEntity.TABLE_NAME}.class_id = ${ClassEntity.TABLE_NAME}.id " +
"WHERE ${SessionEntity.TABLE_NAME}.id = :sessionId" +
")"
)
override fun getAllBySession(sessionId: Long): List<PersonEntity>
/** /**
* Allow to get all the classes the person is attending as a student * Allow to get all the classes the person is attending as a student
*/ */

View file

@ -2,22 +2,35 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.ClassEntity
import com.faraphel.tasks_valider.database.entities.RelationClassPersonEntity import com.faraphel.tasks_valider.database.entities.RelationClassPersonEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity
@Dao @Dao
interface RelationClassPersonDao : BaseDao<RelationClassPersonEntity> { interface RelationClassPersonDao : BaseTaskDao<RelationClassPersonEntity> {
@Query("SELECT * FROM ${RelationClassPersonEntity.TABLE_NAME}") @Query("SELECT * FROM ${RelationClassPersonEntity.TABLE_NAME}")
override fun getAll(): List<RelationClassPersonEntity> override fun getAll(): List<RelationClassPersonEntity>
@Query(
"SELECT ${RelationClassPersonEntity.TABLE_NAME}.* FROM ${RelationClassPersonEntity.TABLE_NAME} " +
"JOIN ${ClassEntity.TABLE_NAME} " +
"ON ${RelationClassPersonEntity.TABLE_NAME}.class_id = ${ClassEntity.TABLE_NAME}.id " +
"JOIN ${SessionEntity.TABLE_NAME} " +
"ON ${SessionEntity.TABLE_NAME}.class_id = ${ClassEntity.TABLE_NAME}.id " +
"WHERE ${SessionEntity.TABLE_NAME}.id = :sessionId"
)
override fun getAllBySession(sessionId: Long): List<RelationClassPersonEntity>
/** /**
* Get the object from its identifiers * Get the object from its identifiers
*/ */
@Query( @Query(
"SELECT * FROM ${RelationClassPersonEntity.TABLE_NAME} " + "SELECT * FROM ${RelationClassPersonEntity.TABLE_NAME} " +
"WHERE " + "WHERE class_id = :classId " +
"class_id = :classId AND " + "AND student_id = :studentId"
"student_id = :studentId"
) )
fun getById(classId: Long, studentId: Long): RelationClassPersonEntity? fun getById(classId: Long, studentId: Long): RelationClassPersonEntity?
} }

View file

@ -0,0 +1,32 @@
package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao
import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity
@Dao
interface RelationPersonSessionSubjectDao : BaseTaskDao<RelationPersonSessionSubjectEntity> {
@Query("SELECT * FROM ${RelationPersonSessionSubjectEntity.TABLE_NAME}")
override fun getAll(): List<RelationPersonSessionSubjectEntity>
@Query(
"SELECT * FROM ${RelationPersonSessionSubjectEntity.TABLE_NAME} " +
"WHERE session_id = :sessionId"
)
override fun getAllBySession(sessionId: Long): List<RelationPersonSessionSubjectEntity>
/**
* Get the object from its identifiers
*/
@Query(
"SELECT * FROM ${RelationPersonSessionSubjectEntity.TABLE_NAME} " +
"WHERE student_id = :studentId " +
"AND session_id = :sessionId " +
"AND subject_id = :subjectId"
)
fun getById(studentId: Long, sessionId: Long, subjectId: Long): RelationPersonSessionSubjectEntity?
}

View file

@ -2,14 +2,20 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.ClassEntity
import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SessionEntity
@Dao @Dao
interface SessionDao : BaseDao<SessionEntity> { interface SessionDao : BaseTaskDao<SessionEntity> {
@Query("SELECT * FROM ${SessionEntity.TABLE_NAME}") @Query("SELECT * FROM ${SessionEntity.TABLE_NAME}")
override fun getAll(): List<SessionEntity> override fun getAll(): List<SessionEntity>
@Query("SELECT * FROM ${SessionEntity.TABLE_NAME} WHERE id = :sessionId")
override fun getAllBySession(sessionId: Long): List<SessionEntity>
/** /**
* Get the object from its identifier * Get the object from its identifier
*/ */

View file

@ -2,15 +2,28 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.ClassEntity
import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity
import com.faraphel.tasks_valider.database.entities.SubjectEntity import com.faraphel.tasks_valider.database.entities.SubjectEntity
import com.faraphel.tasks_valider.database.entities.TaskEntity import com.faraphel.tasks_valider.database.entities.TaskEntity
@Dao @Dao
interface SubjectDao : BaseDao<SubjectEntity> { interface SubjectDao : BaseTaskDao<SubjectEntity> {
@Query("SELECT * FROM ${SubjectEntity.TABLE_NAME}") @Query("SELECT * FROM ${SubjectEntity.TABLE_NAME}")
override fun getAll(): List<SubjectEntity> override fun getAll(): List<SubjectEntity>
@Query(
"SELECT * FROM ${SubjectEntity.TABLE_NAME} " +
"WHERE id IN (" +
"SELECT subject_id FROM ${RelationPersonSessionSubjectEntity.TABLE_NAME} " +
"WHERE session_id = :sessionId" +
")"
)
override fun getAllBySession(sessionId: Long): List<SubjectEntity>
/** /**
* Get the object from its identifier * Get the object from its identifier
*/ */

View file

@ -2,15 +2,27 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity
import com.faraphel.tasks_valider.database.entities.TaskEntity import com.faraphel.tasks_valider.database.entities.TaskEntity
import com.faraphel.tasks_valider.database.entities.ValidationEntity import com.faraphel.tasks_valider.database.entities.ValidationEntity
@Dao @Dao
interface TaskDao : BaseDao<TaskEntity> { interface TaskDao : BaseTaskDao<TaskEntity> {
@Query("SELECT * FROM ${TaskEntity.TABLE_NAME}") @Query("SELECT * FROM ${TaskEntity.TABLE_NAME}")
override fun getAll(): List<TaskEntity> override fun getAll(): List<TaskEntity>
@Query(
"SELECT * FROM ${TaskEntity.TABLE_NAME} " +
"WHERE subject_id IN (" +
"SELECT subject_id FROM ${RelationPersonSessionSubjectEntity.TABLE_NAME} " +
"WHERE session_id = :sessionId" +
")"
)
override fun getAllBySession(sessionId: Long): List<TaskEntity>
/** /**
* Get the object from its identifier * Get the object from its identifier
*/ */

View file

@ -2,14 +2,29 @@ package com.faraphel.tasks_valider.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.faraphel.tasks_valider.database.dao.base.BaseDao import com.faraphel.tasks_valider.database.dao.base.BaseCronDao
import com.faraphel.tasks_valider.database.dao.base.BaseSessionDao
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity
import com.faraphel.tasks_valider.database.entities.TaskEntity
import com.faraphel.tasks_valider.database.entities.ValidationEntity import com.faraphel.tasks_valider.database.entities.ValidationEntity
@Dao @Dao
interface ValidationDao : BaseDao<ValidationEntity> { interface ValidationDao : BaseTaskDao<ValidationEntity> {
@Query("SELECT * FROM ${ValidationEntity.TABLE_NAME}") @Query("SELECT * FROM ${ValidationEntity.TABLE_NAME}")
override fun getAll(): List<ValidationEntity> override fun getAll(): List<ValidationEntity>
@Query(
"SELECT * FROM ${ValidationEntity.TABLE_NAME} " +
"JOIN ${TaskEntity.TABLE_NAME} " +
"ON ${ValidationEntity.TABLE_NAME}.task_id = ${TaskEntity.TABLE_NAME}.id " +
"WHERE ${TaskEntity.TABLE_NAME}.subject_id IN (" +
"SELECT subject_id FROM ${RelationPersonSessionSubjectEntity.TABLE_NAME} " +
"WHERE session_id = :sessionId" +
")"
)
override fun getAllBySession(sessionId: Long): List<ValidationEntity>
/** /**
* Get the object from its identifiers * Get the object from its identifiers
*/ */

View file

@ -3,17 +3,13 @@ package com.faraphel.tasks_valider.database.dao.base
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Update
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
/** /**
* A base DAO to handle the database operations. * A base DAO to handle the database operations (CRON).
* @param Entity the entity to handle * @param Entity the entity to handle
*/ */
interface BaseDao<Entity> { interface BaseCronDao<Entity> {
/** /**
* Check if the entities exists in the database. * Check if the entities exists in the database.
*/ */

View file

@ -0,0 +1,11 @@
package com.faraphel.tasks_valider.database.dao.base
/**
* The base for a DAO that is inside a session scope.
*/
interface BaseSessionDao<Entity> {
/**
* Return all the objects concerned by a session.
*/
fun getAllBySession(sessionId: Long): List<Entity>
}

View file

@ -0,0 +1,6 @@
package com.faraphel.tasks_valider.database.dao.base
/**
* The base for a DAO for the task project.
*/
interface BaseTaskDao<Entity>: BaseCronDao<Entity>, BaseSessionDao<Entity>

View file

@ -0,0 +1,48 @@
package com.faraphel.tasks_valider.database.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
/**
* Represent the relation that associate a subject to a person for a specific session.
*/
@Entity(
tableName = RelationPersonSessionSubjectEntity.TABLE_NAME,
primaryKeys = [
"student_id",
"session_id",
"subject_id"
],
foreignKeys = [
ForeignKey(
entity = PersonEntity::class,
parentColumns = ["id"],
childColumns = ["student_id"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = SessionEntity::class,
parentColumns = ["id"],
childColumns = ["session_id"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = SubjectEntity::class,
parentColumns = ["id"],
childColumns = ["subject_id"],
onDelete = ForeignKey.CASCADE
),
]
)
data class RelationPersonSessionSubjectEntity (
@ColumnInfo("student_id", index = true) val studentId: Long,
@ColumnInfo("session_id", index = true) val sessionId: Long,
@ColumnInfo("subject_id", index = true) val subjectId: Long,
) : BaseEntity() {
companion object {
const val TABLE_NAME = "relation_person_session_subject"
}
}

View file

@ -115,3 +115,19 @@ fun populateTaskDatabaseTest(database: TaskDatabase) {
val taskB1 = database.taskDao().getById(taskB1Id)!! val taskB1 = database.taskDao().getById(taskB1Id)!!
val taskB2 = database.taskDao().getById(taskB2Id)!! val taskB2 = database.taskDao().getById(taskB2Id)!!
} }
fun populateSubjectSessionPersonTest(database: TaskDatabase, session: SessionEntity) {
// get the list of available subjects
val subjects = database.subjectDao().getAll()
// give a random subject to everyone in the session
database.personDao().getAllBySession(session.id).forEach { person ->
// get a random subject
val subject = subjects.random()
// insert a new subject for a person for a specific session
database.relationPersonSessionSubjectDao().insert(
RelationPersonSessionSubjectEntity(person.id, session.id, subject.id)
)
}
}

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.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -9,16 +11,18 @@ import androidx.navigation.NavController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.faraphel.tasks_valider.database.TaskDatabase
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun CommunicationInternetSelectScreen(activity: Activity) { fun CommunicationInternetSelectScreen(activity: Activity, database: TaskDatabase) {
val controller = rememberNavController() val controller = rememberNavController()
NavHost(navController = controller, startDestination = "mode") { NavHost(navController = controller, startDestination = "mode") {
composable("mode") { CommunicationInternetSelectContent(controller) } composable("mode") { CommunicationInternetSelectContent(controller) }
composable("client") { CommunicationInternetClientScreen(activity) } composable("client") { CommunicationInternetClientScreen(activity) }
composable("server") { CommunicationInternetServerScreen(activity) } composable("server") { CommunicationInternetServerScreen(activity, database) }
} }
} }

View file

@ -1,89 +1,115 @@
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 android.util.Log import android.util.Log
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.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.room.Room import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.faraphel.tasks_valider.connectivity.task.TaskClient import com.faraphel.tasks_valider.connectivity.task.TaskClient
import com.faraphel.tasks_valider.connectivity.task.TaskServer import com.faraphel.tasks_valider.connectivity.task.TaskServer
import com.faraphel.tasks_valider.database.TaskDatabase import com.faraphel.tasks_valider.database.TaskDatabase
import com.faraphel.tasks_valider.database.entities.ClassEntity import com.faraphel.tasks_valider.database.entities.ClassEntity
import com.faraphel.tasks_valider.database.entities.PersonEntity import com.faraphel.tasks_valider.database.entities.PersonEntity
import com.faraphel.tasks_valider.database.populateTaskDatabaseTest import com.faraphel.tasks_valider.database.entities.SessionEntity
import com.faraphel.tasks_valider.database.populateSubjectSessionPersonTest
import com.faraphel.tasks_valider.ui.screen.authentification.AuthentificationServerScreen import com.faraphel.tasks_valider.ui.screen.authentification.AuthentificationServerScreen
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 java.time.Instant
/** /**
* Screen for the host to configure the server * Screen for the host to configure the server
*/ */
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun CommunicationInternetServerScreen(activity: Activity) { fun CommunicationInternetServerScreen(
activity: Activity,
database: TaskDatabase,
) {
val controller = rememberNavController()
val adminPersonEntity = remember { mutableStateOf<PersonEntity?>(null) } val adminPersonEntity = remember { mutableStateOf<PersonEntity?>(null) }
// if the admin person is not created, prompt the user for the admin information
if (adminPersonEntity.value == null) {
return AuthentificationServerScreen(adminPersonEntity)
}
val client = remember { mutableStateOf<TaskClient?>(null) } val client = remember { mutableStateOf<TaskClient?>(null) }
// if the client to the server is not created, prompt the user for the server configuration NavHost(navController = controller, startDestination = "authentication") {
if (client.value == null) { composable("authentication") {
return CommunicationInternetServerContent( // if the admin person is not created, prompt the user for the admin information
activity, if (adminPersonEntity.value == null) AuthentificationServerScreen(adminPersonEntity)
else controller.navigate("configuration")
}
composable("configuration") {
if (client.value == null)
CommunicationInternetServerContent(
database,
adminPersonEntity.value!!, adminPersonEntity.value!!,
client client
) )
else controller.navigate("session")
}
composable("session") {
TaskSessionScreen(activity, client.value!!)
}
} }
// otherwise, go to the base tasks screen
return TaskSessionScreen(activity, client.value!!)
} }
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun CommunicationInternetServerContent( fun CommunicationInternetServerContent(
activity: Activity, database: TaskDatabase,
adminPersonEntity: PersonEntity, adminPersonEntity: PersonEntity,
client: MutableState<TaskClient?> client: MutableState<TaskClient?>
) { ) {
val expandedStudentList = remember { mutableStateOf(false) } val classes = remember { mutableStateOf<List<ClassEntity>?>(null) }
val selectedClass = remember { mutableStateOf<ClassEntity?>(null) }
val areClassesExpanded = remember { mutableStateOf(false) }
val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) } val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) }
LaunchedEffect(true) {
// refresh the list of classes
Thread { refreshClasses(database, classes) }.start()
}
Column { Column {
// student list // classes
Button(onClick = { expandedStudentList.value = !expandedStudentList.value }) { Button(onClick = { areClassesExpanded.value = !areClassesExpanded.value }) {
Text(text = "Select Students List") Row {
Text(text = "Class")
// display the selected class, if selected
if (selectedClass.value != null)
Text(text = selectedClass.value!!.name)
}
} }
DropdownMenu( DropdownMenu(
expanded = expandedStudentList.value, expanded = areClassesExpanded.value,
onDismissRequest = { expandedStudentList.value = false } onDismissRequest = { areClassesExpanded.value = false }
) { ) {
// TODO(Faraphel): student lists should be loaded from the database or a file // TODO(Faraphel): student lists should be loaded from the database or a file
classes.value?.forEach { class_ ->
DropdownMenuItem( DropdownMenuItem(
text = { Text("ISRI") }, text = { Text(class_.name) },
onClick = {} onClick = {
) selectedClass.value = class_
DropdownMenuItem( areClassesExpanded.value = false
text = { Text("MIAGE") }, }
onClick = {}
) )
} }
}
// server port // server port
TextField( TextField(
@ -97,28 +123,42 @@ fun CommunicationInternetServerContent(
} }
) )
// check if a class is selected
if (selectedClass.value != null)
// button to create the server
Button(onClick = { Button(onClick = {
Thread { // a thread is used for networking Thread { // a thread is used for networking
// Reset the database | TODO(Faraphel): only for testing purpose
activity.deleteDatabase("local")
// Create the database
val database = Room.databaseBuilder(
activity,
TaskDatabase::class.java,
"local"
).build()
// Populate the database with test data
// TODO(Faraphel): remove test data
populateTaskDatabaseTest(database)
// Insert the admin in the database // Insert the admin in the database
database.personDao().insert(adminPersonEntity) database.personDao().insert(adminPersonEntity)
// Create a new session
// TODO(Faraphel): name
val (sessionId) = database.sessionDao().insert(
SessionEntity(
name="NOM",
start=Instant.now(),
classId=selectedClass.value!!.id,
)
)
val session = database.sessionDao().getById(sessionId)!!
// TODO(Faraphel): remove, this is a test function
Thread {
populateSubjectSessionPersonTest(database, session)
}.let { thread ->
thread.start()
thread.join()
}
// Create the server // Create the server
Log.i("room-server", "creating the server") Log.i("room-server", "creating the server")
val server = TaskServer(serverPort.intValue, database, adminPersonEntity) val server = TaskServer(
serverPort.intValue,
database,
session,
adminPersonEntity,
)
server.start() server.start()
// Get the client from the server // Get the client from the server
@ -129,3 +169,11 @@ fun CommunicationInternetServerContent(
} }
} }
} }
/**
* Refresh the list of classes
*/
fun refreshClasses(database: TaskDatabase, classes: MutableState<List<ClassEntity>?>) {
classes.value = database.classDao().getAll()
}

View file

@ -1,7 +1,9 @@
package com.faraphel.tasks_valider.ui.screen.communication package com.faraphel.tasks_valider.ui.screen.communication
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.ButtonDefaults import androidx.compose.material3.ButtonDefaults
@ -13,6 +15,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
import com.faraphel.tasks_valider.database.TaskDatabase
import com.faraphel.tasks_valider.ui.screen.communication.internet.CommunicationInternetSelectScreen import com.faraphel.tasks_valider.ui.screen.communication.internet.CommunicationInternetSelectScreen
import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.CommunicationWifiP2pScreen import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.CommunicationWifiP2pScreen
@ -21,10 +24,13 @@ import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.CommunicationW
* CommunicationController is the main controller for the communication screen. * CommunicationController is the main controller for the communication screen.
* It is responsible for handling the navigation between the different communication methods. * It is responsible for handling the navigation between the different communication methods.
* It is also responsible for initializing the communication methods. * It is also responsible for initializing the communication methods.
*
* @param activity: The activity that hosts the communication screen. * @param activity: The activity that hosts the communication screen.
* @param database: the database.
*/ */
@RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
fun CommunicationModeSelectionScreen(activity: Activity) { fun CommunicationModeSelectionScreen(activity: Activity, database: TaskDatabase) {
val controller = rememberNavController() val controller = rememberNavController()
NavHost( NavHost(
@ -35,7 +41,7 @@ fun CommunicationModeSelectionScreen(activity: Activity) {
CommunicationSelectContent(controller, activity) CommunicationSelectContent(controller, activity)
} }
composable("internet") { composable("internet") {
CommunicationInternetSelectScreen(activity) CommunicationInternetSelectScreen(activity, database)
} }
composable("wifi-p2p") { composable("wifi-p2p") {
val bwfManager = BwfManager.fromActivity(activity) val bwfManager = BwfManager.fromActivity(activity)

View file

@ -23,19 +23,19 @@ import com.google.gson.reflect.TypeToken
@Composable @Composable
fun TaskSessionScreen(activity: Activity, client: TaskClient) { fun TaskSessionScreen(activity: Activity, client: TaskClient) {
val students = remember { mutableStateOf<List<PersonEntity>?>(null) } val students = remember { mutableStateOf<List<PersonEntity>?>(null) }
val selected_student = remember { mutableStateOf<PersonEntity?>(null) } val selectedStudent = remember { mutableStateOf<PersonEntity?>(null) }
// if the groups are not yet defined, refresh the list // if the groups are not yet defined, refresh the list
if (students.value == null) if (students.value == null)
return Thread { refreshStudents(activity, client, students) }.start() return Thread { refreshStudents(activity, client, students) }.start()
if (selected_student.value != null) if (selectedStudent.value != null)
return TaskStudentScreen(selected_student.value!!) return TaskStudentScreen(activity, client, selectedStudent.value!!)
Column { Column {
// if the groups have already been defined, display them // if the groups have already been defined, display them
for (student in students.value!!) { for (student in students.value!!) {
Button(onClick = { selected_student.value = student }) { Button(onClick = { selectedStudent.value = student }) {
Text(text = student.fullName()) Text(text = student.fullName())
} }
} }

View file

@ -1,14 +1,89 @@
package com.faraphel.tasks_valider.ui.screen.task package com.faraphel.tasks_valider.ui.screen.task
import android.app.Activity
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import com.faraphel.tasks_valider.database.entities.PersonEntity import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.faraphel.tasks_valider.connectivity.task.TaskClient
import com.faraphel.tasks_valider.database.entities.*
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
/** /**
* This screen represent a student * This screen represent a student
* @param student the student object * @param student the student object
*/ */
@Composable @Composable
fun TaskStudentScreen(student: PersonEntity) { fun TaskStudentScreen(activity: Activity, client: TaskClient, student: PersonEntity) {
val tasks = remember { mutableStateOf<List<TaskEntity>?>(null) }
if (tasks.value == null)
Thread { refreshTasks(activity, client, student, tasks) }.start()
Column {
Text(text = student.fullName()) Text(text = student.fullName())
tasks.value?.forEach { task ->
Button(onClick = {}) {
Column {
Text(task.title)
task.description?.let { description -> Text(description) }
}
}
}
}
}
fun refreshTasks(
activity: Activity,
client: TaskClient,
student: PersonEntity,
tasks: MutableState<List<TaskEntity>?>
) {
val jsonParser = Gson()
// try to obtain the list of subject
val responseSubjects = client.get("entities/" + RelationPersonSessionSubjectEntity.TABLE_NAME)
// in case of error, notify it
if (!responseSubjects.isSuccessful)
return activity.runOnUiThread { Toast.makeText(activity, responseSubjects.message, Toast.LENGTH_LONG).show() }
// parse the list of subjects
val allPersonSessionSubjects = jsonParser.fromJson<List<RelationPersonSessionSubjectEntity>>(
responseSubjects.body.string(),
object : TypeToken<List<RelationPersonSessionSubjectEntity>>(){}.type
)
// get the subject that the student is using
val relationPersonSessionSubjects = allPersonSessionSubjects.firstOrNull { relation -> relation.studentId == student.id }
if (relationPersonSessionSubjects == null)
// TODO(Faraphel): should be able to assign a subject ?
return activity.runOnUiThread { Toast.makeText(activity, "No subject assigned", Toast.LENGTH_LONG).show() }
// try to obtain the list of tasks
val responseTasks = client.get("entities/" + TaskEntity.TABLE_NAME)
// in case of error, notify it
if (!responseTasks.isSuccessful)
return activity.runOnUiThread { Toast.makeText(activity, responseTasks.message, Toast.LENGTH_LONG).show() }
// parse the list of subjects
val allTasks = jsonParser.fromJson<List<TaskEntity>>(
responseTasks.body.string(),
object : TypeToken<List<TaskEntity>>(){}.type
)
// get the tasks that are linked to this subject
tasks.value = allTasks.filter { task ->
task.subjectId == relationPersonSessionSubjects.subjectId
}.sortedBy { task ->
task.order
}
} }