diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..fcdb2e1 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +4.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d8ad95 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bazel-* \ No newline at end of file diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..efbe1f4 --- /dev/null +++ b/BUILD @@ -0,0 +1,61 @@ +""" +Top-level definitions for Oppia's proto API project. Only libraries that are part of the project's +public, importable API should be defined here (and all such libraries should be defined here and +nowhere else). +""" + +load("@com_github_bazelbuild_buildtools//buildifier:buildifier.bzl", "buildifier") +load("@rules_java//java:defs.bzl", "java_lite_proto_library", "java_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") +load("//defs:defs.bzl", "BUILDIFIER_LINT_WARNINGS", "define_buildifier_tests") + +package_group( + name = "api_visibility", + packages = [ + "//", + ], +) + +package_group( + name = "proto_impl_visibility", + packages = [ + "//org/oppia/proto/...", + ], +) + +# The protos needed to interact with Oppia's proto API. This is meant to be used in cases when these +# protos are used by domain-specific protos in downstream projects. +# NOTE TO DEVELOPERS: 'deps' specifically needs to be used (versus just 'exports') since downstream +# proto_library targets otherwise won't pull in the protos, and language libraries need it to +# actually generate corresponding proto code. +proto_library( + name = "android_protos", + visibility = ["//visibility:public"], + deps = [ + "//org/oppia/proto/v1/api:android_proto", + "//org/oppia/proto/v1/structure:structure_proto", + "//org/oppia/proto/v1/versions:versions_proto", + ], +) + +# The Java versions of the protos needed to interact with Oppia's proto API. +java_proto_library( + name = "android_java_protos", + visibility = ["//visibility:public"], + deps = [":android_protos"], +) + +# The Java lite versions of the protos needed to interact with Oppia's proto API. +java_lite_proto_library( + name = "android_java_lite_protos", + visibility = ["//visibility:public"], + deps = [":android_protos"], +) + +buildifier( + name = "fix_bazel_lint_problems", + lint_mode = "fix", + lint_warnings = BUILDIFIER_LINT_WARNINGS, +) + +define_buildifier_tests() diff --git a/README.md b/README.md index d3a516f..aea6f49 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ -# oppia-android-api -Published Android-specific API for the Oppia backend. +# oppia-proto-api +Published common proto API for the Oppia server/client. + +This API governs the transfer of data between the Oppia Python server and the Android app. + +### API Versions +Each version of this API is formatted as a major & minor version (e.g. 1.0) and corresponds to a separate release. + +- The major version is represented directly in the directory structure (e.g. 'v1'), and is only incremented if a breaking change must be introduced into the API. (Note that this repository might house multiple major versions, in order to maintain long-term backward compatibility with older API versions.) +- The minor version is incremented whenever a compatible change to the API is released. It is only ever represented in the release numbers, and never in code form. + +Because protobuf is backward/forward interoperable, incompatibilities are unlikely as long as proto versions are respected (though, even in those cases, there should be potential for graceful failures). + +### Content Versions +Content versions represent the version of the corresponding entity instance (e.g. a topic, skill, or exploration). These are used to track whether a particular structure has content updates. They map to the versions stored in the corresponding structure's VersionedModel in the backend. + +### Proto Versions +Proto versions correspond to the proto structures defined in this repository. They only need to be incremented when a proto structure is updated in such a way that the default data will break existing logical assumptions (thus requiring a data migration). Such upgrades should always happen in a compatible way (except for major API version upgrades -- see the "API Version" section above). + +Three notes: + +1. Some proto versions correspond to **groups** of substructures (such as SubtitledHtml, or other language-based substructures), because these substructures are shared across high-level structures. +2. There is no need to version interactions separately. This is because their structure and how they relate to the exploration/question experience is implied as part of State's structure version. +3. If any changes happen in the proto structure which are getting tracked, the version specified in the comment must be incremented. + +The following are the list of structure types whose versions are tracked: +- TopicSummaryProtoVersion +- RevisionCardProtoVersion +- ConceptCardProtoVersion +- ExplorationProtoVersion +- QuestionProtoVersion +- StateProtoVersion +- LanguageProtosVersion +- ImageProtoVersion + +## Support +If you have any feature requests or bug reports, please log them on our [issue tracker](https://github.com/oppia/oppia-proto-api/issues/new?title=Describe%20your%20feature%20request%20or%20bug%20report%20succinctly&body=If%20you%27d%20like%20to%20propose%20a%20feature,%20describe%20what%20you%27d%20like%20to%20see.%0A%0AIf%20you%27re%20reporting%20a%20bug,%20please%20be%20sure%20to%20include%20the%20expected%20behaviour,%20the%20observed%20behaviour,%20and%20steps%20to%20reproduce%20the%20problem.%20Console%20copy-pastes%20and%20any%20background%20on%20the%20environment%20would%20also%20be%20helpful.%0A%0AThanks!). + +Please report security issues directly to admin@oppia.org. + +## Licence +The Oppia-Proto-API code is released under the [Apache v2 license](https://github.com/oppia/oppia-proto-api/blob/master/LICENSE). diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..b7156f6 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,11 @@ +""" +The top-level WORKSPACE definition for the Oppia proto API Bazel workspace. +""" + +load("//repo:deps.bzl", "initializeDepsForWorkspace") + +initializeDepsForWorkspace() + +load("//repo:toolchains.bzl", "initializeToolchainsForWorkspace") + +initializeToolchainsForWorkspace() diff --git a/defs/BUILD b/defs/BUILD new file mode 100644 index 0000000..44381ab --- /dev/null +++ b/defs/BUILD @@ -0,0 +1,5 @@ +""" +Starlark macros that help ensure clean proto & Bazel dependency management. +""" + +exports_files(["buf.yaml"]) diff --git a/defs/buf.yaml b/defs/buf.yaml new file mode 100644 index 0000000..a2311f8 --- /dev/null +++ b/defs/buf.yaml @@ -0,0 +1,7 @@ +version: v1 +lint: + use: + - DEFAULT + except: + - PACKAGE_VERSION_SUFFIX + - ENUM_VALUE_PREFIX diff --git a/defs/defs.bzl b/defs/defs.bzl new file mode 100644 index 0000000..2e84a7b --- /dev/null +++ b/defs/defs.bzl @@ -0,0 +1,51 @@ +""" +Defines Starlark macros that are used to define protos and set up Bazel build directories (and +automatically define Buf & Buildifier lint tests). +""" + +load("@com_github_bazelbuild_buildtools//buildifier:buildifier.bzl", "buildifier_test") +load("@rules_buf//buf:defs.bzl", "buf_lint_test") +load("@rules_proto//proto:defs.bzl", "proto_library") + +# Configured lint warnings for Buildifier linter tests & autofixing configuration. +BUILDIFIER_LINT_WARNINGS = [ + "+out-of-order-load", + "+unsorted-dict-items", +] + +def oppia_proto_library(name, relative_parent_dir_path, **kwargs): + """ + Defines an Oppia API-compatible proto library, and a lint test with the same name ending in + "_lint_test"." + + Args: + name: str. The name of the library being defined. + relative_parent_dir_path: str. The relative directory path to the containing Bazel package. + **kwargs: additional parameters passed into proto_library. + """ + + proto_library( + name = name, + import_prefix = relative_parent_dir_path, # Make directory prefix match declared package. + strip_import_prefix = "", # Strip the default file prefix so that it may be redefined. + **kwargs + ) + + buf_lint_test( + name = "%s_lint_test" % name, + config = "//defs:buf.yaml", + targets = [name], + ) + +def define_buildifier_tests(): + """ + Defines a Bazel lint test for this package with the name "bazel_lint_check_test". + """ + + buildifier_test( + name = "bazel_lint_check_test", + srcs = native.glob(["WORKSPACE"]) + native.glob(["BUILD"]) + native.glob(["*.bazel"]) + native.glob(["*.bzl"]), + lint_mode = "warn", + lint_warnings = BUILDIFIER_LINT_WARNINGS, + verbose = True, + ) diff --git a/org/oppia/proto/BUILD b/org/oppia/proto/BUILD new file mode 100644 index 0000000..97b9d69 --- /dev/null +++ b/org/oppia/proto/BUILD @@ -0,0 +1,5 @@ +""" +The top-level directory for the Oppia proto API. Generally, no actual proto libraries should be +defined at this level. Instead, protos should go into the corresponding version in which they +belong. See the README for more details on the API is versioned. +""" diff --git a/org/oppia/proto/v1/BUILD b/org/oppia/proto/v1/BUILD new file mode 100644 index 0000000..845543b --- /dev/null +++ b/org/oppia/proto/v1/BUILD @@ -0,0 +1,3 @@ +""" +Version 1 of the Oppia proto API. See the README for specifics on how the API is versioned. +""" diff --git a/org/oppia/proto/v1/api/BUILD b/org/oppia/proto/v1/api/BUILD new file mode 100644 index 0000000..418adf6 --- /dev/null +++ b/org/oppia/proto/v1/api/BUILD @@ -0,0 +1,18 @@ +""" +Libraries that define endpoint API protos for interacting with Oppia's backend. +""" + +load("//defs:defs.bzl", "define_buildifier_tests", "oppia_proto_library") + +oppia_proto_library( + name = "android_proto", + srcs = ["android.proto"], + relative_parent_dir_path = "org/oppia/proto/v1/api", + visibility = ["//:api_visibility"], + deps = [ + "//org/oppia/proto/v1/structure:structure_proto", + "//org/oppia/proto/v1/versions:versions_proto", + ], +) + +define_buildifier_tests() diff --git a/org/oppia/proto/v1/api/android.proto b/org/oppia/proto/v1/api/android.proto new file mode 100644 index 0000000..b60f1e9 --- /dev/null +++ b/org/oppia/proto/v1/api/android.proto @@ -0,0 +1,509 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.api; + +import "org/oppia/proto/v1/structure/classroom.proto"; +import "org/oppia/proto/v1/structure/concept_card.proto"; +import "org/oppia/proto/v1/structure/exploration.proto"; +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/structure/question.proto"; +import "org/oppia/proto/v1/structure/revision_card.proto"; +import "org/oppia/proto/v1/structure/topic_summary.proto"; +import "org/oppia/proto/v1/versions/api_versions.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.api"; +option java_multiple_files = true; + +// A request for the list of topics available to a local client. +// +// Note that the backend is principally responsible for providing the client with exact content +// versions and structure IDs that are compatible with that client. For the most part, this involves +// ensuring that the returned proto structures match the requested proto structures (and for cases +// when that's not possible, such as if a new interaction is introduced, the corresponding topic +// will not be provided as an available topic for downloading or updating). +// +// Another dimension of compatibility is the list of supported languages. For an optimal user +// experience, the backend won't provide the client with topics or topic updates that aren't fully +// localized to the languages expected by the client. +message TopicListRequestDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The common proto version defining this request/response pair. Note that the response may be + // provided with a different version since it should match the requested proto version defined + // below. The request can use any version at the choice of the client since the server guarantees + // full backward compatibility for all proto structure versions. + org.oppia.proto.v1.versions.TopicListRequestResponseProtoVersion proto_version = 1; + + // Details about the client that is making the request. + AndroidClientContextDto client_context = 2; + + // Details about the latest proto versions that this client can support. The server is expected to + // only provide structures that exactly match the proto versions provided. + ClientCompatibilityContextDto compatibility_context = 3; + + // The current structures that are downloaded by the local client. Note that this must be a + // comprehensive list of downloaded structures in order to leverage certain consistency guarantees + // by the backend, including ensuring that structures are never re-downloaded or violate the + // one-version expectation (that is, only the latest compatible version of a shared cross-topic + // structure is downloaded and never multiple versions of it). + repeated VersionedStructureDetailsDto current_downloads_context = 4; + + // Dictates what default localizations should be included with all topics and lessons available to + // download. Note that this will guarantee exclusion of an topics or lessons that do not have 100% + // translations of the specified language (from the client's perspective, the topic will not even + // exist except possibly in the dependency graph provided by TopicListResponseDto). + // + // It is the *client's* responsibility to not change this language across requests. This should be + // a default language indicated by the client's infrastructure, not the user (as separate + // localization packs can be downloaded by the client). The server makes no compatibility + // guarantee for subsequent requests with different languages. Different languages should be + // downloaded using a topic content request. + // + // This value is required for proper functioning of the system. The server is may fail the request + // or return a suitable default if this value is omitted. + org.oppia.proto.v1.structure.LanguageType requested_default_language = 5; + + // The list of additional languages supported by the client. This is only used for the purpose of + // localizing topic & topic substructure summaries, not downloaded content (which should use a + // topic content request, instead). The server makes no effort to remember these languages, nor + // guarantee compatibility in the returned list of topics. This list of languages is only meant to + // populate summary language data on a best-effort basis. + // + // This list may contain any languages (including the requested default language), except the + // requested default language will be ignored in this list if it's present. This list is optional + // for clients to populate if localization isn't needed. This list is *not* used to compute + // updates to language packs (as the context for those packs are defined as part of + // VersionedStructureDetailsDto). + repeated org.oppia.proto.v1.structure.LanguageType supported_additional_languages = 6; + + // The list of additional languages that require 100% translations in order to be considered + // compatible with the client. If, for example, Swahili is included in this list but a topic was + // updated and is now at 99% Swahili translations, the previous version that contained 100% will + // be provided to the client. Topics that do not have any versions with 100% Swahili translations + // would be included as future topics. + repeated org.oppia.proto.v1.structure.LanguageType required_additional_languages = 7; +} + +// The expected response from the server when provided with a TopicListRequest. +message TopicListResponseDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The common proto version defining this request/response pair. This is expected to match the + // version provided in the compatibility context included in the topic list request. + org.oppia.proto.v1.versions.TopicListRequestResponseProtoVersion proto_version = 1; + + // A list of all topics that are compatibly available to the client whose context corresponds to + // this response. Note that topics marked as available for downloading or updating will always be + // compatible with the local client, and topics that are marked as up-to-date are guaranteed to + // be at the latest compatible version for the local client. Clients are also expected to lean on + // existing local resources for topics that aren't yet downloaded. + repeated AvailableTopicDto available_topics = 2; + + // A list of all topics that will be available to the client to install in the future. + repeated FutureTopicDto future_topics = 3; + + // A list of available classrooms for learning. Clients may opt to only show topics that + // correspond to a classroom, and it is possible for a topic to belong to more than one classroom + // simultaneously. + repeated org.oppia.proto.v1.structure.ClassroomDto classrooms = 4; + + // A topic that's available to & compatible with the local client. + message AvailableTopicDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The nature of the topic's availability. + oneof availability_type { + // Indicates a topic that's not yet downloaded by the local client, but is available to be + // downloaded. + DownloadableTopicDto downloadable_topic = 1; + + // Indicates a topic that's downloaded by the local client, but has a compatible update. + UpdatableTopicDto updatable_topic = 2; + + // Indicates a topic that's both downloaded by the local client & fully up-to-date. This + // mainly serves as a lightweight indicator to ensure that the server & client are properly + // in-sync with the state of the local client. This will only be sent if the client has the + // very latest version of the topic. If there's a newer version, either updatable_topic or + // not_updatable_topic will be sent. + UpToDateTopicDto up_to_date_topic = 3; + + // Indicates a topic that cannot be updated due to a binary incompatibility with the local + // client. This will only be sent if there's a topic version that the client cannot download, + // and it already has the latest internally consistent representation of the topic downloaded. + NotUpdatableTopicDto not_updatable_topic = 4; + } + } + + // Represents a topic that's available to download and is not yet downloaded by the local client. + message DownloadableTopicDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The ID of the topic that's available to newly download. This is expected to be used to + // identify the local copy of the topic's summary in the event that the topic_summary field is + // empty. Note that content_version is not also included since topic_summary is only missing if + // the local client already has the latest, most up-to-date summary (including the corresponding + // content version). + string topic_id = 1; + + // The full summary for the current topic. Note that this will be absent if the local client + // already has the latest summary downloaded as indicated by its download state context. + org.oppia.proto.v1.structure.DownloadableTopicSummaryDto topic_summary = 2; + + // The size, in bytes, needed to download the topic. + uint32 download_size_bytes = 3; + } + + // Represents a topic that's available to be updated. Clients should use their existing topic + // summary when displaying this topic, and not update it until the user requests to download the + // topic. Future iterations of this API could include a preview summary here for clients to + // provide context on the changes to the topic to help inform users. + message UpdatableTopicDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The ID of the topic that's available to be updated. This is expected to correspond to an + // existing summary that the local client already has for the topic. In the event it doesn't, it + // may use the topic content API to retrieve the topic summary & treat this as a topic that's + // available to download. + string topic_id = 1; + + // The new topic content version that may be updated to (as compared with the local summary for + // this topic which should have a smaller content version). + uint32 content_version = 2; + + // The size, in bytes, required to download all missing pieces to update this topic. + uint32 update_size_bytes = 3; + + // The precise list of structures that need to be updated in the local client in order to update + // the topic. This may be a full update of the topic, but the server may also attempt to perform + // an incremental update to minimize the download work & bandwidth needed by the local client. + // update_size_bytes will reflect the data determined by the server to be needed to update the + // topic. + repeated VersionedStructureDetailsDto updated_structures = 4; + } + + // Represents a topic that's been downloaded by the local client and is fully up-to-date. + message UpToDateTopicDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The ID of the topic that the server believes the local client has downloaded. In the event + // that this is incorrect, this topic can be treated as downloadable & its summary can be + // fetched using the topic cnotent API. + string topic_id = 1; + + // The content version of the topic that the server believes the local client has downloaded. + uint32 content_version = 2; + } + + // Represents a topic that's not up-to-date, but that the app cannot install due to binary + // incompatibility. + message NotUpdatableTopicDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The ID of the topic that the server cannot create a compatible binary representation of, for + // its latest version. + string topic_id = 1; + + // The content version of the topic that cannot be downloaded by the client. Clients can assume + // that the server has already provided the latest compatible content version. + uint32 content_version = 2; + } + + // Represents a topic that's not yet available to download (due to it not being released). + message FutureTopicDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The ID of the topic that will eventually exist, but doesn't yet. + string topic_id = 1; + + // A summary of the topic that will be later released and available to download. + org.oppia.proto.v1.structure.UpcomingTopicSummaryDto topic_summary = 2; + } +} + +// The request made to the server to get the latest compatible content for specific topics. This +// request is designed such that any number of structures may be requested to download along with a +// max payload size, and multiple requests can be set in parallel and at any time. Together, this +// enables pagination-based retrieval and download pausing/restarting in clients, if either feature +// is needed by the client. +message TopicContentRequestDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_CONTENT_REQUEST_RESPONSE_PROTO_VERSION; + + // The common proto version defining this request/response pair. + org.oppia.proto.v1.versions.TopicContentRequestResponseProtoVersion proto_version = 1; + + // Details about the client that is making the request. + AndroidClientContextDto client_context = 2; + + // Specific structure identifiers that should be fetched, preferably in the order of the list. + // Note that the server only aims to prioritize earlier items in the list, but does not guarantee + // that they will be delivered if other factors affect sending a response for a particular item + // (such as the max payload size or a particular structure not existing). Note that no effort is + // made to ensure compatibility between the local client and the requested structures; the client + // is expected to make this determination based on the structure content & proto versions received + // from the topic list API. Finally, duplicates will be ignored to avoid repeating the same data + // multiple times. + repeated DownloadRequestStructureIdentifierDto identifiers = 3; + + // The maximum payload size, in bytes, that the server should furnish as a single download + // operation. This is only a request, and may not be honoured by the server (which will only + // adhere to this field on a best-effort basis). This is calculated by taking the wire byte size + // (without compression) corresponding to a computed response (though the server may use estimates + // for efficiency, and will guarantee that the estimate isn't far off from the actual size sent). + uint32 requested_max_payload_size_bytes = 4; +} + +// The expected response from the server when provided with a TopicContentRequest. +message TopicContentResponseDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_CONTENT_REQUEST_RESPONSE_PROTO_VERSION; + + // The common proto version defining this request/response pair. + org.oppia.proto.v1.versions.TopicContentRequestResponseProtoVersion proto_version = 1; + + // A list of the individual items that were downloaded. Items in this list are expected to be + // relatively in the same order as the identifiers in the request, but some items may be missing + // (see the request's documentation for details). Note also that these will never include external + // assets such as voiceovers or images--those always must be downloaded separately. + repeated DownloadResultDto download_results = 2; + + // The result of downloading a specific structure. This may either contain the result of the + // download, or an indication that the download was skipped and roughly why. The potential results + // correspond to the download requests that can be made per DownloadRequestStructureIdentifier. + message DownloadResultDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_CONTENT_REQUEST_RESPONSE_PROTO_VERSION; + + // The entity type and ID corresponding to this download. This is guaranteed to exactly match at + // least one of the original identifiers in the request. + DownloadRequestStructureIdentifierDto identifier = 1; + + // The type/nature of the download result (see fields for specifics). + oneof result_type { + // The presence of this value indicates that the request corresponding to this particular + // identifier was skipped and suggested to retry by the Oppia backend. Note that the presence + // of this value in the oneof is sufficient to indicate its state; its actual value does not + // matter. + bool skipped_should_retry = 2; + + // The presence of this value indicates that the request outright failed for this particular + // identifier, potentially due to incorrect version/identifier information or an internal + // server failure. It is not recommended to retry the request since it's expected to fail + // again. Note that the presence of this value in the oneof is sufficient to indicate its + // state; its actual value does not matter. Note also that this is different than an HTTP + // error since certain error codes can be retried (in those cases, the entire response is + // expected to fail rather than a particular item within the response). + bool skipped_from_failure = 3; + + // The presence of this value indicates that the request doesn't correspond to an actual + // structure for the given ID/version. Note that the value of the field itself doesn't matter, + // just its presence. + bool skipped_does_not_exist = 14; + + // The result is a requested topic summary. + org.oppia.proto.v1.structure.DownloadableTopicSummaryDto topic_summary = 4; + + // The result is a requested revision card. + org.oppia.proto.v1.structure.RevisionCardDto revision_card = 5; + + // The result is a requested concept card. + org.oppia.proto.v1.structure.ConceptCardDto concept_card = 6; + + // The result is a requested exploration. + org.oppia.proto.v1.structure.ExplorationDto exploration = 7; + + // The result is a requested list of question IDs. + QuestionIdListDto question_id_list = 8; + + // The result is a requested question. + org.oppia.proto.v1.structure.QuestionDto question = 9; + + // The result is a requested revision card language pack. + org.oppia.proto.v1.structure.RevisionCardLanguagePackDto revision_card_language_pack = 10; + + // The result is a requested concept card language pack. + org.oppia.proto.v1.structure.ConceptCardLanguagePackDto concept_card_language_pack = 11; + + // The result is a requested exploration language pack. + org.oppia.proto.v1.structure.ExplorationLanguagePackDto exploration_language_pack = 12; + + // The result is a requested question language pack. + org.oppia.proto.v1.structure.QuestionLanguagePackDto question_language_pack = 13; + } + } + + // A list of question IDs that directly correspond to a particular skill ID. + message QuestionIdListDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_CONTENT_REQUEST_RESPONSE_PROTO_VERSION; + + // The list of question IDs that correspond to a particular skill ID. + repeated string question_ids = 1; + } +} + +// Represents the Oppia Android client that's making a request to Oppia's backend. This message +// includes metadata to help the backend understand the specific client that's connecting (generally +// for enabling certain platform parameters, checking for app deprecation, adjusting response +// protocols if necessary, and for telemetry). +message AndroidClientContextDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = UNVERSIONED_API_PROTO; + + // The app version name corresponding to the locally installed Android client. For more + // information, see: https://developer.android.com/studio/publish/versioning#appversioning. + string app_version_name = 1; + + // The app version code corresponding to the locally installed Android client. For more + // information, see: https://developer.android.com/studio/publish/versioning#appversioning. + uint64 app_version_code = 2; +} + +// Information about the latest proto versions that the client can support. The client is expected +// to only support responses & response contents that match the exact versions provided in this +// message, but it's permitted to send requests using any valid proto version for the request +// structure. The backend is expected to always structure responses accordingly to the requested +// versions provided here. +message ClientCompatibilityContextDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The latest proto version for topic list responses that the client can support. + org.oppia.proto.v1.versions.TopicListRequestResponseProtoVersion topic_list_request_response_proto_version = 1; + + // The latest proto version for topic content responses that the client can support. + org.oppia.proto.v1.versions.TopicContentRequestResponseProtoVersion topic_content_request_response_proto_version = 2; + + // The latest proto version for topic summaries that the client can support. + org.oppia.proto.v1.versions.TopicSummaryProtoVersion topic_summary_proto_version = 3; + + // The latest proto version for revision cards that the client can support. + org.oppia.proto.v1.versions.RevisionCardProtoVersion revision_card_proto_version = 4; + + // The latest proto version for concept cards that the client can support. + org.oppia.proto.v1.versions.ConceptCardProtoVersion concept_card_proto_version = 5; + + // The latest proto version for explorations that the client can support. + org.oppia.proto.v1.versions.ExplorationProtoVersion exploration_proto_version = 6; + + // The latest proto version for questions that the client can support. + org.oppia.proto.v1.versions.QuestionProtoVersion question_proto_version = 7; + + // The latest proto version for states that the client can support. + org.oppia.proto.v1.versions.StateProtoVersion state_proto_version = 8; + + // The latest proto version for language-related protos that the client can support. + org.oppia.proto.v1.versions.LanguageProtosVersion language_protos_version = 9; + + // The latest proto version for images that the client can support. + org.oppia.proto.v1.versions.ImageProtoVersion image_proto_version = 10; + + // The latest proto version for classrooms that the client can support. + org.oppia.proto.v1.versions.ClassroomProtoVersion classroom_proto_version = 11; +} + +// Details about a versione dstructure that's compatible with the local client. This can be used +// both to indicate the local download state of a client's topics, and to provide details for +// available changes to client-compatible topics. +// +// Note also that all structures represented by this message are considered "global" (that is, not +// tied to any particular topics) even though this message may be used in the context of a topic +// (generally to represent structures corresponding to the learning experience provided by that +// topic). This is an important distinction since some structures (like concept cards) aren't +// actually tied to any particular topics, and can be used across multiple topics. However, they are +// downloaded with the first topic that references them, and shouldn't be re-downloaded in +// subsequent topics (the backend makes guarantees to ensure that structures are never repeated). +message VersionedStructureDetailsDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION; + + // The backend-tracked and creator-affected content version of the structure. This is the primary + // indicator of how "new" a particular structure is, or whether it has updates available. This is + // always a monotonically increasing positive integer for newer versions. + uint32 content_version = 1; + + // The type of structure being detailed. + oneof structure_type { + // Indicates that these details correspond to a complete topic, as specified by its ID. + string topic_id = 2; + + // Indicates that these details correspond to a topic summary, as specified by a topic ID. + string topic_summary_id = 3; + + // Indicates that these details correspond to a revision card, as specified by its ID structure. + org.oppia.proto.v1.structure.LocalizedRevisionCardIdDto revision_card = 4; + + // Indicates that these details correspond to a concept card, as specified by its skill ID. + org.oppia.proto.v1.structure.LocalizedConceptCardIdDto concept_card = 5; + + // Indicates that these details correspond to a story, as specified by its ID. + string story_id = 6; + + // Indicates that these details correspond to an exploration, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedExplorationIdDto exploration = 7; + + // Indicates that these details correspond to a question, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedQuestionIdDto question = 8; + + // Indicates that these details correspond to a skill, as specified by its ID. + string skill_id = 9; + + // Indicates these details correspond to a revision card language pack, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedRevisionCardIdDto revision_card_language_pack = 10; + + // Indicates these details correspond to a concept card language pack, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedConceptCardIdDto concept_card_language_pack = 11; + + // Indicates these details correspond to an exploration language pack, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedExplorationIdDto exploration_language_pack = 12; + + // Indicates that these details correspond to a question language pack, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedQuestionIdDto question_language_pack = 13; + } +} + +// TODO: Downloading topic summaries & representing topics, topic summaries, stories, and skills +// probably requires having a similar language representation as those used for language packs (so +// that language changes can trigger re-downloads). + +// An identifier used to download a specific structure at a particular content version as part of +// the topic content API. It's expected that the server-provided structure will utilize the provided +// proto version requested by the client. +// +// Note that there is a special caveat on the "global-ness" of the identifiers reference here. See +// VersionedStructureDetails for more details as it shares the same caveat. +message DownloadRequestStructureIdentifierDto { + option (org.oppia.proto.v1.versions.api_proto_version_type) = TOPIC_CONTENT_REQUEST_RESPONSE_PROTO_VERSION; + + // The content (creator-affected) version of the request. See the similar field in + // VersionedStructureDetails for more details on what this value represents. + uint32 content_version = 1; + + // The type of structure being requested for downloading. + oneof structure_type { + // Indicates that a topic summary should be downloaded, as specified by a topic ID. + string topic_summary_id = 2; + + // Indicates that a revision card ("subtopic") should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedRevisionCardIdDto revision_card = 3; + + // Indicates that a concept card should be downloaded, as specified by its skill ID. + org.oppia.proto.v1.structure.LocalizedConceptCardIdDto concept_card = 4; + + // Indicates that an exploration should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedExplorationIdDto exploration = 5; + + // Indicates that the list of questions associated with a specific skill ID should be + // downloaded, as specified by that ID. + string question_list_skill_id = 6; + + // Indicates that a question should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedQuestionIdDto question = 7; + + // Indicates that a revision card language pack should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedRevisionCardIdDto revision_card_language_pack = 8; + + // Indicates that a concept card language pack should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedConceptCardIdDto concept_card_language_pack = 9; + + // Indicates that an exploration language pack should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedExplorationIdDto exploration_language_pack = 10; + + // Indicates that a question language pack should be downloaded, as specified by its ID. + org.oppia.proto.v1.structure.LocalizedQuestionIdDto question_language_pack = 11; + } +} diff --git a/org/oppia/proto/v1/structure/BUILD b/org/oppia/proto/v1/structure/BUILD new file mode 100644 index 0000000..bbd8e19 --- /dev/null +++ b/org/oppia/proto/v1/structure/BUILD @@ -0,0 +1,31 @@ +""" +Libraries that define core lesson structure protos. +""" + +load("//defs:defs.bzl", "define_buildifier_tests", "oppia_proto_library") + +oppia_proto_library( + name = "structure_proto", + srcs = [ + "classroom.proto", + "concept_card.proto", + "exploration.proto", + "image.proto", + "languages.proto", + "objects.proto", + "question.proto", + "revision_card.proto", + "state.proto", + "topic_summary.proto", + ], + relative_parent_dir_path = "org/oppia/proto/v1/structure", + visibility = [ + "//:api_visibility", + "//:proto_impl_visibility", + ], + deps = [ + "//org/oppia/proto/v1/versions:versions_proto", + ], +) + +define_buildifier_tests() diff --git a/org/oppia/proto/v1/structure/classroom.proto b/org/oppia/proto/v1/structure/classroom.proto new file mode 100644 index 0000000..37a7fd2 --- /dev/null +++ b/org/oppia/proto/v1/structure/classroom.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents the summary of a classroom whose topics are available to the user to view and possibly +// download. Note tha topics are considered the primary top-level learning 'unit' even though +// they're organized categorically into classrooms. +message ClassroomDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = CLASSROOM_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ClassroomProtoVersion proto_version = 1; + + // The globally unique ID of the classroom. + string id = 2; + + // The localizable name of the classroom. + SubtitledTextDto name = 3; + + // The list of topics contained within this classroom. These topics do not have to be available + // for the local client to download (instead, this list will comprise of all published or planned + // topics for the classroom). + repeated string topic_ids = 4; + + // The localized text, images, and voiceovers of this classroom. + ContentLocalizationsDto localizations = 5; +} diff --git a/org/oppia/proto/v1/structure/concept_card.proto b/org/oppia/proto/v1/structure/concept_card.proto new file mode 100644 index 0000000..129782c --- /dev/null +++ b/org/oppia/proto/v1/structure/concept_card.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents a single concept card that can be displayed for a specific skill. Skills are expected +// to have exactly one concept card associated with them. Concept cards provide a lightweight +// reminder experience for learners that can help them get unstuck when they might have forgotten +// aspects of a certain skill that they need to demonstrate in a practice session or exploration. +message ConceptCardDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = CONCEPT_CARD_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ConceptCardProtoVersion proto_version = 1; + + // The ID corresponding to the skill this concept is representing. + string skill_id = 2; + + // A localizable description for the skill. + SubtitledTextDto description = 3; + + // The localizable explanation of the skill that is to be shown in the concept card. + SubtitledTextDto explanation = 4; + + // A list of worked examples to present to the learner. + repeated WorkedExampleDto worked_examples = 5; + + // The default localized text, images, and voiceovers of this concept card. + ContentLocalizationDto default_localization = 6; + + // The version of the contents of this concept card (which corresponds to the 'version' field for + // the concept card's skill on the server). + uint32 content_version = 7; + + // Represents a worked example (which provides a sample question and answer for the learner to + // read and learn from). + message WorkedExampleDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = CONCEPT_CARD_PROTO_VERSION; + + // The localizable question for this worked example. + SubtitledTextDto question = 1; + + // The localizable explanation for how to solve this worked example. + SubtitledTextDto explanation = 2; + } +} + +// Represents a localized language pack corresponding to both a single concept card and a single +// language. +message ConceptCardLanguagePackDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = CONCEPT_CARD_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ConceptCardProtoVersion proto_version = 1; + + // The ID of the pack. + LocalizedConceptCardIdDto id = 2; + + // The language-specific localized text, images, and voiceovers of this concept card. + ContentLocalizationDto localization = 3; + + // The version of the pack's contents (e.g. specific translations). + uint32 content_version = 4; +} + +// Represents the identifier for a localized concept card, either indicating the concept card's +// default language, or referencing a language pack for the concept card. +message LocalizedConceptCardIdDto { + // This proto message type is shared among multiple protos, so it has no direct migration pathway. + // Changes here should be carefully done in a way that does not break compatibility or assumptions + // across all relevant proto versions, or the proto should be duplicated according to whichever + // proto requires alterations. + option (org.oppia.proto.v1.versions.structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + + // The ID corresponding to the skill the concept card is representing. + string skill_id = 1; + + // The language being localized either as the default for the concept card, or provided by a + // language pack. + LanguageType language = 2; +} diff --git a/org/oppia/proto/v1/structure/exploration.proto b/org/oppia/proto/v1/structure/exploration.proto new file mode 100644 index 0000000..bd32d8f --- /dev/null +++ b/org/oppia/proto/v1/structure/exploration.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/structure/state.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents a single exploration that can be played by a learner. Explorations are interactive +// lessons that are meant to teach or build on specific skills. +message ExplorationDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = EXPLORATION_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ExplorationProtoVersion proto_version = 1; + + // The ID of the exploration. + string id = 2; + + // The localizable title of the exploration. + SubtitledTextDto title = 3; + + // The name of the initial state of the exploration. This corresponds to keys in the 'states' + // field. + string init_state_name = 4; + + // The mapping from state names to state objects. State names are not meant to be + // learner-readable. + map states = 5; + + // The version of the contents of this exploration (which corresponds to the 'version' field on + // the server). + uint32 content_version = 6; + + // The default localized text, images, and voiceovers of this exploration. + ContentLocalizationDto default_localization = 7; +} + +// Represents a localized language pack corresponding to both a single exploration and a single +// language. +message ExplorationLanguagePackDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = EXPLORATION_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ExplorationProtoVersion proto_version = 1; + + // The ID of the pack. + LocalizedExplorationIdDto id = 2; + + // The language-specific localized text, images, and voiceovers of this exploration. + ContentLocalizationDto localization = 3; + + // The version of the pack's contents (e.g. specific translations). + uint32 content_version = 4; +} + +// Represents the identifier for a localized exploration, either indicating the exploration's +// default language, or referencing a language pack for the exploration. +message LocalizedExplorationIdDto { + // This proto message type is shared among multiple protos, so it has no direct migration pathway. + // Changes here should be carefully done in a way that does not break compatibility or assumptions + // across all relevant proto versions, or the proto should be duplicated according to whichever + // proto requires alterations. + option (org.oppia.proto.v1.versions.structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + + // The ID of the exploration. + string exploration_id = 1; + + // The language being localized either as the default for the exploration, or provided by a + // language pack. + LanguageType language = 2; +} diff --git a/org/oppia/proto/v1/structure/image.proto b/org/oppia/proto/v1/structure/image.proto new file mode 100644 index 0000000..3cbf7cd --- /dev/null +++ b/org/oppia/proto/v1/structure/image.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents a single thumbnail image that may be shown for various lesson structures (including +// topics, stories, chapters, and revision cards). Note that while this message doesn't define a +// particular image resolution, it's expected that: +// - Topic and story thumbnails should work in both 4:3 and 16:9 contexts +// - Revision card thumbnails should work in 4:3 contexts +// - Chapter (exploration) thumbnaisl should work in 16:9 contexts +// - All other thumbnails should default to 4:3 (since that can be made workable to 16:9) +message ThumbnailDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = IMAGE_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ImageProtoVersion proto_version = 1; + + // A reference to the actual thumbnail image. + ReferencedImageDto referenced_image = 2; + + // The RGB background color to be shown behind the thumbnail image. Note that this is given in + // RGB888 format, following local endianness (which is automatically handled by protobuf). There + // is never expected to be an alpha component on this color, but it's also not guaranteed that the + // upper 8 bits will be zeroed (so it's recommended to always mask those out when extracting the + // red component of the color, or when treating it as a proper RGB value). + uint32 background_color_rgb = 3; +} + +// Represents an image that may be referenced by various topic structures. +message ReferencedImageDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = IMAGE_PROTO_VERSION; + + // The name of the image file. This will never be a URL (and can always be expected to just be a + // filename). The image is stored in GCS static file storage and must be downloaded separately by + // the client. The client can expect that this image is immutable on the server. Further, the + // client can also assume that all images are one of four formats: + // - PNG (indicated with a .png extension) + // - WebP (indicated with a .webp extension) + // - SVG (indicated with a .svg extension) + // - SVGZ (indicated with a .svgz extension) + string filename = 1; + + // The size of the voiceover file, in bytes. + uint32 file_size_bytes = 2; +} + +// Represents a list of images that may be referenced by various topic structures. +message ReferencedImageListDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = IMAGE_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.ImageProtoVersion proto_version = 1; + + // The list of referenced images contained in the list. + repeated ReferencedImageDto referenced_images = 2; +} diff --git a/org/oppia/proto/v1/structure/languages.proto b/org/oppia/proto/v1/structure/languages.proto new file mode 100644 index 0000000..395790a --- /dev/null +++ b/org/oppia/proto/v1/structure/languages.proto @@ -0,0 +1,174 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/image.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// NOTE: many of the structures in this file share a common LanguageProtosVersion version. See the +// documentation on that structure for details as to why. + +// The language types supported by this API. These are used both for content string localization and +// to represent voiceover subtitles. Note that any changes to this structure will correspond to +// changes to LanguageProtosVersion (similar to the proto structures that use this version below). +// Note also that from a design perspective, languages should always be represented using this as a +// strong type rather than string-based language codes which can introduce support issues and +// inconsistencies. +enum LanguageType { + option (org.oppia.proto.v1.versions.structure_enum_version_type) = LANGUAGE_PROTOS_VERSION; + + // The default language. This generally corresponds to cases when a language isn't defined, is + // unsupported, or it's not known whether the language is supported. Clients are expected to + // default to no-op behaviors when they encounter this language type. + LANGUAGE_CODE_UNSPECIFIED = 0; + + // Corresponds to general English (that is, not particularly regionally tied). This is the assumed + // default language for all content strings. + ENGLISH = 1; + + // Corresponds to general Arabic (that is, not particularly regionally tied). + ARABIC = 2; + + // Corresponds to general Hindi (that is, not particularly regionally tied). + HINDI = 3; + + // Corresponds to the macaronic language Hinglish which combines Hindi and English. This + // corresponds to a generic 'Hinglish' which combines localisms from different areas, but is not + // predominately tied to a particular region. + HINGLISH = 4; + + // Corresponds to a regional Portuguese that's primarily used in Brazil. + BRAZILIAN_PORTUGUESE = 5; + + // Corresponds to general Swahili (that is, not particularly regionally tied). + SWAHILI = 6; + + // Corresponds to the creole language Naija that's predominately used in Nigeria. + NIGERIAN_PIDGIN = 7; +} + +// Represents a block of (subtitled) text. This structure is used for content strings so that they +// may be supplemented with audio translations, or localized by replacing the text string with a +// language-specific translation of it. +// +// Note that this represents both ASCII & Unicode HTML & plain text strings, and combines two +// different Oppia's backend structures ('SubtitledHtml' and 'SubtitledUnicode'). Protobuf takes +// care of properly representing Unicode strings internally by using UTF-8 encoding, so no special +// care is needed by implementations or clients. +message SubtitledTextDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.LanguageProtosVersion protos_version = 1; + + // The content ID for this text that can be used to retrieve a voiceover or translation + // representation of this text. This ID is unique in its local context (e.g. a 'State' for + // subtitled texts used within states). + string content_id = 2; +} + +// Represents all localizations of a particular activity (like an exploration or topic) for all +// supported languages of that activity. See ContentLocalizationDto for more details on the specific +// content included. +message ContentLocalizationsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.LanguageProtosVersion protos_version = 1; + + // The default mapping (including the language that the activity was originally created in). This + // contains the base strings and images of the activity that will be used as fallbacks if there + // are no corresponding localizations, as per the behavior of the client. + ContentLocalizationDto default_mapping = 2; + + // A list of mappings from language to content localization mappings (see the message's + // documentation for specifics on the latter). A missing mapping indicates no localization support + // for the corresponding language. + repeated ContentLocalizationDto localizations = 3; +} + +// Represents a language-specific localization of a top-level activity (like an exploration or a +// topic). This message is meant to contain all of the necesary data to fully localize that specific +// activity to the specified language. The mapping will also only contain localizations for that +// specific activity, and not any referenced ones (e.g. the topic itself but not its subtopics). +message ContentLocalizationDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.LanguageProtosVersion protos_version = 1; + + // The language corresponding to this mapping. + LanguageType language = 2; + + // The mapping of SubtitledText content IDs to their corresponding localized text representations. + map localizable_text_content_mapping = 3; + + // The mapping of SubtitledText content IDs to their corresponding voiceover files. + map voiceover_content_mapping = 4; + + // The thumbnail that should be used for this activity when it's viewed in the mapping's + // corresponding language. This field's absence indicates that the default thumbnail should be + // used. + ThumbnailDto thumbnail = 5; + + // The list of images localized to this mapping (within content). + ReferencedImageListDto localized_image_list = 6; +} + +// Represents an audio file that, when played, reads out content text in a particular language. +message VoiceoverFileDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // The name of the voiceover audio file. This will never be a URL (and can always be expected to + // just be a filename). The audio file is stored in GCS static file storage and must be downloaded + // or streamed separately by the client. The client can expect that this file is immutable on the + // server. Further, the client can also assume that all audio files are always of the format MP3. + // There's no support for transcoding audio at a given bitrate, it's always fixed based on the + // uploaded bitrate (as managed by Oppia's backend). + string filename = 1; + + // The size of the voiceover file, in bytes. + uint32 file_size_bytes = 2; + + // The duration of the voiceover audio track, in seconds. + float duration_secs = 3; +} + +// Represents a textual representation of specific content text in a particular language. +message LocalizableTextDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // The data format of the source material for this localization. + oneof data_format { + // Indicates that this localization corresponds to a single piece of text. + SingleLocalizableTextDto single_localizable_text = 1; + + // Indicates that this localization corresponds to a list of texts. + SetOfLocalizableTextDto set_of_localizable_text = 2; + } +} + +// Represents the localization of a single text string (which may be either plain text or HTML, and +// should always be expected to potentially contain Unicode characters). +message SingleLocalizableTextDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // TODO: Add support for fully structured HTML (to ensure proper RTE & structure validation and versioning). Also, differentiate between HTML & non-HTML. + // The human-readable and localized text. + string text = 1; +} + +// Represents a set of localized strings. As indicated in SingleLocalizableTextDto, each string may +// be either plain text or HTML, and can contain Unicode characters. +// +// Note that there are no guarantees of the length or order of each element of the set to correspond +// to localizations of this same text in other languages. +message SetOfLocalizableTextDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = LANGUAGE_PROTOS_VERSION; + + // The list of human-readable localized strings. + repeated string text = 1; +} diff --git a/org/oppia/proto/v1/structure/objects.proto b/org/oppia/proto/v1/structure/objects.proto new file mode 100644 index 0000000..2ce0ff9 --- /dev/null +++ b/org/oppia/proto/v1/structure/objects.proto @@ -0,0 +1,136 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// TODO: Figure out how to cleanly organize and version this file's protos. Some are specific to +// rule inputs, some are used as solution answers, and some can correspond to both. Perhaps +// structures should be duplicated such that there are distinct answer & input proto files, with +// all protos versioned by the state proto structure version. This probably has the cleaner typing +// even if it is slightly duplicative. That being said, some core structures could easily be +// isolated and shared (such as fractions & normalized point). More thought is needed here. + +// Proto message representing a single fraction or mixed number. +message FractionDto { + // Whether the fraction is negative. + bool is_negative = 1; + + // The whole number part of the fraction. + uint32 whole_number = 2; + + // The numerator of the fraction. + uint32 numerator = 3; + + // The denominator of the fraction. + uint32 denominator = 4; +} + +// Proto representing a single content ID for an HTML field. This content ID is used to map HTML +// fields to their corresponding translations and voiceovers (see e.g. the WrittenTranslations +// substructure of the corresponding State). +message TranslatableHtmlContentIdDto { + // The content ID for the HTML field, which is unique within a given State. + string content_id = 1; +} + +// Proto representing a set of TranslatableHtmlContentIds. +message SetOfTranslatableHtmlContentIdsDto { + // The set of content IDs. + repeated TranslatableHtmlContentIdDto content_ids = 1; +} + +// Proto representing a list of SetOfTranslatableHtmlContentIds. +message ListOfSetsOfTranslatableHtmlContentIdsDto { + // The sets of content IDs. + repeated SetOfTranslatableHtmlContentIdsDto content_id_sets = 1; +} + +// Proto representing a 2D point for the ImageRegion interaction type. +message NormalizedPoint2dDto { + // Relative x-coordinate of the point within the image. This lies within the range [0, 1] and + // measures horizontal distance (with the left boundary of the image as the origin). + double x = 1; + + // Relative y-coordinate of the point within the image. This lies within the range [0, 1] and + // measures vertical distance (with the top boundary of the image as the origin). + double y = 2; +} + +// Proto representing a ratio object in list form, e.g. [1,2,3] for 1:2:3. +message RatioExpressionDto { + // List of components in the ratio. The list is expected to have more than one element. + repeated uint32 components = 1; +} + +// Proto representing a translatable set of normalized strings. This arises in the context of rules, +// which may check the learner's answer against one of a given set of strings. The corresponding +// replacement strings for a different language can be found in the WrittenTranslations structure of +// the containing State. +message TranslatableSetOfNormalizedStringDto { + // The content ID corresponding to this set of normalized strings where the activity's + // translations should include a list of translated strings corresponding to this content ID. + string content_id = 1; +} + +// Proto representing an image that is overlaid with labeled regions. +message ImageWithRegionsDto { + // The filepath of the image, which will be prefixed with '[exploration_id]/assets'. + string image_file_path = 1; + + // The labeled regions of this image. + repeated LabeledRegionDto labeled_regions = 2; + + // Proto representing a labeled region of the image. + message LabeledRegionDto { + // The name of the region's label. + string label = 1; + + // The type of the region. + oneof region_type { + // A rectangle that is normalized so that its boundary coordinates are within the range [0,1], + // where 0 corresponds to one end of the image and 1 to the other. + NormalizedRectangle2dDto normalized_rectangle_2d = 2; + } + + // Proto representing a 2D rectangle defined using normalized coordinates relative to the image. + // The origin of the coordinate system is at the top left of the image. The x-coordinate + // measures horizontal distance (to the right), and the y-coordinate measures vertical distance + // (downwards). + message NormalizedRectangle2dDto { + // The top-left corner of the rectangle, in normalized coordinates. + NormalizedPoint2dDto top_left = 1; + + // The bottom-right corner of the rectangle, in normalized coordinates. + NormalizedPoint2dDto bottom_right = 2; + } + } +} + +// Proto representing a physical quantity with both a numeric and unit component. The numeric +// portion can take different forms (such as a real number or a fraction). +message NumberWithUnitsDto { + // The units corresponding to this physical quantity. There may be more than one, e.g. m/s^2 would + // have one unit of m^1 and a second unit of s^-2. + repeated Unit units = 1; + + // The type of number represented in this physical quantity. + oneof type { + // Corresponds to a real numeric quantity, such as 10.5m/s^2. + double real = 2; + + // Corresponds to a fractional numeric quantity, such as 1/2m/s^2. + FractionDto fraction = 3; + } + + // Represents either a multiplication or division unit, such as m/s. + message Unit { + // The human-readable name of the unit, such as 'm' for meters. + string label = 1; + + // The exponent of the unit, such as '1' for 'm' in 2m, or '-2' for 's' in 2m/s^2. The sign of + // this exponent indicates whether this unit is a multiplied or divided unit. + int32 exponent = 2; + } +} diff --git a/org/oppia/proto/v1/structure/question.proto b/org/oppia/proto/v1/structure/question.proto new file mode 100644 index 0000000..d644bfc --- /dev/null +++ b/org/oppia/proto/v1/structure/question.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/structure/state.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents a single question corresponding to one or more skills, to be used in practice or +// training sessions. Note that skills may have multiple questions associated with them, and a +// single question may correspond to more than one skill. +// +// Questions are not directly tied to topics, but clients are expected to initiate training sessions +// by picking questions that correspond to the skills of the topic's subtopics. +message QuestionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = QUESTION_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.QuestionProtoVersion proto_version = 1; + + // The ID of the question. + string id = 2; + + // The State object that makes up the main part of this question. + StateDto question_state = 3; + + // The list of all skill IDs that are linked to this question. + repeated string linked_skill_ids = 4; + + // The version of the contents of this question. + uint32 content_version = 5; + + // The default localized text, images, and voiceovers of this question. + ContentLocalizationDto default_localization = 6; +} + +// Represents a localized language pack corresponding to both a single question and a single +// language. +message QuestionLanguagePackDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = QUESTION_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.QuestionProtoVersion proto_version = 1; + + // The ID of the pack. + LocalizedQuestionIdDto id = 2; + + // The language-specific localized text, images, and voiceovers of this question. + ContentLocalizationDto localization = 3; + + // The version of the pack's contents (e.g. specific translations). + uint32 content_version = 4; +} + +// Represents the identifier for a localized question, either indicating the question's default +// language, or referencing a language pack for the question. +message LocalizedQuestionIdDto { + // This proto message type is shared among multiple protos, so it has no direct migration pathway. + // Changes here should be carefully done in a way that does not break compatibility or assumptions + // across all relevant proto versions, or the proto should be duplicated according to whichever + // proto requires alterations. + option (org.oppia.proto.v1.versions.structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + + // The ID of the question. + string question_id = 1; + + // The language being localized either as the default for the question, or provided by a language + // pack. + LanguageType language = 2; +} + diff --git a/org/oppia/proto/v1/structure/revision_card.proto b/org/oppia/proto/v1/structure/revision_card.proto new file mode 100644 index 0000000..6179e0b --- /dev/null +++ b/org/oppia/proto/v1/structure/revision_card.proto @@ -0,0 +1,88 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents a revision card that can be displayed for a specific subtopic. Revision cards are +// longer reference cards for categories of skills taught by a topic, and are meant to be used on an +// ad hoc basis by learners when they need to refresh previously learned skills before starting a +// practice session or new exploration. +// +// Topics may contain one or more subtopics, but subtopics only ever belong to a single topic. +// Each subtopic always has a single corresponding revision card. +message RevisionCardDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = REVISION_CARD_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.RevisionCardProtoVersion proto_version = 1; + + // The structured ID of the subtopic corresponding to this revision card. + SubtopicPageIdDto id = 2; + + // The localizable title of the revision card. + SubtitledTextDto title = 3; + + // The main content of this revision card. + SubtitledTextDto content = 4; + + // The default localized text, images, and voiceovers of this revision card. + ContentLocalizationDto default_localization = 5; + + // The version of the revision card's contents. + uint32 content_version = 6; +} + +// Represents a localized language pack corresponding to both a single revision card and a single +// language. +message RevisionCardLanguagePackDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = REVISION_CARD_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.RevisionCardProtoVersion proto_version = 1; + + // The ID of the pack. + LocalizedRevisionCardIdDto id = 2; + + // The language-specific localized text, images, and voiceovers of this revision card. + ContentLocalizationDto localization = 3; + + // The version of the pack's contents (e.g. specific translations). + uint32 content_version = 4; +} + +// Represents the identifier for a subtopic (which are tightly tied to specific topics). +message SubtopicPageIdDto { + // This proto message type is shared among multiple protos, so it has no direct migration pathway. + // Changes here should be carefully done in a way that does not break compatibility or assumptions + // across all relevant proto versions, or the proto should be duplicated according to whichever + // proto requires alterations. + option (org.oppia.proto.v1.versions.structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + + // The ID of the topic that contains this subtopic. + string topic_id = 1; + + // The index-based ID of the subtopic. + uint32 subtopic_index = 2; +} + +// Represents the identifier for a localized revision card, either indicating the revision card's +// default language, or referencing a language pack for the revision card. +message LocalizedRevisionCardIdDto { + // This proto message type is shared among multiple protos, so it has no direct migration pathway. + // Changes here should be carefully done in a way that does not break compatibility or assumptions + // across all relevant proto versions, or the proto should be duplicated according to whichever + // proto requires alterations. + option (org.oppia.proto.v1.versions.structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + + // The structured ID of the subtopic corresponding to the revision card. + SubtopicPageIdDto id = 1; + + // The language being localized either as the default for the revision card, or provided by a + // language pack. + LanguageType language = 2; +} diff --git a/org/oppia/proto/v1/structure/state.proto b/org/oppia/proto/v1/structure/state.proto new file mode 100644 index 0000000..4bb4a18 --- /dev/null +++ b/org/oppia/proto/v1/structure/state.proto @@ -0,0 +1,1497 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/structure/objects.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents a 'State' within a learning session (such as a practice quiz or exploration). A +// 'State' corresponds to a visual card shown to the learner with initial content (often a +// question) along with an 'interaction' (a type of answer that the user can input, such as +// fractions or text). When users submit answers to this interaction, they receive feedback in the +// form of a textual response and may be routed to the same state, or to a new state (which may be a +// repeat of one they've completed in the past). +message StateDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.StateProtoVersion proto_version = 1; + + // The main content of the state which is the first thing shown to a learner viewing this state. + SubtitledTextDto content = 2; + + // The interaction of the state (which must be interacted with by the learner in order to + // proceed). + InteractionInstanceDto interaction = 3; +} + +// Represents an instance of a single interaction within a state. Note that interactions are +// specialized so that the client can perform very specific analysis on the user's answer as to +// provide more useful or targeted feedback (and to help detect specific misconceptions that the +// learner may be having). Specific interactions may also provide interaction-specific error +// messages (such as malformed fractions). +// +// Note that interactions work by matching user answers to rules that are organized into 'answer +// groups'. If an answer group is matched, the textual feedback corresponding to that group is shown +// to the user and the route defined by that group is taken (i.e. the defined state is the next +// state shown to the user). This route & the feedback are organized into a concept called an +// 'outcome'. Many interactions have 'default' outcomes that are selected if no answers match (or in +// cases when the user doesn't have choice on providing dynamic answers). +// +// For specifics, answer groups are selected by matching an answer to one or more 'rules' in the +// answer group (defined via a 'RuleSpec'). Rules represent inputs to predefined classifiers that +// can be used to match an answer. +// +// Interactions may also show hints to the learner if they get stuck and, after a certain number of +// hints, may offer to show the solution to the interaction. Not all interactions support hints and +// solutions. +// +// Specific interaction messages below more explicitly define what they support, and the types of +// answers that they expect. +// +// Finally, interactions may have 'customization arguments' which are creator-provided options to +// specialize the interaction for that particular context (such as tweaking hint or placeholder text +// for input boxes). +message InteractionInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The type of the interaction. + oneof interaction_type { + // An interaction that takes the form of a simple 'Continue' button. + ContinueInstanceDto continue_instance = 1; + + // An interaction for inputting fractions. + FractionInputInstanceDto fraction_input = 2; + + // An interaction for selecting one or more items from a list. + ItemSelectionInputInstanceDto item_selection_input = 3; + + // An interaction for selecting a single option among multiple presented choices. + MultipleChoiceInputInstanceDto multiple_choice_input = 4; + + // An interaction for inputting numbers. + NumericInputInstanceDto numeric_input = 5; + + // An interaction for inputting a line of text. + TextInputInstanceDto text_input = 6; + + // An interaction for rearranging a list of items by dragging & dropping them. + DragAndDropSortInputInstanceDto drag_and_drop_sort_input = 7; + + // An interaction for selecting a learner-identified part of an image. + ImageClickInputInstanceDto image_click_input = 8; + + // An interaction for inputting ratios. + RatioExpressionInputInstanceDto ratio_expression_input = 9; + + // An interaction for inputting algebric expressions. + AlgebraicExpressionInputInstanceDto algebraic_expression_input = 10; + + // An interaction for inputting math equations. + MathEquationInputInstanceDto math_equation_input = 11; + + // An interaction for inputting numeric equations. + NumericExpressionInputInstanceDto numeric_expression_input = 12; + + // An interaction for inputting numbers with units attached. + NumberWithUnitsInputInstanceDto number_with_units_input = 14; + + // An interaction that represents the end of an exploration and takes no direct user input other + // than navigating away from the exploration. This is not used in question play sessions. + EndExplorationInstanceDto end_exploration = 13; + } +} + +// Represents an interaction where the user is presented with a simple 'Continue' button. Details: +// - Answer type: none (N/A since no answers are submitted for this interaction) +// - Has a default outcome: yes (it's actually the only outcome) +// - Has support for showing hints: no +// - Has support for showing a solution: no +message ContinueInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 2; + + // Continue interactions cannot have custom answer groups, and do not support solutions. Users + // cannot receive hints since only one answer is possible (clicking the button). + + // Represents the customization arguments available for instances of the continue interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Custom text to use for the 'Continue' button displayed for this interaction rather than using + // the button's default text ('Continue'). + SubtitledTextDto button_text = 1; + } +} + +// Represents an interaction where the user is expected to input a textual fraction. Details: +// - Answer type: Fraction +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message FractionInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the fraction input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Specifics whether fractions must be provided in simplest form (that is, '2/4' would not be + // allowed, but '1/2' would be). + bool requires_simplest_form = 1; + + // Specifics whether to allow improper fractions (i.e. whether fractions like '5/4' would be + // allowed). + bool allow_improper_fractions = 2; + + // Specifics whether the fraction input can contain an integer part, that is, whether it can be + // a mixed fraction like '1 2/3'. + bool allow_nonzero_integer_part = 3; + + // Specifies the placeholder/hint text that should be shown in the fraction input box prior to + // the user starting to enter an answer. Note that this will override the client's default text. + SubtitledTextDto placeholder = 4; + } + + // Represents the specific solution available to instances of the fraction input interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + FractionDto correct_answer = 2; + } + + // Represents the specific answer group used by instances of the fraction input interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the fraction input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for fraction answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + IsExactlyEqualToSpecDto is_exactly_equal_to = 1; + + // See the specific message's documentation for details on this rule type. + IsEquivalentToSpecDto is_equivalent_to = 2; + + // See the specific message's documentation for details on this rule type. + IsEquivalentToAndInSimplestFormSpecDto is_equivalent_to_and_in_simplest_form = 3; + + // See the specific message's documentation for details on this rule type. + IsLessThanSpecDto is_less_than = 4; + + // See the specific message's documentation for details on this rule type. + IsGreaterThanSpecDto is_greater_than = 5; + + // See the specific message's documentation for details on this rule type. + HasNumeratorEqualToSpecDto has_numerator_equal_to = 6; + + // See the specific message's documentation for details on this rule type. + HasDenominatorEqualToSpecDto has_denominator_equal_to = 7; + + // See the specific message's documentation for details on this rule type. + HasIntegerPartEqualToSpecDto has_integer_part_equal_to = 8; + + // See the specific message's documentation for details on this rule type. + HasNoFractionalPartSpecDto has_no_fractional_part = 9; + + // See the specific message's documentation for details on this rule type. + HasFractionalPartExactlyEqualToSpecDto has_fractional_part_exactly_equal_to = 10; + } + + // Represents a rule spec for checking whether an answer exactly matches a creator-specified + // fraction. + message IsExactlyEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified fraction to match against the user's answer. + FractionDto input = 1; + } + + // Represents a rule spec for checking whether an answer is equivalent to a creator-specified + // fraction (but not necessarily exactly the same). + message IsEquivalentToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified fraction to match against the user's answer. + FractionDto input = 1; + } + + // Represents a rule spec for checking whether an answer is equivalent to a creator-specified + // fraction, in a similar way to IsEquivalentToSpec, except this also requires that the user's + // answer is in simplest form. + message IsEquivalentToAndInSimplestFormSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified fraction to match against the user's answer. + FractionDto input = 1; + } + + // Represents a rule spec for checking whether an answer is mathematically less than a + // creator-specified fraction. + message IsLessThanSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified fraction to match against the user's answer. + FractionDto input = 1; + } + + // Represents a rule spec for checking whether an answer is mathematically greater than a + // creator-specified fraction. + message IsGreaterThanSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified fraction to match against the user's answer. + FractionDto input = 1; + } + + // Represents a rule spec for checking whether an answer has a numerator equal to a + // creator-specified numerator. + message HasNumeratorEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified numerator to match against the user's answer. + int32 input = 1; + } + + // Represents a rule spec for checking whether an answer has a denominator equal to a + // creator-specified denominator. + message HasDenominatorEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified denominator to match against the user's answer. + uint32 input = 1; + } + + // Represents a rule spec for checking whether an answer has an integer part equal to a + // creator-specified integer part. + message HasIntegerPartEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified integer part to match against the user's answer. + int32 input = 1; + } + + // Represents a rule spec for checking whether an answer is missing its fractional part (i.e. + // that it's just a whole number). + message HasNoFractionalPartSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // No inputs for this rule spec. + } + + // Represents a rule spec for checking whether an answer has a fractional part exactly equal to + // a creator-specified fraction (whose integer part is ignored during classification). + message HasFractionalPartExactlyEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified fraction to match against the user's answer. + FractionDto input = 1; + } + } +} + +// Represents an interaction where the user can select one or more options from a list of choices. +// Details: +// - Answer type: SetOfTranslatableHtmlContentIds +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: no +message ItemSelectionInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // Item selection does not support solutions. + + // Represents the customization arguments available for instances of the item selection input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Specifies the minimum number of items that must be selected before an answer can be + // submitted. + uint32 min_allowable_selection_count = 1; + + // Specifies the maximum number of items that may be selected. If this '1' then the options will + // be displayed as radio buttons instead of checkboxes. + uint32 max_allowable_selection_count = 2; + + // The list of selectable items. + repeated SubtitledTextDto choices = 3; + } + + // Represents the specific answer group used by instances of the item selection input interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the item selection input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for item selection answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + EqualsSpecDto equals = 1; + + // See the specific message's documentation for details on this rule type. + ContainsAtLeastOneOfSpecDto contains_at_least_one_of = 2; + + // See the specific message's documentation for details on this rule type. + DoesNotContainAtLeastOneOfSpecDto does_not_contain_at_least_one_of = 3; + + // See the specific message's documentation for details on this rule type. + IsProperSubsetOfSpecDto is_proper_subset_of = 4; + } + + // Represents a rule spec for checking whether an answer has exactly the same items as a + // creator-specified list of item selections. Note that order does not matter for these items. + message EqualsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified list of item selections to match against the user's + // answer. + SetOfTranslatableHtmlContentIdsDto input = 1; + } + + // Represents a rule spec for checking whether an answer has at least one of the items defined + // in a creator-specified list of item selections. Note that order does not matter for these + // items. + message ContainsAtLeastOneOfSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified list of item selections to match against the user's + // answer. + SetOfTranslatableHtmlContentIdsDto input = 1; + } + + // Represents a rule spec for checking whether an answer has none of the items defined in a + // creator-specified list of item selections. Note that order does not matter for these items. + message DoesNotContainAtLeastOneOfSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified list of item selections to match against the user's + // answer. + SetOfTranslatableHtmlContentIdsDto input = 1; + } + + // Represents a rule spec for checking whether an answer is a proper subset of a + // creator-specified list of item selections (treated as a set), that is, that it's fully + // contained in the creator-specified set but is not exactly equal to it. + message IsProperSubsetOfSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified list of item selections to match against the user's + // answer. + SetOfTranslatableHtmlContentIdsDto input = 1; + } + } +} + +// Represents an interaction where the user can select one choice among a list of choices. Details: +// - Answer type: non-negative int (protobuf type: uint32) +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: no +message MultipleChoiceInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // Multiple choice does not support solutions. + + // Represents the customization arguments available for instances of the multiple choice input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The list of selectable items. + repeated SubtitledTextDto choices = 1; + } + + // Represents the specific answer group used by instances of the multiple choice input + // interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the multiple choice input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for multiple choice answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + EqualsSpecDto equals = 1; + } + + // Represents a rule spec for checking whether an answer matches to a creator-specified multiple + // choice option index. + message EqualsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified multiple choice option index to match against the + // user's answer. + uint32 input = 1; + } + } +} + +// Represents an interaction where the user can input an numeric-only answer. Details: +// - Answer type: real number (protobuf type: uint32) +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message NumericInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 1; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 2; + + // The hints corresponding to this interaction. + repeated HintDto hints = 3; + + // The solution corresponding to this interaction. + SolutionDto solution = 4; + + // Numeric input does not have any customization arguments. + + // Represents the specific solution available to instances of the numeric input interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + double correct_answer = 2; + } + + // Represents the specific answer group used by instances of the numeric input interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the numeric input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for numeric input answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + EqualsSpecDto equals = 1; + + // See the specific message's documentation for details on this rule type. + IsLessThanSpecDto is_less_than = 2; + + // See the specific message's documentation for details on this rule type. + IsGreaterThanSpecDto is_greater_than = 3; + + // See the specific message's documentation for details on this rule type. + IsLessThanOrEqualToSpecDto is_less_than_or_equal_to = 4; + + // See the specific message's documentation for details on this rule type. + IsGreaterThanOrEqualToSpecDto is_greater_than_or_equal_to = 5; + + // See the specific message's documentation for details on this rule type. + IsInclusivelyBetweenSpecDto is_inclusively_between = 6; + + // See the specific message's documentation for details on this rule type. + IsWithinToleranceSpecDto is_within_tolerance = 7; + } + + // Represents a rule spec for checking whether an answer exactly* equals a creator-specified real + // value. *Note that the match here uses an epsilon comparison to account for IEEE 754 floating + // point rounding errors. + message EqualsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified real value to match against the user's answer. + double input = 1; + } + + // Represents a rule spec for checking whether an answer is numerically less than a + // creator-specified real value. + message IsLessThanSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified real value to match against the user's answer. + double input = 1; + } + + // Represents a rule spec for checking whether an answer is numerically greater than a + // creator-specified real value. + message IsGreaterThanSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified real value to match against the user's answer. + double input = 1; + } + + // Represents a rule spec for checking whether an answer is numerically less than or exactly + // equal to a creator-specified real value. + message IsLessThanOrEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified real value to match against the user's answer. + double input = 1; + } + + // Represents a rule spec for checking whether an answer is numerically greater than or exactly + // equal to a creator-specified real value. + message IsGreaterThanOrEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified real value to match against the user's answer. + double input = 1; + } + + // Represents a rule spec for checking whether an answer is within a creator-specified fully + // closed/inclusive range. + message IsInclusivelyBetweenSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified inclusive lower bound for comparison against the + // user's answer. + double input_lower_inclusive = 1; + + // Corresponds to the creator-specified inclusive upper bound for comparison against the + // user's answer. + double input_upper_inclusive = 2; + } + + // Represents a rule spec for checking whether an answer is equal to a creator-specified value + // when accounting for a creator-specific tolerance range. + message IsWithinToleranceSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified tolerance to consider when matching against the user's + // answer. + double input_tolerance = 1; + + // Corresponds to the creator-specified value to compare against the user's answer. + double input_compared_value = 2; + } + } +} + +// Represents an interaction where the user can input a line of text. Details: +// - Answer type: normalized string (protobuf type: string) +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +// +// Note that all answers submitted to this interaction undergo 'string normalization' which involves +// trimming leading & trailing whitespace, removing all non-space whitespace, and replacing extra +// spaces such that at most one space separates characters. +message TextInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the text input interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Specifies the placeholder/hint text that should be shown in the text input box prior to the + // user starting to enter an answer. Note that this will override the client's default text. + SubtitledTextDto placeholder = 1; + + // Specifies the number of rows (visual lines) that should be allowed for text input. + uint32 rows = 2; + } + + // Represents the specific solution available to instances of the text input interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + string correct_answer = 2; + } + + // Represents the specific answer group used by instances of the text input interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the text input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for text input answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + EqualsSpecDto equals = 1; + + // See the specific message's documentation for details on this rule type. + StartsWithSpecDto starts_with = 2; + + // See the specific message's documentation for details on this rule type. + ContainsSpecDto contains = 3; + + // See the specific message's documentation for details on this rule type. + FuzzyEqualsSpecDto fuzzy_equals = 4; + } + + // Represents a rule spec for checking whether an answer is exactly equal to (after string + // normalization) to any of the strings in a creator-specified string list. + message EqualsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified set of text input values to match against the user's + // answer. + TranslatableSetOfNormalizedStringDto input = 1; + } + + // Represents a rule spec for checking whether an answer starts with any of the strings in a + // creator-specified string list (with string normalization). + message StartsWithSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified set of text input values to match against the user's + // answer. + TranslatableSetOfNormalizedStringDto input = 1; + } + + // Represents a rule spec for checking whether an answer contains any of the strings in a + // creator-specified string list (with string normalization). + message ContainsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified set of text input values to match against the user's + // answer. + TranslatableSetOfNormalizedStringDto input = 1; + } + + // Represents a rule spec for checking whether an answer fuzzily equals any of the strings in a + // creator-specified string list (with string normalization), that is, whether it equals one of + // the strings with at most one character misspelled. + message FuzzyEqualsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified set of text input values to match against the user's + // answer. + TranslatableSetOfNormalizedStringDto input = 1; + } + } +} + +// Represents an interaction where the user can rearrange a list of options using drag & drop +// functionality. Details: +// - Answer type: ListOfSetsOfTranslatableHtmlContentIds +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message DragAndDropSortInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the drag and drop sort input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The list of items that can be dragged & dropped. + repeated SubtitledTextDto choices = 1; + + // Specifies whether items can be allowed at the same position in the list (i.e. linked or + // unlinked). + bool allow_multiple_items_in_same_position = 2; + } + + // Represents the specific solution available to instances of the drag and drop sort input + // interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + ListOfSetsOfTranslatableHtmlContentIdsDto correct_answer = 2; + } + + // Represents the specific answer group used by instances of the drag and drop sort input + // interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the drag and drop sort input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for drag and drop answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + IsEqualToOrderingSpecDto is_equal_to_ordering = 1; + + // See the specific message's documentation for details on this rule type. + IsEqualToOrderingWithOneItemAtIncorrectPositionSpecDto is_equal_to_ordering_with_one_item_at_incorrect_position = 2; + + // See the specific message's documentation for details on this rule type. + HasElementXAtPositionYSpecDto has_element_x_at_position_y = 3; + + // See the specific message's documentation for details on this rule type. + HasElementXBeforeElementYSpecDto has_element_x_before_element_y = 4; + } + + // Represents a rule spec for checking whether an answer exactly matches a creator-specified + // drag and drop configuration (that is, a list of drag and drop items with some items + // potentially on the same line), with ordering being considered. + message IsEqualToOrderingSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified drag and drop configuration to match against the + // user's answer. + ListOfSetsOfTranslatableHtmlContentIdsDto input = 1; + } + + // Represents a rule spec for checking whether an answer exactly matches a creator-specified + // drag and drop configuration (that is, a list of drag and drop items with some items + // potentially on the same line), with ordering being considered and with exactly one item being + // in the incorrect position. + message IsEqualToOrderingWithOneItemAtIncorrectPositionSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified drag and drop configuration to match against the + // user's answer. + ListOfSetsOfTranslatableHtmlContentIdsDto input = 1; + } + + // Represents a rule spec for checking whether an answer has an element at a creator-specified + // position exactly matched a creator-specified option (which should correspond to one of the + // options defined in the interaction's customization arguments). + message HasElementXAtPositionYSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified single element to match against the user's answer. + TranslatableHtmlContentIdDto element = 1; + + // Corresponds to the creator-specified element index with which to extract from the user's + // answer a value to compare against the defined element. + uint32 position = 2; + } + + // Represents a rule spec for checking whether an answer has two creator-specified elements such + // that one of the particular elements occurs before another one of the specified elements, + // where both elements should correspond to options defined in the interaction's customization + // arguments). + message HasElementXBeforeElementYSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified element that's expected to be contained in the user's + // answer. + TranslatableHtmlContentIdDto considered_element = 1; + + // Corresponds to the creator-specified element that's expected to be contained in the user's + // answer. + TranslatableHtmlContentIdDto later_element = 2; + } + } +} + +// Represents an interaction where the user can select a region of an image. Details: +// - Answer type: ClickOnImage +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: no +message ImageClickInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // Image click input doesn't yet support solutions. + + // Represents the customization arguments available for instances of the image click input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Defines the image to display to the user, and all of the constituent regions that the user + // may selected. + ImageWithRegionsDto image_and_regions = 1; + } + + // Represents the specific answer group used by instances of the image click input interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the image click input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for image click answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + IsInRegionSpecDto is_in_region = 1; + } + + // Represents a rule spec for checking whether an answer corresponds to a creator-specified + // region (as a determination of that region being clicked by the user). + message IsInRegionSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified image region label to match against the user's answer. + string input_region = 1; + } + } +} + +// Represents an interaction where the user can input the ratio between two integers. Details: +// - Answer type: RatioExpression +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message RatioExpressionInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 2; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 3; + + // The hints corresponding to this interaction. + repeated HintDto hints = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the ratio expression input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Specifies the placeholder/hint text that should be shown in the ratio input box prior to the + // user starting to enter an answer. Note that this will override the client's default text. + SubtitledTextDto placeholder = 1; + + // The number of elements that the answer must have, or 0 to indicate that a ratio of any length + // will be accepted. For example, if this is set to '5' then the ratio must have 5 components + // (such as 1:2:3:4:5). + uint32 number_of_terms = 2; + } + + // Represents the specific solution available to instances of the ratio expression input + // interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + RatioExpressionDto correct_answer = 2; + } + + // Represents the specific answer group used by instances of the ratio expression input + // interaction. + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + // Represents specific rule specs used by instances of the ratio expression input interaction. + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Possible rule types for ratio expression answers. + oneof rule_type { + // See the specific message's documentation for details on this rule type. + EqualsSpecDto equals = 1; + + // See the specific message's documentation for details on this rule type. + IsEquivalentSpecDto is_equivalent = 2; + + // See the specific message's documentation for details on this rule type. + HasNumberOfTermsEqualToSpecDto has_number_of_terms_equal_to = 3; + + // See the specific message's documentation for details on this rule type. + HasSpecificTermEqualToSpecDto has_specific_term_equal_to = 4; + } + + // Represents a rule spec for checking whether an answer exactly equals a creator-specified + // ratio expression. + message EqualsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified ratio expression to match against the user's answer. + RatioExpressionDto input = 1; + } + + // Represents a rule spec for checking whether an answer is equivalent to a creator-specified + // ratio expression (e.g. '1:2' amd '2:4' would be considered equivalent, but not equal). + message IsEquivalentSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified ratio expression to match against the user's answer. + RatioExpressionDto input = 1; + } + + // Represents a rule spec for checking whether an answer has a creator-specified number of + // terms. + message HasNumberOfTermsEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified number of terms to match against the user's answer. + uint32 input_term_count = 1; + } + + // Represents a rule spec for checking whether an answer has a creator-specified term value at a + // creator-specified term position within the answer. + message HasSpecificTermEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // Corresponds to the creator-specified term index to match against the user's answer. + uint32 input_term_index = 1; + + // Corresponds to the creator-specified expected term value to match against the user's + // answer. + uint32 input_expected_term_value = 2; + } + } +} + +// Represents an interaction where the user can input the algebric expressions. Details: +// - Answer type: normalized string (protobuf type: string) +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message AlgebraicExpressionInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 2; + + // The hints corresponding to this interaction. + repeated HintDto hints = 3; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the algebric expression input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + repeated string custom_osk_letters = 1; + + bool use_fraction_for_division = 2; + } + + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + oneof rule_type { + MatchesExactlyWithSpecDto matches_exactly_with = 1; + + MatchesUpToTrivialManipulationsSpecDto matches_up_to_trivial_manipulations = 2; + + IsEquivalentToSpecDto is_equivalent_to = 3; + } + + message MatchesExactlyWithSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string algebraic_expression = 1; + } + + message MatchesUpToTrivialManipulationsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string algebraic_expression = 1; + } + + message IsEquivalentToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string algebraic_expression = 1; + } + } + + // Represents the specific solution available to instances of the algebric expression input + // interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + string correct_answer = 2; + } +} + +// Represents an interaction where the user can input the math equaltions. Details: +// - Answer type: normalized string (protobuf type: string) +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message MathEquationInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 2; + + // The hints corresponding to this interaction. + repeated HintDto hints = 3; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the math equation input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + repeated string custom_osk_letters = 1; + + bool use_fraction_for_division = 2; + } + + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + oneof rule_type { + MatchesExactlyWithSpecDto matches_exactly_with = 1; + + MatchesUpToTrivialManipulationsSpecDto matches_up_to_trivial_manipulations = 2; + + IsEquivalentToSpecDto is_equivalent_to = 3; + } + + message MatchesExactlyWithSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string math_equation = 1; + } + + message MatchesUpToTrivialManipulationsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string math_equation = 1; + } + + message IsEquivalentToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string math_equation = 1; + } + } + + // Represents the specific solution available to instances of the math equation input + // interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + string correct_answer = 2; + } +} + +// Represents an interaction where the user can input the numeric expressions. Details: +// - Answer type: normalized string (protobuf type: string) +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message NumericExpressionInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The customization arguments for this interaction. + CustomizationArgsDto customization_args = 1; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 2; + + // The hints corresponding to this interaction. + repeated HintDto hints = 3; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 4; + + // The solution corresponding to this interaction. + SolutionDto solution = 5; + + // Represents the customization arguments available for instances of the numeric expression input + // interaction. + message CustomizationArgsDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + SubtitledTextDto placeholder = 1; + + bool use_fraction_for_division = 2; + } + + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + oneof rule_type { + MatchesExactlyWithSpecDto matches_exactly_with = 1; + + MatchesUpToTrivialManipulationsSpecDto matches_up_to_trivial_manipulations = 2; + + IsEquivalentToSpecDto is_equivalent_to = 3; + } + + message MatchesExactlyWithSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string numeric_expression = 1; + } + + message MatchesUpToTrivialManipulationsSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string numeric_expression = 1; + } + + message IsEquivalentToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + string numeric_expression = 1; + } + } + + // Represents the specific solution available to instances of the numeric expression input + // interaction. + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + string correct_answer = 2; + } +} + +// Represents an interaction where the user can input an numeric answer with units. Details: +// - Answer type: NumberWithUnits +// - Has a default outcome: yes +// - Has support for showing hints: yes +// - Has support for showing a solution: yes +message NumberWithUnitsInputInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The default outcome for this interaction. + OutcomeDto default_outcome = 1; + + // The hints corresponding to this interaction. + repeated HintDto hints = 2; + + // The answer groups for this interaction. + repeated AnswerGroupDto answer_groups = 3; + + // The solution corresponding to this interaction. + SolutionDto solution = 4; + + message AnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the answer group. + BaseAnswerGroupDto base_answer_group = 1; + + // The list of all rules to match for this answer group. + repeated RuleSpecDto rule_specs = 2; + } + + message RuleSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + oneof rule_type { + IsEqualToSpecDto is_equal_to = 1; + + IsEquivalentToSpecDto is_equivalent_to = 2; + } + + message IsEqualToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + NumberWithUnitsDto number_with_units = 1; + } + + message IsEquivalentToSpecDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + NumberWithUnitsDto number_with_units = 1; + } + } + + message SolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The base properties of the solution. + BaseSolutionDto base_solution = 1; + + // The correct answer for this interaction that should be shown to the user. + NumberWithUnitsDto correct_answer = 2; + } +} + +// Represents an interaction where the user has reached the end of the exploration, and has the +// choice to navigate away from it. Details: +// - Answer type: none (N/A since no answers are submitted for this interaction) +// - Has a default outcome: no (N/A since there's no state routing after this state; it's terminal) +// - Has support for showing hints: no +// - Has support for showing a solution: no +message EndExplorationInstanceDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // No answers can be submitted to the end exploration interaction, so there are neither answer + // groups nor solutions. The interaction does have customization arguments, but they aren't + // supported in the Android app. No default outcome is possible since the user takes no + // interactions. No hints can be shown to the user. +} + +// Represents the base structure for an answer group (that is, the group of fields common among all +// interaction answer groups). +message BaseAnswerGroupDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The outcome details for how the platform should response to an answer matched to this group. + OutcomeDto outcome = 1; + + // The misconception (if any) associated with answers matched to this group. + MisconceptionDto tagged_skill_misconception = 2; +} + +// Represents the response the platform should provide for a particular answer. +message OutcomeDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The name of the state to which the user should next be navigated (which may be the same as the + // current state if the learner should try a different answer). + string destination_state = 1; + + // The textual feedback, if any, to show the user based on their response. + SubtitledTextDto feedback = 2; + + // Whether the answer matched to this outcome should be considered 'correct' (which may lead to + // different platform behavior, such as showing a 'correct' banner). Note that some correct + // answers may not be labelled as correct, so this being false or absent does not necessarily + // indicate an answer is wrong. + bool labelled_as_correct = 3; +} + +// Represents a hint that may be shown to the learner to help them get unstuck (in cases when the +// platform detects that the learner may be having difficulty proceeding in a lesson or training +// session). +message HintDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The main content of the hint to show the user. + SubtitledTextDto hint_content = 1; +} + +// Represents the base structure for a solution (that is, the group of fields common among all +// interaction solutions). +message BaseSolutionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The explanation of the solution to show the user. + SubtitledTextDto explanation = 1; +} + +// Represents a single misconception of a particular skill. +message MisconceptionDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = STATE_PROTO_VERSION; + + // The ID of the skill that the user is likely misunderstanding. + string skill_id = 1; + + // The ID of the misconception (for cataloging purposes, such as during training sessions). + string misconception_id = 2; +} diff --git a/org/oppia/proto/v1/structure/topic_summary.proto b/org/oppia/proto/v1/structure/topic_summary.proto new file mode 100644 index 0000000..c6c0357 --- /dev/null +++ b/org/oppia/proto/v1/structure/topic_summary.proto @@ -0,0 +1,203 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.structure; + +import "org/oppia/proto/v1/structure/languages.proto"; +import "org/oppia/proto/v1/versions/structure_versions.proto"; + +option java_package = "org.oppia.proto.v1.structure"; +option java_multiple_files = true; + +// Represents the summary of a topic that is available to the user to download. A topic is a fully +// contained module of lessons, explanations, and practice questions to learn a specific subcategory +// of a subject area (such as 'place values' or 'fractions'). +// +// Topics organize lessons into stories which organize teaching around a plot to help contextualize +// the lessons and to make them more engaging to learners. +// +// Topics organize 'skills' (i.e. granular units of learning) into groups called 'subtopics' which +// have corresponding revision cards that learners may use to get a high-level refresher of the +// corresponding skills. +message DownloadableTopicSummaryDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.TopicSummaryProtoVersion proto_version = 1; + + // The ID of the topic. + string id = 2; + + // The localizable name of the topic. + SubtitledTextDto name = 3; + + // The localizable description of the topic. + SubtitledTextDto description = 4; + + // The list of stories available to play in the topic corresponding to this summary. + repeated StorySummaryDto story_summaries = 5; + + // The list of subtopics available in the topic corresponding to this summary. + repeated SubtopicSummaryDto subtopic_summaries = 6; + + // The version of this topic's contents. + uint32 content_version = 7; + + // IDs of topics that should be completed before this topic is suggested to the user. This list + // may change over time, but it is expected to be relatively stable. + repeated string prerequisite_topic_ids = 8; + + // The localized text, images, and voiceovers of this topic summary. + ContentLocalizationsDto localizations = 9; + + // A list of all skills referenced by the topic represented by this summuary (including in + // rich-text). + repeated SkillSummaryDto referenced_skills = 10; +} + +// Represents the summary of a topic that hasn't yet been released for users. This summary is meant +// to provide as much detail as possible for the indication of a future topic release to be +// meaningful and useful to the user (including a name, description, and estimated release +// time frame). +message UpcomingTopicSummaryDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The version defining the internal structure of this message. + org.oppia.proto.v1.versions.TopicSummaryProtoVersion proto_version = 1; + + // The ID of the topic. + string id = 2; + + // The localizable name of the topic. + SubtitledTextDto name = 3; + + // The localizable description of the topic. + SubtitledTextDto description = 4; + + // The approximated estimation for when this topic will be available for download. + ReleaseTimeEstimation release_time_estimation = 5; + + // Anticipated prerequisite topics that will need to be completed before this topic would be + // suggested. Note that this is not a stable list since the topic is still a work-in-progress. + // It's only meant to provide a preview to learners on what they may want to practice in + // preparation for this topic's release. + string anticipated_prerequisite_topic_ids = 6; + + // The localized text, images, and voiceovers of this topic summary. + ContentLocalizationsDto localizations = 7; + + // A variably granular representation of the estimated release time of a topic. The fields in this + // are provided in-order to provide a more granular estimate, and each subsequent field is + // relative to the previous field. + message ReleaseTimeEstimation { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The year that the topic is expected to be available to download, per the Gregorian calendar. + // This value should always be provided for a valid estimation. A value of 0 indicates that the + // release date is not known for the topic. + int32 year = 1; + + // The quarter within the year above that the topic is expected to be available, per the + // Gregorian calendar. This should always be a value within [0, 4] as each year has only 4 + // quarters. A value of 0 indicates that this estimation is not granular to quarters. This value + // is ignored if year is ignored. + // + // Quarters follow the calendar year, that is, quarter 1 corresponds to January, February, and + // March. + int32 quarter = 2; + + // The month within the quarter defined above that the topic is expected to be available, per + // the Gregorian calendar. This should always be a value within [0, 3] as each quarter has only + // 3 months. A value of 0 indicates that this estimation is not granular to months. This value + // is ignored if quarter is ignored. + int32 month = 3; + + // The day within the month defined above that the topic is expected to be available, per the + // Gregorian calendar. This should always be a value within [0, 31], though the upper bound will + // depend on the specific month estimated for release. A value of 0 indicates that this + // estimation is not granular to days. This value is ignored if month is ignored. + int32 day = 4; + } +} + +// Represents the summary of a story that can be played within a particular topic. Stories aim to +// teach all of the skills covered by a topic (as defined by the topic's subtopics) via a linear +// pathway of 'chapters' (which correspond to explorations). +message StorySummaryDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The ID of the story. + string id = 1; + + // The localizable title of the story. + SubtitledTextDto title = 2; + + // The localizable description of the story. + SubtitledTextDto description = 3; + + // The summaries of all chapters in this story. + repeated ChapterSummaryDto chapters = 4; + + // The version of this story's contents. + uint32 content_version = 5; + + // The localized text, images, and voiceovers of this story summary. + ContentLocalizationsDto localizations = 6; +} + +// Represents the summary of a single chapter within a story of a topic. Each chapter corresponds to +// a single exploration (see Exploration's documentation for more details). +message ChapterSummaryDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The localizable title of the chapter. This is expected to exactly match, in content, the title + // of the exploration associated to this chapter. + SubtitledTextDto title = 1; + + // A localizable explanation for what the learner is expected to learn when playing this chapter. + SubtitledTextDto description = 2; + + // The ID of the exploration to which this ChapterSummary is associated. + string exploration_id = 3; + + // The version of this chapter's contents. This is expected to always match the exploration + // associated to this chapter. + uint32 content_version = 4; + + // The localized text, images, and voiceovers of this chapter summary. + ContentLocalizationsDto localizations = 5; +} + +// Represents the summary of a subtopic (that is, a group of skills logically organized within a +// topic). +message SubtopicSummaryDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The index of the subtopic (within its correspnoding topic). This helps to indicate the + // subtopic's relative pedagogical order within the topic (that is, it should occur before topics + // with greater indexes). Indexes will never be shared between subtopics in the same topic. + uint32 index = 1; + + // The list of IDs for skills grouped by this subtopic. Each of these skills should be included in + // the subtopic's containing topic summary list of skills. + repeated string referenced_skill_ids = 2; + + // The version of this subtopic's contents. + uint32 content_version = 3; +} + +// Represents the summary of a skill (see DownloadableTopicSummaryDto for context on skills). +message SkillSummaryDto { + option (org.oppia.proto.v1.versions.structure_proto_version_type) = TOPIC_SUMMARY_PROTO_VERSION; + + // The ID of the skill. + string id = 1; + + // The localizable name of the skill. + SubtitledTextDto name = 2; + + // The version of this skill's contents. + uint32 content_version = 3; + + // The localized text, images, and voiceovers of this skill summary. + ContentLocalizationsDto localizations = 4; +} diff --git a/org/oppia/proto/v1/versions/BUILD b/org/oppia/proto/v1/versions/BUILD new file mode 100644 index 0000000..4898f43 --- /dev/null +++ b/org/oppia/proto/v1/versions/BUILD @@ -0,0 +1,24 @@ +""" +Libraries that define proto versions used by API & structural protos for client-server +compatibility. +""" + +load("//defs:defs.bzl", "define_buildifier_tests", "oppia_proto_library") + +oppia_proto_library( + name = "versions_proto", + srcs = [ + "api_versions.proto", + "structure_versions.proto", + ], + relative_parent_dir_path = "org/oppia/proto/v1/versions", + visibility = [ + "//:api_visibility", + "//:proto_impl_visibility", + ], + deps = [ + "@com_google_protobuf//:descriptor_proto", + ], +) + +define_buildifier_tests() diff --git a/org/oppia/proto/v1/versions/api_versions.proto b/org/oppia/proto/v1/versions/api_versions.proto new file mode 100644 index 0000000..06bf2c7 --- /dev/null +++ b/org/oppia/proto/v1/versions/api_versions.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.versions; + +import "google/protobuf/descriptor.proto"; + +option java_package = "org.oppia.proto.v1.versions"; +option java_multiple_files = true; + +// Custom options. Note that these do not actually enforce semantics--they're +// just hints. Implementation code is expected to define the actual versioning +// semantics. See the README for more details. +extend google.protobuf.MessageOptions { + // Defines the version type corresponding to an API proto. + ApiProtoVersionType api_proto_version_type = 2000; + + // Indicates the actual structure version. This should only be used in version protos. + uint32 latest_api_proto_version = 2001; +} + +// Represents the version of the proto structures for topic list request & response messages. This +// structure is never expected to change. +// +// See the README for details on how these versions are meant to be used. +message TopicListRequestResponseProtoVersion { + option (api_proto_version_type) = UNVERSIONED_API_PROTO; + option (latest_api_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for topic content request & response messages. +// This structure is never expected to change. +// +// See the README for details on how these versions are meant to be used. +message TopicContentRequestResponseProtoVersion { + option (api_proto_version_type) = UNVERSIONED_API_PROTO; + option (latest_api_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the type of versioning corresponding to an API proto message. +enum ApiProtoVersionType { + // The default version type (that is, unspecified and not corresponding to any versioning scheme). + // This generally should not be used. + API_PROTO_VERSION_TYPES_UNSPECIFIED = 0; + + // Indicates that the proto is not bound by a version, and thus extreme care must be taken in + // changing its structure. + UNVERSIONED_API_PROTO = 1; + + // Indicates that the proto is bound by TopicListRequestResponseProtoVersion. + TOPIC_LIST_REQUEST_RESPONSE_PROTO_VERSION = 2; + + // Indicates that the proto is bound by TopicContentRequestResponseProtoVersion. + TOPIC_CONTENT_REQUEST_RESPONSE_PROTO_VERSION = 3; +} diff --git a/org/oppia/proto/v1/versions/structure_versions.proto b/org/oppia/proto/v1/versions/structure_versions.proto new file mode 100644 index 0000000..7c29ba0 --- /dev/null +++ b/org/oppia/proto/v1/versions/structure_versions.proto @@ -0,0 +1,177 @@ +syntax = "proto3"; + +package org.oppia.proto.v1.versions; + +import "google/protobuf/descriptor.proto"; + +option java_package = "org.oppia.proto.v1.versions"; +option java_multiple_files = true; + +// Custom options. Note that these do not actually enforce semantics--they're just hints. +// Implementation code is expected to define the actual versioning semantics. See the README for +// more details. +extend google.protobuf.MessageOptions { + // Defines the version type corresponding to a structure proto. + StructureProtoVersionType structure_proto_version_type = 1000; + + // Indicates the actual structure version. This should only be used in version protos. + uint32 latest_structure_proto_version = 1001; +} +extend google.protobuf.EnumOptions { + // Defines the version type corresponding to a structure enum. + StructureProtoVersionType structure_enum_version_type = 1002; +} + +// Represents the version of the proto structures for topic summary messages. This structure is +// never expected to change. +// +// See the README for details on how these versions are meant to be used. +message TopicSummaryProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for revision card messages. This structure is +// never expected to change. +// +// See the README for details on how these versions are meant to be used. +message RevisionCardProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for concept card messages. This structure is never +// expected to change. +// +// See the README for details on how these versions are meant to be used. +message ConceptCardProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for explorations messages. This structure is never +// expected to change. +// +// See the README for details on how these versions are meant to be used. +message ExplorationProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for question messages. This structure is never +// expected to change. +// +// See the README for details on how these versions are meant to be used. +message QuestionProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for state messages. +// This structure is never expected to change. +// +// Note that this version also implies compatibility among changes to interactions since +// interactions are defined using strong types, and all of those proto messages are bound to this +// version. +// +// See the README for details on how these versions are meant to be used. +message StateProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the proto version of multiple language-tied messages. Note that these are versioned +// separately from the structures that reference them since multiple topic substructures reference +// common messages (e.g. RecordedVoiceovers), so changes in the structures need to be properly +// shared across all use cases without necessarily updating the individual proto versions for each +// of those messages. Note also that this structrure is never expected to change. +// +// See also the README for details on how these versions are meant to be used. +message LanguageProtosVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for image messages. This structure is never +// expected to change. This also shares some characteristics with LanguageProtosVersion in that it's +// used to track versioning for highly centralized & shared structures. +// +// See the README for details on how these versions are meant to be used. +message ImageProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the version of the proto structures for classrooms. This structure is never expected +// to change. +// +// See the README for details on how these versions are meant to be used. +message ClassroomProtoVersion { + option (structure_proto_version_type) = UNVERSIONED_STRUCTURE_PROTO; + option (latest_structure_proto_version) = 1; + + // The numeric version corresponding to the containing proto's structure. + uint32 version = 1; +} + +// Represents the type of versioning corresponding to a structure proto message. +enum StructureProtoVersionType { + // The default version type (that is, unspecified and not corresponding to any versioning scheme). + // This generally should not be used. + STRUCTURE_PROTO_VERSION_TYPES_UNSPECIFIED = 0; + + // Indicates that the proto is not bound by a version, and thus extreme care must be taken in + // changing its structure. + UNVERSIONED_STRUCTURE_PROTO = 1; + + // Indicates that the proto is bound by TopicSummaryProtoVersion. + TOPIC_SUMMARY_PROTO_VERSION = 2; + + // Indicates that the proto is bound by RevisionCardProtoVersion. + REVISION_CARD_PROTO_VERSION = 3; + + // Indicates that the proto is bound by ConceptCardProtoVersion. + CONCEPT_CARD_PROTO_VERSION = 4; + + // Indicates that the proto is bound by ExplorationProtoVersion. + EXPLORATION_PROTO_VERSION = 5; + + // Indicates that the proto is bound by QuestionProtoVersion. + QUESTION_PROTO_VERSION = 6; + + // Indicates that the proto is bound by StateProtoVersion. + STATE_PROTO_VERSION = 7; + + // Indicates that the proto is bound by LanguageProtosVersion. + LANGUAGE_PROTOS_VERSION = 8; + + // Indicates that the proto is bound by ImageProtoVersion. + IMAGE_PROTO_VERSION = 9; + + // Indicates that the proto is bound by ClassroomProtoVersion. + CLASSROOM_PROTO_VERSION = 10; +} diff --git a/repo/BUILD b/repo/BUILD new file mode 100644 index 0000000..9d122a3 --- /dev/null +++ b/repo/BUILD @@ -0,0 +1,9 @@ +""" +Reusable Starlark macros for setting up both the local Bazel WORKSPACE, and also for preparing other +workspaces for importing the protos defined in this project. See the README for more details on +importing this repository in other Bazel workspaces. +""" + +load("//defs:defs.bzl", "define_buildifier_tests") + +define_buildifier_tests() diff --git a/repo/deps.bzl b/repo/deps.bzl new file mode 100644 index 0000000..651c997 --- /dev/null +++ b/repo/deps.bzl @@ -0,0 +1,42 @@ +""" +Defines Starlark macros that are used to import the dependencies needed to build the Oppia proto API +project (such as for cases when another Bazel workspace depends on this project). See the importing +instructions in the README for more details. +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("//repo:versions.bzl", "MAVEN_DEPENDENCY_VERSIONS") + +def _setUpHttpArchiveDependency(name): + metadata = MAVEN_DEPENDENCY_VERSIONS[name] + sha = metadata["sha"] + strip_prefix = metadata.get("strip_prefix") + url = metadata["url"] + version = metadata["version"] + + formatted_strip_prefix = strip_prefix.format(version) if not (strip_prefix == None) else None + + http_archive( + name = name, + urls = [url.format(version)], + sha256 = sha, + strip_prefix = formatted_strip_prefix, + ) + +def initializeDepsForWorkspace(): + """ + Loads the dependencies needed to be able to build the Oppia proto API project. + + Note that this must be called after loading in this deps file, for example: + + load("//repo:deps.bzl", "initializeDepsForWorkspace") + initializeDepsForWorkspace() + + Note also that toolchains may need to be set up after loading this dependencies (see + toolchains.bzl). + """ + + # Set up all dependencies (the order doesn't matter here since it's just downloading corresponding + # HTTP archives). + for dependency_name in MAVEN_DEPENDENCY_VERSIONS.keys(): + _setUpHttpArchiveDependency(name = dependency_name) diff --git a/repo/toolchains.bzl b/repo/toolchains.bzl new file mode 100644 index 0000000..7c37053 --- /dev/null +++ b/repo/toolchains.bzl @@ -0,0 +1,47 @@ +""" +Defines Starlark macros that are used to set up dependency toolcahins needed to build the Oppia +proto API project (such as for cases when another Bazel workspace depends on this project). See the +importing instructions in the README for more details. +""" + +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@rules_buf//buf:repositories.bzl", "rules_buf_dependencies", "rules_buf_toolchains") +load("@rules_java//java:repositories.bzl", "rules_java_dependencies", "rules_java_toolchains") +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") +load("//repo:versions.bzl", "BUF_VERSION", "GO_VERSION") + +def initializeToolchainsForWorkspace(): + """ + Initializes the toolchains needed to be able to build the Oppia proto API project. + + Note that this must be called after loading in this toolchains file, for example: + + load("//repo:toolchains.bzl", "initializeToolchainsForWorkspace") + initializeToolchainsForWorkspace() + + Note also that this can't be called until the dependencies themselves are loaded (see deps.bzl). + """ + + # Set up the toolchains for rules_go. + go_rules_dependencies() + go_register_toolchains(version = GO_VERSION) + + # Set up the dependencies for Gazelle. + gazelle_dependencies() + + # Set up the toolchains for rules_proto. + rules_proto_dependencies() + rules_proto_toolchains() + + # Set up the toolchains for rules_java. + rules_java_dependencies() + rules_java_toolchains() + + # Set up the toolchains for rules_buf. + rules_buf_dependencies() + rules_buf_toolchains(version = "v%s" % BUF_VERSION) + + # Set up the dependencies for proto build tools (including Buildifier). + protobuf_deps() diff --git a/repo/versions.bzl b/repo/versions.bzl new file mode 100644 index 0000000..37aed8f --- /dev/null +++ b/repo/versions.bzl @@ -0,0 +1,54 @@ +""" +Defines third-party dependencies and their versions. +""" + +# Note to developers: Please keep this dict sorted by key to make it easier to find dependencies. +MAVEN_DEPENDENCY_VERSIONS = { + "bazel_gazelle": { + "sha": "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb", + "url": "https://github.com/bazelbuild/bazel-gazelle/releases/download/v{0}/bazel-gazelle-v{0}.tar.gz", + "version": "0.24.0", + }, + "com_github_bazelbuild_buildtools": { + "sha": "ae34c344514e08c23e90da0e2d6cb700fcd28e80c02e23e4d5715dddcb42f7b3", + "strip_prefix": "buildtools-{0}", + "url": "https://github.com/bazelbuild/buildtools/archive/refs/tags/{0}.tar.gz", + "version": "4.2.2", + }, + "com_google_protobuf": { + "sha": "c6003e1d2e7fefa78a3039f19f383b4f3a61e81be8c19356f85b6461998ad3db", + "strip_prefix": "protobuf-{0}", + "url": "https://github.com/protocolbuffers/protobuf/archive/v{0}.tar.gz", + "version": "3.17.3", + }, + "io_bazel_rules_go": { + "sha": "8e968b5fcea1d2d64071872b12737bbb5514524ee5f0a4f54f5920266c261acb", + "url": "https://github.com/bazelbuild/rules_go/releases/download/v{0}/rules_go-v{0}.zip", + "version": "0.28.0", # Last version compatible with Bazel 4.0.0. + }, + "rules_buf": { + "sha": "523a4e06f0746661e092d083757263a249fedca535bd6dd819a8c50de074731a", + "strip_prefix": "rules_buf-{0}", + "url": "https://github.com/bufbuild/rules_buf/archive/refs/tags/v{0}.zip", + "version": "0.1.1", + }, + "rules_java": { + "sha": "220b87d8cfabd22d1c6d8e3cdb4249abd4c93dcc152e0667db061fb1b957ee68", + "url": "https://github.com/bazelbuild/rules_java/releases/download/{0}/rules_java-{0}.tar.gz", + "version": "0.1.1", + }, + "rules_proto": { + "sha": "e017528fd1c91c5a33f15493e3a398181a9e821a804eb7ff5acdd1d2d6c2b18d", + "strip_prefix": "rules_proto-{0}", + "url": "https://github.com/bazelbuild/rules_proto/archive/refs/tags/{0}.tar.gz", + "version": "4.0.0-3.20.0", + }, + "rules_python": { + "sha": "934c9ceb552e84577b0faf1e5a2f0450314985b4d8712b2b70717dc679fdc01b", + "url": "https://github.com/bazelbuild/rules_python/releases/download/{0}/rules_python-{0}.tar.gz", + "version": "0.3.0", + }, +} + +BUF_VERSION = "1.14.0" +GO_VERSION = "1.17.2"