feat: Expand domain models and implement full DTO to domain mapping

This commit significantly expands the domain models to fully represent the quiz structure and implements the complete mapping logic in `QuizMapper` to convert `QuizResponseDto` and its nested DTOs to their corresponding domain models.

Key changes:

- **Domain Layer (`domain` module):**
    - Introduced a `QuizId` value class for type safety.
    - Added comprehensive domain models for all aspects of a quiz, including:
        - `Quiz`: Updated to include all fields from the DTO.
        - `Question`, `Choice`, `Video`, `ImageMetadata`, `MediaItem`, `ChoiceRange`: For question details.
        - `CoverMetadata`, `ExtractedColor`, `Crop`, `Point`: For cover image information.
        - `ContentTags`: For curriculum and generated codes.
        - `Metadata`, `Access`, `FeaturedListMembership`, `LastEdit`, `VersionMetadata`: For quiz metadata.
        - `LanguageInfo`, `Channel`: Common reusable models.
    - Organized domain models into separate files for better maintainability (e.g., `Question.kt`, `CoverMetadata.kt`).
    - Added a placeholder `QuestionModels.kt` to indicate that models were moved.
- **Data Layer (`model:data` module):**
    - Implemented a complete `toDomainModel()` extension function in `QuizMapper.kt` to map all fields from `QuizResponseDto` and its nested DTOs (like `CoverMetadataDto`, `QuestionDto`, etc.) to the new domain models.
    - This includes mapping for lists and nullable fields.
- **App Module (`app` module):**
    - Updated `MainActivity` to access `quizId.value` due to `QuizId` being a value class.
- **Network Layer (`core:network` module):**
    - Changed `QuizResponseDto.uuid` to be non-nullable (`String`) as it's essential for mapping to `QuizId`.
    - Removed `QuizResponse.kt` as DTOs were previously split into individual files.
This commit is contained in:
GitHub Actions Bot
2025-09-03 12:45:53 +02:00
parent 293b7f75b9
commit fd0678c1fd
27 changed files with 417 additions and 18 deletions

View File

@@ -1,7 +1,176 @@
package dev.adriankuta.kahootquiz.model.data.mappers
import dev.adriankuta.kahootquiz.core.network.models.QuizResponseDto
import dev.adriankuta.kahootquiz.domain.models.Quiz
import dev.adriankuta.kahootquiz.core.network.models.*
import dev.adriankuta.kahootquiz.domain.models.*
internal fun QuizResponseDto.toDomainModel(): Quiz =
Quiz(this.uuid ?: "")
Quiz(
id = QuizId(uuid),
language = language,
creator = creator,
creatorUsername = creatorUsername,
compatibilityLevel = compatibilityLevel,
creatorPrimaryUsage = creatorPrimaryUsage,
folderId = folderId,
themeId = themeId,
visibility = visibility,
audience = audience,
title = title,
description = description,
quizType = quizType,
cover = cover,
coverMetadata = coverMetadata?.toDomain(),
questions = questions?.map { it.toDomain() } ?: emptyList(),
contentTags = contentTags?.toDomain(),
metadata = metadata?.toDomain(),
resources = resources,
slug = slug,
languageInfo = languageInfo?.toDomain(),
inventoryItemIds = inventoryItemIds ?: emptyList(),
channels = channels?.map { it.toDomain() } ?: emptyList(),
isValid = isValid,
playAsGuest = playAsGuest,
hasRestrictedContent = hasRestrictedContent,
type = type,
created = created,
modified = modified
)
private fun CoverMetadataDto.toDomain(): CoverMetadata = CoverMetadata(
id = id,
altText = altText,
contentType = contentType,
origin = origin,
externalRef = externalRef,
resources = resources,
width = width,
height = height,
extractedColors = extractedColors?.map { it.toDomain() },
blurhash = blurhash,
crop = crop?.toDomain()
)
private fun ExtractedColorDto.toDomain(): ExtractedColor = ExtractedColor(
swatch = swatch,
rgbHex = rgbHex
)
private fun CropDto.toDomain(): Crop = Crop(
origin = origin?.toDomain(),
target = target?.toDomain(),
circular = circular
)
private fun PointDto.toDomain(): Point = Point(x = x, y = y)
private fun ContentTagsDto.toDomain(): ContentTags = ContentTags(
curriculumCodes = curriculumCodes,
generatedCurriculumCodes = generatedCurriculumCodes
)
private fun MetadataDto.toDomain(): Metadata = Metadata(
access = access?.toDomain(),
duplicationProtection = duplicationProtection,
featuredListMemberships = featuredListMemberships?.map { it.toDomain() },
lastEdit = lastEdit?.toDomain(),
versionMetadata = versionMetadata?.toDomain()
)
private fun AccessDto.toDomain(): Access = Access(
groupRead = groupRead,
folderGroupIds = folderGroupIds,
features = features
)
private fun FeaturedListMembershipDto.toDomain(): FeaturedListMembership = FeaturedListMembership(
list = list,
addedAt = addedAt
)
private fun LastEditDto.toDomain(): LastEdit = LastEdit(
editorUserId = editorUserId,
editorUsername = editorUsername,
editTimestamp = editTimestamp
)
private fun VersionMetadataDto.toDomain(): VersionMetadata = VersionMetadata(
version = version,
created = created,
creator = creator
)
private fun LanguageInfoDto.toDomain(): LanguageInfo = LanguageInfo(
language = language,
lastUpdatedOn = lastUpdatedOn,
readAloudSupported = readAloudSupported
)
private fun ChannelDto.toDomain(): Channel = Channel(id = id)
private fun QuestionDto.toDomain(): Question = Question(
type = type,
question = question,
time = time,
points = points,
pointsMultiplier = pointsMultiplier,
choices = choices?.map { it.toDomain() },
layout = layout,
image = image,
imageMetadata = imageMetadata?.toDomain(),
resources = resources,
video = video?.toDomain(),
questionFormat = questionFormat,
languageInfo = languageInfo?.toDomain(),
media = media?.map { it.toDomain() },
choiceRange = choiceRange?.toDomain()
)
private fun ChoiceDto.toDomain(): Choice = Choice(
answer = answer,
correct = correct,
languageInfo = languageInfo?.toDomain()
)
private fun VideoDto.toDomain(): Video = Video(
id = id,
startTime = startTime,
endTime = endTime,
service = service,
fullUrl = fullUrl
)
private fun ImageMetadataDto.toDomain(): ImageMetadata = ImageMetadata(
id = id,
altText = altText,
contentType = contentType,
origin = origin,
externalRef = externalRef,
resources = resources,
width = width,
height = height,
effects = effects,
crop = crop?.toDomain()
)
private fun MediaItemDto.toDomain(): MediaItem = MediaItem(
type = type,
zIndex = zIndex,
isColorOnly = isColorOnly,
id = id,
altText = altText,
contentType = contentType,
origin = origin,
externalRef = externalRef,
resources = resources,
width = width,
height = height,
crop = crop?.toDomain()
)
private fun ChoiceRangeDto.toDomain(): ChoiceRange = ChoiceRange(
start = start,
end = end,
step = step,
correct = correct,
tolerance = tolerance
)