diff --git a/Core/Core/Assets.xcassets/CourseDates/Contents.json b/Core/Core/Assets.xcassets/CourseDates/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/assignment_icon.imageset/Contents.json b/Core/Core/Assets.xcassets/CourseDates/assignment_icon.imageset/Contents.json new file mode 100644 index 000000000..40941b157 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/assignment_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "edit_square.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/assignment_icon.imageset/edit_square.svg b/Core/Core/Assets.xcassets/CourseDates/assignment_icon.imageset/edit_square.svg new file mode 100644 index 000000000..0fd888059 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/assignment_icon.imageset/edit_square.svg @@ -0,0 +1,3 @@ + + + diff --git a/Core/Core/Assets.xcassets/CourseDates/calendar_icon.imageset/Contents.json b/Core/Core/Assets.xcassets/CourseDates/calendar_icon.imageset/Contents.json new file mode 100644 index 000000000..2a14fd4fa --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/calendar_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "calender_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/calendar_icon.imageset/calender_icon.svg b/Core/Core/Assets.xcassets/CourseDates/calendar_icon.imageset/calender_icon.svg new file mode 100644 index 000000000..e0cdf5f31 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/calendar_icon.imageset/calender_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Core/Core/Assets.xcassets/CourseDates/certificate_icon.imageset/Contents.json b/Core/Core/Assets.xcassets/CourseDates/certificate_icon.imageset/Contents.json new file mode 100644 index 000000000..47d94b155 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/certificate_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "certificate_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/certificate_icon.imageset/certificate_icon.svg b/Core/Core/Assets.xcassets/CourseDates/certificate_icon.imageset/certificate_icon.svg new file mode 100644 index 000000000..532dd1baf --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/certificate_icon.imageset/certificate_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Core/Core/Assets.xcassets/CourseDates/lock_icon.imageset/Contents.json b/Core/Core/Assets.xcassets/CourseDates/lock_icon.imageset/Contents.json new file mode 100644 index 000000000..f94ec1e8c --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/lock_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "lock_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/lock_icon.imageset/lock_icon.svg b/Core/Core/Assets.xcassets/CourseDates/lock_icon.imageset/lock_icon.svg new file mode 100644 index 000000000..823dd2e69 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/lock_icon.imageset/lock_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Core/Core/Assets.xcassets/CourseDates/lock_with_watch_icon.imageset/Contents.json b/Core/Core/Assets.xcassets/CourseDates/lock_with_watch_icon.imageset/Contents.json new file mode 100644 index 000000000..705033805 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/lock_with_watch_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "locked.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/lock_with_watch_icon.imageset/locked.svg b/Core/Core/Assets.xcassets/CourseDates/lock_with_watch_icon.imageset/locked.svg new file mode 100644 index 000000000..bebb2111f --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/lock_with_watch_icon.imageset/locked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Core/Core/Assets.xcassets/CourseDates/school_cap_icon.imageset/Contents.json b/Core/Core/Assets.xcassets/CourseDates/school_cap_icon.imageset/Contents.json new file mode 100644 index 000000000..f31579037 --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/school_cap_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "school_cap_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Core/Core/Assets.xcassets/CourseDates/school_cap_icon.imageset/school_cap_icon.svg b/Core/Core/Assets.xcassets/CourseDates/school_cap_icon.imageset/school_cap_icon.svg new file mode 100644 index 000000000..4636ce2fd --- /dev/null +++ b/Core/Core/Assets.xcassets/CourseDates/school_cap_icon.imageset/school_cap_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Core/Core/Extensions/DateExtension.swift b/Core/Core/Extensions/DateExtension.swift index 362a3dc33..36f93d5ee 100644 --- a/Core/Core/Extensions/DateExtension.swift +++ b/Core/Core/Extensions/DateExtension.swift @@ -9,12 +9,18 @@ import Foundation public extension Date { init(iso8601: String) { - let date: Date + let formats = ["yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"] + var date: Date var dateFormatter: DateFormatter? dateFormatter = DateFormatter() - dateFormatter?.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" dateFormatter?.locale = Locale(identifier: "en_US_POSIX") - date = dateFormatter?.date(from: iso8601) ?? Date() + + date = formats.compactMap { format in + dateFormatter?.dateFormat = format + return dateFormatter?.date(from: iso8601) + } + .first ?? Date() + defer { dateFormatter = nil } @@ -26,7 +32,7 @@ public extension Date { formatter.locale = .current formatter.unitsStyle = .full formatter.locale = Locale(identifier: "en_US_POSIX") - if self.description == Date().description { + if description == Date().description { return CoreLocalization.Date.justNow } else { return formatter.localizedString(for: self, relativeTo: Date()) @@ -104,7 +110,7 @@ public extension Date { case .iso8601: dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" case .shortWeekdayMonthDayYear: - dateFormatter.dateFormat = "EEE, MMM d, yyyy" + applyShortWeekdayMonthDayYear(dateFormatter: dateFormatter) } let date = dateFormatter.string(from: self) @@ -124,7 +130,7 @@ public extension Date { let days = Calendar.current.dateComponents([.day], from: self, to: Date()) if let day = days.day { if day < 2 { - return self.timeAgoDisplay() + return timeAgoDisplay() } else { return date } @@ -134,7 +140,57 @@ public extension Date { case .iso8601: return date case .shortWeekdayMonthDayYear: - return date + return getShortWeekdayMonthDayYear(dateFormatterString: date) + } + } + + private func applyShortWeekdayMonthDayYear(dateFormatter: DateFormatter) { + if isCurrentYear() { + let days = Calendar.current.dateComponents([.day], from: self, to: Date()) + if let day = days.day, (-6 ... -2).contains(day) { + dateFormatter.dateFormat = "EEEE" + } else { + dateFormatter.dateFormat = "MMMM d" + } + } else { + dateFormatter.dateFormat = "MMMM d, yyyy" } } + + private func getShortWeekdayMonthDayYear(dateFormatterString: String) -> String { + let days = Calendar.current.dateComponents([.day], from: self, to: Date()) + + if let day = days.day { + guard isCurrentYear() else { + // It's past year or future year + return dateFormatterString + } + + switch day { + case -6...(-2): + return dateFormatterString + case 2...6: + return timeAgoDisplay() + case -1: + return CoreLocalization.CourseDates.tomorrow + case 1: + return CoreLocalization.CourseDates.yesterday + default: + if day > 6 || day < -6 { + return dateFormatterString + } else { + // It means, date is in hours past due or upcoming + return timeAgoDisplay() + } + } + } else { + return dateFormatterString + } + } + + func isCurrentYear() -> Bool { + let selfYear = Calendar.current.component(.year, from: self) + let runningYear = Calendar.current.component(.year, from: Date()) + return selfYear == runningYear + } } diff --git a/Core/Core/SwiftGen/Assets.swift b/Core/Core/SwiftGen/Assets.swift index 202ab2639..1d1c84625 100644 --- a/Core/Core/SwiftGen/Assets.swift +++ b/Core/Core/SwiftGen/Assets.swift @@ -28,6 +28,12 @@ public enum CoreAssets { public static let facebookButtonColor = ColorAsset(name: "FacebookButtonColor") public static let googleButtonColor = ColorAsset(name: "GoogleButtonColor") public static let microsoftButtonColor = ColorAsset(name: "MicrosoftButtonColor") + public static let assignmentIcon = ImageAsset(name: "assignment_icon") + public static let calendarIcon = ImageAsset(name: "calendar_icon") + public static let certificateIcon = ImageAsset(name: "certificate_icon") + public static let lockIcon = ImageAsset(name: "lock_icon") + public static let lockWithWatchIcon = ImageAsset(name: "lock_with_watch_icon") + public static let schoolCapIcon = ImageAsset(name: "school_cap_icon") public static let bookCircle = ImageAsset(name: "book.circle") public static let bubbleLeftCircle = ImageAsset(name: "bubble.left.circle") public static let docCircle = ImageAsset(name: "doc.circle") diff --git a/Core/Core/SwiftGen/Strings.swift b/Core/Core/SwiftGen/Strings.swift index 71a71130f..dad529b53 100644 --- a/Core/Core/SwiftGen/Strings.swift +++ b/Core/Core/SwiftGen/Strings.swift @@ -63,14 +63,22 @@ public enum CoreLocalization { public static let completed = CoreLocalization.tr("Localizable", "COURSE_DATES.COMPLETED", fallback: "Completed") /// Due next public static let dueNext = CoreLocalization.tr("Localizable", "COURSE_DATES.DUE_NEXT", fallback: "Due next") + /// Item Hidden + public static let itemHidden = CoreLocalization.tr("Localizable", "COURSE_DATES.ITEM_HIDDEN", fallback: "Item Hidden") + /// Items Hidden + public static let itemsHidden = CoreLocalization.tr("Localizable", "COURSE_DATES.ITEMS_HIDDEN", fallback: "Items Hidden") /// Past due public static let pastDue = CoreLocalization.tr("Localizable", "COURSE_DATES.PAST_DUE", fallback: "Past due") /// Today public static let today = CoreLocalization.tr("Localizable", "COURSE_DATES.TODAY", fallback: "Today") + /// Tomorrow + public static let tomorrow = CoreLocalization.tr("Localizable", "COURSE_DATES.TOMORROW", fallback: "Tomorrow") /// Unreleased public static let unreleased = CoreLocalization.tr("Localizable", "COURSE_DATES.UNRELEASED", fallback: "Unreleased") /// Verified Only public static let verifiedOnly = CoreLocalization.tr("Localizable", "COURSE_DATES.VERIFIED_ONLY", fallback: "Verified Only") + /// Yesterday + public static let yesterday = CoreLocalization.tr("Localizable", "COURSE_DATES.YESTERDAY", fallback: "Yesterday") } public enum Date { /// Ended diff --git a/Core/Core/en.lproj/Localizable.strings b/Core/Core/en.lproj/Localizable.strings index 673144473..a132e9445 100644 --- a/Core/Core/en.lproj/Localizable.strings +++ b/Core/Core/en.lproj/Localizable.strings @@ -94,6 +94,10 @@ "COURSE_DATES.DUE_NEXT" = "Due next"; "COURSE_DATES.UNRELEASED" = "Unreleased"; "COURSE_DATES.VERIFIED_ONLY" = "Verified Only"; +"COURSE_DATES.TOMORROW" = "Tomorrow"; +"COURSE_DATES.YESTERDAY" = "Yesterday"; +"COURSE_DATES.ITEMS_HIDDEN" = "Items Hidden"; +"COURSE_DATES.ITEM_HIDDEN" = "Item Hidden"; "SOCIAL_SIGN_CANCELED" = "The user canceled the sign-in flow."; diff --git a/Core/Core/uk.lproj/Localizable.strings b/Core/Core/uk.lproj/Localizable.strings index ab592e519..f5763d308 100644 --- a/Core/Core/uk.lproj/Localizable.strings +++ b/Core/Core/uk.lproj/Localizable.strings @@ -94,6 +94,10 @@ "COURSE_DATES.DUE_NEXT" = "Due next"; "COURSE_DATES.UNRELEASED" = "Unreleased"; "COURSE_DATES.VERIFIED_ONLY" = "Verified Only"; +"COURSE_DATES.TOMORROW" = "Tomorrow"; +"COURSE_DATES.YESTERDAY" = "Yesterday"; +"COURSE_DATES.ITEMS_HIDDEN" = "Items Hidden"; +"COURSE_DATES.ITEM_HIDDEN" = "Item Hidden"; "SOCIAL_SIGN_CANCELED" = "The user canceled the sign-in flow."; "AUTHORIZATION_FAILED" = "Authorization failed."; diff --git a/Course/Course.xcodeproj/project.pbxproj b/Course/Course.xcodeproj/project.pbxproj index d408ec8e0..38f92443f 100644 --- a/Course/Course.xcodeproj/project.pbxproj +++ b/Course/Course.xcodeproj/project.pbxproj @@ -64,6 +64,8 @@ 0766DFCE299AB26D00EBEF6A /* EncodedVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */; }; 0766DFD0299AB29000EBEF6A /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCF299AB29000EBEF6A /* PlayerViewController.swift */; }; 197FB8EA8F92F00A8F383D82 /* Pods_App_Course.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E795BD160CDA7D9C120DE6 /* Pods_App_Course.framework */; }; + 975F475E2B6151FD00E5B031 /* CourseDatesMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975F475D2B6151FD00E5B031 /* CourseDatesMock.swift */; }; + 975F47602B615DA700E5B031 /* CourseStructureMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975F475F2B615DA700E5B031 /* CourseStructureMock.swift */; }; B8F50317B6B830A0E520C954 /* Pods_App_Course_CourseTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50E59D2B81E12610964282C5 /* Pods_App_Course_CourseTests.framework */; }; BAAD62C82AFD00EE000E6103 /* CourseExpandableContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD62C72AFD00EE000E6103 /* CourseExpandableContentView.swift */; }; DB205BFB2AE81B1200136EC2 /* CourseDateViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB205BFA2AE81B1200136EC2 /* CourseDateViewModelTests.swift */; }; @@ -154,6 +156,8 @@ 600A464AEDA7BEFEC1A7FE99 /* Pods-App-Course-CourseTests.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Course-CourseTests.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Course-CourseTests/Pods-App-Course-CourseTests.debugdev.xcconfig"; sourceTree = ""; }; 69A261790A0B4C724CCFA4F2 /* Pods-App-CourseDetails.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-CourseDetails.release.xcconfig"; path = "Target Support Files/Pods-App-CourseDetails/Pods-App-CourseDetails.release.xcconfig"; sourceTree = ""; }; 831B08139890634968EDD44D /* Pods-App-Course-CourseTests.debugstage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Course-CourseTests.debugstage.xcconfig"; path = "Target Support Files/Pods-App-Course-CourseTests/Pods-App-Course-CourseTests.debugstage.xcconfig"; sourceTree = ""; }; + 975F475D2B6151FD00E5B031 /* CourseDatesMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDatesMock.swift; sourceTree = ""; }; + 975F475F2B615DA700E5B031 /* CourseStructureMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseStructureMock.swift; sourceTree = ""; }; 99AEF08FD75F1509863D3302 /* Pods-App-CourseDetails.debugprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-CourseDetails.debugprod.xcconfig"; path = "Target Support Files/Pods-App-CourseDetails/Pods-App-CourseDetails.debugprod.xcconfig"; sourceTree = ""; }; 9B5D3D31A9CFA08B6C4347BD /* Pods-App-CourseDetails.releasedev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-CourseDetails.releasedev.xcconfig"; path = "Target Support Files/Pods-App-CourseDetails/Pods-App-CourseDetails.releasedev.xcconfig"; sourceTree = ""; }; A47C63D9EB0D866F303D4588 /* Pods-App-Course.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Course.releasestage.xcconfig"; path = "Target Support Files/Pods-App-Course/Pods-App-Course.releasestage.xcconfig"; sourceTree = ""; }; @@ -286,6 +290,7 @@ 0208666A29CC6D8000BC05B2 /* Persistence */, 02B6B3BF28E1DB8800232911 /* Model */, 02B6B3B928E1D13500232911 /* Network */, + 975F475C2B61517A00E5B031 /* Mock */, 02B6B3BB28E1D14F00232911 /* CourseRepository.swift */, ); path = Data; @@ -446,6 +451,15 @@ path = Unit; sourceTree = ""; }; + 975F475C2B61517A00E5B031 /* Mock */ = { + isa = PBXGroup; + children = ( + 975F475D2B6151FD00E5B031 /* CourseDatesMock.swift */, + 975F475F2B615DA700E5B031 /* CourseStructureMock.swift */, + ); + path = Mock; + sourceTree = ""; + }; D52670044E8768425E23C627 /* Pods */ = { isa = PBXGroup; children = ( @@ -712,7 +726,9 @@ 02B6B3BC28E1D14F00232911 /* CourseRepository.swift in Sources */, 02280F60294B50030032823A /* CoursePersistenceProtocol.swift in Sources */, 02454CAA2A2619B40043052A /* LessonProgressView.swift in Sources */, + 975F475E2B6151FD00E5B031 /* CourseDatesMock.swift in Sources */, DB7D6EB22ADFE9510036BB13 /* Data_CourseDates.swift in Sources */, + 975F47602B615DA700E5B031 /* CourseStructureMock.swift in Sources */, 02280F5E294B4FDA0032823A /* CourseCoreModel.xcdatamodeld in Sources */, 0766DFCE299AB26D00EBEF6A /* EncodedVideoPlayer.swift in Sources */, 0276D75B29DDA3890004CDF8 /* Data_ResumeBlock.swift in Sources */, diff --git a/Course/Course/Data/CourseRepository.swift b/Course/Course/Data/CourseRepository.swift index 7a154fa0e..df55aec12 100644 --- a/Course/Course/Data/CourseRepository.swift +++ b/Course/Course/Data/CourseRepository.swift @@ -209,7 +209,6 @@ public class CourseRepository: CourseRepositoryProtocol { } // Mark - For testing and SwiftUI preview -// swiftlint:disable all #if DEBUG class CourseRepositoryMock: CourseRepositoryProtocol { func getCourseDatesOffline(courseID: String) async throws -> CourseDates { @@ -230,7 +229,8 @@ class CourseRepositoryMock: CourseRepositoryProtocol { func getCourseDates(courseID: String) async throws -> CourseDates { do { - let courseDates = try courseDatesJSON.data(using: .utf8)!.mapResponse(DataLayer.CourseDates.self) + let courseDates = try + CourseRepository.courseDatesJSON.data(using: .utf8)!.mapResponse(DataLayer.CourseDates.self) return courseDates.domain } catch { throw error @@ -239,14 +239,15 @@ class CourseRepositoryMock: CourseRepositoryProtocol { func getLoadedCourseBlocks(courseID: String) throws -> CourseStructure { let decoder = JSONDecoder() - let jsonData = Data(courseStructureJson.utf8) + let jsonData = Data(CourseRepository.courseStructureJson.utf8) let courseBlocks = try decoder.decode(DataLayer.CourseStructure.self, from: jsonData) return parseCourseStructure(course: courseBlocks) } public func getCourseBlocks(courseID: String) async throws -> CourseStructure { do { - let courseBlocks = try courseStructureJson.data(using: .utf8)!.mapResponse(DataLayer.CourseStructure.self) + let courseBlocks = try + CourseRepository.courseStructureJson.data(using: .utf8)!.mapResponse(DataLayer.CourseStructure.self) return parseCourseStructure(course: courseBlocks) } catch { throw error @@ -378,995 +379,5 @@ And there are various ways of describing it-- call it oral poetry or videoUrl: block.userViewData?.encodedVideo?.fallback?.url, youTubeUrl: block.userViewData?.encodedVideo?.youTube?.url) } - - private let courseStructureJson: String = """ - {"root": "block-v1:QA+comparison+2022+type@course+block@course", - "blocks": { - "block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78": { - "id": "block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", - "block_id": "be1704c576284ba39753c6f0ea4a4c78", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", - "type": "comparison", - "display_name": "Comparison", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296": { - "id": "block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", - "block_id": "93acc543871e4c73bc20a72a64e93296", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", - "type": "problem", - "display_name": "Dropdown with Hints and Feedback", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b": { - "id": "block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", - "block_id": "06c17035106e48328ebcd042babcf47b", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", - "type": "comparison", - "display_name": "Comparison", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58": { - "id": "block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", - "block_id": "c19e41b61db14efe9c45f1354332ae58", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", - "type": "problem", - "display_name": "Text Input with Hints and Feedback", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb": { - "id": "block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", - "block_id": "0d96732f577b4ff68799faf8235d1bfb", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", - "type": "problem", - "display_name": "Numerical Input with Hints and Feedback", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96": { - "id": "block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", - "block_id": "dd2e22fdf0724bd88c8b2e6b68dedd96", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", - "type": "problem", - "display_name": "Blank Common Problem", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd": { - "id": "block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", - "block_id": "d1e091aa305741c5bedfafed0d269efd", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", - "type": "problem", - "display_name": "Blank Common Problem", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7": { - "id": "block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7", - "block_id": "23e10dea806345b19b77997b4fc0eea7", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7", - "type": "comparison", - "display_name": "Comparison", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904": { - "id": "block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904", - "block_id": "29e7eddbe8964770896e4036748c9904", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904", - "type": "vertical", - "display_name": "Unit", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", - "block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", - "block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", - "block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", - "block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", - "block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", - "block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", - "block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6": { - "id": "block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", - "block_id": "f468bb5c6e8641179e523c7fcec4e6d6", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", - "type": "sequential", - "display_name": "Subsection", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234": { - "id": "block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", - "block_id": "eaf91d8fc70547339402043ba1a1c234", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", - "type": "problem", - "display_name": "Dropdown with Hints and Feedback", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751": { - "id": "block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", - "block_id": "fac531c3f1f3400cb8e3b97eb2c3d751", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", - "type": "comparison", - "display_name": "Comparison", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de": { - "id": "block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de", - "block_id": "74a1074024fe401ea305534f2241e5de", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de", - "type": "html", - "display_name": "Raw HTML", - "graded": false, - "student_view_data": { - "last_modified": "2023-05-04T19:08:07Z", - "html_data": "https://s3.eu-central-1.amazonaws.com/vso-dev-edx-sorage/htmlxblock/QA/comparison/html/74a1074024fe401ea305534f2241e5de/content_html.zip", - "size": 576, - "index_page": "index.html", - "icon_class": "other" - }, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca": { - "id": "block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca", - "block_id": "e5b2e105f4f947c5b76fb12c35da1eca", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca", - "type": "vertical", - "display_name": "Unit", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", - "block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", - "block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2": { - "id": "block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2", - "block_id": "d37cb0c5c2d24ddaacf3494760a055f2", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2", - "type": "sequential", - "display_name": "Another one subsection", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846": { - "id": "block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", - "block_id": "abecaefe203c4c93b441d16cea3b7846", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", - "type": "chapter", - "display_name": "Section", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", - "block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de": { - "id": "block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", - "block_id": "a0c3ac29daab425f92a34b34eb2af9de", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", - "type": "pdf", - "display_name": "PDF title", - "graded": false, - "student_view_data": { - "last_modified": "2023-04-26T08:43:45Z", - "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", - }, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9": { - "id": "block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", - "block_id": "bcd1b0f3015b4d3696b12f65a5d682f9", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", - "type": "pdf", - "display_name": "PDF", - "graded": false, - "student_view_data": { - "last_modified": "2023-04-26T08:43:45Z", - "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", - }, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59": { - "id": "block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", - "block_id": "67d805daade34bd4b6ace607e6d48f59", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", - "type": "pdf", - "display_name": "PDF", - "graded": false, - "student_view_data": { - "last_modified": "2023-04-26T08:43:45Z", - "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", - }, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974": { - "id": "block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", - "block_id": "828606a51f4e44198e92f86a45be7974", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", - "type": "pdf", - "display_name": "PDF", - "graded": false, - "student_view_data": { - "last_modified": "2023-04-26T08:43:45Z", - "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", - }, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee": { - "id": "block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee", - "block_id": "8646c3bc2184467b86e5ef01ecd452ee", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee", - "type": "pdf", - "display_name": "PDF", - "graded": false, - "student_view_data": { - "last_modified": "2023-04-26T08:43:45Z", - "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", - }, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1": { - "id": "block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", - "block_id": "e2faa0e62223489e91a41700865c5fc1", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", - "type": "vertical", - "display_name": "Unit", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", - "block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", - "block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", - "block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", - "block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52": { - "id": "block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52", - "block_id": "0c5e89fa6d7a4fac8f7b26f2ca0bbe52", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52", - "type": "problem", - "display_name": "Checkboxes with Hints and Feedback", - "graded": false, - "student_view_multi_device": true, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4": { - "id": "block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", - "block_id": "8ba437d8b20d416d91a2d362b0c940a4", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", - "type": "vertical", - "display_name": "Unit", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d": { - "id": "block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d", - "block_id": "021f70794f7349998e190b060260b70d", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d", - "type": "pdf", - "display_name": "PDF", - "graded": false, - "student_view_data": { - "last_modified": "2023-04-26T08:43:45Z", - "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", - }, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ], - "completion": 0.0 - }, - "block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1": { - "id": "block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1", - "block_id": "2c344115d3554ac58c140ec86e591aa1", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1", - "type": "vertical", - "display_name": "Unit", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe": { - "id": "block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe", - "block_id": "6c9c6ba663b54c0eb9cbdcd0c6b4bebe", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe", - "type": "sequential", - "display_name": "Subsection", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", - "block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", - "block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7": { - "id": "block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", - "block_id": "d5a4f1f2f5314288aae400c270fb03f7", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", - "type": "chapter", - "display_name": "PDF", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe" - ], - "completion": 0 - }, - "block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf": { - "id": "block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf", - "block_id": "7ab45affb80f4846a60648ec6aff9fbf", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf", - "type": "chapter", - "display_name": "Section", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - - ] - }, - "block-v1:QA+comparison+2022+type@course+block@course": { - "id": "block-v1:QA+comparison+2022+type@course+block@course", - "block_id": "course", - "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@course+block@course", - "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@course+block@course?experience=legacy", - "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@course+block@course", - "type": "course", - "display_name": "Comparison xblock test coursre", - "graded": false, - "student_view_multi_device": false, - "block_counts": { - "video": 0 - }, - "descendants": [ - "block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", - "block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", - "block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf" - ], - "completion": 0 - } - }, - "id": "course-v1:QA+comparison+2022", - "name": "Comparison xblock test coursre", - "number": "comparison", - "org": "QA", - "start": "2022-01-01T00:00:00Z", - "start_display": "01 january 2022 р.", - "start_type": "timestamp", - "end": null, - "courseware_access": { - "has_access": true, - "error_code": null, - "developer_message": null, - "user_message": null, - "additional_context_user_message": null, - "user_fragment": null - }, - "media": { - "image": { - "raw": "/asset-v1:QA+comparison+2022+type@asset+block@images_course_image.jpg", - "small": "/asset-v1:QA+comparison+2022+type@asset+block@images_course_image.jpg", - "large": "/asset-v1:QA+comparison+2022+type@asset+block@images_course_image.jpg" - } - }, - "certificate": { - - }, - "is_self_paced": false - } - """ - - private let courseDatesJSON: String = """ - { - "dates_banner_info": { - "missed_deadlines": false, - "content_type_gating_enabled": true, - "missed_gated_content": false, - "verified_upgrade_link": null - }, - "course_date_blocks": [ - { - "assignment_type": null, - "complete": null, - "date": "2023-08-30T15:00:00Z", - "date_type": "course-start-date", - "description": "", - "learner_has_access": true, - "link": "", - "link_text": "", - "title": "Course starts", - "extra_info": null, - "first_component_block_id": "" - }, - { - "assignment_type": "Problem Set", - "complete": true, - "date": "2023-09-14T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@ca19e125470846f2a36ad1225410e39a", - "link_text": "", - "title": "Problem Set 1", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@problem+block@bd89c1dd129240f99bb8c5cbe3f56530" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-09-14T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "", - "link_text": "", - "title": "Problem Set 1.1", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@problem+block@bd89c1dd129240f99bb8c5cbe3f56530a" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-09-21T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@e137765987514da7851a59dedeb5ecec", - "link_text": "", - "title": "Problem Set 2", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@html+block@c99e81ffff4546e28fecab0a0c381abd" - }, - { - "assignment_type": "Problem Set", - "complete": true, - "date": "2023-09-21T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@e137765987514da7851a59dedeb5ececc", - "link_text": "", - "title": "Problem Set 2.1", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@html+block@c99e81ffff4546e28fecab0a0c381abdc" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-09-21T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": false, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@e137765987514da7851a59dedeb5ececcc", - "link_text": "", - "title": "Problem Set 2.2", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@html+block@c99e81ffff4546e28fecab0a0c381abdcc" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-09-28T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@bfe9eb02884a4812883ff9e543887968", - "link_text": "", - "title": "Problem Set 3", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@html+block@5e117d71433647eaa6de63434641c011" - }, - { - "assignment_type": "Midterm", - "complete": false, - "date": "2023-10-04T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": false, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@bb284b9c4ff04091951f77b50e3b72f4", - "link_text": "", - "title": "Midterm Exam (time limit removed due to grader issues)", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@vertical+block@ec1c5d83de6244d38b1f3ff4d32b6e17" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-10-12T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@64f4d344ecdc48d2bef514882e6236ab", - "link_text": "", - "title": "Problem Set 4", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@html+block@eeb64a67e52e4f3e80656b9233204f25" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-10-19T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@79d22d4ab4f740158930fca4e80d67db", - "link_text": "", - "title": "Problem Set 5", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@html+block@3dde572871fc4b6ebdb47722a184a514" - }, - { - "assignment_type": "Problem Set", - "complete": false, - "date": "2023-10-26T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@3d419098708e4bcd9209ffa31a4cb3dc", - "link_text": "", - "title": "Problem Set 6", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@problem+block@9b2a0176bf6a4c21ad4a63c2fce2d0cb" - }, - { - "assignment_type": "Final Exam", - "complete": false, - "date": "2023-10-31T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": false, - "link": "", - "link_text": "", - "title": "Final Exam (time limit removed due to grader issues)", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@vertical+block@e7b4f091d7ad457097d0bbda9d9af267" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@221a4c17dba341d6a970a0d80343253c", - "link_text": "", - "title": "1. Introduction to Python (TIME: 1:03:12)", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@ad9387910b7e47069c452efebd7b36dd" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@35f82f6c3ecb4e9e913dc279a9b73a9f", - "link_text": "", - "title": "2. Core Elements of Programs (TIME: 54:14)", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@8fb4fa767a204d41a6366c2bc53bea22" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@62f08cc899344863a1ab678aee506dec", - "link_text": "", - "title": "3. Simple Algorithms (TIME: 41:06)", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@1f2b055948c9467492649b59e24e8fdc" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@38007cdb67c44b46b124cdbce33510b5", - "link_text": "", - "title": "4. Functions (TIME: 1:08:06)", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@9dc4c11c46274b87964c7534b449d50a" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@01df98c1e74a459b8fb20d2d785622cd", - "link_text": "", - "title": "5. Tuples and Lists", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@3464df78190b43948ba0507ef4287290" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@8a590293a22e46dd9760ec917d122ec1", - "link_text": "", - "title": "6. Dictionaries", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@d2abc5b3db0d43ba90c5d3a25e95e2d5" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@78648402e8bf4738ade97101cc1ba263", - "link_text": "", - "title": "7. Testing and Debugging", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@dd0621fbfe594e789b187a1e4f8406eb" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@c81c3de20ec54c37a04a8b3d1806e82c", - "link_text": "", - "title": "8. Exceptions and Assertions", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@6038a1b2f8a340eb8cdb41c021d62234" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@37cb9a5012e443bbaa776a80afd9c87a", - "link_text": "", - "title": "9. Classes and Inheritance", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@b87e596b827142f09e9664fac3ab0be0" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@54cd6b1bbbbe40f294ac0b5664c03f1e", - "link_text": "", - "title": "10. An Extended Example", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@6bc79b1a29ac46a7857caa53a8e203d0" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@1334ab336b1b4458b5c2972c50e903b2", - "link_text": "", - "title": "11. Computational Complexity", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@be73e5a3ee7847d98805a257189b9fad" - }, - { - "assignment_type": "Finger Exercises", - "complete": false, - "date": "2023-11-01T23:30:00Z", - "date_type": "assignment-due-date", - "description": "", - "learner_has_access": true, - "link": "https://courses.edx.org/courses/course-v1:MITx+6.00.1x+2T2023a/jump_to/block-v1:MITx+6.00.1x+2T2023a+type@sequential+block@a7387dbd3728491c8f834e29a73e0cf4", - "link_text": "", - "title": "12. Searching and Sorting Algorithms", - "extra_info": null, - "first_component_block_id": "block-v1:MITx+6.00.1x+2T2023a+type@video+block@fa7e29b3b95b4a3b963d1c5dfdd4e8f8" - }, - { - "assignment_type": null, - "complete": null, - "date": "2023-11-01T23:30:00Z", - "date_type": "course-end-date", - "description": "After the course ends, the course content will be archived and no longer active.", - "learner_has_access": true, - "link": "", - "link_text": "", - "title": "Course ends", - "extra_info": null, - "first_component_block_id": "" - }, - { - "assignment_type": null, - "complete": null, - "date": "2023-11-03T00:00:00Z", - "date_type": "certificate-available-date", - "description": "Day certificates will become available for passing verified learners.", - "learner_has_access": true, - "link": "", - "link_text": "", - "title": "Certificate Available", - "extra_info": null, - "first_component_block_id": "" - }, - { - "assignment_type": null, - "complete": null, - "date": "2023-11-23T12:34:28Z", - "date_type": "course-expired-date", - "description": "You lose all access to this course, including your progress.", - "learner_has_access": true, - "link": "", - "link_text": "", - "title": "Audit Access Expires", - "extra_info": null, - "first_component_block_id": "" - } - ], - "has_ended": false, - "learner_is_full_access": false, - "user_timezone": null - } - """ } #endif -// swiftlint:enable all diff --git a/Course/Course/Data/Mock/CourseDatesMock.swift b/Course/Course/Data/Mock/CourseDatesMock.swift new file mode 100644 index 000000000..66dcce53c --- /dev/null +++ b/Course/Course/Data/Mock/CourseDatesMock.swift @@ -0,0 +1,616 @@ +// +// CourseDatesMock.swift +// Course +// +// Created by Shafqat Muneer on 1/24/24. +// + +import Foundation + +// Mark - For testing and SwiftUI preview +// swiftlint:disable all +#if DEBUG +extension CourseRepository { + static let courseDatesJSON: String = """ + { + "dates_banner_info": { + "missed_deadlines": true, + "content_type_gating_enabled": false, + "missed_gated_content": false, + "verified_upgrade_link": "https://ecommerce.edx.org/basket/add/?sku=87701A8" + }, + "course_date_blocks": [ + { + "assignment_type": null, + "complete": null, + "date": "2023-09-26T04:41:52Z", + "date_type": "course-start-date", + "description": "", + "learner_has_access": true, + "link": "", + "link_text": "", + "title": "Course starts", + "extra_info": null, + "first_component_block_id": "" + }, + { + "assignment_type": "Module 0", + "complete": true, + "date": "2024-01-11T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@fcc82b68eb944b83962be3fdaa004ac0", + "link_text": "", + "title": "Course Policies and Expectations", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@problem+block@c0e15c6127404c6d9f21b588342067ac" + }, + { + "assignment_type": "Module 0", + "complete": false, + "date": "2024-01-11T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@2a3ed0b868cd429ebdfbd1fe416d9c5a", + "link_text": "", + "title": "Syllabus Quiz", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@987158ea28e24f13855ff6abfb3e63e2" + }, + { + "assignment_type": "Module 1", + "complete": false, + "date": "2024-01-15T20:19:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@84141e77957c45cca6b5039da8ee679d", + "link_text": "", + "title": "1.2 Why Should we Invest in ECD Interventions?", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@bf278c64a4034e88a33e0904fe03e256" + }, + { + "assignment_type": "Module 1", + "complete": false, + "date": "2024-01-16T09:19:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@e2d228da088648479e5fda7478c006b8", + "link_text": "", + "title": "1.6 Closing", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@c97497458d30419ea0d41f6b3688e584" + }, + { + "assignment_type": "Module 2", + "complete": false, + "date": "2024-01-16T21:59:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@ade8a7cfbf724bf5b58902bd27b613b0", + "link_text": "", + "title": "2.2 What can we learn about active ingredients from case studies of ECD interventions around the world?", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@e14daeca668d4c77a6f918b41b486195" + }, + { + "assignment_type": "Module 3", + "complete": false, + "date": "2024-01-17T17:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@57ee148192f046859d6c8a2c3531ab49", + "link_text": "", + "title": "3.1 Delivery and Dose", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@ded1a7f220cb403887d3f894d0171762" + }, + { + "assignment_type": "Module 3", + "complete": false, + "date": "2024-01-22T06:59:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@5be015e209ee4a44b71a10682ddb3eee", + "link_text": "", + "title": "3.2 Demand", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@e4f7b5f3cf9c498cb9d41a665e420ac8" + }, + { + "assignment_type": "Module 3", + "complete": false, + "date": "2024-01-18T17:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@1da589ea848740628d173cba2c6833c8", + "link_text": "", + "title": "3.4 Closure", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@problem+block@eae438670a9844818518d52fff4d8aa4" + }, + { + "assignment_type": "Module 4", + "complete": false, + "date": "2024-01-19T04:19:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@47f62ec6b1744ba5a1350a9f69e28cdc", + "link_text": "", + "title": "4.5 Closure", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@aff4b3b4b4ad4301a6293be7f626287a" + }, + { + "assignment_type": "Module 5", + "complete": false, + "date": "2024-01-27T04:19:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@d28b2ee7d1a4425b872d62bfaeb56128", + "link_text": "", + "title": "5.1 Measuring Progress in ECD Interventions", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@b2f2e069242945728fd7e7fbc401eed5" + }, + { + "assignment_type": "Module 5", + "complete": false, + "date": "2024-01-28T14:59:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@e9875420bbb9405e95c18186718d7529", + "link_text": "", + "title": "5.2 Measuring Progress in ECD Policies", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@0d0111f05f974c1ebaf66e8262f39da4" + }, + { + "assignment_type": "Module 5", + "complete": false, + "date": "2024-01-30T14:59:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@504b2e012ec143d2a45ca5a6d04f8b22", + "link_text": "", + "title": "5.3 Challenges and Data-Driven Solutions", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@2b94eda3fe694579b941a055f3fb963a" + }, + { + "assignment_type": "Module 6", + "complete": false, + "date": "2024-01-31T01:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@3aed70e1f3354a4eb5d21d63ec44618c", + "link_text": "", + "title": "6.2 Examples of Innovations", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@c23ad5f08cd842b49d30a1c8b7ce176a" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-02-01T12:00:00Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239", + "link_text": "", + "title": "Open Response Assessment (Self Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239" + }, + { + "assignment_type": "Module 0", + "complete": false, + "date": "2024-02-03T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@fcc82b68eb944b83962be3fdaa004ac0", + "link_text": "", + "title": "Course Policies and Expectations", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@problem+block@c0e15c6127404c6d9f21b588342067ac" + }, + { + "assignment_type": "Module 0", + "complete": false, + "date": "2024-01-15T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@2a3ed0b868cd429ebdfbd1fe416d9c5a", + "link_text": "", + "title": "Syllabus Quiz", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@987158ea28e24f13855ff6abfb3e63e2" + }, + { + "assignment_type": "Module 1", + "complete": true, + "date": "2024-01-14T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@84141e77957c45cca6b5039da8ee679d", + "link_text": "", + "title": "1.2 Why Should we Invest in ECD Interventions?", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@bf278c64a4034e88a33e0904fe03e256" + }, + { + "assignment_type": "Module 1", + "complete": false, + "date": "2024-01-13T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@d11c050c03de4cb48b80faf011162195", + "link_text": "", + "title": "1.5: What Implementation Features Matter to Implement ECD Interventions?", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@1655c4b7d4934734b401e70babc14d28" + }, + { + "assignment_type": "Module 1", + "complete": true, + "date": "2024-01-12T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@e2d228da088648479e5fda7478c006b8", + "link_text": "", + "title": "1.6 Closing", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@c97497458d30419ea0d41f6b3688e584" + }, + { + "assignment_type": "Module 2", + "complete": true, + "date": "2024-01-11T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@ed3b874a340d4dfa9245a3d2deaee5da", + "link_text": "", + "title": "2.1 What are the active ingredients of effective ECD interventions?", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@286da9b199c24445b1685040ded994d1" + }, + { + "assignment_type": "Module 2", + "complete": false, + "date": "2024-01-10T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@ade8a7cfbf724bf5b58902bd27b613b0", + "link_text": "", + "title": "2.2 What can we learn about active ingredients from case studies of ECD interventions around the world?", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@e14daeca668d4c77a6f918b41b486195" + }, + { + "assignment_type": "Module 3", + "complete": false, + "date": "2024-01-09T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@57ee148192f046859d6c8a2c3531ab49", + "link_text": "", + "title": "3.1 Delivery and Dose", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@ded1a7f220cb403887d3f894d0171762" + }, + { + "assignment_type": "Module 3", + "complete": false, + "date": "2024-01-08T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@5be015e209ee4a44b71a10682ddb3eee", + "link_text": "", + "title": "3.2 Demand", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@e4f7b5f3cf9c498cb9d41a665e420ac8" + }, + { + "assignment_type": "Module 3", + "complete": false, + "date": "2020-01-07T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@1da589ea848740628d173cba2c6833c8", + "link_text": "", + "title": "3.4 Closure", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@problem+block@eae438670a9844818518d52fff4d8aa4" + }, + { + "assignment_type": "Module 4", + "complete": false, + "date": "2024-01-16T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@47f62ec6b1744ba5a1350a9f69e28cdc", + "link_text": "", + "title": "4.5 Closure", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@aff4b3b4b4ad4301a6293be7f626287a" + }, + { + "assignment_type": "Module 5", + "complete": false, + "date": "2024-01-17T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@d28b2ee7d1a4425b872d62bfaeb56128", + "link_text": "", + "title": "5.1 Measuring Progress in ECD Interventions", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@b2f2e069242945728fd7e7fbc401eed5" + }, + { + "assignment_type": "Module 5", + "complete": false, + "date": "2024-01-18T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@e9875420bbb9405e95c18186718d7529", + "link_text": "", + "title": "5.2 Measuring Progress in ECD Policies", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@0d0111f05f974c1ebaf66e8262f39da4" + }, + { + "assignment_type": "Module 5", + "complete": false, + "date": "2024-01-19T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@504b2e012ec143d2a45ca5a6d04f8b22", + "link_text": "", + "title": "5.3 Challenges and Data-Driven Solutions", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@2b94eda3fe694579b941a055f3fb963a" + }, + { + "assignment_type": "Module 6", + "complete": false, + "date": "2024-01-20T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@3aed70e1f3354a4eb5d21d63ec44618c", + "link_text": "", + "title": "6.2 Examples of Innovations", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@c23ad5f08cd842b49d30a1c8b7ce176a" + }, + { + "assignment_type": "Module 7", + "complete": false, + "date": "2024-01-23T15:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/course-v1:HarvardX+ECD01+1T2023/jump_to/block-v1:HarvardX+ECD01+1T2023+type@sequential+block@1aea57e69f2a40f8a25c8edd389e3608", + "link_text": "", + "title": "7.0 Course Summary", + "extra_info": null, + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@html+block@625c5399993344fdaa852a967be6f690" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-01-23T12:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239", + "link_text": "", + "title": "Open Response Assessment (Submission)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-01-23T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103", + "link_text": "", + "title": "Practice Creating a Theory of Change (Submission)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-01-24T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9", + "link_text": "", + "title": "Activity 1: Logic Model (Submission)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-01-25T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239", + "link_text": "", + "title": "Open Response Assessment (Peer Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-02-26T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239", + "link_text": "", + "title": "Open Response Assessment (Self Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@217e302038da4a638cc5c0eb8aa6a239" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-03-27T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103", + "link_text": "", + "title": "Practice Creating a Theory of Change (Peer Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103" + }, + { + "assignment_type": null, + "complete": false, + "date": "2024-04-28T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103", + "link_text": "", + "title": "Practice Creating a Theory of Change (Self Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103" + }, + { + "assignment_type": null, + "complete": false, + "date": "2025-01-29T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": false, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9", + "link_text": "", + "title": "Activity 1: Logic Model (Peer Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9" + }, + { + "assignment_type": null, + "complete": false, + "date": "2026-01-30T09:39:37.467099Z", + "date_type": "assignment-due-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9", + "link_text": "", + "title": "Activity 1: Logic Model (Self Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9" + }, + { + "assignment_type": null, + "complete": false, + "date": "2027-02-21T12:00:00Z", + "date_type": "verified-upgrade-deadline", + "description": "You must successfully complete verification before this date to qualify for a Verified Certificate.", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103", + "link_text": "", + "title": "Practice Creating a Theory of Change (Peer Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103" + }, + { + "assignment_type": null, + "complete": false, + "date": "2027-02-21T12:00:00Z", + "date_type": "verified-upgrade-deadline", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103", + "link_text": "", + "title": "Practice Creating a Theory of Change (Self Assessment)", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@c3880270b8c7471287dd72a4b4b7e103" + }, + { + "assignment_type": null, + "complete": false, + "date": "2027-02-21T12:00:00Z", + "date_type": "certificate-available-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9", + "link_text": "", + "title": "Certificate", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9" + }, + { + "assignment_type": null, + "complete": false, + "date": "2027-02-21T12:00:00Z", + "date_type": "course-expired-date", + "description": "", + "learner_has_access": true, + "link": "https://courses.edx.org/courses/block-v1:HarvardX+ECD01+1T2023+type@course+block@course/jump_to/block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9", + "link_text": "", + "title": "Course Access Expires", + "extra_info": "This Open Response Assessment's due dates are set by your instructor and can't be shifted.", + "first_component_block_id": "block-v1:HarvardX+ECD01+1T2023+type@openassessment+block@7f7924ba31fe4dfba5aee3304fd56cc9" + }, + { + "assignment_type": null, + "complete": null, + "date": "2027-02-22T12:00:00Z", + "date_type": "course-end-date", + "description": "After this date, the course will be archived, which means you can review the course content but can no longer participate in graded assignments or work towards earning a certificate.", + "learner_has_access": true, + "link": "", + "link_text": "", + "title": "Course ends", + "extra_info": null, + "first_component_block_id": "" + } + ], + "has_ended": false, + "learner_is_full_access": true, + "user_timezone": null + } + """ +} +#endif +// swiftlint:enable all diff --git a/Course/Course/Data/Mock/CourseStructureMock.swift b/Course/Course/Data/Mock/CourseStructureMock.swift new file mode 100644 index 000000000..fe2f39e37 --- /dev/null +++ b/Course/Course/Data/Mock/CourseStructureMock.swift @@ -0,0 +1,637 @@ +// +// CourseStructureMock.swift +// Course +// +// Created by Shafqat Muneer on 1/24/24. +// + +import Foundation + +// Mark - For testing and SwiftUI preview +// swiftlint:disable all +#if DEBUG +extension CourseRepository { + static let courseStructureJson: String = """ + {"root": "block-v1:QA+comparison+2022+type@course+block@course", + "blocks": { + "block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78": { + "id": "block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", + "block_id": "be1704c576284ba39753c6f0ea4a4c78", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", + "type": "comparison", + "display_name": "Comparison", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296": { + "id": "block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", + "block_id": "93acc543871e4c73bc20a72a64e93296", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", + "type": "problem", + "display_name": "Dropdown with Hints and Feedback", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b": { + "id": "block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", + "block_id": "06c17035106e48328ebcd042babcf47b", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", + "type": "comparison", + "display_name": "Comparison", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58": { + "id": "block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", + "block_id": "c19e41b61db14efe9c45f1354332ae58", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", + "type": "problem", + "display_name": "Text Input with Hints and Feedback", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb": { + "id": "block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", + "block_id": "0d96732f577b4ff68799faf8235d1bfb", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", + "type": "problem", + "display_name": "Numerical Input with Hints and Feedback", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96": { + "id": "block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", + "block_id": "dd2e22fdf0724bd88c8b2e6b68dedd96", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", + "type": "problem", + "display_name": "Blank Common Problem", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd": { + "id": "block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", + "block_id": "d1e091aa305741c5bedfafed0d269efd", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", + "type": "problem", + "display_name": "Blank Common Problem", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7": { + "id": "block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7", + "block_id": "23e10dea806345b19b77997b4fc0eea7", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7", + "type": "comparison", + "display_name": "Comparison", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904": { + "id": "block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904", + "block_id": "29e7eddbe8964770896e4036748c9904", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904", + "type": "vertical", + "display_name": "Unit", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@comparison+block@be1704c576284ba39753c6f0ea4a4c78", + "block-v1:QA+comparison+2022+type@problem+block@93acc543871e4c73bc20a72a64e93296", + "block-v1:QA+comparison+2022+type@comparison+block@06c17035106e48328ebcd042babcf47b", + "block-v1:QA+comparison+2022+type@problem+block@c19e41b61db14efe9c45f1354332ae58", + "block-v1:QA+comparison+2022+type@problem+block@0d96732f577b4ff68799faf8235d1bfb", + "block-v1:QA+comparison+2022+type@problem+block@dd2e22fdf0724bd88c8b2e6b68dedd96", + "block-v1:QA+comparison+2022+type@problem+block@d1e091aa305741c5bedfafed0d269efd", + "block-v1:QA+comparison+2022+type@comparison+block@23e10dea806345b19b77997b4fc0eea7" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6": { + "id": "block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", + "block_id": "f468bb5c6e8641179e523c7fcec4e6d6", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", + "type": "sequential", + "display_name": "Subsection", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@vertical+block@29e7eddbe8964770896e4036748c9904" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234": { + "id": "block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", + "block_id": "eaf91d8fc70547339402043ba1a1c234", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", + "type": "problem", + "display_name": "Dropdown with Hints and Feedback", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751": { + "id": "block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", + "block_id": "fac531c3f1f3400cb8e3b97eb2c3d751", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", + "type": "comparison", + "display_name": "Comparison", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de": { + "id": "block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de", + "block_id": "74a1074024fe401ea305534f2241e5de", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de", + "type": "html", + "display_name": "Raw HTML", + "graded": false, + "student_view_data": { + "last_modified": "2023-05-04T19:08:07Z", + "html_data": "https://s3.eu-central-1.amazonaws.com/vso-dev-edx-sorage/htmlxblock/QA/comparison/html/74a1074024fe401ea305534f2241e5de/content_html.zip", + "size": 576, + "index_page": "index.html", + "icon_class": "other" + }, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca": { + "id": "block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca", + "block_id": "e5b2e105f4f947c5b76fb12c35da1eca", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca", + "type": "vertical", + "display_name": "Unit", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@problem+block@eaf91d8fc70547339402043ba1a1c234", + "block-v1:QA+comparison+2022+type@comparison+block@fac531c3f1f3400cb8e3b97eb2c3d751", + "block-v1:QA+comparison+2022+type@html+block@74a1074024fe401ea305534f2241e5de" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2": { + "id": "block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2", + "block_id": "d37cb0c5c2d24ddaacf3494760a055f2", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2", + "type": "sequential", + "display_name": "Another one subsection", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@vertical+block@e5b2e105f4f947c5b76fb12c35da1eca" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846": { + "id": "block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", + "block_id": "abecaefe203c4c93b441d16cea3b7846", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", + "type": "chapter", + "display_name": "Section", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@sequential+block@f468bb5c6e8641179e523c7fcec4e6d6", + "block-v1:QA+comparison+2022+type@sequential+block@d37cb0c5c2d24ddaacf3494760a055f2" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de": { + "id": "block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", + "block_id": "a0c3ac29daab425f92a34b34eb2af9de", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", + "type": "pdf", + "display_name": "PDF title", + "graded": false, + "student_view_data": { + "last_modified": "2023-04-26T08:43:45Z", + "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", + }, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9": { + "id": "block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", + "block_id": "bcd1b0f3015b4d3696b12f65a5d682f9", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", + "type": "pdf", + "display_name": "PDF", + "graded": false, + "student_view_data": { + "last_modified": "2023-04-26T08:43:45Z", + "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", + }, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59": { + "id": "block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", + "block_id": "67d805daade34bd4b6ace607e6d48f59", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", + "type": "pdf", + "display_name": "PDF", + "graded": false, + "student_view_data": { + "last_modified": "2023-04-26T08:43:45Z", + "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", + }, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974": { + "id": "block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", + "block_id": "828606a51f4e44198e92f86a45be7974", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", + "type": "pdf", + "display_name": "PDF", + "graded": false, + "student_view_data": { + "last_modified": "2023-04-26T08:43:45Z", + "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", + }, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee": { + "id": "block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee", + "block_id": "8646c3bc2184467b86e5ef01ecd452ee", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee", + "type": "pdf", + "display_name": "PDF", + "graded": false, + "student_view_data": { + "last_modified": "2023-04-26T08:43:45Z", + "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", + }, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1": { + "id": "block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", + "block_id": "e2faa0e62223489e91a41700865c5fc1", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", + "type": "vertical", + "display_name": "Unit", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@pdf+block@a0c3ac29daab425f92a34b34eb2af9de", + "block-v1:QA+comparison+2022+type@pdf+block@bcd1b0f3015b4d3696b12f65a5d682f9", + "block-v1:QA+comparison+2022+type@pdf+block@67d805daade34bd4b6ace607e6d48f59", + "block-v1:QA+comparison+2022+type@pdf+block@828606a51f4e44198e92f86a45be7974", + "block-v1:QA+comparison+2022+type@pdf+block@8646c3bc2184467b86e5ef01ecd452ee" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52": { + "id": "block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52", + "block_id": "0c5e89fa6d7a4fac8f7b26f2ca0bbe52", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52", + "type": "problem", + "display_name": "Checkboxes with Hints and Feedback", + "graded": false, + "student_view_multi_device": true, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4": { + "id": "block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", + "block_id": "8ba437d8b20d416d91a2d362b0c940a4", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", + "type": "vertical", + "display_name": "Unit", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@problem+block@0c5e89fa6d7a4fac8f7b26f2ca0bbe52" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d": { + "id": "block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d", + "block_id": "021f70794f7349998e190b060260b70d", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d", + "type": "pdf", + "display_name": "PDF", + "graded": false, + "student_view_data": { + "last_modified": "2023-04-26T08:43:45Z", + "url": "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf", + }, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ], + "completion": 0.0 + }, + "block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1": { + "id": "block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1", + "block_id": "2c344115d3554ac58c140ec86e591aa1", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1", + "type": "vertical", + "display_name": "Unit", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@pdf+block@021f70794f7349998e190b060260b70d" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe": { + "id": "block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe", + "block_id": "6c9c6ba663b54c0eb9cbdcd0c6b4bebe", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe", + "type": "sequential", + "display_name": "Subsection", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@vertical+block@e2faa0e62223489e91a41700865c5fc1", + "block-v1:QA+comparison+2022+type@vertical+block@8ba437d8b20d416d91a2d362b0c940a4", + "block-v1:QA+comparison+2022+type@vertical+block@2c344115d3554ac58c140ec86e591aa1" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7": { + "id": "block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", + "block_id": "d5a4f1f2f5314288aae400c270fb03f7", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", + "type": "chapter", + "display_name": "PDF", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@sequential+block@6c9c6ba663b54c0eb9cbdcd0c6b4bebe" + ], + "completion": 0 + }, + "block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf": { + "id": "block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf", + "block_id": "7ab45affb80f4846a60648ec6aff9fbf", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf", + "type": "chapter", + "display_name": "Section", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + + ] + }, + "block-v1:QA+comparison+2022+type@course+block@course": { + "id": "block-v1:QA+comparison+2022+type@course+block@course", + "block_id": "course", + "lms_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@course+block@course", + "legacy_web_url": "https://lms.lilac-vso-dev.raccoongang.com/courses/course-v1:QA+comparison+2022/jump_to/block-v1:QA+comparison+2022+type@course+block@course?experience=legacy", + "student_view_url": "https://lms.lilac-vso-dev.raccoongang.com/xblock/block-v1:QA+comparison+2022+type@course+block@course", + "type": "course", + "display_name": "Comparison xblock test coursre", + "graded": false, + "student_view_multi_device": false, + "block_counts": { + "video": 0 + }, + "descendants": [ + "block-v1:QA+comparison+2022+type@chapter+block@abecaefe203c4c93b441d16cea3b7846", + "block-v1:QA+comparison+2022+type@chapter+block@d5a4f1f2f5314288aae400c270fb03f7", + "block-v1:QA+comparison+2022+type@chapter+block@7ab45affb80f4846a60648ec6aff9fbf" + ], + "completion": 0 + } + }, + "id": "course-v1:QA+comparison+2022", + "name": "Comparison xblock test coursre", + "number": "comparison", + "org": "QA", + "start": "2022-01-01T00:00:00Z", + "start_display": "01 january 2022 р.", + "start_type": "timestamp", + "end": null, + "courseware_access": { + "has_access": true, + "error_code": null, + "developer_message": null, + "user_message": null, + "additional_context_user_message": null, + "user_fragment": null + }, + "media": { + "image": { + "raw": "/asset-v1:QA+comparison+2022+type@asset+block@images_course_image.jpg", + "small": "/asset-v1:QA+comparison+2022+type@asset+block@images_course_image.jpg", + "large": "/asset-v1:QA+comparison+2022+type@asset+block@images_course_image.jpg" + } + }, + "certificate": { + + }, + "is_self_paced": false + } + """ +} +#endif +// swiftlint:enable all diff --git a/Course/Course/Domain/Model/CourseDates.swift b/Course/Course/Domain/Model/CourseDates.swift index 0909610d4..a796a944b 100644 --- a/Course/Course/Domain/Model/CourseDates.swift +++ b/Course/Course/Domain/Model/CourseDates.swift @@ -14,41 +14,46 @@ public struct CourseDates { let hasEnded, learnerIsFullAccess: Bool let userTimezone: String? - var sortedDateToCourseDateBlockDict: [Date: [CourseDateBlock]] { - var dateToCourseDateBlockDict: [Date: [CourseDateBlock]] = [:] - var hasToday = false - let today = Date.today - - let calendar = Calendar.current - let todayComponents = calendar.dateComponents([.year, .month, .day], from: .today) + var statusDatesBlocks: [CompletionStatus: [Date: [CourseDateBlock]]] { + var statusDatesBlocks: [CompletionStatus: [Date: [CourseDateBlock]]] = [:] + var statusBlocks: [CompletionStatus: [CourseDateBlock]] = [:] for block in courseDateBlocks { let date = block.date - let dateComponents = calendar.dateComponents([.year, .month, .day], from: date) - - if dateComponents == todayComponents { - hasToday = true + switch true { + case block.complete ?? false || block.blockStatus == .courseStartDate: + statusBlocks[.completed, default: []].append(block) + case date.isInPast: + statusBlocks[.pastDue, default: []].append(block) + case date.isToday: + if date < Date() { + statusBlocks[.pastDue, default: []].append(block) + } else { + statusBlocks[.today, default: []].append(block) + } + case date.isThisWeek: + statusBlocks[.thisWeek, default: []].append(block) + case date.isNextWeek: + statusBlocks[.nextWeek, default: []].append(block) + case date.isUpcoming: + statusBlocks[.upcoming, default: []].append(block) + default: + statusBlocks[.upcoming, default: []].append(block) } - - dateToCourseDateBlockDict[date, default: []].append(block) } - if !hasToday { - let todayBlock = CourseDateBlock( - assignmentType: nil, - complete: nil, - date: today, - dateType: "", - description: "", - learnerHasAccess: true, - link: "", linkText: nil, - title: CoreLocalization.CourseDates.today, - extraInfo: nil, - firstComponentBlockID: "uniqueIDForToday") - dateToCourseDateBlockDict[today] = [todayBlock] + for status in statusBlocks.keys { + let courseDateBlocks = statusBlocks[status] + var dateToCourseDateBlockDict: [Date: [CourseDateBlock]] = [:] + + for block in courseDateBlocks ?? [] { + let date = block.date + dateToCourseDateBlockDict[date, default: []].append(block) + } + statusDatesBlocks[status] = dateToCourseDateBlockDict } - return dateToCourseDateBlockDict + return statusDatesBlocks } } @@ -80,6 +85,29 @@ extension Date { var isInFuture: Bool { return Date.compare(self, to: .today) == .orderedDescending } + + var isThisWeek: Bool { + // Items due within the next 7 days (7*24 hours from now) + let calendar = Calendar.current + let nextDay = calendar.date(byAdding: .day, value: 1, to: .today) ?? .distantPast + let nextSeventhDay = calendar.date(byAdding: .day, value: 8, to: .today) ?? .distantPast + return (nextDay...nextSeventhDay).contains(self) + } + + var isNextWeek: Bool { + // Items due within the next 14 days (14*24 hours from now) + let calendar = Calendar.current + let nextEighthDay = calendar.date(byAdding: .day, value: 8, to: .today) ?? .distantPast + let nextFourteenthDay = calendar.date(byAdding: .day, value: 15, to: .today) ?? .distantPast + return (nextEighthDay...nextFourteenthDay).contains(self) + } + + var isUpcoming: Bool { + // Items due after the next 14 days (14*24 hours from now) + let calendar = Calendar.current + let nextFourteenthDay = calendar.date(byAdding: .day, value: 14, to: .today) ?? .distantPast + return Date.compare(self, to: nextFourteenthDay) == .orderedDescending + } } public struct CourseDateBlock: Identifiable { @@ -116,6 +144,18 @@ public struct CourseDateBlock: Identifiable { return date.isInFuture } + var isThisWeek: Bool { + return date.isThisWeek + } + + var isNextWeek: Bool { + return date.isNextWeek + } + + var isUpcoming: Bool { + return date.isUpcoming + } + var isAssignment: Bool { return BlockStatus.status(of: dateType) == .assignment } @@ -167,6 +207,29 @@ public struct CourseDateBlock: Identifiable { return BlockStatus.status(of: dateType) } + + var blockImage: ImageAsset? { + if !learnerHasAccess { + return CoreAssets.lockIcon + } + + if isAssignment { + return CoreAssets.assignmentIcon + } + + switch blockStatus { + case .courseStartDate, .courseEndDate: + return CoreAssets.schoolCapIcon + case .verifiedUpgradeDeadline, .verificationDeadlineDate: + return CoreAssets.calendarIcon + case .courseExpiredDate: + return CoreAssets.lockWithWatchIcon + case .certificateAvailbleDate: + return CoreAssets.certificateIcon + default: + return CoreAssets.calendarIcon + } + } } public struct DatesBannerInfo { @@ -202,3 +265,12 @@ public enum BlockStatus { } } } + +public enum CompletionStatus: String { + case completed = "Completed" + case pastDue = "Past Due" + case today = "Today" + case thisWeek = "This Week" + case nextWeek = "Next Week" + case upcoming = "Upcoming" +} diff --git a/Course/Course/Presentation/Dates/CourseDatesView.swift b/Course/Course/Presentation/Dates/CourseDatesView.swift index 36bb79b9f..8110c350e 100644 --- a/Course/Course/Presentation/Dates/CourseDatesView.swift +++ b/Course/Course/Presentation/Dates/CourseDatesView.swift @@ -75,46 +75,17 @@ struct Line: Shape { } struct TimeLineView: View { - let block: CourseDateBlock - let date: Date - let firstDate: Date? - let lastDate: Date? - let allHaveSameStatus: Bool + let status: CompletionStatus var body: some View { ZStack(alignment: .top) { VStack { Line() - .stroke(style: StrokeStyle(lineWidth: 1)) - .frame(maxHeight: lastDate == date ? 10 : .infinity, alignment: .top) - .padding(.top, firstDate == date && lastDate != date ? 10 : 0) - - if lastDate == date { - Spacer() - } + .stroke(style: StrokeStyle(lineWidth: 8, lineCap: .round, lineJoin: .round)) + .frame(maxHeight: .infinity, alignment: .top) + .padding(.top, 0) + .foregroundColor(status.foregroundColor) } - - Circle() - .frame(width: date.isToday ? 12 : 8, height: date.isToday ? 12 : 8) - .foregroundColor({ - if date.isToday { - return Theme.Colors.warning - } else if date.isInPast { - switch block.blockStatus { - case .completed: return allHaveSameStatus ? Color.white : Color.gray - case .courseStartDate: return Color.white - case .verifiedOnly: return Color.black - case .pastDue: return Color.gray - default: return Color.gray - } - } else if date.isInFuture { - return Color.black - } else { - return Color.white - } - }()) - .overlay(Circle().stroke(Color.black, lineWidth: 1)) - .padding(.top, 5) } .frame(width: 16) } @@ -122,87 +93,197 @@ struct TimeLineView: View { struct CourseDateListView: View { @ObservedObject var viewModel: CourseDatesViewModel + @State private var isExpanded = false var courseDates: CourseDates var body: some View { VStack { ScrollView { VStack(alignment: .leading, spacing: 0) { - ForEach(viewModel.sortedDates, id: \.self) { date in - let blocks = courseDates.sortedDateToCourseDateBlockDict[date]! - let block = blocks[0] - - HStack(alignment: .center) { - let ignoredStatuses: [BlockStatus] = [.courseStartDate, .courseEndDate] - let allHaveSameStatus = blocks - .filter { !ignoredStatuses.contains($0.blockStatus) } - .allSatisfy { $0.blockStatus == block.blockStatus } - - TimeLineView(block: block, date: date, - firstDate: viewModel.sortedDates.first, - lastDate: viewModel.sortedDates.last, - allHaveSameStatus: allHaveSameStatus) - - BlockStatusView(viewModel: viewModel, + ForEach(Array(viewModel.sortedStatuses), id: \.self) { status in + let courseDateBlockDict = courseDates.statusDatesBlocks[status]! + if status == .completed { + CompletedBlocks( + isExpanded: $isExpanded, + courseDateBlockDict: courseDateBlockDict, + viewModel: viewModel + ) + } else { + Text(status.rawValue) + .font(Theme.Fonts.titleSmall) + .padding(.top, 10) + .padding(.bottom, 10) + HStack { + TimeLineView(status: status) + .padding(.bottom, 15) + VStack(alignment: .leading) { + ForEach(courseDateBlockDict.keys.sorted(), id: \.self) { date in + let blocks = courseDateBlockDict[date]! + let block = blocks[0] + Text(block.formattedDate) + .font(Theme.Fonts.labelMedium) + .foregroundStyle(Theme.Colors.textPrimary) + BlockStatusView( + viewModel: viewModel, block: block, - allHaveSameStatus: allHaveSameStatus, - blocks: blocks) - - Spacer() + blocks: blocks + ) + } + } + } } } } + .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 16) + .padding(.vertical, 5) } .frame(maxWidth: .infinity, maxHeight: .infinity) } } } +struct CompletedBlocks: View { + @Binding var isExpanded: Bool + let courseDateBlockDict: [Date: [CourseDateBlock]] + let viewModel: CourseDatesViewModel + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + // Toggle button to expand/collapse the cell + Button(action: { + withAnimation { + isExpanded.toggle() + } + }) { + HStack { + VStack(alignment: .leading) { + Text(CompletionStatus.completed.rawValue) + .font(Theme.Fonts.titleSmall) + .foregroundColor(Theme.Colors.textPrimary) + + if !isExpanded { + let totalCount = courseDateBlockDict.values.reduce(0) { $0 + $1.count } + let itemsHidden = totalCount == 1 ? + CoreLocalization.CourseDates.itemHidden : + CoreLocalization.CourseDates.itemsHidden + Text("\(totalCount) \(itemsHidden)") + .font(Theme.Fonts.labelMedium) + .foregroundColor(Theme.Colors.textPrimary) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.leading, 16) + .padding(.vertical, 8) + + Image(systemName: "chevron.right") + .labelStyle(.iconOnly) + .dropdownArrowRotationAnimation(value: isExpanded) + .foregroundColor(Theme.Colors.textPrimary) + .padding() + } + } + + // Your expandable content goes here + if isExpanded { + VStack(alignment: .leading) { + ForEach(courseDateBlockDict.keys.sorted(), id: \.self) { date in + let blocks = courseDateBlockDict[date]! + let block = blocks[0] + + Spacer() + Text(block.formattedDate) + .font(Theme.Fonts.labelMedium) + .foregroundStyle(Theme.Colors.textPrimary) + + ForEach(blocks) { block in + HStack(alignment: .top) { + block.blockImage?.swiftUIImage + .foregroundColor(Theme.Colors.textPrimary) + StyleBlock(block: block, viewModel: viewModel) + .padding(.bottom, 15) + Spacer() + if block.canShowLink && !block.firstComponentBlockID.isEmpty { + Image(systemName: "chevron.right") + .resizable() + .scaledToFit() + .frame(width: 6.55, height: 11.15) + .labelStyle(.iconOnly) + .foregroundColor(Theme.Colors.textPrimary) + } + } + .padding(.trailing, 15) + } + } + } + .padding(.bottom, 15) + .padding(.leading, 16) + } + + } + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Theme.Colors.datesSectionStroke, lineWidth: 2) + ) + .background(Theme.Colors.datesSectionBackground) + } +} + struct BlockStatusView: View { let viewModel: CourseDatesViewModel let block: CourseDateBlock - let allHaveSameStatus: Bool let blocks: [CourseDateBlock] var body: some View { VStack(alignment: .leading) { - HStack { - Text(block.formattedDate) - .font(Theme.Fonts.bodyLarge) - .bold() - - if block.isToday { - Text(CoreLocalization.CourseDates.today) - .font(Theme.Fonts.bodySmall) - .foregroundColor(Color.black) - .padding(EdgeInsets(top: 2, leading: 6, bottom: 2, trailing: 8)) - .background(Theme.Colors.warning) - .cornerRadius(5) - } - - if allHaveSameStatus { - let lockImageText = block.isVerifiedOnly ? Text(Image(systemName: "lock.fill")) : Text("") - Text("\(lockImageText) \(block.blockStatus.title)") - .font(Theme.Fonts.bodySmall) - .foregroundColor(block.blockStatus.foregroundColor) - .padding(EdgeInsets(top: 2, leading: 6, bottom: 2, trailing: 8)) - .background(block.blockStatus.backgroundColor) - .cornerRadius(5) + ForEach(blocks) { block in + HStack(alignment: .top) { + block.blockImage?.swiftUIImage + .foregroundColor(Theme.Colors.textPrimary) + StyleBlock(block: block, viewModel: viewModel) + .padding(.bottom, 15) + Spacer() + if block.canShowLink && !block.firstComponentBlockID.isEmpty { + Image(systemName: "chevron.right") + .resizable() + .scaledToFit() + .frame(width: 6.55, height: 11.15) + .labelStyle(.iconOnly) + .foregroundColor(Theme.Colors.textPrimary) + } } + .padding(.trailing, 15) } + .padding(.top, 0.2) + } + } + + func applyStyle(string: String, forgroundColor: Color, backgroundColor: Color) -> AttributedString { + var attributedString = AttributedString(string) + attributedString.font = Theme.Fonts.bodySmall + attributedString.foregroundColor = forgroundColor + attributedString.backgroundColor = backgroundColor + return attributedString + } +} - ForEach(blocks) { block in - styleBlock(block: block, allHaveSameStatus: allHaveSameStatus) +struct StyleBlock: View { + let block: CourseDateBlock + let viewModel: CourseDatesViewModel + + var body: some View { + VStack(alignment: .leading) { + styleBlock(block: block) + if !block.description.isEmpty { + Text(block.description) + .font(Theme.Fonts.labelMedium) + .foregroundStyle(Theme.Colors.thisWeekTimelineColor) + .fixedSize(horizontal: false, vertical: true) } - .padding(.top, 0.2) } - .padding(.vertical, 0) - .padding(.leading, 5) - .padding(.bottom, 10) } - func styleBlock(block: CourseDateBlock, allHaveSameStatus: Bool) -> some View { + func styleBlock(block: CourseDateBlock) -> some View { var attributedString = AttributedString("") if let prefix = block.assignmentType, !prefix.isEmpty { @@ -211,44 +292,28 @@ struct BlockStatusView: View { attributedString += styleTitle(block: block) - if !allHaveSameStatus { - attributedString.appendSpaces(2) - attributedString += applyStyle( - string: block.blockStatus.title, - forgroundColor: block.blockStatus.foregroundColor, - backgroundColor: block.blockStatus.backgroundColor) - } - return Text(attributedString) - .font(Theme.Fonts.bodyMedium) - .foregroundColor({ + .font(Theme.Fonts.titleSmall) + .lineLimit(1) + .foregroundStyle({ if block.isAssignment { - return block.isAvailable ? Color.black : Color.gray.opacity(0.6) + return block.isAvailable ? Theme.Colors.textPrimary : Color.gray.opacity(0.6) } else { - return Color.black + return Theme.Colors.textPrimary } }()) .onTapGesture { - Task { - await viewModel.showCourseDetails(componentID: block.firstComponentBlockID) + if block.canShowLink && !block.firstComponentBlockID.isEmpty { + Task { + await viewModel.showCourseDetails(componentID: block.firstComponentBlockID) + } } } } func styleTitle(block: CourseDateBlock) -> AttributedString { var attributedString = AttributedString(block.title) - attributedString.font = Theme.Fonts.bodyMedium - if block.canShowLink && !block.firstComponentBlockID.isEmpty { - attributedString.underlineStyle = .single - } - return attributedString - } - - func applyStyle(string: String, forgroundColor: Color, backgroundColor: Color) -> AttributedString { - var attributedString = AttributedString(string) - attributedString.font = Theme.Fonts.bodySmall - attributedString.foregroundColor = forgroundColor - attributedString.backgroundColor = backgroundColor + attributedString.font = Theme.Fonts.titleSmall return attributedString } } @@ -286,6 +351,19 @@ fileprivate extension BlockStatus { } } +fileprivate extension CompletionStatus { + var foregroundColor: Color { + switch self { + case .pastDue: return Theme.Colors.pastDueTimelineColor + case .today: return Theme.Colors.todayTimelineColor + case .thisWeek: return Theme.Colors.thisWeekTimelineColor + case .nextWeek: return Theme.Colors.nextWeekTimelineColor + case .upcoming: return Theme.Colors.upcomingTimelineColor + default: return Color.white.opacity(0) + } + } +} + fileprivate extension AttributedString { mutating func appendSpaces(_ count: Int = 1) { self += AttributedString(String(repeating: " ", count: count)) diff --git a/Course/Course/Presentation/Dates/CourseDatesViewModel.swift b/Course/Course/Presentation/Dates/CourseDatesViewModel.swift index 75851d85e..3ee731bd0 100644 --- a/Course/Course/Presentation/Dates/CourseDatesViewModel.swift +++ b/Course/Course/Presentation/Dates/CourseDatesViewModel.swift @@ -43,12 +43,20 @@ public class CourseDatesViewModel: ObservableObject { self.courseID = courseID } - var sortedDates: [Date] { - courseDates?.sortedDateToCourseDateBlockDict.keys.sorted() ?? [] - } - - func blocks(for date: Date) -> [CourseDateBlock] { - courseDates?.sortedDateToCourseDateBlockDict[date] ?? [] + var sortedStatuses: [CompletionStatus] { + let desiredSequence = [ + CompletionStatus.completed, + CompletionStatus.pastDue, + CompletionStatus.today, + CompletionStatus.thisWeek, + CompletionStatus.nextWeek, + CompletionStatus.upcoming + ] + + // Filter out keys that don't exist in the dictionary + let filteredKeys = desiredSequence.filter { + courseDates?.statusDatesBlocks.keys.contains($0) ?? false } + return filteredKeys } @MainActor diff --git a/Course/Course/Presentation/Outline/CourseOutlineView.swift b/Course/Course/Presentation/Outline/CourseOutlineView.swift index 04ee19406..81d566c03 100644 --- a/Course/Course/Presentation/Outline/CourseOutlineView.swift +++ b/Course/Course/Presentation/Outline/CourseOutlineView.swift @@ -81,7 +81,6 @@ public struct CourseOutlineView: View { sequentialIndex: continueWith.sequentialIndex ) } -//"Saeed" } } diff --git a/Course/CourseTests/Presentation/Unit/CourseDateViewModelTests.swift b/Course/CourseTests/Presentation/Unit/CourseDateViewModelTests.swift index 690b93325..797c27440 100644 --- a/Course/CourseTests/Presentation/Unit/CourseDateViewModelTests.swift +++ b/Course/CourseTests/Presentation/Unit/CourseDateViewModelTests.swift @@ -114,7 +114,7 @@ final class CourseDateViewModelTests: XCTestCase { let block2 = CourseDateBlock( assignmentType: nil, - complete: nil, + complete: true, date: Date.today, dateType: "event", description: "", @@ -139,15 +139,15 @@ final class CourseDateViewModelTests: XCTestCase { userTimezone: nil ) - let sortedDict = courseDates.sortedDateToCourseDateBlockDict + let sortedDict = courseDates.statusDatesBlocks[.completed] - XCTAssertEqual(sortedDict.keys.sorted().first, Date.today) + XCTAssertEqual(sortedDict?.keys.sorted().first, Date.today) } func testMultipleBlocksForSameDate() { let block1 = CourseDateBlock( assignmentType: nil, - complete: nil, + complete: true, date: Date.today, dateType: "event", description: "", @@ -161,7 +161,7 @@ final class CourseDateViewModelTests: XCTestCase { let block2 = CourseDateBlock( assignmentType: nil, - complete: nil, + complete: true, date: Date.today, dateType: "event", description: "", @@ -186,8 +186,8 @@ final class CourseDateViewModelTests: XCTestCase { userTimezone: nil ) - let sortedDict = courseDates.sortedDateToCourseDateBlockDict - XCTAssertEqual(sortedDict[block1.date]?.count, 2, "There should be two blocks for the given date.") + let sortedDict = courseDates.statusDatesBlocks[.completed] + XCTAssertEqual(sortedDict?[block1.date]?.count, 2, "There should be two blocks for the given date.") } func testBlockStatusForAssignmentType() { diff --git a/Podfile.lock b/Podfile.lock index d9169ef4b..79fbd8c97 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -182,4 +182,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 544edab2f9ecc4ac18973fb8865f1d0613ec8a28 -COCOAPODS: 1.13.0 +COCOAPODS: 1.14.3 diff --git a/Theme/Theme/Assets.xcassets/CourseDates/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/DatesSectionBackground.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/DatesSectionBackground.colorset/Contents.json new file mode 100644 index 000000000..b73d804ea --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/DatesSectionBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.984", + "green" : "0.980", + "red" : "0.976" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.275", + "green" : "0.200", + "red" : "0.153" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/DatesSectionStroke.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/DatesSectionStroke.colorset/Contents.json new file mode 100644 index 000000000..432fab345 --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/DatesSectionStroke.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.878", + "green" : "0.831", + "red" : "0.800" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.439", + "green" : "0.353", + "red" : "0.306" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/NextWeekTimelineColor.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/NextWeekTimelineColor.colorset/Contents.json new file mode 100644 index 000000000..5cd29db93 --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/NextWeekTimelineColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.733", + "green" : "0.647", + "red" : "0.592" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.439", + "green" : "0.353", + "red" : "0.306" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/ThisWeekTimelineColor.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/ThisWeekTimelineColor.colorset/Contents.json new file mode 100644 index 000000000..abd91436e --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/ThisWeekTimelineColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.392", + "green" : "0.286", + "red" : "0.239" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.624", + "green" : "0.533", + "red" : "0.475" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/TodayTimelineColor.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/TodayTimelineColor.colorset/Contents.json new file mode 100644 index 000000000..3e35599d4 --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/TodayTimelineColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.667", + "red" : "0.259" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.584", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/UpcomingTimelineColor.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/UpcomingTimelineColor.colorset/Contents.json new file mode 100644 index 000000000..2af3cc3c3 --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/UpcomingTimelineColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.878", + "green" : "0.831", + "red" : "0.800" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.275", + "green" : "0.200", + "red" : "0.153" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/Assets.xcassets/CourseDates/pastDueTimelineColor.colorset/Contents.json b/Theme/Theme/Assets.xcassets/CourseDates/pastDueTimelineColor.colorset/Contents.json new file mode 100644 index 000000000..a72efa461 --- /dev/null +++ b/Theme/Theme/Assets.xcassets/CourseDates/pastDueTimelineColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.302", + "green" : "0.788", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.282", + "green" : "0.761", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Theme/Theme/SwiftGen/ThemeAssets.swift b/Theme/Theme/SwiftGen/ThemeAssets.swift index b50937911..6796840e6 100644 --- a/Theme/Theme/SwiftGen/ThemeAssets.swift +++ b/Theme/Theme/SwiftGen/ThemeAssets.swift @@ -52,6 +52,13 @@ public enum ThemeAssets { public static let textInputUnfocusedStroke = ColorAsset(name: "TextInputUnfocusedStroke") public static let warning = ColorAsset(name: "warning") public static let white = ColorAsset(name: "white") + public static let datesSectionBackground = ColorAsset(name: "DatesSectionBackground") + public static let datesSectionStroke = ColorAsset(name: "DatesSectionStroke") + public static let nextWeekTimelineColor = ColorAsset(name: "NextWeekTimelineColor") + public static let thisWeekTimelineColor = ColorAsset(name: "ThisWeekTimelineColor") + public static let todayTimelineColor = ColorAsset(name: "TodayTimelineColor") + public static let upcomingTimelineColor = ColorAsset(name: "UpcomingTimelineColor") + public static let pastDueTimelineColor = ColorAsset(name: "pastDueTimelineColor") public static let appLogo = ImageAsset(name: "appLogo") } // swiftlint:enable identifier_name line_length nesting type_body_length type_name diff --git a/Theme/Theme/Theme.swift b/Theme/Theme/Theme.swift index fb75972e0..afc1cd7ff 100644 --- a/Theme/Theme/Theme.swift +++ b/Theme/Theme/Theme.swift @@ -24,6 +24,11 @@ public struct Theme { public private(set) static var cardViewStroke = ThemeAssets.cardViewStroke.swiftUIColor public private(set) static var certificateForeground = ThemeAssets.certificateForeground.swiftUIColor public private(set) static var commentCellBackground = ThemeAssets.commentCellBackground.swiftUIColor + public private(set) static var nextWeekTimelineColor = ThemeAssets.nextWeekTimelineColor.swiftUIColor + public private(set) static var pastDueTimelineColor = ThemeAssets.pastDueTimelineColor.swiftUIColor + public private(set) static var thisWeekTimelineColor = ThemeAssets.thisWeekTimelineColor.swiftUIColor + public private(set) static var todayTimelineColor = ThemeAssets.todayTimelineColor.swiftUIColor + public private(set) static var upcomingTimelineColor = ThemeAssets.upcomingTimelineColor.swiftUIColor public private(set) static var shadowColor = ThemeAssets.shadowColor.swiftUIColor public private(set) static var snackbarErrorColor = ThemeAssets.snackbarErrorColor.swiftUIColor public private(set) static var snackbarErrorTextColor = ThemeAssets.snackbarErrorTextColor.swiftUIColor @@ -39,6 +44,8 @@ public struct Theme { public private(set) static var warning = ThemeAssets.warning.swiftUIColor public private(set) static var white = ThemeAssets.white.swiftUIColor public private(set) static var loginNavigationText = ThemeAssets.loginNavigationText.swiftUIColor + public private(set) static var datesSectionBackground = ThemeAssets.datesSectionBackground.swiftUIColor + public private(set) static var datesSectionStroke = ThemeAssets.datesSectionStroke.swiftUIColor public static func update( accentColor: Color = ThemeAssets.accentColor.swiftUIColor, @@ -50,6 +57,11 @@ public struct Theme { cardViewStroke: Color = ThemeAssets.cardViewStroke.swiftUIColor, certificateForeground: Color = ThemeAssets.certificateForeground.swiftUIColor, commentCellBackground: Color = ThemeAssets.commentCellBackground.swiftUIColor, + nextWeekTimelineColor: Color = ThemeAssets.nextWeekTimelineColor.swiftUIColor, + pastDueTimelineColor: Color = ThemeAssets.pastDueTimelineColor.swiftUIColor, + thisWeekTimelineColor: Color = ThemeAssets.thisWeekTimelineColor.swiftUIColor, + todayTimelineColor: Color = ThemeAssets.todayTimelineColor.swiftUIColor, + upcomingTimelineColor: Color = ThemeAssets.upcomingTimelineColor.swiftUIColor, shadowColor: Color = ThemeAssets.shadowColor.swiftUIColor, snackbarErrorColor: Color = ThemeAssets.snackbarErrorColor.swiftUIColor, snackbarErrorTextColor: Color = ThemeAssets.snackbarErrorTextColor.swiftUIColor, @@ -63,7 +75,9 @@ public struct Theme { textInputUnfocusedBackground: Color = ThemeAssets.textInputUnfocusedBackground.swiftUIColor, textInputUnfocusedStroke: Color = ThemeAssets.textInputUnfocusedStroke.swiftUIColor, warning: Color = ThemeAssets.warning.swiftUIColor, - white: Color = ThemeAssets.white.swiftUIColor + white: Color = ThemeAssets.white.swiftUIColor, + datesSectionBackground: Color = ThemeAssets.datesSectionBackground.swiftUIColor, + datesSectionStroke: Color = ThemeAssets.datesSectionStroke.swiftUIColor ) { self.accentColor = accentColor self.alert = alert @@ -74,6 +88,11 @@ public struct Theme { self.cardViewStroke = cardViewStroke self.certificateForeground = certificateForeground self.commentCellBackground = commentCellBackground + self.nextWeekTimelineColor = nextWeekTimelineColor + self.pastDueTimelineColor = pastDueTimelineColor + self.thisWeekTimelineColor = thisWeekTimelineColor + self.todayTimelineColor = todayTimelineColor + self.upcomingTimelineColor = upcomingTimelineColor self.shadowColor = shadowColor self.snackbarErrorColor = snackbarErrorColor self.snackbarErrorTextColor = snackbarErrorTextColor @@ -88,6 +107,8 @@ public struct Theme { self.textInputUnfocusedStroke = textInputUnfocusedStroke self.warning = warning self.white = white + self.datesSectionBackground = datesSectionBackground + self.datesSectionStroke = datesSectionStroke } }