diff --git a/build.gradle.kts b/build.gradle.kts index 15907da..933932e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } group = "com.aitrainer" -version = "1.0.31" +version = "1.0.32" java.sourceCompatibility = JavaVersion.VERSION_1_8 repositories { diff --git a/data/db/install.sql b/data/db/install.sql index 9f280a9..bf5b9ff 100644 --- a/data/db/install.sql +++ b/data/db/install.sql @@ -1377,6 +1377,47 @@ INSERT INTO `sport_translation` (`translation_id`, `language_code`, `sport_id`, /*!40000 ALTER TABLE `tracking` DISABLE KEYS */; /*!40000 ALTER TABLE `tracking` ENABLE KEYS */; + + +CREATE TABLE `tutorial` ( + `tutorial_id` INT(11) NOT NULL AUTO_INCREMENT, + `name` CHAR(50) NOT NULL COLLATE 'utf8_hungarian_ci', + PRIMARY KEY (`tutorial_id`) USING BTREE +) +COLLATE='utf8_hungarian_ci' +ENGINE=InnoDB +; + + +CREATE TABLE `tutorial_steps` ( + `tutorial_step_id` INT(11) NOT NULL AUTO_INCREMENT, + `tutorial_id` INT(11) NOT NULL DEFAULT '0', + `step` INT(3) NOT NULL DEFAULT '0', + `tutorial_text` TEXT NOT NULL COLLATE 'utf8_hungarian_ci', + `error_text` TEXT NULL DEFAULT NULL COLLATE 'utf8_hungarian_ci', + `check_text` CHAR(50) NULL DEFAULT NULL COLLATE 'utf8_hungarian_ci', + PRIMARY KEY (`tutorial_step_id`) USING BTREE, + INDEX `tutorial_id` (`tutorial_id`) USING BTREE +) +COLLATE='utf8_hungarian_ci' +ENGINE=InnoDB +; + +CREATE TABLE `tutorial_translation` ( + `translation_id` INT(13) NOT NULL AUTO_INCREMENT, + `language_code` CHAR(2) NOT NULL DEFAULT 'en' COLLATE 'utf8mb4_general_ci', + `tutorial_step_id` INT(13) NOT NULL DEFAULT '0', + `tutorial_text` TEXT NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', + `error_text` TEXT NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', + `check_text` CHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', + PRIMARY KEY (`translation_id`) USING BTREE, + INDEX `tutorial_step_id` (`tutorial_step_id`) USING BTREE +) +COLLATE='utf8mb4_general_ci' +ENGINE=InnoDB +; + + /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; /*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/data/db/update_1_0_32.sql b/data/db/update_1_0_32.sql new file mode 100644 index 0000000..7308200 --- /dev/null +++ b/data/db/update_1_0_32.sql @@ -0,0 +1,45 @@ +START TRANSACTION; + +CREATE TABLE `tutorial` ( + `tutorial_id` INT(11) NOT NULL AUTO_INCREMENT, + `name` CHAR(50) NOT NULL COLLATE 'utf8_hungarian_ci', + PRIMARY KEY (`tutorial_id`) USING BTREE +) +COLLATE='utf8_hungarian_ci' +ENGINE=InnoDB +; + + +CREATE TABLE `tutorial_steps` ( + `tutorial_step_id` INT(11) NOT NULL AUTO_INCREMENT, + `tutorial_id` INT(11) NOT NULL DEFAULT '0', + `step` INT(3) NOT NULL DEFAULT '0', + `tutorial_text` TEXT NOT NULL COLLATE 'utf8_hungarian_ci', + `error_text` TEXT NULL DEFAULT NULL COLLATE 'utf8_hungarian_ci', + `check_text` CHAR(50) NULL DEFAULT NULL COLLATE 'utf8_hungarian_ci', + PRIMARY KEY (`tutorial_step_id`) USING BTREE, + INDEX `tutorial_id` (`tutorial_id`) USING BTREE +) +COLLATE='utf8_hungarian_ci' +ENGINE=InnoDB +; + +CREATE TABLE `tutorial_translation` ( + `translation_id` INT(13) NOT NULL AUTO_INCREMENT, + `language_code` CHAR(2) NOT NULL DEFAULT 'en' COLLATE 'utf8mb4_general_ci', + `tutorial_step_id` INT(13) NOT NULL DEFAULT '0', + `tutorial_text` TEXT NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', + `error_text` TEXT NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', + `check_text` CHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', + PRIMARY KEY (`translation_id`) USING BTREE, + INDEX `tutorial_step_id` (`tutorial_step_id`) USING BTREE +) +COLLATE='utf8mb4_general_ci' +ENGINE=InnoDB +; + + +UPDATE configuration set config_value = "1.0.32", date_change=CURRENT_DATE WHERE config_key = "db_version"; + +COMMIT; + diff --git a/src/main/kotlin/com/aitrainer/api/controller/PackageController.kt b/src/main/kotlin/com/aitrainer/api/controller/PackageController.kt index 37b8963..cf36fb1 100644 --- a/src/main/kotlin/com/aitrainer/api/controller/PackageController.kt +++ b/src/main/kotlin/com/aitrainer/api/controller/PackageController.kt @@ -21,7 +21,8 @@ class PackageController(private val exerciseAbilityRepository: ExerciseAbilityRe private val exerciseTreeParentsRepository: ExerciseTreeParentsRepository, private val exercisePlanTemplateRepository: ExercisePlanTemplateRepository, private val evaluationRepository: EvaluationRepository, - private val sportRepository: SportRepository + private val sportRepository: SportRepository, + private val tutorialRepository: TutorialRepository ) { private val logger = LoggerFactory.getLogger(javaClass) @@ -61,7 +62,10 @@ class PackageController(private val exerciseAbilityRepository: ExerciseAbilityRe val listEvaluationJson: String = gson.toJson(listEvaluations) val listSports = sportRepository.getSports() - val listSportsJson: String = gson.toJson((listSports)) + val listSportsJson: String = gson.toJson(listSports) + + val listTutorials = tutorialRepository.findAll() + val listTutorialJson: String = gson.toJson(listTutorials) val packageJson: String = getClassRecord(ExerciseDevice::class.simpleName, listDevicesJson) + @@ -73,7 +77,8 @@ class PackageController(private val exerciseAbilityRepository: ExerciseAbilityRe "|||" + getClassRecord(ExerciseTreeParents::class.simpleName, listExerciseTreeParentsJson) + "|||" + getClassRecord(ExercisePlanTemplate::class.simpleName, listPlanTemplateJson) + "|||" + getClassRecord(Evaluation::class.simpleName, listEvaluationJson) + - "|||" + getClassRecord(Sport::class.simpleName, listSportsJson) + "|||" + getClassRecord(Sport::class.simpleName, listSportsJson) + + "|||" + getClassRecord(Tutorial::class.simpleName, listTutorialJson) return if (packageJson.isEmpty()) ResponseEntity.notFound().build() else ResponseEntity.ok().body(packageJson) diff --git a/src/main/kotlin/com/aitrainer/api/model/Tutorial.kt b/src/main/kotlin/com/aitrainer/api/model/Tutorial.kt new file mode 100644 index 0000000..a576598 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/Tutorial.kt @@ -0,0 +1,20 @@ +package com.aitrainer.api.model + +import com.google.gson.annotations.Expose +import kotlinx.serialization.Serializable +import org.hibernate.annotations.Fetch +import org.hibernate.annotations.FetchMode +import org.springframework.lang.NonNull +import javax.persistence.* +import javax.validation.constraints.NotBlank + +@Entity +data class Tutorial ( + @Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @get: NonNull var tutorialId: Long = 0, + @Expose @get: NotBlank var name: String = "", +) { + @OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.EAGER, mappedBy = "tutorial") + @Fetch(value = FetchMode.SUBSELECT) + @Expose val steps: List = mutableListOf().toList() + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/model/TutorialSteps.kt b/src/main/kotlin/com/aitrainer/api/model/TutorialSteps.kt new file mode 100644 index 0000000..ec1791c --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/TutorialSteps.kt @@ -0,0 +1,30 @@ +package com.aitrainer.api.model + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.google.gson.annotations.Expose +import org.hibernate.annotations.Fetch +import org.hibernate.annotations.FetchMode +import org.springframework.lang.NonNull +import javax.persistence.* +import javax.validation.constraints.NotBlank + +@Entity +data class TutorialSteps ( + @Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @get: NonNull var tutorialStepId: Long = 0, + @Expose @get: NotBlank var tutorialText: String = "", + @Expose @get: NotBlank var step: Int = 0, + @Expose var errorText: String? = null, + @Expose var checkText: String? = null, +) { + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "tutorialId", nullable = false) + @JsonIgnore + val tutorial: Tutorial? = null + + @OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.EAGER, mappedBy = "tutorialSteps") + @Fetch(value = FetchMode.SUBSELECT) + @Expose val translations: List = mutableListOf().toList() + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/model/TutorialTranslation.kt b/src/main/kotlin/com/aitrainer/api/model/TutorialTranslation.kt new file mode 100644 index 0000000..00dc029 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/model/TutorialTranslation.kt @@ -0,0 +1,21 @@ +package com.aitrainer.api.model + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.google.gson.annotations.Expose +import javax.persistence.* +import javax.validation.constraints.NotBlank + +@Entity +data class TutorialTranslation ( + @Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val translationId: Long = 0, + + @Expose @get: NotBlank var languageCode: String?, + @Expose @get: NotBlank var tutorialText: String = "", + @Expose var errorText: String? = null, + + ) { + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "tutorialStepId", nullable = false) + @JsonIgnore + val tutorialSteps: TutorialSteps? = null +} \ No newline at end of file diff --git a/src/main/kotlin/com/aitrainer/api/repository/TutorialRepository.kt b/src/main/kotlin/com/aitrainer/api/repository/TutorialRepository.kt new file mode 100644 index 0000000..cfe1a73 --- /dev/null +++ b/src/main/kotlin/com/aitrainer/api/repository/TutorialRepository.kt @@ -0,0 +1,9 @@ +package com.aitrainer.api.repository + +import com.aitrainer.api.model.Tutorial +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface TutorialRepository: JpaRepository { +} \ No newline at end of file diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index 2972ffb..2063886 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -17,6 +17,6 @@ logging.config=classpath:logback-spring.xml logging.file=logs # if the database structure has been changed, increment this version number -application.version=1.0.31 +application.version=1.0.32 jwt.secret=aitrainer \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index da7d01c..6433459 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -17,6 +17,6 @@ logging.config=classpath:logback-spring.xml logging.file=logs # if the database structure has been changed, increment this version number -application.version=1.0.31 +application.version=1.0.32 jwt.secret=aitrainer \ No newline at end of file diff --git a/src/test/kotlin/com/aitrainer/api/test/AppPackageTest.kt b/src/test/kotlin/com/aitrainer/api/test/AppPackageTest.kt index 7e1417b..0ed7d8c 100644 --- a/src/test/kotlin/com/aitrainer/api/test/AppPackageTest.kt +++ b/src/test/kotlin/com/aitrainer/api/test/AppPackageTest.kt @@ -37,6 +37,8 @@ class AppPackageTest { private lateinit var evaluationRepository: EvaluationRepository @Autowired private lateinit var sportRepository: SportRepository + @Autowired + private lateinit var tutorialRepository: TutorialRepository @Test fun testAppPackage() { @@ -52,7 +54,8 @@ class AppPackageTest { exerciseTreeParentsRepository, exercisePlanTemplateRepository, evaluationRepository, - sportRepository + sportRepository, + tutorialRepository ) val response: ResponseEntity<*> = controller.getPackageData() @@ -66,7 +69,7 @@ class AppPackageTest { val record = it.split("***") print(record[0] + "\n") if ( record[0] == ExerciseType::class.simpleName) { - print("List ExerciseType: " + record[1]) + //print("List ExerciseType: " + record[1]) val exerciseTypeJson: String = record[1] val type = object : TypeToken?>() {}.type val listExerciseType: List = gson.fromJson(exerciseTypeJson, type) @@ -125,6 +128,15 @@ class AppPackageTest { assertEquals(sports[2].name, "Footgolf") assertEquals(sports[2].translations[0].sportName, "Footgolf") assertEquals(sports[3].translations[0].sportName, "Tenisz") + } else if (record[0] == Tutorial::class.simpleName) { + print("List Tutorial: " + record[1]) + val tutorialJson: String = record[1] + val type = object : TypeToken?>() {}.type + val tutorials: List = gson.fromJson(tutorialJson, type) + assertEquals(tutorials.size, 1) + assertEquals(tutorials[0].name, "Basic") + assertEquals(tutorials[0].steps.size, 2) + assertEquals(tutorials[0].steps[0].translations.size, 2) } }