Compare commits

..

10 Commits

Author SHA1 Message Date
bossanyit
8d45635684 v1.2.9.10 diet_user.macro 2023-06-28 21:44:00 +02:00
bossanyit
acc914f170 v1.2.9.4 fix on generated diet JSON handling 2023-06-15 17:14:19 +02:00
bossanyit
7ea9164f66 v1.2.9.3 fix on generated diet JSON handling 2023-06-15 16:31:22 +02:00
bossanyit
4362988c4a v1.2.9.2 fix on generated diet JSON handling 2023-06-15 07:38:55 +02:00
bossanyit
d2ef1415a1 v1.2.9 generate diet with meals on the server 2023-06-14 18:55:06 +02:00
bossanyit
8b0e6fe6c3 v1.2.8.1 premium diet generation modif 2023-05-18 08:18:57 +02:00
bossanyit
e3182c210c v1.2.8 premium diet generation endpoint 2023-05-11 15:37:52 +02:00
bossanyit
7a6dd693cb v1.2.7 memberships 2023-05-08 07:58:57 +02:00
bossanyit
a8d54fa78f v1.2.6.1 openai timeout 2023-05-04 08:05:09 +02:00
bossanyit
63c0dc5595 v1.2.6 diet.premium, diet registration email 2023-05-02 18:10:49 +02:00
44 changed files with 1007 additions and 91 deletions

2
.gitignore vendored
View File

@ -30,4 +30,4 @@ out/
### VS Code ###
.vscode/
logs/aitrainer.log
logs/

View File

@ -11,7 +11,7 @@ plugins {
}
group = "com.aitrainer"
version = "1.2.5"
version = "1.2.10"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
@ -48,8 +48,10 @@ dependencies {
implementation("jakarta.mail:jakarta.mail-api:2.1.1")
implementation("org.eclipse.angus:angus-mail:2.0.1")
implementation ("com.aallam.openai:openai-client:3.2.2")
implementation("com.aallam.openai:openai-client:3.2.2")
implementation("io.ktor:ktor-client-java:2.2.3")
implementation("ognl:ognl:3.3.4")
runtimeOnly("mysql:mysql-connector-java")
testImplementation("org.springframework.boot:spring-boot-starter-test") {

View File

@ -1 +0,0 @@
docker build --no-cache -t diet4you_api_test -f Dockerfile .

View File

@ -0,0 +1,7 @@
START TRANSACTION;
ALTER TABLE `diet_user` ADD COLUMN `macro` char(255) NULL;
UPDATE configuration set config_value = "1.2.10", date_change=CURRENT_DATE WHERE config_key = "db_version";
COMMIT;

8
data/db/update_1_2_6.sql Normal file
View File

@ -0,0 +1,8 @@
START TRANSACTION;
ALTER TABLE `diet`
ADD COLUMN `premium` TINYINT(1) NULL DEFAULT 0 AFTER `start_date`;
UPDATE configuration set config_value = "1.2.6", date_change=CURRENT_DATE WHERE config_key = "db_version";
COMMIT;

8
data/db/update_1_2_7.sql Normal file
View File

@ -0,0 +1,8 @@
START TRANSACTION;
ALTER TABLE `customer_membership`
ADD COLUMN `end_date` DATETIME NULL DEFAULT NULL AFTER `start_date`;
UPDATE configuration set config_value = "1.2.7", date_change=CURRENT_DATE WHERE config_key = "db_version";
COMMIT;

5
data/db/update_1_2_8.sql Normal file
View File

@ -0,0 +1,5 @@
START TRANSACTION;
UPDATE configuration set config_value = "1.2.8", date_change=CURRENT_DATE WHERE config_key = "db_version";
COMMIT;

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;

1
src/deploy/diet4you/build_api Executable file
View File

@ -0,0 +1 @@
docker buildx build --platform linux/amd64 --no-cache -t diet4you_api -f Dockerfile .

View File

@ -0,0 +1 @@
docker buildx build --platform linux/amd64 --no-cache -t diet4you_api_test -f Dockerfile .

2
src/deploy/diet4you/tag Normal file
View File

@ -0,0 +1,2 @@
docker tag diet4you_api_test:latest registry.workouttest.org/diet4you_api_test:v1.2.5.1
docker push registry.workouttest.org/diet4you_api_test:v1.2.5.1

View File

@ -3,12 +3,14 @@ package com.aitrainer.api.controller
import com.aitrainer.api.model.*
import com.aitrainer.api.service.ServiceBeans
import com.aitrainer.api.repository.CustomerRepository
import com.aitrainer.api.repository.MembershipRepository
import com.aitrainer.api.service.Email
import com.aitrainer.api.service.EmailTemplateService
import com.aitrainer.api.service.Firebase
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseEntity
import org.springframework.security.access.annotation.Secured
@ -22,7 +24,7 @@ import java.util.Base64
@RestController
@RequestMapping("/api")
class CustomerController ( private val customerRepository: CustomerRepository) {
class CustomerController (private val customerRepository: CustomerRepository, private val membershipRepository: MembershipRepository,) {
private val logger = LoggerFactory.getLogger(javaClass)
@Autowired
@ -325,4 +327,60 @@ class CustomerController ( private val customerRepository: CustomerRepository) {
ResponseEntity.badRequest().body("Customer does not exist or the password is wrong")
}
}
@PostMapping("/membership/{id}")
fun newMembership(@PathVariable(value = "id") membershipId: Long, @Valid @RequestBody customer: Customer): ResponseEntity<Customer> {
val returnCustomer: Customer = customerRepository.findById(customer.customerId).orElse(null)
?: return ResponseEntity.notFound().build()
println("found customer ${returnCustomer.customerId}")
val membership: Membership = membershipRepository.findByIdOrNull(membershipId)
?: return ResponseEntity.notFound().build()
println("found membership $membershipId")
val customerMembership = CustomerMembership()
customerMembership.membershipId = membershipId
customerMembership.customer = returnCustomer
val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
customerMembership.startDate = now.format(formatter)
returnCustomer.memberships.add(customerMembership)
customerRepository.save(returnCustomer)
val savedCustomer: Customer = customerRepository.findById(customer.customerId).orElse(null)
?: return ResponseEntity.notFound().build()
println("saved ${savedCustomer.customerId}")
return ResponseEntity.ok(savedCustomer)
}
@PostMapping("/membership/cancel/{id}")
fun cancelMembership(@PathVariable(value = "id") membershipId: Long, @Valid @RequestBody customer: Customer): ResponseEntity<Customer> {
val returnCustomer: Customer = customerRepository.findById(customer.customerId).orElse(null)
?: return ResponseEntity.notFound().build()
println("found customer ${returnCustomer.customerId}")
val membership: Membership = membershipRepository.findByIdOrNull(membershipId)
?: return ResponseEntity.notFound().build()
println("found membership $membershipId")
var found = false
var savedCustomer: Customer? = null
for ( customerMembership in returnCustomer.memberships ) {
if ( customerMembership.membershipId == membershipId) {
val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
customerMembership.endDate = now.format(formatter)
savedCustomer = customerRepository.save(returnCustomer)
found = true
}
}
return if ( ! found ) {
println("not found customer membership to cancel $membershipId")
ResponseEntity.notFound().build()
} else {
ResponseEntity.ok(savedCustomer)
}
}
}

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,32 @@
package com.aitrainer.api.controller.diet
import com.aitrainer.api.model.diet.Diet
import com.aallam.openai.api.BetaOpenAI
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
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
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<*> {
@ -58,4 +73,404 @@ class DietController(private val dietRepository: DietRepository) {
return if (list.isEmpty()) ResponseEntity.notFound().build() else
ResponseEntity.ok().body(list)
}
@OptIn(BetaOpenAI::class, DelicateCoroutinesApi::class)
@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")
val todayString = now.format(formatter)
var diet: Diet? = dietRepository.findTheLastEmptyDiet(dietUserId)
val dietSaved: Diet?
this.openAiKey = openaiKey
if (diet == null) {
diet = Diet(
dietUserId = dietUserId,
dietText = "",
startDate = todayString,
premium = 0,
generating = 1
)
diet = dietRepository.save(diet)
var result: String?
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 JSON kimeneti formátumban válaszolj, semmilyen más formátum nem elfogadott.\n"
val deferred = GlobalScope.async {
OpenAIController().chat(
modelName = "gpt-4",
temperature = 0.9,
userInput = input,
systemInput = systemInput,
openaiKey = openaiKey
)
}
runBlocking {
result = deferred.await().toString()
result = extractJson(result!!)
println("extracted json: $result")
}
if (result == null || result!!.isEmpty()) {
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!!
diet.startDate = todayString
diet.premium = 1
dietSaved = dietRepository.save(diet)
} else {
dietSaved = diet
}
val gson = Gson()
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++
}
dietSaved.generating = 0
val dietSavedWithMeals = dietRepository.save(dietSaved)
return ResponseEntity.ok().body(dietSavedWithMeals)
}
fun extractJson(input: String): String {
val jsonStart = input.indexOf("{")
val jsonEnd = input.lastIndexOf("}")
return if(jsonStart != -1 && jsonEnd != -1) {
input.substring(jsonStart, jsonEnd + 1)
} else {
""
}
}
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

@ -7,6 +7,8 @@ import com.aitrainer.api.model.diet.DietCustomer
import com.aitrainer.api.model.diet.DietUser
import com.aitrainer.api.repository.CustomerRepository
import com.aitrainer.api.repository.diet.DietUserRepository
import com.aitrainer.api.service.Email
import com.aitrainer.api.service.EmailTemplateService
import com.aitrainer.api.service.Firebase
import com.aitrainer.api.service.ServiceBeans
import org.springframework.beans.factory.annotation.Autowired
@ -24,6 +26,10 @@ class DietCustomerController(private val dietUserRepository: DietUserRepository,
@Autowired
var serviceBeans: ServiceBeans? = null
@Autowired
private var emailTemplateService: EmailTemplateService? = null
@PostMapping("/diet_registration")
fun insert(@RequestBody dietCustomerJson: String, @Value("\${firebase.key}") apiKey: java.lang.String): ResponseEntity<*> {
val newDietCustomer: DietCustomer = DietCustomer().fromJson(dietCustomerJson)
@ -120,17 +126,16 @@ class DietCustomerController(private val dietUserRepository: DietUserRepository,
idToken = existingCustomer.firebaseRegToken!!
}
// create email link
/*val activationLink = "https://diet4you.andio.hu/welcome/id=$idToken"
if ( emailTemplateService == null ) {
emailTemplateService = EmailTemplateService()
}
val html = emailTemplateService!!.getEmailBody(newDietCustomer.firstname, activationLink, "diet_registration_email")
val html = emailTemplateService!!.getDietRegistrationEmailBody(newDietCustomer.firstname, newDietCustomer.email, "diet_registration_email")
val subject = emailTemplateService!!.getSubjectDiet()
// send email
val email = Email()
email.send(newDietCustomer.email, html, subject)*/
email.send("service@diet4you.eu", html, subject)
return ResponseEntity.ok().body(existingCustomer)
}

View File

@ -21,4 +21,11 @@ class DietUserController(private val dietUserRepository: DietUserRepository) {
return if (dietUser == null) ResponseEntity.notFound().build() else
ResponseEntity.ok().body(dietUser)
}
@PostMapping("/diet_user/{customerId}")
fun updateCustomer(@PathVariable customerId: Long, @RequestBody dietUser: DietUser): ResponseEntity<DietUser> {
dietUserRepository.findByCustomerId(customerId) ?: return ResponseEntity.notFound().build()
val changedUser = dietUserRepository.save(dietUser)
return ResponseEntity.ok().body(changedUser)
}
}

View File

@ -10,6 +10,7 @@ data class CustomerMembership (
@Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @get: NonNull var id: Long = 0,
@Expose @get: NonNull var membershipId: Long = 0,
@Expose @get: NonNull var startDate: String? = null,
@Expose @get: NonNull var endDate: String? = null,
) {
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "customerId", nullable = false)

View File

@ -10,8 +10,10 @@ import org.jetbrains.annotations.NotNull
data class Diet (
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Expose val dietId: Long = 0,
@Expose @get: NotNull val dietUserId: Long = 0,
@Expose @get: NotNull val dietText: String = "",
@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

@ -10,5 +10,6 @@ import javax.validation.constraints.NotNull
@Entity
data class DietUser(
@Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var dietUserId: Long = 0,
@Expose @get: NotNull val customerId: Long
@Expose @get: NotNull val customerId: Long,
@Expose @get: NotNull var macro: String = "",
)

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

@ -7,6 +7,7 @@ import com.aallam.openai.api.chat.ChatMessage
import com.aallam.openai.client.OpenAI
import com.aallam.openai.api.completion.CompletionRequest
import com.aallam.openai.api.completion.TextCompletion
import com.aallam.openai.api.http.Timeout
import com.aallam.openai.api.logging.LogLevel
import com.aallam.openai.api.model.Model
import com.aallam.openai.api.model.ModelId
@ -16,6 +17,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import kotlin.time.Duration.Companion.seconds
@Service
class OpenAIService(@Value("\${openai.key}") private val openaiKey: String, private val modelName: String?, private val temperature: Double?) {
@ -26,7 +29,8 @@ class OpenAIService(@Value("\${openai.key}") private val openaiKey: String, priv
private suspend fun connect(modelName: String) {
val config = OpenAIConfig(
token = openaiKey,
logLevel = LogLevel.All
logLevel = LogLevel.All,
timeout = Timeout(socket = 600.seconds)
)
openAI = OpenAI(config)
modelId = ModelId(modelName)

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,170 @@
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().lowercase(Locale.getDefault())
}
fun WeekDays.equalsStringTo(dayName: String): Boolean {
return this.toString().equals(dayName, ignoreCase = true)
}
private fun WeekDays.getWeekdayNumber(): Int {
return when (this) {
monday -> 1
tuesday -> 2
wednesday -> 3
thursday -> 4
friday -> 5
saturday -> 6
sunday -> 7
}
}
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 = ""
for (element in hungarianWord) {
englishWord += diacriticals[element] ?: element
}
return englishWord.lowercase(Locale.getDefault())
}
}

View File

@ -21,6 +21,17 @@ class EmailTemplateService {
return templateEngine!!.process(template, context)
}
fun getDietRegistrationEmailBody(firstname: String, email: String, template: String): String {
val context = Context()
context.setVariable("customerName", firstname)
context.setVariable("customerEmail", email)
if ( templateEngine == null) {
templateEngine = TemplateEngine()
}
return templateEngine!!.process(template, context)
}
fun getSubject(): String {
val context = Context()
if ( templateEngine == null) {

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.5
application.version=1.2.10
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.5
application.version=1.2.10
jwt.secret=aitrainer

View File

@ -1,7 +0,0 @@
#spring.config.activate.on-profile=dev,test,prod,prodtest
spring.config.use-legacy-processing = true
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://192.168.100.98:3306/diet4you?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true
spring.datasource.username = aitrainer
spring.datasource.password = ENC(WZplPYr8WmrLHshesY4T6oXplK3MlUVJ)
spring.http.encoding.charset=UTF-8

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.5
application.version=1.2.10
jwt.secret=aitrainer

View File

@ -1,14 +0,0 @@
spring.config.activate.on-profile=testmac
spring.config.use-legacy-processing = true
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
#spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/aitrainer2?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&allowMultiQueries=true
spring.datasource.username = root
spring.datasource.password = ENC(WZplPYr8WmrLHshesY4T6oXplK3MlUVJ)
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect

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.5
application.version=1.2.10
jwt.secret=aitrainer

View File

@ -4,22 +4,19 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'Szia ' + ${firstname} + '!'">Szia [firstname]!</p>
<p>
Üdvözlünk a Diet 4 You (Diéta Neked) tagjai között! Örülünk, hogy velünk vagy. Az étrended a Diet4You zárt felületén tudod elérni. A belépéshez használd ezt a linket:<br/><br/>
<a th:href="${activationLink}" th:text="${activationLink}">${activationLink}</a>
New Diet4You user:
</p>
<p>
Kérlek, kattints a linkre a fiókod aktiválásához. Ha bármilyen problémád van, ne habozz velünk kapcsolatba lépni.
</p>
<p>
Köszönjük, hogy velünk dolgozol.
Email:<span th:text="${customerEmail}">Email</span><br/>
Name:<span th:text="${customerName}">Name</span>
</p>
<p>
Üdvözlettel,<br>
Diéta Neked Csapata<br/>
Diet 4 You Team<br/>
mailto: diet4you@andio.hu<br/>
Diet4You Team<br/>
mailto: service@diet4you.eu<br/>
</p>
</body>
</html>

View File

@ -1 +1 @@
[Diet4You] Üdv a céltudatosok között!
[Diet4You] New Diet User!

View File

@ -394,7 +394,7 @@ class AppPackageTest {
val appTextJson: String = record[1]
val type = object : TypeToken<List<AppText?>?>() {}.type
val texts: List<AppText> = gson.fromJson(appTextJson, type)
assertEquals(texts.size, 15)
assertEquals(texts.size, 33)
assertEquals(texts[13].translations[0].translation, "Done!")
assertEquals(texts[13].translations[1].translation, "Kész!")
} else if (record[0] == TrainingProgram::class.simpleName) {

View File

@ -4,7 +4,9 @@ import com.aitrainer.api.controller.CustomerController
import com.aitrainer.api.model.Customer
import com.aitrainer.api.model.User
import com.aitrainer.api.repository.CustomerRepository
import com.aitrainer.api.repository.MembershipRepository
import com.aitrainer.api.service.ServiceBeans
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.gson.Gson
import org.json.JSONObject
import org.junit.jupiter.api.BeforeAll
@ -22,12 +24,13 @@ import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.MvcResult
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import java.util.Base64
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ -48,6 +51,9 @@ class CustomerTests {
@Autowired
private lateinit var customerRepository: CustomerRepository
@Autowired
private lateinit var membershipRepository: MembershipRepository
private var insertedId: Long? = null
@ -58,7 +64,7 @@ class CustomerTests {
assertEquals( customer.name, "Átlag 18 éves fiú")
val id2: Long = 90
val controller = CustomerController(customerRepository)
val controller = CustomerController(customerRepository, membershipRepository)
val response = controller.getCustomerById(id2)
val customer2: Customer = response.body as Customer
@ -111,7 +117,7 @@ class CustomerTests {
fun testDeactivateCustomer() {
val id: Long = 90
val controller = CustomerController(customerRepository)
val controller = CustomerController(customerRepository, membershipRepository)
controller.deactivateCustomer(id)
val customer: Customer = customerRepository.findById(id).orElse(null)
@ -128,7 +134,7 @@ class CustomerTests {
@Test
fun testFindByEmail() {
val controller = CustomerController(customerRepository)
val controller = CustomerController(customerRepository, membershipRepository)
var response = controller.getCustomerByEmail("sw@andio.biz")
val customer = response.body
@ -204,7 +210,7 @@ class CustomerTests {
customer.birthYear = 1972
customer.dateChange = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
val customerController = CustomerController(customerRepository)
val customerController = CustomerController(customerRepository, membershipRepository)
var response: ResponseEntity<*> = customerController.updateCustomerById(id, customer, HttpHeaders.readOnlyHttpHeaders(HttpHeaders.EMPTY) )
print ("body " + response.body)
var newCustomer: Customer? = response.body as Customer
@ -249,7 +255,7 @@ class CustomerTests {
@Test
fun testClubRegistration(@Value("\${firebase.key}") apiKey: java.lang.String) {
val json = "{\"firstname\":\"Tib\", \"email\": \"mr@andio.biz\", \"goal\": \"shape\", \"fitnessLevel\": \"advanced\", \"weight\": 85}"
val controller = CustomerController(customerRepository)
val controller = CustomerController(customerRepository, membershipRepository)
val response: ResponseEntity<*> = controller.clubRegistration(json, apiKey)
assertEquals(response.statusCode, HttpStatus.BAD_REQUEST)
}
@ -265,7 +271,7 @@ class CustomerTests {
password = user.password
firebaseUid = user.firebaseUid
}
val customerController = CustomerController(customerRepository)
val customerController = CustomerController(customerRepository, membershipRepository)
customerController.serviceBeans = serviceBean
val response: ResponseEntity<*> = customerController.registration(json)
print("body " + response.body)
@ -290,7 +296,7 @@ class CustomerTests {
insertedId = savedCustomer.customerId
val customerController = CustomerController(customerRepository)
val customerController = CustomerController(customerRepository, membershipRepository)
val response: ResponseEntity<*> = customerController.updateCustomerFirebaseUidById(insertedId!!, "3FirebusaeId4")
val newCustomer2: Customer = response.body as Customer
assertEquals(response.statusCode, HttpStatus.OK)
@ -305,7 +311,7 @@ class CustomerTests {
@Test
fun testGetCustomerByFirebaseUid() {
val uid = "3FirebaseU1d"
val customerController = CustomerController(customerRepository)
val customerController = CustomerController(customerRepository, membershipRepository)
val response: ResponseEntity<*> = customerController.getCustomerByFirebaseUid(uid)
assertEquals(response.statusCode, HttpStatus.OK)
val newCustomer: Customer = response.body as Customer
@ -317,7 +323,7 @@ class CustomerTests {
@Test
fun testGetCustomerByEmail() {
val email = "sw2@andio.biz"
val customerController = CustomerController(customerRepository)
val customerController = CustomerController(customerRepository, membershipRepository)
val response: ResponseEntity<*> = customerController.getCustomerByEmail(email)
assertEquals(response.statusCode, HttpStatus.OK)
@ -330,7 +336,7 @@ class CustomerTests {
}
@Test
fun `get customer successfully`() {
fun `insert customer successfully`() {
authToken = Tokenizer.getToken()
val customer = Customer(
@ -347,7 +353,7 @@ class CustomerTests {
.header("Authorization", "Bearer $authToken")
.content(toJson(customer))
)
.andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(status().isOk)
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Kadarka"))
.andExpect(MockMvcResultMatchers.jsonPath("$.age").value(32))
.andExpect(MockMvcResultMatchers.jsonPath("$.birthYear").value(1987))
@ -376,7 +382,7 @@ class CustomerTests {
.header("Authorization", "Bearer $authToken")
.content(toJson(user))
)
.andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(status().isOk)
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Bos"))
.andExpect(MockMvcResultMatchers.jsonPath("$.firstname").value("Kakadu"))
.andExpect(MockMvcResultMatchers.jsonPath("$.birthYear").value(1972))
@ -400,7 +406,7 @@ class CustomerTests {
.header("Authorization", "Bearer $authToken")
.content(password)
)
.andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(status().isOk)
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Bos"))
.andExpect(MockMvcResultMatchers.jsonPath("$.firstname").value("Kakadu"))
.andExpect(MockMvcResultMatchers.jsonPath("$.birthYear").value(1972))
@ -417,10 +423,58 @@ class CustomerTests {
.header("Authorization", "Bearer $authToken")
.content(password)
)
.andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(status().isOk)
}
@Test
fun `new membership successfully`() {
authToken = Tokenizer.getToken()
val gson= Gson()
val customer = getCustomer(103)
mockMvc.perform(
MockMvcRequestBuilders.post("/api/membership/89")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
.content(toJson(customer))
).andExpect(status().isNotFound)
val mvcResult2: MvcResult = mockMvc.perform(
MockMvcRequestBuilders.post("/api/membership/2")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
.content(toJson(customer))
).andExpect(status().isOk)
//.andExpect(MockMvcResultMatchers.jsonPath("$.memberships.length").value(3))
.andReturn()
val customer2Json = mvcResult2.response.contentAsString
println(customer2Json)
val customer2 = gson.fromJson(customer2Json, Customer::class.java)
println(customer2)
}
fun getCustomer(customerId: Long) : Customer {
authToken = Tokenizer.getToken()
val mvcResult: MvcResult = mockMvc.perform(
MockMvcRequestBuilders.get("/api/customers/$customerId")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
)
.andExpect(status().isOk)
.andReturn()
val gson= Gson()
val customerJson = mvcResult.response.contentAsString
return gson.fromJson(customerJson, Customer::class.java)
}
private fun toJson(obj: Any): String {
return Gson().toJson(obj)
}
}

View File

@ -31,8 +31,8 @@ class EmailTest {
assertTrue (html.isNotEmpty())
assertTrue(html.contains("Tibor"))
}
@Test
fun testSend() {
fun rtestSend() {
try {
email.send("tibor.bossanyi@aitrainer.app", "<h1>Üdv</h1><p>id=932989</p>", "Workout Test API test")
@ -41,8 +41,14 @@ class EmailTest {
println("Error sending email: $e")
assertTrue(false)
}
}
@Test
fun testDietReg() {
val html = emailTemplateService.getDietRegistrationEmailBody("Tibor", "sw@aitrainer.app", "diet_registration_email")
val subject = emailTemplateService.getSubjectDiet()
// send email
email.send("service@diet4you.eu", html, subject)
}
}

View File

@ -2,6 +2,7 @@ package com.aitrainer.api.test.diet
import com.aitrainer.api.model.diet.Diet
import com.aitrainer.api.model.diet.DietMeal
import com.aitrainer.api.repository.diet.DietRepository
import com.aitrainer.api.test.Tokenizer
import com.google.gson.Gson
import org.junit.jupiter.api.BeforeAll
@ -18,6 +19,8 @@ import org.springframework.test.web.servlet.MvcResult
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@ExtendWith(SpringExtension::class)
@ -29,11 +32,16 @@ class DietTest {
@Autowired
private lateinit var mockMvc: MockMvc
@Autowired
private lateinit var dietRepository: DietRepository
private val diet = Diet(
dietUserId = 1,
dietText = "Test diet text",
startDate = "2023-02-02"
startDate = "2023-02-02",
premium = 0,
generating = 0
)
private var authToken: String? = ""
@ -120,7 +128,6 @@ class DietTest {
.andExpect(jsonPath("$.meals[2].servingUnit").value("adag"))
// update no_text
newDiet.startDate = "2023-03-20"
newDiet.meals[0].quantity = 102.0
mockMvc.perform(
@ -134,6 +141,74 @@ class DietTest {
.andExpect(jsonPath("$.meals[0].quantity").value(102.0))
.andExpect(jsonPath("$.dietText").value("Test diet text"))
val diet2 = Diet(
dietUserId = 2,
dietText = "Premium Test",
startDate = "2023-05-02",
premium = 1,
generating = 0
)
val mvcResult2: MvcResult = mockMvc.perform(
MockMvcRequestBuilders.post("/api/diet")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
.content(toJson(diet2))
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.dietUserId").value(2))
.andExpect(jsonPath("$.dietText").value("Premium Test"))
.andExpect(jsonPath("$.premium").value(1))
.andReturn()
val newDietJson2 = mvcResult2.response.contentAsString
val newDiet2 = gson.fromJson(newDietJson2, Diet::class.java)
dietRepository.delete(newDiet)
dietRepository.delete(newDiet2)
}
@Test
fun `find the last empty diet successfully`() {
val diet: Diet? = dietRepository.findTheLastEmptyDiet(2)
assertNotNull(diet)
assertEquals(diet.premium, 1)
println("DietID: ${diet.dietId}")
}
//@Test
fun `generate premium diet successfully`() {
val input = "Készíts egy személyre szabott heti étrendet ezekkel a paraméterekkel:\n" +
"Férfi, 50 éves, 120kg, 170 magas, célja fogyás.\n" +
"- Speciális étrend: ketogén\n" +
"- Allergiák: olajos magvak\n" +
"- Preferált ételek: magyar konyha\n" +
"- Ezek az ételek nem szerepelhetnek az étrendben: főzelékek\n" +
//"- napi kalóriacél: 2100 kCal\n" +
"- Étel frekvencia: reggeli, ebéd, uzsonna, vacsora\n" +
"- Ebéd és vacsora megegyezik. \n" +
"- ne legyen ismétlődés az ételeknél\n" +
"\n" +
"A választ ebben a formátumban add meg:\n" +
"{ \"DIET\": [\n" +
"{\"nameDay\": enum<monday, tuesday, wednesday...>\n" +
"\"mealTime\":enum<breakfast, lunch, snack, dinner>\n" +
"\"meals\":[<mealNames in Hungarian>],\n" +
//"\"quantities\":[<quantities for each mealName in gramms>],\n" +
//"\"calories\":[<calorie values for each mealName>],\n" +
"}" +
"]}"
mockMvc.perform(
MockMvcRequestBuilders.post("/api/diet/generate_premium/2/true")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
.content(input)
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.dietUserId").value(2))
.andExpect(jsonPath("$.premium").value(1))
}

View File

@ -50,6 +50,21 @@ class DietUserTest {
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk)
.andExpect(jsonPath("$.customerId").value(1))
dietUser.macro = "Test macro"
mockMvc.perform(post("/api/diet_user/999")
.header("Authorization", "Bearer $authToken")
.contentType(MediaType.APPLICATION_JSON)
.content(gson.toJson(dietUser)))
.andExpect(status().isNotFound)
mockMvc.perform(post("/api/diet_user/${dietUser.customerId}")
.header("Authorization", "Bearer $authToken")
.contentType(MediaType.APPLICATION_JSON)
.content(gson.toJson(dietUser)))
.andExpect(status().isOk)
.andExpect(jsonPath("$.customerId").value(1))
.andExpect(jsonPath("$.macro").value("Test macro"))
}
}

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("$.[111].name").value("Tükörtojás"))
.andExpect(jsonPath("$.[111].normalizedName").value("Tukortojas"))
.andExpect(jsonPath("$.[112].name").value("Tigris buci"))
.andExpect(jsonPath("$.[113].quantity").value(330.0))
.andExpect(jsonPath("$.[113].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")