v1.2.9 generate diet with meals on the server

This commit is contained in:
bossanyit 2023-06-14 18:55:06 +02:00
parent 8b0e6fe6c3
commit d2ef1415a1
17 changed files with 647 additions and 71 deletions

View File

@ -11,7 +11,7 @@ plugins {
}
group = "com.aitrainer"
version = "1.2.8"
version = "1.2.9"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {

7
data/db/update_1_2_9.sql Normal file
View File

@ -0,0 +1,7 @@
START TRANSACTION;
ALTER TABLE `diet` ADD COLUMN `generating` tinyint(1) NULL;
UPDATE configuration set config_value = "1.2.9", date_change=CURRENT_DATE WHERE config_key = "db_version";
COMMIT;

View File

@ -1,9 +1,12 @@
package com.aitrainer.api.controller
import com.aallam.openai.api.BetaOpenAI
import com.aallam.openai.api.chat.ChatMessage
import com.aallam.openai.api.chat.ChatRole
import com.aitrainer.api.model.OpenAI
import com.aitrainer.api.model.OpenAIChat
import com.aitrainer.api.openai.OpenAIService
import com.google.gson.Gson
import kotlinx.coroutines.*
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
@ -72,5 +75,45 @@ class OpenAIController() {
}
return result
}
@OptIn(BetaOpenAI::class, DelicateCoroutinesApi::class)
suspend fun chat(@Value("\${openai.key}") openaiKey: String, modelName: String, temperature: Double, userInput: String, systemInput: String = "") : String? {
val systemMsg = ChatMessage(
role = ChatRole.User,
content = systemInput
)
val userMsg = ChatMessage(
role = ChatRole.User,
content = userInput
)
val listMessages: MutableList<ChatMessage> = mutableListOf()
listMessages.add(systemMsg)
listMessages.add(userMsg)
val gson = Gson()
val messages = gson.toJson(listMessages)
val openai = OpenAIChat(
messages = messages,
modelName = modelName,
temperature = temperature
)
var response: String? = null
val openAIService = OpenAIService(openaiKey, openai.modelName, openai.temperature)
val deferred = GlobalScope.async {
println(openai.messages)
openAIService.chatCompletion(openai.messages)
}
runBlocking {
try {
response = deferred.await().toString()
} catch (exception: Exception) {
println("Timeout OpenAI chat")
}
}
return response
}
}

View File

@ -1,17 +1,18 @@
package com.aitrainer.api.controller.diet
import com.aallam.openai.api.BetaOpenAI
import com.aallam.openai.api.chat.ChatMessage
import com.aallam.openai.api.chat.ChatRole
import com.aitrainer.api.model.OpenAIChat
import com.aitrainer.api.model.diet.Diet
import com.aitrainer.api.openai.OpenAIService
import com.aitrainer.api.controller.OpenAIController
import com.aitrainer.api.model.diet.*
import com.aitrainer.api.repository.diet.DietRepository
import com.aitrainer.api.repository.diet.MealRepository
import com.aitrainer.api.service.DietFunctions.getDateFromDayName
import com.aitrainer.api.service.DietFunctions.normalizeMealName
import com.aitrainer.api.service.Meals
import com.google.gson.Gson
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.json.JSONObject
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.MediaType
@ -23,8 +24,9 @@ import java.util.*
@RestController
@RequestMapping("/api")
class DietController(private val dietRepository: DietRepository) {
class DietController(private val dietRepository: DietRepository, private val mealRepository: MealRepository) {
private val logger = LoggerFactory.getLogger(javaClass)
private var openAiKey: String = ""
@PostMapping("/diet", produces = [MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"])
fun insert(@RequestBody diet: Diet): ResponseEntity<*> {
@ -73,8 +75,8 @@ class DietController(private val dietRepository: DietRepository) {
}
@OptIn(BetaOpenAI::class, DelicateCoroutinesApi::class)
@PostMapping("/diet/generate_premium/{dietUserId}", produces = [MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"])
fun generatePremiumDiet(@PathVariable dietUserId: Long, @RequestBody input: String, @Value("\${openai.key}") openaiKey: String): ResponseEntity<*> {
@PostMapping("/diet/generate_premium/{dietUserId}/{test}", produces = [MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"])
fun generatePremiumDiet(@PathVariable dietUserId: Long, @PathVariable test: Boolean = false, @RequestBody input: String, @Value("\${openai.key}") openaiKey: String): ResponseEntity<*> {
val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
@ -85,56 +87,369 @@ class DietController(private val dietRepository: DietRepository) {
dietUserId = dietUserId,
dietText = "",
startDate = todayString,
premium = 0
premium = 0,
generating = 1
)
diet = dietRepository.save(diet)
println("new diet: $diet.dietId")
}
val systemMsg = ChatMessage(
role = ChatRole.System,
content = "Te egy táplálkozási szakértő vagy. A feladat mindig egy étrend elkészítése ebben a 3 lépésben:\n" +
val result: String?
this.openAiKey = openaiKey
if ( !test) {
val systemInput = "Te egy táplálkozási szakértő vagy. A feladat egy étrend elkészítése ebben a 3 lépésben:\n" +
"\n" +
"1. Vizsgáld meg az input adatokat, és készítsd el az étrendet\n" +
"2. Ellenőrizd az ételeket, hogy tényleg létezik-e olyan, és megfelel az input adatok feltételeinek\n" +
"3. Ellenőrizd a kimeneti formátumot. Csak a megadott kimeneti formátumban válaszolj"
)
val userMsg = ChatMessage(
role = ChatRole.User,
content = input
)
val listMessages: MutableList<ChatMessage> = mutableListOf()
listMessages.add(systemMsg)
listMessages.add(userMsg)
val gson = Gson()
val messages = gson.toJson(listMessages)
val openai = OpenAIChat(
messages = messages,
//modelName = "gpt-3.5-turbo",
modelName = "gpt-4",
temperature = 0.9
)
var result: String? = null
val openAIService = OpenAIService(openaiKey, openai.modelName, openai.temperature)
val deferred = GlobalScope.async {
println(openai.messages)
openAIService.chatCompletion(openai.messages)
OpenAIController().chat(
modelName = "gpt-4",
temperature = 0.9,
userInput = input,
systemInput = systemInput,
openaiKey = openaiKey
)
}
runBlocking {
try {
result = deferred.await().toString()
} catch (exception: Exception) {
println("Timeout for diet generation $dietUserId")
}
}
if ( result == null ) {
if (result == null) {
return ResponseEntity.badRequest().build<String>()
}
} else {
result = "{\n" +
" \"DIET\": [\n" +
" {\n" +
" \"nameDay\": \"monday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"zabkása\", \"mandulatej\", \"málna\", \"méz\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"monday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"grillezett csirkemell\", \"quinoa\", \"spenót\", \"cherry paradicsom\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"monday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"sárgarépa\", \"hummusz\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"monday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"csicseriborsó-saláta\", \"paprika\", \"paradicsom\", \"uborka\", \"olívaolaj\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"tuesday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"rántotta\", \"sonka\", \"paradicsom\", \"gluténmentes kenyér\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"tuesday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"grillezett lazac\", \"barnarizs\", \"zöldbab\", \"citrom\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"tuesday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"natúr joghurt (laktózmentes)\", \"méz\", \"dió\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"tuesday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"paradicsomos quinoa\", \"spárga\", \"tonhal (konzerv)\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"wednesday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"zabkása\", \"mandulatej\", \"eper\", \"magok (napraforgó, tökmag)\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"wednesday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"töltött paprika\", \"rizs\", \"sovány darált pulykahús\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"wednesday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"cékla\", \"avokádó\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"wednesday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"kókusztejes csirke\", \"spenót\", \"barnarizs\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"thursday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"natúr joghurt (laktózmentes)\", \"mogyoróvaj\", \"banán\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"thursday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"görög saláta\", \"olívaolaj\", \"feta sajt (laktózmentes)\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"thursday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"mandula\", \"szárított füge\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"thursday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"grillezett hal\", \"brokkoli\", \"kukoricalisztből készült polenta\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"friday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"gluténmentes müzli\", \"mandulatej\", \"áfonya\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"friday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"lecsó\", \"kolbász (gluténmentes)\", \"gluténmentes kenyér\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"friday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"yoghurt (lactose free)\", \"honey\", \"walnut\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"friday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"grillezett padlizsán\", \"couscous(gluten free)\", \"csicseriborsó\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"saturday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"omlette\", \"sonka\", \"spenót\", \"gluténmentes kenyér\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"saturday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"húsgombóc\", \"paradicsomos mártás\", \"cukkinispirál\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"saturday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"narancs\", \"keksz (gluténmentes)\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"saturday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"saláta\", \"paradicsom\", \"uborka\", \"pulykamell\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"sunday\",\n" +
" \"mealTime\": \"breakfast\",\n" +
" \"meals\": [\"zabkása\", \"mandulatej\", \"mazsola\", \"mogyoró\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"sunday\",\n" +
" \"mealTime\": \"lunch\",\n" +
" \"meals\": [\"grillezett csirkemell\", \"háromszínű quinoa\", \"répa\", \"zöldborsó\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"sunday\",\n" +
" \"mealTime\": \"snack\",\n" +
" \"meals\": [\"natúr joghurt (laktózmentes)\", \"zabpehely\", \"banán\"]\n" +
" },\n" +
" {\n" +
" \"nameDay\": \"sunday\",\n" +
" \"mealTime\": \"dinner\",\n" +
" \"meals\": [\"töltött gomba\", \"couscous(gluten free)\", \"paradicsomsalsa\"]\n" +
" }\n" +
" ]\n" +
"}"
}
diet.dietText = result!!
val gson = Gson()
diet.dietText = result
diet.startDate = todayString
diet.premium = 1
return ResponseEntity.ok().body(dietRepository.save(diet))
diet.generating = 0
val dietSaved = dietRepository.save(diet)
val dietJson = JSONObject(diet.dietText)
val mealPlansJson = dietJson.getJSONArray("DIET")
val dietCustoms = mutableListOf<DietCustom>()
for (i in 0 until mealPlansJson.length()) {
val dietCustom = gson.fromJson(mealPlansJson.get(i).toString(), DietCustom::class.java)
dietCustoms.add(dietCustom)
}
dietCustoms.forEach { dietCustom ->
val dietCustomChanged = addTemporaryQuantity(dietCustom)
var index = 0
dietCustomChanged.meals.forEach {meal ->
val mealId: Long = getMealIdFromCustom(meal, dietCustomChanged, index, false, diet.dietUserId )
val dietMeal = DietMeal(
mealId = mealId,
meal = meal,
mealName = dietCustom.nameDay + "|" + dietCustom.mealTime,
mealDate = getDateFromDayName(dietCustom.nameDay, dietCustom.mealTime),
quantity = dietCustom.quantities[index],
)
dietMeal.diet = dietSaved
dietSaved.meals.add(dietMeal)
}
index++
}
val dietSavedWithMeals = dietRepository.save(dietSaved)
return ResponseEntity.ok().body(dietSavedWithMeals)
}
fun addTemporaryQuantity(dietCustom: DietCustom): DietCustom {
for (i in 0 until dietCustom.meals.size) {
var sum = 400
when (dietCustom.mealTime) {
Meals.breakfast.toString() -> sum = 400
Meals.elevenses.toString() -> sum = 100
Meals.lunch.toString() -> sum = 500
Meals.snack.toString() -> sum = 150
Meals.dinner.toString() -> sum = 350
Meals.eveningsnack.toString() -> sum = 100
}
if (i == 0) {
dietCustom.quantities.add(sum * 0.7)
} else {
dietCustom.quantities.add(sum * 0.3 / (dietCustom.meals.size - 1))
}
}
return dietCustom
}
fun getMealIdFromCustom(mealName: String, dietCustom: DietCustom, mealIndex: Int, onlyQuantity: Boolean, dietUserId: Long): Long {
var mealId: Long = 0
var foundMeal: Meal? = mealRepository.findByName(mealName)
var dietCustomVar = dietCustom
if (foundMeal == null) {
dietCustomVar = addTemporaryQuantity(dietCustomVar)
if (!onlyQuantity) {
foundMeal = getMealCaloriesFromAI(mealName, dietCustomVar.quantities[mealIndex])
}
if (foundMeal != null) {
mealId = foundMeal.id
}
} else {
println("FoundMeal cals: ${foundMeal.calMin}")
mealId = foundMeal.id
}
return mealId
}
@OptIn(BetaOpenAI::class, DelicateCoroutinesApi::class)
fun getMealCaloriesFromAI(mealName: String, quantity: Double): Meal? {
var meal: Meal? = null
val systemInput = "Te egy táplálkozási szakértő vagy. Feladat az ételek kalóriaértékének meghatározása"
val input = """
Add meg ennek az ételnek a kalóriaértékét és tápanyagfelosztását: $mealName 100 gramm.
A választ ebben JSON formátumban írd, ne fűzz semmilyen megjegyzést hozzá: {
"name":<string>,
"quantity":<double>,
"quantityUnit":<string>,
"calMin":<double>,
"calMax":<double>,
"fatMin":<double>,
"fatMax":<double>,
"chMin":<double>,
"chMax":<double>,
"proteinMin":<double>,
"proteinMax":<double>,
"sugar":<double>,
"servingUnit":<enum(db, szelet, adag, ml, liter)>
"serving":<double, servingUnit in grams> }
"""
val response: String?
val openaiKey = this.openAiKey
val deferred = GlobalScope.async {
OpenAIController().chat(
modelName = "gpt-3.5-turbo",
temperature = 0.0,
userInput = input,
systemInput = systemInput,
openaiKey = openaiKey)
}
runBlocking {
response = deferred.await().toString()
}
try {
val newMeal = Gson().fromJson(response, Meal::class.java)
val newMealName = newMeal.name
val found: Meal? = mealRepository.findByName(newMealName)
if (found == null) {
val baseQuantity = 100.0
val rate = quantity / baseQuantity
newMeal.quantity = baseQuantity
newMeal.calMin /= rate
newMeal.calMax /= rate
newMeal.chMin /= rate
newMeal.chMax /= rate
newMeal.fatMin /= rate
newMeal.fatMax /= rate
newMeal.proteinMin /= rate
newMeal.proteinMax /= rate
newMeal.sugar /= rate
newMeal.normalizedName = normalizeMealName(newMeal.name)
newMeal.serving = newMeal.serving
newMeal.servingUnit = newMeal.servingUnit
val savedMeal = mealRepository.save(newMeal)
println("save new meal: ${newMeal.name}, ${newMeal.calMin} kCal")
//updateServingForMeal(savedMeal)
meal = savedMeal
} else {
meal = found
}
} catch (e: Exception) {
println( "Error in newMeal decode JSON: '$e'")
}
return meal
}
@OptIn(BetaOpenAI::class, DelicateCoroutinesApi::class)
fun updateServingForMeal(meal: Meal) {
val input = """
Add meg, hány gramm egy adag "${meal.name}" átlagosan.
A választ pontosan ebben a formátumban kérem: {"meal": <string>, "serving":<double (average in gramms)>, "servingUnit": <enum (adag, szelet, db, ml, liter)>}.
The response MUST NOT contain other words just the JSON.
"""
val response: String?
val openaiKey = this.openAiKey
val deferred = GlobalScope.async {
OpenAIController().chat(modelName = "gpt-3.5-turbo", temperature = 0.0, userInput = input, openaiKey = openaiKey)
}
runBlocking {
response = deferred.await().toString()
}
val serving = Gson().fromJson(response, MealServing::class.java)
meal.serving = serving.serving
meal.servingUnit = serving.servingUnit
meal.normalizedName = normalizeMealName(meal.name) // placeholder for your function
mealRepository.save(meal)
}
}

View File

@ -13,6 +13,7 @@ data class Diet (
@Expose @get: NotNull var dietText: String = "",
@Expose @get: NotNull var startDate: String = "",
@Expose @get: NotNull var premium: Int = 0,
@Expose @get: NotNull var generating: Int = 0,
) {
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.EAGER, mappedBy = "diet")
@Fetch(value = FetchMode.SUBSELECT)

View File

@ -0,0 +1,17 @@
package com.aitrainer.api.model.diet
import com.google.gson.annotations.Expose
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import org.jetbrains.annotations.NotNull
@Entity
data class DietCustom(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Expose val id: Int = 0,
@Expose @get: NotNull val nameDay: String = "",
@Expose @get: NotNull val mealTime: String = "",
@Expose @get: NotNull val meals: List<String> = listOf(),
@Expose @get: NotNull val quantities: MutableList<Double> = mutableListOf()
)

View File

@ -9,18 +9,18 @@ data class Meal (
@Expose @get: NotNull val name: String = "",
@Expose @get: NotNull var normalizedName: String = "",
@Expose @get: NotNull val quantity: Double = 0.0,
@Expose @get: NotNull var quantity: Double = 0.0,
@Expose @get: NotNull val quantityUnit: String = "",
@Expose @get: NotNull var serving: Double = 0.0,
@Expose @get: NotNull var servingUnit: String = "",
@Expose @get: NotNull val description: String = "",
@Expose @get: NotNull val calMin: Double = 0.0,
@Expose @get: NotNull val calMax: Double = 0.0,
@Expose @get: NotNull val proteinMin: Double = 0.0,
@Expose @get: NotNull val proteinMax: Double = 0.0,
@Expose @get: NotNull val fatMin: Double = 0.0,
@Expose @get: NotNull val fatMax: Double = 0.0,
@Expose @get: NotNull val chMin: Double = 0.0,
@Expose @get: NotNull val chMax: Double = 0.0,
@Expose @get: NotNull val sugar: Double = 0.0,
@Expose @get: NotNull var calMin: Double = 0.0,
@Expose @get: NotNull var calMax: Double = 0.0,
@Expose @get: NotNull var proteinMin: Double = 0.0,
@Expose @get: NotNull var proteinMax: Double = 0.0,
@Expose @get: NotNull var fatMin: Double = 0.0,
@Expose @get: NotNull var fatMax: Double = 0.0,
@Expose @get: NotNull var chMin: Double = 0.0,
@Expose @get: NotNull var chMax: Double = 0.0,
@Expose @get: NotNull var sugar: Double = 0.0,
)

View File

@ -0,0 +1,13 @@
package com.aitrainer.api.model.diet
import com.google.gson.annotations.Expose
import jakarta.persistence.*
import org.jetbrains.annotations.NotNull
@Entity
data class MealServing(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Expose val id: Int = 0,
@Expose @get: NotNull val meal: String = "",
@Expose @get: NotNull val serving: Double = 0.0,
@Expose @get: NotNull val servingUnit: String = "",
)

View File

@ -11,4 +11,7 @@ interface DietRepository : JpaRepository<Diet, Long> {
@Query(" FROM Diet WHERE dietId = :dietId")
fun findByDietId(dietId: Long): Diet?
@Query(" FROM Diet WHERE ((LENGTH(dietText) = 0 AND premium = 1 ) OR (generating = 1)) AND dietUserId = :dietUserId ORDER BY dietId DESC LIMIT 1")
fun findTheLastEmptyDiet(dietUserId: Long): Diet?
}

View File

@ -0,0 +1,175 @@
package com.aitrainer.api.service
import java.time.LocalDateTime
import java.util.*
enum class Meals {
breakfast,
elevenses,
lunch,
snack,
dinner,
eveningsnack;
companion object {
private fun Meals.enumToString(): String = this.toString().lowercase(Locale.getDefault())
fun Meals.equalsStringTo(mealName: String): Boolean = this.toString().lowercase(Locale.getDefault()) == mealName.lowercase(
Locale.getDefault()
)
fun getMealsByIndex(index: Int): String? {
for (meal in Meals.values()) {
if (index == meal.ordinal) {
return meal.enumToString()
}
}
return null
}
private fun Meals.getMealTimeHour(): Int {
return when (this) {
breakfast -> 7
elevenses -> 9
lunch -> 11
snack -> 14
dinner -> 17
eveningsnack -> 20
}
}
fun getHour(text: String): Int? {
for (meal in Meals.values()) {
if (text.lowercase(Locale.getDefault()) == meal.enumToString() || text.lowercase(Locale.getDefault())
.contains(meal.enumToString().lowercase(Locale.getDefault()))) {
return meal.getMealTimeHour()
}
}
return null
}
fun contains(text: String): Meals? {
for (meal in Meals.values()) {
if (text.lowercase(Locale.getDefault()).contains(meal.enumToString().lowercase(Locale.getDefault()))) {
return meal
}
}
return null
}
}
}
enum class WeekDays {
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday;
companion object WeekDaysExt {
private fun WeekDays.enumToString(): String {
return this.toString().toLowerCase()
}
fun WeekDays.equalsStringTo(dayName: String): Boolean {
return this.toString().equals(dayName, ignoreCase = true)
}
private fun WeekDays.getWeekdayNumber(): Int {
return when (this) {
WeekDays.monday -> 1
WeekDays.tuesday -> 2
WeekDays.wednesday -> 3
WeekDays.thursday -> 4
WeekDays.friday -> 5
WeekDays.saturday -> 6
WeekDays.sunday -> 7
else -> 1
}
}
fun contains(text: String): WeekDays? {
var actualDay: WeekDays? = null
for (day in WeekDays.values()) {
if (text.contains(day.enumToString())) {
actualDay = day
break
}
}
return actualDay
}
fun weekDay(text: String): Int? {
var actualDay: Int? = null
for (day in WeekDays.values()) {
val weekDayHu = day.enumToString()
if (day.name.equals(text, true) || weekDayHu.equals(text, true)) {
actualDay = day.getWeekdayNumber()
break
}
}
return actualDay
}
}
}
object DietFunctions {
@Throws(Exception::class)
fun getDateFromDayName(dayName: String, mealName: String): String {
var dayNameVar = dayName
if (dayNameVar == "evening_snack") {
dayNameVar = "eveningSnack"
}
val today = LocalDateTime.now()
val weekDay = WeekDays.weekDay(dayNameVar)
val hour = Meals.getHour(mealName)
weekDay ?: throw Exception("$dayNameVar is not a week day")
hour ?: throw Exception("$mealName is not a meal")
val difference = today.dayOfWeek.value - weekDay
val day = today.minusDays(difference.toLong())
return "${day.year}-${day.monthValue}-${day.dayOfMonth} $hour:00"
}
fun normalizeMealName(hungarianWord: String): String {
val diacriticals: Map<Char, Char> = mapOf(
'á' to 'a',
'é' to 'e',
'í' to 'i',
'ó' to 'o',
'ö' to 'o',
'ő' to 'o',
'ú' to 'u',
'ü' to 'u',
'ű' to 'u',
'Á' to 'A',
'É' to 'E',
'Í' to 'I',
'Ó' to 'O',
'Ö' to 'O',
'Ő' to 'O',
'Ú' to 'U',
'Ü' to 'U',
'Ű' to 'U',
'-' to ' ',
',' to ' ',
)
var englishWord = ""
var found = false
for (element in hungarianWord) {
englishWord += diacriticals[element] ?: element
if (diacriticals.containsKey(element)) {
found = true
}
}
return englishWord.lowercase(Locale.getDefault())
}
}

View File

@ -16,7 +16,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs
# if the database structure has been changed, increment this version number
application.version=1.2.8
application.version=1.2.9
jwt.secret=aitrainer

View File

@ -14,7 +14,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs
# if the database structue has been changed, increment this version number
application.version=1.2.8
application.version=1.2.9
jwt.secret=aitrainer

View File

@ -14,7 +14,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs
# if the database structue has been changed, increment this version number
application.version=1.2.8
application.version=1.2.9
jwt.secret=aitrainer

View File

@ -17,7 +17,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs
# if the database structure has been changed, increment this version number
application.version=1.2.8
application.version=1.2.9
jwt.secret=aitrainer

View File

@ -41,6 +41,7 @@ class DietTest {
dietText = "Test diet text",
startDate = "2023-02-02",
premium = 0,
generating = 0
)
private var authToken: String? = ""
@ -145,6 +146,7 @@ class DietTest {
dietText = "Premium Test",
startDate = "2023-05-02",
premium = 1,
generating = 0
)
val mvcResult2: MvcResult = mockMvc.perform(
@ -166,14 +168,14 @@ class DietTest {
dietRepository.delete(newDiet2)
}
@Test
//@Test
fun `find the last empty diet successfully`() {
val diet: Diet? = dietRepository.findTheLastEmptyDiet(2)
assertNotNull(diet)
assertEquals(diet.premium, 1)
}
//@Test
@Test
fun `generate premium diet successfully`() {
val input = "Készíts egy személyre szabott heti étrendet ezekkel a paraméterekkel:\n" +
@ -198,7 +200,7 @@ class DietTest {
"]}"
mockMvc.perform(
MockMvcRequestBuilders.post("/api/diet/generate_premium/2")
MockMvcRequestBuilders.post("/api/diet/generate_premium/2/true")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
.content(input)

View File

@ -138,11 +138,11 @@ class MealTest {
.header("Authorization", "Bearer $authToken")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk)
.andExpect(jsonPath("$.[3].name").value("Tükörtojás"))
.andExpect(jsonPath("$.[3].normalizedName").value("Tukortojas"))
.andExpect(jsonPath("$.[4].name").value("Tigris buci"))
.andExpect(jsonPath("$.[5].quantity").value(330.0))
.andExpect(jsonPath("$.[5].name").value("Töltötttojás"))
.andExpect(jsonPath("$.[73].name").value("Tükörtojás"))
.andExpect(jsonPath("$.[73].normalizedName").value("Tukortojas"))
.andExpect(jsonPath("$.[74].name").value("Tigris buci"))
.andExpect(jsonPath("$.[75].quantity").value(330.0))
.andExpect(jsonPath("$.[75].name").value("Töltötttojás"))
// Act & Assert

View File

@ -54,7 +54,7 @@ class OpenAITest {
}*/
@OptIn(BetaOpenAI::class)
@Test
//@Test
fun `get a chat message completion`() {
val systemMsg = ChatMessage(
role = ChatRole.User,
@ -86,7 +86,7 @@ class OpenAITest {
}
@OptIn(BetaOpenAI::class)
@Test
// @Test
fun `get a chat message completion no modelname`() {
val systemMsg = ChatMessage(
role = ChatRole.User,
@ -115,7 +115,7 @@ class OpenAITest {
}
@Test
// @Test
fun `get a question successfully with model name`() {
val question = "Who the f. is Alice? Who sing that song?"
val openai = OpenAI(
@ -134,7 +134,7 @@ class OpenAITest {
}
@Test
// @Test
fun `get models successfully`() {
mockMvc.perform(
MockMvcRequestBuilders.get("/api/openai/list_models")