feat: updated DTO

This commit is contained in:
MaximOksiuta
2025-11-22 14:47:34 +03:00
parent 5084dedf90
commit 064157bf2c
10 changed files with 235 additions and 24 deletions
@@ -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')"
]
}
}
@@ -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
@@ -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<WorkExperience>): String {
val type = object : TypeToken<List<WorkExperience>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toWorkExperienceList(value: String): List<WorkExperience> {
val type = object : TypeToken<List<WorkExperience>>() {}.type
return gson.fromJson(value, type)
}
@TypeConverter
fun fromEducationList(value: List<Education>): String {
val type = object : TypeToken<List<Education>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toEducationList(value: String): List<Education> {
val type = object : TypeToken<List<Education>>() {}.type
return gson.fromJson(value, type)
}
@TypeConverter
fun fromProjectList(value: List<Project>): String {
val type = object : TypeToken<List<Project>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toProjectList(value: String): List<Project> {
val type = object : TypeToken<List<Project>>() {}.type
return gson.fromJson(value, type)
}
}
@@ -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)
)
}
@@ -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<String>,
val position: String,
val city: String,
val experience: List<ExperienceDTO>,
val education: List<EducationDTO>,
val project: List<ProjectDTO>,
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<String>,
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<ExperienceDTO>,
val education: List<EducationDTO>,
val project: List<ProjectDTO>,
)
@Serializable
@@ -5,7 +5,11 @@ data class ResumeModel(
val position: String,
val about: String,
val skills: List<String>,
val city: String,
val experienceType: ExperienceType,
val experience: List<WorkExperience>,
val education: List<Education>,
val projects: List<Project>,
val prediction: Pair<Int?, Int?>,
val recommendedSkills: List<String>
)
@@ -14,7 +18,38 @@ data class ResumeCreationModel(
val position: String,
val about: String,
val skills: List<String>,
val experienceType: ExperienceType
val city: String?,
val experienceType: ExperienceType,
val experience: List<WorkExperience>,
val education: List<Education>,
val projects: List<Project>
)
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 {
@@ -8,5 +8,6 @@ import org.koin.core.annotation.Single
class CreateResumeUseCase(
private val resumeRepository: ResumeRepository
) {
suspend operator fun invoke(resumeForm: ResumeCreationModel): Result<String> = resumeRepository.createResume(resumeForm)
suspend operator fun invoke(resumeForm: ResumeCreationModel): Result<String> =
resumeRepository.createResume(resumeForm)
}
@@ -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")
)
@@ -154,7 +154,7 @@ fun CreateResumeScreen(
Spacer(modifier = Modifier.height(Paddings.large))
BigButton(
onClick = {},
onClick = viewModel::submit,
buttonText = "Узнать свою ЗП",
isLoading = viewModel.resumeFillState.collectAsState().value.isLoading
)
@@ -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()
)
}