From 064157bf2ccc1cba82d50678fec0b7ebf3b510cf Mon Sep 17 00:00:00 2001 From: MaximOksiuta <63787095+MaximOksiuta@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:47:34 +0300 Subject: [PATCH] feat: updated DTO --- .../1.json | 30 ++++- .../data_providers/local_db/AppDatabase.kt | 3 + .../local_db/entities/JsonTypeConverters.kt | 49 ++++++++ .../local_db/entities/ResumeEntity.kt | 13 ++- .../moscow2025/data/dto/ResumeDtos.kt | 110 +++++++++++++++--- .../moscow2025/domain/models/ResumeModel.kt | 37 +++++- .../usecase/resumes/CreateResumeUseCase.kt | 5 +- .../usecase/resumes/LoadResumeListUseCase.kt | 4 + .../createResume/CreateResumeScreen.kt | 2 +- .../createResume/CreateResumeViewModel.kt | 6 +- 10 files changed, 235 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/JsonTypeConverters.kt diff --git a/app/schemas/com.prodhack.moscow2025.data.data_providers.local_db.AppDatabase/1.json b/app/schemas/com.prodhack.moscow2025.data.data_providers.local_db.AppDatabase/1.json index 853ed4f..13c07ed 100644 --- a/app/schemas/com.prodhack.moscow2025.data.data_providers.local_db.AppDatabase/1.json +++ b/app/schemas/com.prodhack.moscow2025.data.data_providers.local_db.AppDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "3e896e9a3d3b2f61149f8c0fde7e5964", + "identityHash": "b16cf19ddaafa74ea796a48650e53014", "entities": [ { "tableName": "users", @@ -55,7 +55,7 @@ }, { "tableName": "resumes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `experience_type` TEXT NOT NULL, `about_me` TEXT NOT NULL, `key_skills` TEXT NOT NULL, `position` TEXT NOT NULL, `from_salary` INTEGER, `to_salary` INTEGER, `recommended_skills` TEXT NOT NULL, PRIMARY KEY(`id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `experience_type` TEXT NOT NULL, `about_me` TEXT NOT NULL, `key_skills` TEXT NOT NULL, `position` TEXT NOT NULL, `from_salary` INTEGER, `to_salary` INTEGER, `recommended_skills` TEXT NOT NULL, `city` TEXT NOT NULL, `experience` TEXT NOT NULL, `education` TEXT NOT NULL, `projects` TEXT NOT NULL, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -102,6 +102,30 @@ "columnName": "recommended_skills", "affinity": "TEXT", "notNull": true + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "experience", + "columnName": "experience", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "education", + "columnName": "education", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "projects", + "columnName": "projects", + "affinity": "TEXT", + "notNull": true } ], "primaryKey": { @@ -114,7 +138,7 @@ ], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3e896e9a3d3b2f61149f8c0fde7e5964')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b16cf19ddaafa74ea796a48650e53014')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/AppDatabase.kt b/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/AppDatabase.kt index 76fd244..cffae1c 100644 --- a/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/AppDatabase.kt +++ b/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/AppDatabase.kt @@ -2,9 +2,11 @@ package com.prodhack.moscow2025.data.data_providers.local_db import androidx.room.Database import androidx.room.RoomDatabase +import androidx.room.TypeConverters import com.prodhack.moscow2025.data.data_providers.local_db.dao.CleanUpDao import com.prodhack.moscow2025.data.data_providers.local_db.dao.ResumeDao import com.prodhack.moscow2025.data.data_providers.local_db.dao.UserDao +import com.prodhack.moscow2025.data.data_providers.local_db.entities.JsonTypeConverters import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeEntity import com.prodhack.moscow2025.data.data_providers.local_db.entities.UserEntity @@ -13,6 +15,7 @@ import com.prodhack.moscow2025.data.data_providers.local_db.entities.UserEntity version = 1, exportSchema = true ) +@TypeConverters(JsonTypeConverters::class) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao diff --git a/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/JsonTypeConverters.kt b/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/JsonTypeConverters.kt new file mode 100644 index 0000000..e7bab86 --- /dev/null +++ b/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/JsonTypeConverters.kt @@ -0,0 +1,49 @@ +package com.prodhack.moscow2025.data.data_providers.local_db.entities + +import androidx.room.TypeConverter +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.prodhack.moscow2025.domain.models.Education +import com.prodhack.moscow2025.domain.models.Project +import com.prodhack.moscow2025.domain.models.WorkExperience + +object JsonTypeConverters { + + private val gson = Gson() + + @TypeConverter + fun fromWorkExperienceList(value: List): String { + val type = object : TypeToken>() {}.type + return gson.toJson(value, type) + } + + @TypeConverter + fun toWorkExperienceList(value: String): List { + val type = object : TypeToken>() {}.type + return gson.fromJson(value, type) + } + + @TypeConverter + fun fromEducationList(value: List): String { + val type = object : TypeToken>() {}.type + return gson.toJson(value, type) + } + + @TypeConverter + fun toEducationList(value: String): List { + val type = object : TypeToken>() {}.type + return gson.fromJson(value, type) + } + + @TypeConverter + fun fromProjectList(value: List): String { + val type = object : TypeToken>() {}.type + return gson.toJson(value, type) + } + + @TypeConverter + fun toProjectList(value: String): List { + val type = object : TypeToken>() {}.type + return gson.fromJson(value, type) + } +} diff --git a/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/ResumeEntity.kt b/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/ResumeEntity.kt index fc73756..bbfe5d7 100644 --- a/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/ResumeEntity.kt +++ b/app/src/main/java/com/prodhack/moscow2025/data/data_providers/local_db/entities/ResumeEntity.kt @@ -5,7 +5,6 @@ import androidx.room.Entity import androidx.room.PrimaryKey import com.prodhack.moscow2025.domain.models.ExperienceType import com.prodhack.moscow2025.domain.models.ResumeModel -import kotlin.math.exp @Entity(tableName = "resumes") data class ResumeEntity( @@ -23,7 +22,11 @@ data class ResumeEntity( @ColumnInfo("to_salary") val toSalary: Int?, @ColumnInfo("recommended_skills") - val recommendedSkills: String + val recommendedSkills: String, + val city: String, + val experience: String, // Store as JSON string, requires TypeConverter + val education: String, // Store as JSON string, requires TypeConverter + val projects: String // Store as JSON string, requires TypeConverter ) { fun mapToDomain(): ResumeModel = ResumeModel( id = id, @@ -32,6 +35,10 @@ data class ResumeEntity( experienceType = ExperienceType.valueOf(experienceType), skills = keySkills.split("|"), prediction = Pair(fromSalary, toSalary), - recommendedSkills = recommendedSkills.split("|") + recommendedSkills = recommendedSkills.split("|"), + city = city, + experience = JsonTypeConverters.toWorkExperienceList(experience), + education = JsonTypeConverters.toEducationList(education), + projects = JsonTypeConverters.toProjectList(projects) ) } diff --git a/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt b/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt index 36ccca7..1f263e4 100644 --- a/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt +++ b/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt @@ -1,9 +1,13 @@ package com.prodhack.moscow2025.data.dto +import com.prodhack.moscow2025.data.data_providers.local_db.entities.JsonTypeConverters import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeEntity +import com.prodhack.moscow2025.domain.models.Education +import com.prodhack.moscow2025.domain.models.EducationGrades import com.prodhack.moscow2025.domain.models.ExperienceType -import com.prodhack.moscow2025.domain.models.ResumeCreationModel +import com.prodhack.moscow2025.domain.models.Project import com.prodhack.moscow2025.domain.models.ResumeModel +import com.prodhack.moscow2025.domain.models.WorkExperience import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -51,6 +55,10 @@ data class ResumeDTO( @SerialName("key_skills") val keySkills: List, val position: String, + val city: String, + val experience: List, + val education: List, + val project: List, val prediction: PredictionDTO ) { fun mapToDomain(): ResumeModel = ResumeModel( @@ -63,7 +71,11 @@ data class ResumeDTO( prediction.fromSalary.toIntOrNull(), prediction.toSalary.toIntOrNull() ), - recommendedSkills = prediction.recommendedSkills + recommendedSkills = prediction.recommendedSkills, + city = city, + experience = experience.map { it.mapToDomain() }, + education = education.map { it.mapToDomain() }, + projects = project.map { it.mapToDomain() } ) fun mapToDB(): ResumeEntity = ResumeEntity( @@ -74,25 +86,97 @@ data class ResumeDTO( fromSalary = prediction.fromSalary.toIntOrNull(), toSalary = prediction.toSalary.toIntOrNull(), recommendedSkills = prediction.recommendedSkills.joinToString("|"), - experienceType = experienceType.mapToDomain().name + experienceType = experienceType.mapToDomain().name, + city = city, + experience = JsonTypeConverters.fromWorkExperienceList(experience.map { it.mapToDomain() }), + education = JsonTypeConverters.fromEducationList(education.map { it.mapToDomain() }), + projects = JsonTypeConverters.fromProjectList(project.map { it.mapToDomain() }), + ) +} + +@Serializable +data class ExperienceDTO( + val place: String, + val description: String, + @SerialName("month_duration") + val monthDuration: Int, +) { + fun mapToDomain(): WorkExperience = WorkExperience( + place = place, + description = description, + monthDuration = monthDuration + ) +} + +@Serializable +data class EducationDTO( + val place: String, + val grade: EducationGradesDTO, + val specialization: String, + val description: String +) { + fun mapToDomain(): Education = Education( + place = place, + grade = grade.mapToDomain(), + specialization = specialization, + description = description + ) +} + +@Serializable +enum class EducationGradesDTO { + @SerialName("common") + Common, + + @SerialName("middle") + Middle, + + @SerialName("middle_spec") + MiddleSpec, + + @SerialName("high_not_finished") + HighNotFinished, + + @SerialName("high") + High, + + @SerialName("additional") + Additional; + + fun mapToDomain(): EducationGrades = when (this) { + Common -> EducationGrades.Common + Middle -> EducationGrades.Middle + MiddleSpec -> EducationGrades.MiddleSpec + HighNotFinished -> EducationGrades.HighNotFinished + High -> EducationGrades.High + Additional -> EducationGrades.Additional + } +} + +@Serializable +data class ProjectDTO( + val name: String, + val description: String +) { + fun mapToDomain(): Project = Project( + name = name, + description = description ) } @Serializable data class ResumeCreateDTO( - @SerialName("about_me") - val aboutMe: String, @SerialName("experience_type") val experienceType: ExperienceTypeDTO, + @SerialName("about_me") + val aboutMe: String, + @SerialName("key_skills") val keySkills: List, - val position: String -) - -fun ResumeCreationModel.mapToData(): ResumeCreateDTO = ResumeCreateDTO( - aboutMe = about, - experienceType = experienceType.mapToData(), - keySkills = skills, - position = position + val position: String, + val city: String, + val experience: List, + val education: List, + val project: List, ) @Serializable diff --git a/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt b/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt index a3636cf..2ed15f3 100644 --- a/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt +++ b/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt @@ -5,7 +5,11 @@ data class ResumeModel( val position: String, val about: String, val skills: List, + val city: String, val experienceType: ExperienceType, + val experience: List, + val education: List, + val projects: List, val prediction: Pair, val recommendedSkills: List ) @@ -14,7 +18,38 @@ data class ResumeCreationModel( val position: String, val about: String, val skills: List, - val experienceType: ExperienceType + val city: String?, + val experienceType: ExperienceType, + val experience: List, + val education: List, + val projects: List +) + +data class WorkExperience( + val place: String, + val description: String, + val monthDuration: Int +) + +data class Education( + val place: String, + val grade: EducationGrades, + val specialization: String, + val description: String +) + +enum class EducationGrades { + Common, + Middle, + MiddleSpec, + HighNotFinished, + High, + Additional +} + +data class Project( + val name: String, + val description: String ) enum class ExperienceType { diff --git a/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/CreateResumeUseCase.kt b/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/CreateResumeUseCase.kt index 5a80e26..40ba678 100644 --- a/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/CreateResumeUseCase.kt +++ b/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/CreateResumeUseCase.kt @@ -7,6 +7,7 @@ import org.koin.core.annotation.Single @Single class CreateResumeUseCase( private val resumeRepository: ResumeRepository -){ - suspend operator fun invoke(resumeForm: ResumeCreationModel): Result = resumeRepository.createResume(resumeForm) +) { + suspend operator fun invoke(resumeForm: ResumeCreationModel): Result = + resumeRepository.createResume(resumeForm) } diff --git a/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/LoadResumeListUseCase.kt b/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/LoadResumeListUseCase.kt index ab40b6d..4f145ec 100644 --- a/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/LoadResumeListUseCase.kt +++ b/app/src/main/java/com/prodhack/moscow2025/domain/usecase/resumes/LoadResumeListUseCase.kt @@ -30,6 +30,10 @@ class LoadResumeListUseCase(private val resumeRepository: ResumeRepository) { "Ktor" ), experienceType = ExperienceType.Between3And6, + city = "Moscow", + experience = listOf(), + education = listOf(), + projects = listOf(), prediction = Pair(200000, 230000), recommendedSkills = listOf("KMP") ) diff --git a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt index 1ebc920..8547b05 100644 --- a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt +++ b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt @@ -154,7 +154,7 @@ fun CreateResumeScreen( Spacer(modifier = Modifier.height(Paddings.large)) BigButton( - onClick = {}, + onClick = viewModel::submit, buttonText = "Узнать свою ЗП", isLoading = viewModel.resumeFillState.collectAsState().value.isLoading ) diff --git a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt index f356967..91d975e 100644 --- a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt +++ b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt @@ -138,7 +138,11 @@ class CreateResumeViewModel( position = position, about = about, skills = keySkills.toList(), - experienceType = experience!!.mapToDomain() + experienceType = experience!!.mapToDomain(), + city = null, + experience = emptyList(), + education = emptyList(), + projects = emptyList() ) }