Skip to content

feat: [FC-0047] Course progress and collapsing sections #323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
eb64023
feat: Course Home progress bar
PavloNetrebchuk May 20, 2024
ff0bfd1
feat: Collapsing course sections
PavloNetrebchuk May 21, 2024
78628f5
feat: New download icons
PavloNetrebchuk May 24, 2024
3d5ff50
feat: show CourseContainerFragment if COURSE_NESTED_LIST_ENABLED false
PavloNetrebchuk May 28, 2024
9f75619
fix: course progress bar updating
PavloNetrebchuk May 28, 2024
4989c52
feat: Renamed COURSE_NESTED_LIST_ENABLE feature flag
PavloNetrebchuk May 29, 2024
2ca4f5f
feat: Course home. Moved certificate access.
volodymyr-chekyrta May 14, 2024
0bd69bd
chore: enhance app theme capability for prod edX theme/branding (#262)
farhan-arshad-dev May 23, 2024
d04c020
feat: [FC-0047] Calendar main screen and dialogs (#322)
PavloNetrebchuk May 29, 2024
442496d
fix: DiscussionTopicsViewModelTest.kt jUnit test
PavloNetrebchuk May 30, 2024
23dcabf
fix: assignment dates
PavloNetrebchuk May 30, 2024
654bcf0
feat: [FC-0047] Improved Dashboard Level Navigation (#308)
PavloNetrebchuk May 30, 2024
d0feb18
fix: Assignment date string
PavloNetrebchuk May 30, 2024
b72fc59
fix: Lint error
PavloNetrebchuk May 30, 2024
203aee6
Merge remote-tracking branch 'origin/develop' into feat/progress_bar_…
PavloNetrebchuk May 30, 2024
84dfdba
fix: Assignment date string
PavloNetrebchuk May 31, 2024
8eb71c7
fix: Fixes according to PR feedback
PavloNetrebchuk May 31, 2024
b820e45
Merge remote-tracking branch 'origin/develop' into feat/progress_bar_…
PavloNetrebchuk May 31, 2024
6df86fc
fix: Fixes according to designer feedback
PavloNetrebchuk Jun 4, 2024
1d3203b
fix: Fixes according to PR feedback
PavloNetrebchuk Jun 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/ConfigurationManagement.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ android:
- **PRE_LOGIN_EXPERIENCE_ENABLED:** Enables the pre login courses discovery experience.
- **WHATS_NEW_ENABLED:** Enables the "What's New" feature to present the latest changes to the user.
- **SOCIAL_AUTH_ENABLED:** Enables SSO buttons on the SignIn and SignUp screens.
- **COURSE_NESTED_LIST_ENABLED:** Enables an alternative visual representation for the course structure.
- **COURSE_DROPDOWN_NAVIGATION_ENABLED:** Enables an alternative navigation through units.
- **COURSE_UNIT_PROGRESS_ENABLED:** Enables the display of the unit progress within the courseware.

## Future Support
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/openedx/core/config/UIConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package org.openedx.core.config
import com.google.gson.annotations.SerializedName

data class UIConfig(
@SerializedName("COURSE_NESTED_LIST_ENABLED")
val isCourseNestedListEnabled: Boolean = false,
@SerializedName("COURSE_DROPDOWN_NAVIGATION_ENABLED")
val isCourseDropdownNavigationEnabled: Boolean = false,
@SerializedName("COURSE_UNIT_PROGRESS_ENABLED")
val isCourseUnitProgressEnabled: Boolean = false,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.openedx.core.data.model

import com.google.gson.annotations.SerializedName
import org.openedx.core.data.model.room.AssignmentProgressDb
import org.openedx.core.domain.model.AssignmentProgress

data class AssignmentProgress(
@SerializedName("assignment_type")
val assignmentType: String?,
@SerializedName("num_points_earned")
val numPointsEarned: Float?,
@SerializedName("num_points_possible")
val numPointsPossible: Float?,
) {
fun mapToDomain() = AssignmentProgress(
assignmentType = assignmentType ?: "",
numPointsEarned = numPointsEarned ?: 0f,
numPointsPossible = numPointsPossible ?: 0f
)

fun mapToRoomEntity() = AssignmentProgressDb(
assignmentType = assignmentType,
numPointsEarned = numPointsEarned,
numPointsPossible = numPointsPossible
)
}
35 changes: 23 additions & 12 deletions core/src/main/java/org/openedx/core/data/model/Block.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package org.openedx.core.data.model

import com.google.gson.annotations.SerializedName
import org.openedx.core.BlockType
import org.openedx.core.domain.model.Block
import org.openedx.core.utils.TimeUtils
import org.openedx.core.domain.model.Block as DomainBlock
import org.openedx.core.domain.model.BlockCounts as DomainBlockCounts
import org.openedx.core.domain.model.EncodedVideos as DomainEncodedVideos
import org.openedx.core.domain.model.StudentViewData as DomainStudentViewData
import org.openedx.core.domain.model.VideoInfo as DomainVideoInfo

data class Block(
@SerializedName("id")
Expand Down Expand Up @@ -33,8 +38,12 @@ data class Block(
val completion: Double?,
@SerializedName("contains_gated_content")
val containsGatedContent: Boolean?,
@SerializedName("assignment_progress")
val assignmentProgress: AssignmentProgress?,
@SerializedName("due")
val due: String?
) {
fun mapToDomain(blockData: Map<String, org.openedx.core.data.model.Block>): Block {
fun mapToDomain(blockData: Map<String, Block>): DomainBlock {
val blockType = BlockType.getBlockType(type ?: "")
val descendantsType = if (blockType == BlockType.VERTICAL) {
val types = descendants?.map { descendant ->
Expand All @@ -46,7 +55,7 @@ data class Block(
blockType
}

return org.openedx.core.domain.model.Block(
return DomainBlock(
id = id ?: "",
blockId = blockId ?: "",
lmsWebUrl = lmsWebUrl ?: "",
Expand All @@ -61,7 +70,9 @@ data class Block(
studentViewMultiDevice = studentViewMultiDevice ?: false,
blockCounts = blockCounts?.mapToDomain()!!,
completion = completion ?: 0.0,
containsGatedContent = containsGatedContent ?: false
containsGatedContent = containsGatedContent ?: false,
assignmentProgress = assignmentProgress?.mapToDomain(),
due = TimeUtils.iso8601ToDate(due ?: ""),
)
}
}
Expand All @@ -80,8 +91,8 @@ data class StudentViewData(
@SerializedName("topic_id")
val topicId: String?
) {
fun mapToDomain(): org.openedx.core.domain.model.StudentViewData {
return org.openedx.core.domain.model.StudentViewData(
fun mapToDomain(): DomainStudentViewData {
return DomainStudentViewData(
onlyOnWeb = onlyOnWeb ?: false,
duration = duration ?: "",
transcripts = transcripts,
Expand All @@ -106,8 +117,8 @@ data class EncodedVideos(
var mobileLow: VideoInfo?
) {

fun mapToDomain(): org.openedx.core.domain.model.EncodedVideos {
return org.openedx.core.domain.model.EncodedVideos(
fun mapToDomain(): DomainEncodedVideos {
return DomainEncodedVideos(
youtube = videoInfo?.mapToDomain(),
hls = hls?.mapToDomain(),
fallback = fallback?.mapToDomain(),
Expand All @@ -124,8 +135,8 @@ data class VideoInfo(
@SerializedName("file_size")
var fileSize: Int?
) {
fun mapToDomain(): org.openedx.core.domain.model.VideoInfo {
return org.openedx.core.domain.model.VideoInfo(
fun mapToDomain(): DomainVideoInfo {
return DomainVideoInfo(
url = url ?: "",
fileSize = fileSize ?: 0
)
Expand All @@ -136,8 +147,8 @@ data class BlockCounts(
@SerializedName("video")
var video: Int?
) {
fun mapToDomain(): org.openedx.core.domain.model.BlockCounts {
return org.openedx.core.domain.model.BlockCounts(
fun mapToDomain(): DomainBlockCounts {
return DomainBlockCounts(
video = video ?: 0
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName
import org.openedx.core.data.model.room.BlockDb
import org.openedx.core.data.model.room.CourseStructureEntity
import org.openedx.core.data.model.room.MediaDb
import org.openedx.core.data.model.room.discovery.ProgressDb
import org.openedx.core.domain.model.CourseStructure
import org.openedx.core.utils.TimeUtils

Expand Down Expand Up @@ -35,7 +36,9 @@ data class CourseStructureModel(
@SerializedName("certificate")
val certificate: Certificate?,
@SerializedName("is_self_paced")
var isSelfPaced: Boolean?
var isSelfPaced: Boolean?,
@SerializedName("course_progress")
val progress: Progress?,
) {
fun mapToDomain(): CourseStructure {
return CourseStructure(
Expand All @@ -54,7 +57,8 @@ data class CourseStructureModel(
coursewareAccess = coursewareAccess?.mapToDomain(),
media = media?.mapToDomain(),
certificate = certificate?.mapToDomain(),
isSelfPaced = isSelfPaced ?: false
isSelfPaced = isSelfPaced ?: false,
progress = progress?.mapToDomain()
)
}

Expand All @@ -73,7 +77,8 @@ data class CourseStructureModel(
coursewareAccess = coursewareAccess?.mapToRoomEntity(),
media = MediaDb.createFrom(media),
certificate = certificate?.mapToRoomEntity(),
isSelfPaced = isSelfPaced ?: false
isSelfPaced = isSelfPaced ?: false,
progress = progress?.mapToRoomEntity() ?: ProgressDb.DEFAULT_PROGRESS
)
}
}
70 changes: 52 additions & 18 deletions core/src/main/java/org/openedx/core/data/model/room/BlockDb.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ package org.openedx.core.data.model.room
import androidx.room.ColumnInfo
import androidx.room.Embedded
import org.openedx.core.BlockType
import org.openedx.core.domain.model.*
import org.openedx.core.data.model.Block
import org.openedx.core.data.model.BlockCounts
import org.openedx.core.data.model.EncodedVideos
import org.openedx.core.data.model.StudentViewData
import org.openedx.core.data.model.VideoInfo
import org.openedx.core.utils.TimeUtils
import org.openedx.core.domain.model.AssignmentProgress as DomainAssignmentProgress
import org.openedx.core.domain.model.Block as DomainBlock
import org.openedx.core.domain.model.BlockCounts as DomainBlockCounts
import org.openedx.core.domain.model.EncodedVideos as DomainEncodedVideos
import org.openedx.core.domain.model.StudentViewData as DomainStudentViewData
import org.openedx.core.domain.model.VideoInfo as DomainVideoInfo

data class BlockDb(
@ColumnInfo("id")
Expand Down Expand Up @@ -33,9 +44,13 @@ data class BlockDb(
@ColumnInfo("completion")
val completion: Double,
@ColumnInfo("contains_gated_content")
val containsGatedContent: Boolean
val containsGatedContent: Boolean,
@Embedded
val assignmentProgress: AssignmentProgressDb?,
@ColumnInfo("due")
val due: String?
) {
fun mapToDomain(blocks: List<BlockDb>): Block {
fun mapToDomain(blocks: List<BlockDb>): DomainBlock {
val blockType = BlockType.getBlockType(type)
val descendantsType = if (blockType == BlockType.VERTICAL) {
val types = descendants.map { descendant ->
Expand All @@ -47,7 +62,7 @@ data class BlockDb(
blockType
}

return Block(
return DomainBlock(
id = id,
blockId = blockId,
lmsWebUrl = lmsWebUrl,
Expand All @@ -62,14 +77,16 @@ data class BlockDb(
descendants = descendants,
descendantsType = descendantsType,
completion = completion,
containsGatedContent = containsGatedContent
containsGatedContent = containsGatedContent,
assignmentProgress = assignmentProgress?.mapToDomain(),
due = TimeUtils.iso8601ToDate(due ?: ""),
)
}

companion object {

fun createFrom(
block: org.openedx.core.data.model.Block
block: Block
): BlockDb {
with(block) {
return BlockDb(
Expand All @@ -86,7 +103,9 @@ data class BlockDb(
studentViewMultiDevice = studentViewMultiDevice ?: false,
blockCounts = BlockCountsDb.createFrom(blockCounts),
completion = completion ?: 0.0,
containsGatedContent = containsGatedContent ?: false
containsGatedContent = containsGatedContent ?: false,
assignmentProgress = assignmentProgress?.mapToRoomEntity(),
due = due
)
}
}
Expand All @@ -105,8 +124,8 @@ data class StudentViewDataDb(
@Embedded
val encodedVideos: EncodedVideosDb?
) {
fun mapToDomain(): StudentViewData {
return StudentViewData(
fun mapToDomain(): DomainStudentViewData {
return DomainStudentViewData(
onlyOnWeb,
duration,
transcripts,
Expand All @@ -117,7 +136,7 @@ data class StudentViewDataDb(

companion object {

fun createFrom(studentViewData: org.openedx.core.data.model.StudentViewData?): StudentViewDataDb {
fun createFrom(studentViewData: StudentViewData?): StudentViewDataDb {
return StudentViewDataDb(
onlyOnWeb = studentViewData?.onlyOnWeb ?: false,
duration = studentViewData?.duration.toString(),
Expand All @@ -144,9 +163,9 @@ data class EncodedVideosDb(
@ColumnInfo("mobileLow")
var mobileLow: VideoInfoDb?
) {
fun mapToDomain(): EncodedVideos {
return EncodedVideos(
youtube?.mapToDomain(),
fun mapToDomain(): DomainEncodedVideos {
return DomainEncodedVideos(
youtube = youtube?.mapToDomain(),
hls = hls?.mapToDomain(),
fallback = fallback?.mapToDomain(),
desktopMp4 = desktopMp4?.mapToDomain(),
Expand All @@ -156,7 +175,7 @@ data class EncodedVideosDb(
}

companion object {
fun createFrom(encodedVideos: org.openedx.core.data.model.EncodedVideos?): EncodedVideosDb {
fun createFrom(encodedVideos: EncodedVideos?): EncodedVideosDb {
return EncodedVideosDb(
youtube = VideoInfoDb.createFrom(encodedVideos?.videoInfo),
hls = VideoInfoDb.createFrom(encodedVideos?.hls),
Expand All @@ -176,10 +195,10 @@ data class VideoInfoDb(
@ColumnInfo("fileSize")
val fileSize: Int
) {
fun mapToDomain() = VideoInfo(url, fileSize)
fun mapToDomain() = DomainVideoInfo(url, fileSize)

companion object {
fun createFrom(videoInfo: org.openedx.core.data.model.VideoInfo?): VideoInfoDb? {
fun createFrom(videoInfo: VideoInfo?): VideoInfoDb? {
if (videoInfo == null) return null
return VideoInfoDb(
videoInfo.url ?: "",
Expand All @@ -193,11 +212,26 @@ data class BlockCountsDb(
@ColumnInfo("video")
val video: Int
) {
fun mapToDomain() = BlockCounts(video)
fun mapToDomain() = DomainBlockCounts(video)

companion object {
fun createFrom(blocksCounts: org.openedx.core.data.model.BlockCounts?): BlockCountsDb {
fun createFrom(blocksCounts: BlockCounts?): BlockCountsDb {
return BlockCountsDb(blocksCounts?.video ?: 0)
}
}
}

data class AssignmentProgressDb(
@ColumnInfo("assignment_type")
val assignmentType: String?,
@ColumnInfo("num_points_earned")
val numPointsEarned: Float?,
@ColumnInfo("num_points_possible")
val numPointsPossible: Float?,
) {
fun mapToDomain() = DomainAssignmentProgress(
assignmentType = assignmentType ?: "",
numPointsEarned = numPointsEarned ?: 0f,
numPointsPossible = numPointsPossible ?: 0f
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
import org.openedx.core.data.model.room.discovery.CertificateDb
import org.openedx.core.data.model.room.discovery.CoursewareAccessDb
import org.openedx.core.data.model.room.discovery.ProgressDb
import org.openedx.core.domain.model.CourseStructure
import org.openedx.core.utils.TimeUtils

Expand Down Expand Up @@ -39,7 +40,9 @@ data class CourseStructureEntity(
@Embedded
val certificate: CertificateDb?,
@ColumnInfo("isSelfPaced")
val isSelfPaced: Boolean
val isSelfPaced: Boolean,
@Embedded
val progress: ProgressDb,
) {

fun mapToDomain(): CourseStructure {
Expand All @@ -57,7 +60,8 @@ data class CourseStructureEntity(
coursewareAccess?.mapToDomain(),
media?.mapToDomain(),
certificate?.mapToDomain(),
isSelfPaced
isSelfPaced,
progress.mapToDomain()
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.openedx.core.domain.model

data class AssignmentProgress(
val assignmentType: String,
val numPointsEarned: Float,
val numPointsPossible: Float
)
5 changes: 4 additions & 1 deletion core/src/main/java/org/openedx/core/domain/model/Block.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.openedx.core.module.db.DownloadModel
import org.openedx.core.module.db.DownloadedState
import org.openedx.core.module.db.FileType
import org.openedx.core.utils.VideoUtil
import java.util.Date


data class Block(
Expand All @@ -25,7 +26,9 @@ data class Block(
val descendantsType: BlockType,
val completion: Double,
val containsGatedContent: Boolean = false,
val downloadModel: DownloadModel? = null
val downloadModel: DownloadModel? = null,
val assignmentProgress: AssignmentProgress?,
val due: Date?
) {
val isDownloadable: Boolean
get() {
Expand Down
Loading
Loading