Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

draft pr for gsoc what section #5367

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
package org.oppia.android.domain.classroom

import org.json.JSONObject
import org.oppia.android.app.model.ClassroomSummary
import org.oppia.android.app.model.EphemeralTopicSummary
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.model.StoryRecord
import org.oppia.android.app.model.SubtitledHtml
import org.oppia.android.app.model.TopicIdList
import org.oppia.android.app.model.TopicList
import org.oppia.android.app.model.TopicPlayAvailability
import org.oppia.android.app.model.TopicRecord
import org.oppia.android.app.model.TopicSummary
import org.oppia.android.domain.topic.createTopicThumbnailFromJson
import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.util.JsonAssetRetriever
import org.oppia.android.domain.util.getStringFromObject
import org.oppia.android.util.caching.AssetRepository
import org.oppia.android.util.caching.LoadLessonProtosFromAssets
import org.oppia.android.util.data.DataProvider
import org.oppia.android.util.data.DataProviders.Companion.transform
import org.oppia.android.util.locale.OppiaLocale
import javax.inject.Inject
import javax.inject.Singleton

private const val GET_CLASSROOM_SUMMARY_LIST_PROVIDER_ID = "get_classroom_summary_list_provider_id"

@Singleton
class ClassroomController @Inject constructor(
private val jsonAssetRetriever: JsonAssetRetriever,
private val assetRepository: AssetRepository,
private val translationController: TranslationController,
@LoadLessonProtosFromAssets private val loadLessonProtosFromAssets: Boolean
) {

fun getClassroomList(profileId: ProfileId): DataProvider<List<ClassroomSummary>> {
val translationLocaleProvider =
translationController.getWrittenTranslationContentLocale(profileId)
return translationLocaleProvider.transform(
GET_CLASSROOM_SUMMARY_LIST_PROVIDER_ID,
::createClassroomSummaryList
)
}

private fun createClassroomSummaryList(contentLocale: OppiaLocale.ContentLocale): List<ClassroomSummary> {
val classroomSummaryList = mutableListOf<ClassroomSummary>()
if (false
// loadLessonProtosFromAssets
) {
// val classroomIdList =
// assetRepository.loadProtoFromLocalAssets(
// assetName = "classroom",
// baseMessage = ClassroomIdList.getDefaultInstance()
// )
} else {
val classroomIdJsonArray = jsonAssetRetriever
.loadJsonFromAsset("classrooms.json")!!
.getJSONArray("classroom_id_list")

for (i in 0 until classroomIdJsonArray.length()) {
val classroomSummary =
createClassroomSummary(contentLocale, classroomIdJsonArray.optString(i)!!)
classroomSummaryList.add(classroomSummary)
}
}
return classroomSummaryList
}

private fun createClassroomSummary(
contentLocale: OppiaLocale.ContentLocale,
classroomId: String
): ClassroomSummary {
return if (
false
// loadLessonProtosFromAssets
) {
TODO()
} else {
createClassroomSummaryFromJson(
contentLocale,
classroomId
)
}
}

private fun createClassroomSummaryFromJson(
contentLocale: OppiaLocale.ContentLocale,
classroomId: String
): ClassroomSummary {
val topicList = createTopicList(contentLocale, classroomId)
var totalLessonCount = 0
val topicSummaryCount = topicList.topicSummaryCount
for (i in 0 until topicSummaryCount) {
totalLessonCount += topicList.getTopicSummary(i).topicSummary.totalChapterCount
}
val classroomIdAndNameJsonArray =
jsonAssetRetriever.loadJsonFromAsset("classroomIdAndNameMap.json")
val classroomTitle = SubtitledHtml.newBuilder().apply {
contentId = "title"
html = classroomIdAndNameJsonArray?.getStringFromObject(classroomId)
}.build()
return ClassroomSummary.newBuilder()
.setClassroomId(classroomId)
.setClassroomTitle(classroomTitle)
.setClassroomTitle(SubtitledHtml.getDefaultInstance())
.setTopicList(topicList)
.setTotalLessonCount(5)
.build()
}

private fun createTopicList(
contentLocale: OppiaLocale.ContentLocale,
classroomId: String
): TopicList {
return if (loadLessonProtosFromAssets) {
val topicIdList =
assetRepository.loadProtoFromLocalAssets(
assetName = "topics",
baseMessage = TopicIdList.getDefaultInstance()
)
return TopicList.newBuilder().apply {
// Only include topics currently playable in the topic list.
addAllTopicSummary(
topicIdList.topicIdsList.map {
createEphemeralTopicSummary(it, contentLocale)
}.filter {
it.topicSummary.topicPlayAvailability.availabilityCase == TopicPlayAvailability.AvailabilityCase.AVAILABLE_TO_PLAY_NOW
}.filter {
it.topicSummary.classroomId == classroomId
}
)
}.build()
} else loadTopicListFromJson(contentLocale, classroomId)
}

private fun loadTopicListFromJson(
contentLocale: OppiaLocale.ContentLocale,
classroomId: String
): TopicList {
val topicIdJsonArray = jsonAssetRetriever
.loadJsonFromAsset("topics.json")!!
.getJSONArray("topic_id_list")
val topicListBuilder = TopicList.newBuilder()
for (i in 0 until topicIdJsonArray.length()) {
val ephemeralSummary =
createEphemeralTopicSummary(topicIdJsonArray.optString(i)!!, contentLocale)
val topicPlayAvailability = ephemeralSummary.topicSummary.topicPlayAvailability
val topicClassroomId = ephemeralSummary.topicSummary.classroomId
// Only include topics currently playable in the topic list and part of the classroomId
if (topicPlayAvailability.availabilityCase == TopicPlayAvailability.AvailabilityCase.AVAILABLE_TO_PLAY_NOW &&
topicClassroomId == classroomId
) {
topicListBuilder.addTopicSummary(ephemeralSummary)
}
}
return topicListBuilder.build()
}

private fun createEphemeralTopicSummary(
topicId: String,
contentLocale: OppiaLocale.ContentLocale
): EphemeralTopicSummary {
val topicSummary = createTopicSummary(topicId)
return EphemeralTopicSummary.newBuilder().apply {
this.topicSummary = topicSummary
writtenTranslationContext =
translationController.computeWrittenTranslationContext(
topicSummary.writtenTranslationsMap,
contentLocale
)
}.build()
}

private fun createTopicSummary(topicId: String): TopicSummary {
return if (loadLessonProtosFromAssets) {
val topicRecord =
assetRepository.loadProtoFromLocalAssets(
assetName = topicId,
baseMessage = TopicRecord.getDefaultInstance()
)
val storyRecords = topicRecord.canonicalStoryIdsList.map {
assetRepository.loadProtoFromLocalAssets(
assetName = it,
baseMessage = StoryRecord.getDefaultInstance()
)
}
TopicSummary.newBuilder().apply {
this.topicId = topicId
putAllWrittenTranslations(topicRecord.writtenTranslationsMap)
title = topicRecord.translatableTitle
classroomId = topicRecord.classroomId
classroomTitle = topicRecord.classroomTitle
totalChapterCount = storyRecords.sumOf { it.chaptersList.size }
topicThumbnail = topicRecord.topicThumbnail
topicPlayAvailability = if (topicRecord.isPublished) {
TopicPlayAvailability.newBuilder().setAvailableToPlayNow(true).build()
} else {
TopicPlayAvailability.newBuilder().setAvailableToPlayInFuture(true).build()
}
storyRecords.firstOrNull()?.storyId?.let { this.firstStoryId = it }
}.build()
} else {
createTopicSummaryFromJson(topicId, jsonAssetRetriever.loadJsonFromAsset("$topicId.json")!!)
}
}

private fun createTopicSummaryFromJson(topicId: String, jsonObject: JSONObject): TopicSummary {
var totalChapterCount = 0
val storyData = jsonObject.getJSONArray("canonical_story_dicts")
for (i in 0 until storyData.length()) {
totalChapterCount += storyData
.getJSONObject(i)
.getJSONArray("node_titles")
.length()
}
val firstStoryId =
if (storyData.length() == 0) "" else storyData.getJSONObject(0).getStringFromObject("id")

val topicPlayAvailability = if (jsonObject.getBoolean("published")) {
TopicPlayAvailability.newBuilder().setAvailableToPlayNow(true).build()
} else {
TopicPlayAvailability.newBuilder().setAvailableToPlayInFuture(true).build()
}
val topicTitle = SubtitledHtml.newBuilder().apply {
contentId = "title"
html = jsonObject.getStringFromObject("topic_name")
}.build()
val classroomTitle = SubtitledHtml.newBuilder().apply {
contentId = "title"
html = jsonObject.getStringFromObject("classroom_title")
}
// No written translations are included since none are retrieved from JSON.
return TopicSummary.newBuilder()
.setTopicId(topicId)
.setTitle(topicTitle)
.setClassroomId(jsonObject.getStringFromObject("classroom_id"))
.setClassroomTitle(classroomTitle)
.setVersion(jsonObject.optInt("version"))
.setTotalChapterCount(totalChapterCount)
.setTopicThumbnail(createTopicThumbnailFromJson(jsonObject))
.setTopicPlayAvailability(topicPlayAvailability)
.setFirstStoryId(firstStoryId)
.build()
}
}
41 changes: 41 additions & 0 deletions model/src/main/proto/topic.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ message Topic {
// The topic's title.
SubtitledHtml title = 10;

// The ID of the classroom which contains this Topic.
string classroom_id = 12;

// The title of the classroom this Topic is part of.
SubtitledHtml classroom_title = 13;

// A brief description of the topic.
SubtitledHtml description = 11;

Expand Down Expand Up @@ -204,6 +210,20 @@ message Classroom {
int64 last_update_time_ms = 2;
}

message ClassroomSummary {
// The Id of the ClassroomSummary.
string classroom_id = 1;

// The title of the ClassroomSummary.
SubtitledHtml classroom_title = 2;

// All topics that are part of this ClassroomSummary.
TopicList topic_list = 3;

// The total number of lessons (by adding all the lessons from each topic) in this ClassroomSummary.
int32 total_lesson_count = 4;
}

// Corresponds to the list of topics that are currently being played and are not fully finished.
message OngoingTopicList {
// All topics that are currently being played and have not finished.
Expand Down Expand Up @@ -289,6 +309,12 @@ message PromotedStory {
// The title of the next chapter (exploration title) to complete.
SubtitledHtml next_chapter_title = 17;

// The ID of the classroom this story is part of.
string classroom_id = 18;

// The title of the classroom this story is part of.
SubtitledHtml classroom_title = 19;

// The exploration id next chapter to complete.
string exploration_id = 6;

Expand Down Expand Up @@ -331,9 +357,18 @@ message TopicSummary {
// The title of the topic.
SubtitledHtml title = 8;

// The ID of the classroom which contains this TopicSummary.
string classroom_id = 10;

// The title of the classroom this TopicSummary is part of.
SubtitledHtml classroom_title = 11;

// The structural version of the topic.
int32 version = 3;

// The number of lessons the player has completed in this topic.
int32 completed_chapter_count = 12;

// The total number of lessons associated with this topic.
int32 total_chapter_count = 4;

Expand Down Expand Up @@ -575,6 +610,12 @@ message TopicRecord {
// The topic's description.
SubtitledHtml translatable_description = 10;

// The ID of the classroom which contains this TopicSummary.
string classroom_id = 11;

// The title of the classroom this TopicSummary is part of.
SubtitledHtml classroom_title = 12;

// The list of canonical story IDs that can be used to load stories from the local filesystem.
repeated string canonical_story_ids = 4;

Expand Down
Loading