mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-06-07 07:19:54 +00:00
misc: Cleanup Database
This commit is contained in:
parent
9e463d7fd3
commit
a9da9198be
@ -5,8 +5,6 @@ import androidx.room.AutoMigration
|
|||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.room.migration.Migration
|
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
|
||||||
import com.dzeio.openhealth.data.food.Food
|
import com.dzeio.openhealth.data.food.Food
|
||||||
import com.dzeio.openhealth.data.food.FoodDao
|
import com.dzeio.openhealth.data.food.FoodDao
|
||||||
import com.dzeio.openhealth.data.step.Step
|
import com.dzeio.openhealth.data.step.Step
|
||||||
@ -16,6 +14,11 @@ import com.dzeio.openhealth.data.water.WaterDao
|
|||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
import com.dzeio.openhealth.data.weight.WeightDao
|
import com.dzeio.openhealth.data.weight.WeightDao
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ROOM SQLite database for the application
|
||||||
|
*
|
||||||
|
* It may be replaced if I want to fully encrypt the database
|
||||||
|
*/
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
Weight::class,
|
Weight::class,
|
||||||
@ -23,16 +26,16 @@ import com.dzeio.openhealth.data.weight.WeightDao
|
|||||||
Step::class,
|
Step::class,
|
||||||
Food::class
|
Food::class
|
||||||
],
|
],
|
||||||
|
// TODO: go back to version 1 when the app is published
|
||||||
version = 3,
|
version = 3,
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration(from = 1, to = 2)
|
AutoMigration(from = 1, to = 2),
|
||||||
|
AutoMigration(from = 2, to = 3)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
// private val PREPOPULATE_DATA = listOf(Thing("1", "val"), Thing("2", "val 2"))
|
|
||||||
|
|
||||||
abstract fun weightDao(): WeightDao
|
abstract fun weightDao(): WeightDao
|
||||||
abstract fun waterDao(): WaterDao
|
abstract fun waterDao(): WaterDao
|
||||||
abstract fun stepDao(): StepDao
|
abstract fun stepDao(): StepDao
|
||||||
@ -40,42 +43,29 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract fun foodDao(): FoodDao
|
abstract fun foodDao(): FoodDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* database name duh
|
||||||
|
*/
|
||||||
private const val DATABASE_NAME = "open_health"
|
private const val DATABASE_NAME = "open_health"
|
||||||
|
|
||||||
// For Singleton instantiation
|
// For Singleton instantiation
|
||||||
@Volatile
|
@Volatile
|
||||||
private var instance: AppDatabase? = null
|
private var instance: AppDatabase? = null
|
||||||
|
|
||||||
|
// get the Database instance
|
||||||
fun getInstance(context: Context): AppDatabase {
|
fun getInstance(context: Context): AppDatabase {
|
||||||
return instance ?: synchronized(this) {
|
return instance ?: synchronized(this) {
|
||||||
instance ?: buildDatabase(context).also { instance = it }
|
instance ?: buildDatabase(context).also { instance = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and pre-populate the database. See this article for more details:
|
/**
|
||||||
// https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
|
* build teh database
|
||||||
|
*/
|
||||||
private fun buildDatabase(context: Context): AppDatabase {
|
private fun buildDatabase(context: Context): AppDatabase {
|
||||||
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
|
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
|
||||||
.addMigrations(MIGRATION_2_3)
|
// .addMigrations(MIGRATION_2_3)
|
||||||
// .addCallback(object : Callback() {
|
|
||||||
// override fun onCreate(db: SupportSQLiteDatabase) {
|
|
||||||
// super.onCreate(db)
|
|
||||||
// // moving to a new thread
|
|
||||||
// Executors.newSingleThreadExecutor().execute {
|
|
||||||
// getInstance(context).thingDao()
|
|
||||||
// .insert(PREPOPULATE_DATA)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val MIGRATION_2_3 = object : Migration(2, 3) {
|
|
||||||
override fun migrate(database: SupportSQLiteDatabase) {
|
|
||||||
database.execSQL("ALTER TABLE Food ADD COLUMN serving TEXT NOT NULL")
|
|
||||||
database.execSQL("ALTER TABLE Food ADD COLUMN image TEXT")
|
|
||||||
database.execSQL("ALTER TABLE Food ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
package com.dzeio.openhealth.data.converters
|
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
object TiviTypeConverters {
|
|
||||||
private val formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
|
||||||
|
|
||||||
@TypeConverter
|
|
||||||
@JvmStatic
|
|
||||||
fun toOffsetDateTime(value: String?): OffsetDateTime? {
|
|
||||||
return value?.let {
|
|
||||||
return formatter.parse(value, OffsetDateTime::from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TypeConverter
|
|
||||||
@JvmStatic
|
|
||||||
fun fromOffsetDateTime(date: OffsetDateTime?): String? {
|
|
||||||
return date?.format(formatter)
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,23 +10,62 @@ import java.util.TimeZone
|
|||||||
data class Food(
|
data class Food(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0,
|
var id: Long = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The product name
|
||||||
|
*/
|
||||||
var name: String,
|
var name: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The product serving text
|
||||||
|
*
|
||||||
|
* ex: `250ml`, `520g`, etc
|
||||||
|
*/
|
||||||
var serving: String,
|
var serving: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity taken by the user
|
||||||
|
*/
|
||||||
var quantity: Float,
|
var quantity: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of proteins there is for 100 quantity
|
||||||
|
*/
|
||||||
var proteins: Float,
|
var proteins: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of carbohydrates there is for 100 quantity
|
||||||
|
*/
|
||||||
var carbohydrates: Float,
|
var carbohydrates: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of fat there is for 100 quantity
|
||||||
|
*/
|
||||||
var fat: Float,
|
var fat: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of energy there is for 100 quantity
|
||||||
|
*/
|
||||||
var energy: Float,
|
var energy: Float,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the url of the image
|
* the url of the image of the product
|
||||||
*/
|
*/
|
||||||
var image: String?,
|
var image: String?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the entry was added to our Database
|
||||||
|
*/
|
||||||
var timestamp: Long = Calendar.getInstance(TimeZone.getTimeZone("UTC")).timeInMillis,
|
var timestamp: Long = Calendar.getInstance(TimeZone.getTimeZone("UTC")).timeInMillis,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform an OpenFoodFact product to use for our Database
|
||||||
|
*/
|
||||||
fun fromOpenFoodFact(food: OFFProduct, quantity: Float? = null): Food {
|
fun fromOpenFoodFact(food: OFFProduct, quantity: Float? = null): Food {
|
||||||
|
|
||||||
|
// try to know how much was eaten by the user if not said
|
||||||
var eaten = quantity ?: food.servingQuantity ?: food.productQuantity ?: 0f
|
var eaten = quantity ?: food.servingQuantity ?: food.productQuantity ?: 0f
|
||||||
if (eaten == 0f) {
|
if (eaten == 0f) {
|
||||||
if (food.servingQuantity != null && food.servingQuantity != 0f) {
|
if (food.servingQuantity != null && food.servingQuantity != 0f) {
|
||||||
@ -37,11 +76,13 @@ data class Food(
|
|||||||
}
|
}
|
||||||
return Food(
|
return Food(
|
||||||
name = food.name,
|
name = food.name,
|
||||||
|
// do some slight edit on the serving to remove strange entries like `100 g`
|
||||||
serving = (food.servingSize ?: food.quantity ?: "unknown").replace(Regex(" +"), ""),
|
serving = (food.servingSize ?: food.quantity ?: "unknown").replace(Regex(" +"), ""),
|
||||||
quantity = eaten,
|
quantity = eaten,
|
||||||
proteins = food.nutriments.proteins,
|
proteins = food.nutriments.proteins,
|
||||||
carbohydrates = food.nutriments.carbohydrates,
|
carbohydrates = food.nutriments.carbohydrates,
|
||||||
fat = food.nutriments.fat,
|
fat = food.nutriments.fat,
|
||||||
|
// handle case where the energy is not given in kcal but only in kj
|
||||||
energy = food.nutriments.energy ?: (food.nutriments.energyKJ * 0.2390057361).toFloat(),
|
energy = food.nutriments.energy ?: (food.nutriments.energyKJ * 0.2390057361).toFloat(),
|
||||||
image = food.image
|
image = food.image
|
||||||
)
|
)
|
||||||
|
@ -10,9 +10,8 @@ class FoodRepository @Inject constructor(
|
|||||||
private val dao: FoodDao,
|
private val dao: FoodDao,
|
||||||
private val offSource: OpenFoodFactService
|
private val offSource: OpenFoodFactService
|
||||||
) {
|
) {
|
||||||
suspend fun findOnlineFood(name: String): Response<OFFResult> {
|
suspend fun searchOpenFoodFact(name: String): Response<OFFResult> =
|
||||||
return offSource.searchProducts(name)
|
offSource.searchProducts(name)
|
||||||
}
|
|
||||||
|
|
||||||
fun getAll() = dao.getAll()
|
fun getAll() = dao.getAll()
|
||||||
|
|
||||||
|
@ -3,14 +3,33 @@ package com.dzeio.openhealth.data.openfoodfact
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class OFFNutriments(
|
data class OFFNutriments(
|
||||||
|
/**
|
||||||
|
* the quantity of carbohydrates in a 100g serving
|
||||||
|
*/
|
||||||
@SerializedName("carbohydrates_100g")
|
@SerializedName("carbohydrates_100g")
|
||||||
var carbohydrates: Float,
|
var carbohydrates: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the energy in kcal in a 100g serving
|
||||||
|
*/
|
||||||
@SerializedName("energy-kcal_100g")
|
@SerializedName("energy-kcal_100g")
|
||||||
var energy: Float?,
|
var energy: Float?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the energy KL in a 100g serving
|
||||||
|
*/
|
||||||
@SerializedName("energy-kj_100g")
|
@SerializedName("energy-kj_100g")
|
||||||
var energyKJ: Float,
|
var energyKJ: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of fat in a 100g serving
|
||||||
|
*/
|
||||||
@SerializedName("fat_100g")
|
@SerializedName("fat_100g")
|
||||||
var fat: Float,
|
var fat: Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of proteins in a 100g serving
|
||||||
|
*/
|
||||||
@SerializedName("proteins_100g")
|
@SerializedName("proteins_100g")
|
||||||
var proteins: Float
|
var proteins: Float
|
||||||
)
|
)
|
||||||
|
@ -3,26 +3,51 @@ package com.dzeio.openhealth.data.openfoodfact
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class OFFProduct(
|
data class OFFProduct(
|
||||||
|
/**
|
||||||
|
* the OFF product id
|
||||||
|
*/
|
||||||
@SerializedName("_id")
|
@SerializedName("_id")
|
||||||
var id: String,
|
var id: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the product name
|
||||||
|
*/
|
||||||
@SerializedName("product_name")
|
@SerializedName("product_name")
|
||||||
var name: String,
|
var name: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the size of a serving
|
||||||
|
*/
|
||||||
@SerializedName("serving_size")
|
@SerializedName("serving_size")
|
||||||
var servingSize: String?,
|
var servingSize: String?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the size of a serving without the `g`, `ml`, etc
|
||||||
|
*/
|
||||||
@SerializedName("serving_quantity")
|
@SerializedName("serving_quantity")
|
||||||
var servingQuantity: Float?,
|
var servingQuantity: Float?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the size of a serving
|
||||||
|
*/
|
||||||
@SerializedName("quantity")
|
@SerializedName("quantity")
|
||||||
var quantity: String?,
|
var quantity: String?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the size of a serving without the `g`, `ml`, etc
|
||||||
|
*/
|
||||||
@SerializedName("product_quantity")
|
@SerializedName("product_quantity")
|
||||||
var productQuantity: Float?,
|
var productQuantity: Float?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the product nutriments
|
||||||
|
*/
|
||||||
@SerializedName("nutriments")
|
@SerializedName("nutriments")
|
||||||
var nutriments: OFFNutriments,
|
var nutriments: OFFNutriments,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the product image
|
||||||
|
*/
|
||||||
@SerializedName("image_url")
|
@SerializedName("image_url")
|
||||||
var image: String?
|
var image: String?
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,9 @@ package com.dzeio.openhealth.data.openfoodfact
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class OFFResult(
|
data class OFFResult(
|
||||||
|
/**
|
||||||
|
* the list of products
|
||||||
|
*/
|
||||||
@SerializedName("products")
|
@SerializedName("products")
|
||||||
var products: List<OFFProduct>
|
var products: List<OFFProduct>
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.dzeio.openhealth.data.openfoodfact
|
package com.dzeio.openhealth.data.openfoodfact
|
||||||
|
|
||||||
|
import com.dzeio.openhealth.BuildConfig
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
@ -30,11 +31,17 @@ interface OpenFoodFactService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Headers("User-Agent: OpenHealth - Android - Version 1.0 - https://github.com/dzeiocom/OpenHealth")
|
/**
|
||||||
|
* Search a product by it's name
|
||||||
|
*/
|
||||||
|
@Headers("User-Agent: OpenHealth - Android - Version ${BuildConfig.VERSION_NAME} - https://github.com/dzeiocom/OpenHealth")
|
||||||
@GET("/cgi/search.pl?json=true&fields=_id,nutriments,product_name,serving_quantity,serving_size,quantity,product_quantity,image_url&action=process")
|
@GET("/cgi/search.pl?json=true&fields=_id,nutriments,product_name,serving_quantity,serving_size,quantity,product_quantity,image_url&action=process")
|
||||||
suspend fun searchProducts(@Query("search_terms2") name: String): Response<OFFResult>
|
suspend fun searchProducts(@Query("search_terms2") name: String): Response<OFFResult>
|
||||||
|
|
||||||
@Headers("User-Agent: OpenHealth - Android - Version 1.0 - https://github.com/dzeiocom/OpenHealth")
|
/**
|
||||||
|
* Search a product by it's barcode
|
||||||
|
*/
|
||||||
|
@Headers("User-Agent: OpenHealth - Android - Version ${BuildConfig.VERSION_NAME} - https://github.com/dzeiocom/OpenHealth")
|
||||||
@GET("/api/v2/search?fields=_id,nutriments,product_name,serving_quantity")
|
@GET("/api/v2/search?fields=_id,nutriments,product_name,serving_quantity")
|
||||||
suspend fun findByCode(@Query("code") code: String): Response<OFFResult>
|
suspend fun findByCode(@Query("code") code: String): Response<OFFResult>
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@ import java.util.TimeZone
|
|||||||
@Entity()
|
@Entity()
|
||||||
data class Step(
|
data class Step(
|
||||||
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the raw number of step
|
||||||
|
*/
|
||||||
var value: Int = 0,
|
var value: Int = 0,
|
||||||
/**
|
/**
|
||||||
* Timestamp down to an hour
|
* Timestamp down to an hour
|
||||||
@ -20,6 +24,12 @@ data class Step(
|
|||||||
*/
|
*/
|
||||||
@ColumnInfo(index = true)
|
@ColumnInfo(index = true)
|
||||||
var timestamp: Long = 0,
|
var timestamp: Long = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the source for the Entry
|
||||||
|
*
|
||||||
|
* note: Unused currently but kept for future usage
|
||||||
|
*/
|
||||||
var source: String = "OpenHealth"
|
var source: String = "OpenHealth"
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -12,10 +12,15 @@ import com.dzeio.openhealth.Application
|
|||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that allows us to get Sensor datas for the internal step counter
|
||||||
|
*
|
||||||
|
* TODO: rewrite to use the new libs
|
||||||
|
*/
|
||||||
class StepSource(
|
class StepSource(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val callback: ((Float) -> Unit)? = null
|
private val callback: ((Float) -> Unit)? = null
|
||||||
): SensorEventListener {
|
) : SensorEventListener {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "${Application.TAG}/StepSource"
|
const val TAG = "${Application.TAG}/StepSource"
|
||||||
|
@ -11,9 +11,23 @@ import java.util.TimeZone
|
|||||||
@Entity()
|
@Entity()
|
||||||
data class Water(
|
data class Water(
|
||||||
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the quantity of water in ML drank by the user
|
||||||
|
*/
|
||||||
var value: Int = 0,
|
var value: Int = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when the water was drank precise to the day
|
||||||
|
*/
|
||||||
@ColumnInfo(index = true)
|
@ColumnInfo(index = true)
|
||||||
var timestamp: Long = 0,
|
var timestamp: Long = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the source for the Entry
|
||||||
|
*
|
||||||
|
* note: Unused currently but kept for future usage
|
||||||
|
*/
|
||||||
var source: String = "OpenHealth"
|
var source: String = "OpenHealth"
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
|
@ -14,9 +14,19 @@ data class Weight(
|
|||||||
* Store the weight in kilograms
|
* Store the weight in kilograms
|
||||||
*/
|
*/
|
||||||
var weight: Float = 0f,
|
var weight: Float = 0f,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when the weight was taken precise to the millisecond
|
||||||
|
*/
|
||||||
@ColumnInfo(index = true)
|
@ColumnInfo(index = true)
|
||||||
var timestamp: Long = System.currentTimeMillis(),
|
var timestamp: Long = System.currentTimeMillis(),
|
||||||
var source: String = ""
|
|
||||||
|
/**
|
||||||
|
* the source for the Entry
|
||||||
|
*
|
||||||
|
* note: Unused currently but kept for future usage
|
||||||
|
*/
|
||||||
|
var source: String = "OpenHealth"
|
||||||
) {
|
) {
|
||||||
fun formatTimestamp(): String = getDateInstance().format(Date(timestamp));
|
fun formatTimestamp(): String = getDateInstance().format(Date(timestamp));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user