diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 0f54860..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: 2.1 -jobs: - cloud-storage-build: - machine: - image: ubuntu-2004:202201-02 - steps: - - checkout - - restore_cache: - key: cloud-storage-dependency-cache-{{ checksum "pom.xml" }} - - run: - name: Build - command: mvn scoverage:report - - save_cache: - key: cloud-storage-dependency-cache-{{ checksum "pom.xml" }} - paths: ~/.m2 - - run: - name: sonar - command: | - mvn -X sonar:sonar -Dsonar.projectKey=project-sunbird_sunbird-cloud-storage-sdk -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=project-sunbird - -workflows: - version: 2.1 - workflow: - jobs: - - cloud-storage-build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..002966c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI Build & Test + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Build and run tests + run: mvn clean verify diff --git a/.github/workflows/maven-release.yml b/.github/workflows/maven-release.yml new file mode 100644 index 0000000..c761254 --- /dev/null +++ b/.github/workflows/maven-release.yml @@ -0,0 +1,51 @@ +name: Publish to Maven Central + +on: + push: + branches: + - '**' + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Import GPG key + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + if ! echo "$GPG_PRIVATE_KEY" | base64 -d | gpg --batch --passphrase "$GPG_PASSPHRASE" --import; then + echo "Failed to import GPG private key." >&2 + exit 1 + fi + + - name: Configure Maven settings + uses: s4u/maven-settings-action@v3.0.0 + with: + servers: | + [{ + "id": "central", + "username": "${{ secrets.OSSRH_USERNAME }}", + "password": "${{ secrets.OSSRH_PASSWORD }}" + }] + properties: | + [{ + "gpg.executable": "gpg", + "gpg.passphrase": "${{ secrets.GPG_PASSPHRASE }}" + }] + + - name: Deploy to Maven Central + run: mvn deploy -Prelease -DskipTests diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 0000000..3c15d7c --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,41 @@ +name: SonarCloud Analysis + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + +jobs: + sonar: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Build and run tests + run: mvn clean verify + + - name: SonarCloud analysis + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: > + mvn sonar:sonar + -Dsonar.projectKey=project-sunbird_sunbird-cloud-storage-sdk + -Dsonar.organization=project-sunbird + -Dsonar.host.url=https://sonarcloud.io diff --git a/.gitignore b/.gitignore index 67858ce..886a23d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target/ +**/target/ .classpath .project .settings @@ -7,3 +8,4 @@ src/test/resources/test* .cache* .DS_Store *.iml +.flattened-pom.xml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..559c192 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,306 @@ +# CLAUDE.md — Sunbird Cloud Storage SDK + +This file gives Claude agents the essential context needed to work on this +repository without reading every source file first. + +--- + +## What this repository is + +A **multi-module Java 11 Maven SDK** for cloud object storage. It replaced a +single-module Scala/jclouds implementation (v1.x) with clean Java 11 modules +that use each CSP's official SDK and support OIDC-based workload identity as +the primary authentication approach. + +**Current version: 2.0.0** + +--- + +## Module layout + +``` +sunbird-cloud-storage-sdk/ ← parent POM (packaging=pom) +├── cloud-storage-sdk-api/ ← interface, models, shared abstractions (no CSP deps) +├── cloud-storage-sdk-aws/ ← AWS S3 + Ceph S3-compatible (AWS SDK v2) +├── cloud-storage-sdk-azure/ ← Azure Blob Storage (Azure SDK for Java) +├── cloud-storage-sdk-gcp/ ← Google Cloud Storage (Google Cloud Java SDK) +└── cloud-storage-sdk-oci/ ← OCI Object Storage (OCI Java SDK) +``` + +All CSP modules depend on `cloud-storage-sdk-api`. There are no +cross-dependencies between CSP modules. + +--- + +## Key source files + +### API module (`cloud-storage-sdk-api`) + +| File | Purpose | +|---|---| +| `IStorageService.java` | Main public interface (19 methods + default overloads), extends `AutoCloseable` | +| `StorageConfig.java` | Builder-pattern config; holds `AuthType` and `StorageType` enums | +| `AbstractStorageService.java` | 12 abstract primitives + all orchestration (retry, folder upload, date search, archive extract) | +| `StorageServiceFactory.java` | `ServiceLoader`-based factory: `getStorageService(StorageConfig)` | +| `StorageServiceProvider.java` | SPI interface each CSP implements | +| `model/Blob.java` | Immutable blob metadata (key, contentLength, lastModified, metadata, payload) | +| `model/DeleteTarget.java` | Typed delete target; static factories `DeleteTarget.file(key)` / `DeleteTarget.directory(key)` | +| `util/DateRangeUtil.java` | Pure `java.time` date helpers: `getStartDate`, `getDatesBetween` | +| `util/FileUtil.java` | `listFilesRecursive`, `copyStream`, `unZip` | +| `exception/StorageServiceException.java` | Unchecked runtime exception wrapping all SDK errors | + +### CSP modules + +Each CSP module contains exactly two production classes: + +``` +/src/main/java/org/sunbird/cloud/storage/service// + StorageService.java ← extends AbstractStorageService + StorageServiceProvider.java ← implements StorageServiceProvider + +/src/main/resources/META-INF/services/ + org.sunbird.cloud.storage.StorageServiceProvider ← SPI registration +``` + +### HDFS prefix per CSP + +| CSP | Prefix format | +|---|---| +| AWS | `s3n:///` | +| Azure | `wasb://@.blob.core.windows.net/` | +| GCP | `gs:///` | +| OCI | `oci://@/` | + +--- + +## StorageConfig + +```java +StorageConfig config = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.OIDC) + .region("us-east-1") + .build(); +``` + +### AuthType enum + +| Value | Meaning | +|---|---| +| `ACCESS_KEY` | storageKey + storageSecret (static credentials) | +| `OIDC` | Workload Identity / Web Identity Token | +| `IAM` | Managed Identity / default application credentials | +| `IAM_ROLE` | IAM role (AWS STS assumed role) | +| `INSTANCE_PROFILE` | EC2 instance profile / OCI Instance Principal | + +### StorageType enum + +`AWS`, `AZURE`, `GCLOUD`, `OCI`, `CEPHS3` + +`CEPHS3` is handled by the AWS module with `endpointOverride` + +`pathStyleAccessEnabled(true)`. Set `endPoint` in `StorageConfig`. + +--- + +## AbstractStorageService — the 12 primitives + +CSP subclasses must implement these and nothing else: + +```java +void ensureContainerExists(String container) +String putObject(String container, String objectKey, File file) +String putObject(String container, String objectKey, byte[] content) +InputStream getObjectStream(String container, String objectKey) +String getObjectUri(String container, String objectKey) +BlobDetail getObjectDetail(String container, String objectKey) // inner class +List listKeys(String container, String prefix) +void removeObjects(String container, List objectKeys) +void copyObject(String fromContainer, String fromKey, String toContainer, String toKey) +String generateSignedGetUrl(String container, String objectKey, int ttlSeconds) +String generateSignedPutUrl(String container, String objectKey, int ttlSeconds, + String contentType, Map additionalParams) +String getHdfsPrefix(String container) +``` + +The orchestration in `AbstractStorageService` (retry loops, `uploadFolder`, +`searchObjects` date iteration, `extractArchive`, directory copy) is shared +and must NOT be duplicated in CSP implementations. + +--- + +## Authentication per CSP + +### AWS (`AwsStorageService`) +- `ACCESS_KEY` → `StaticCredentialsProvider(AwsBasicCredentials)` +- `OIDC/IAM/IAM_ROLE/INSTANCE_PROFILE` → `DefaultCredentialsProvider` +- Custom endpoint → `S3ClientBuilder.endpointOverride(URI)` + `pathStyleAccessEnabled(true)` + +### Azure (`AzureStorageService`) +- `ACCESS_KEY` → `StorageSharedKeyCredential(accountName, accountKey)`; `storageKey` = account name, `storageSecret` = account key +- `OIDC` → `WorkloadIdentityCredentialBuilder` (reads `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_FEDERATED_TOKEN_FILE`) or `DefaultAzureCredentialBuilder` +- `IAM/INSTANCE_PROFILE` → `ManagedIdentityCredentialBuilder` +- Signed URLs: shared-key auth uses `generateSas()`; OIDC/IAM uses `getUserDelegationKey()` + `generateUserDelegationSas()` + +### GCP (`GcpStorageService`) +- `ACCESS_KEY` → `GoogleCredentials.fromStream(new ByteArrayInputStream(secret.getBytes()))` where `storageSecret` is the service account JSON string +- `OIDC/IAM` → `GoogleCredentials.getApplicationDefault()` +- Signed URLs use V4 signatures; PUT URL supports chunked upload via `HttpMethod.POST` + `x-goog-resumable: start` + +### OCI (`OciStorageService`) +- `ACCESS_KEY` → `SimpleAuthenticationDetailsProvider`; `storageKey` = user OCID, `storageSecret` = private key PEM; reads `OCI_TENANCY_ID` and `OCI_FINGERPRINT` from env +- `OIDC/IAM/INSTANCE_PROFILE` → `InstancePrincipalsAuthenticationDetailsProvider` +- OCI namespace is fetched via `getNamespace()` in the constructor and stored +- Signed URLs use OCI Pre-Authenticated Requests (PAR) +- All uploads compute and set `contentMD5` (base64-encoded MD5) + +--- + +## Service discovery (ServiceLoader) + +```java +// From user code — auto-discovers the right CSP: +IStorageService svc = StorageServiceFactory.getStorageService(config); + +// Or directly instantiate a CSP class: +IStorageService svc = new AwsStorageService(config); +``` + +The `META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider` file +in each CSP module registers the provider. When all CSP jars are on the +classpath, `StorageServiceFactory` selects the one whose +`provider.supports(config.getType())` returns `true`. + +--- + +## Tests + +### Unit tests (always run, no cloud access needed) + +Located in `cloud-storage-sdk-api/src/test/`: + +| Class | What it tests | +|---|---| +| `DateRangeUtilTest` | Date arithmetic helpers | +| `FileUtilTest` | File utilities with `@TempDir` | +| `AbstractStorageServiceTest` | All orchestration logic via `InMemoryStorageService` inner class | + +### Integration tests (skipped by default) + +Each CSP module has one integration test class that extends +`BaseStorageServiceIntegrationTest` (published as a test-jar from the API +module). Tests are guarded by `assumeTrue()` and only execute when the +`*_STORAGE_TEST_ENABLED` env var is set. + +| Class | Enable with | +|---|---| +| `AwsStorageServiceTest` | `AWS_STORAGE_TEST_ENABLED=true` | +| `AzureStorageServiceTest` | `AZURE_STORAGE_TEST_ENABLED=true` | +| `GcpStorageServiceTest` | `GCP_STORAGE_TEST_ENABLED=true` | +| `OciStorageServiceTest` | `OCI_STORAGE_TEST_ENABLED=true` | + +See **TESTING.md** for the full list of environment variables and usage examples. + +--- + +## Build + +```bash +# Full build, skip tests +mvn clean package -DskipTests + +# Full build with unit tests (integration tests auto-skip) +mvn clean verify + +# Single module +mvn -pl cloud-storage-sdk-aws package -DskipTests +``` + +### Maven build order (reactor) + +1. `cloud-storage-sdk-parent` (parent POM) +2. `cloud-storage-sdk-api` +3. `cloud-storage-sdk-aws`, `cloud-storage-sdk-azure`, `cloud-storage-sdk-gcp`, `cloud-storage-sdk-oci` (parallel) + +### Key versions (managed in parent POM) + +| Dependency | Version | +|---|---| +| AWS SDK v2 | 2.25.0 | +| Azure Blob | 12.25.0 | +| Azure Identity | 1.11.0 | +| GCP Storage | 2.30.0 | +| OCI SDK | 3.30.0 | +| Apache Tika | 2.9.0 | +| JUnit Jupiter | 5.10.1 | +| Mockito | 5.8.0 | +| Java target | 11 | + +--- + +## Package structure + +``` +org.sunbird.cloud.storage +├── IStorageService (interface) +├── AbstractStorageService (abstract class) +├── StorageConfig (config + AuthType/StorageType enums) +├── StorageServiceFactory (factory via ServiceLoader) +├── StorageServiceProvider (SPI interface) +├── BaseStorageServiceIntegrationTest (abstract integration test base, test-jar) +├── exception/ +│ └── StorageServiceException +├── model/ +│ ├── Blob +│ └── DeleteTarget +├── util/ +│ ├── DateRangeUtil +│ └── FileUtil +└── service/ + ├── aws/ AwsStorageService, AwsStorageServiceProvider + ├── azure/ AzureStorageService, AzureStorageServiceProvider + ├── gcp/ GcpStorageService, GcpStorageServiceProvider + └── oci/ OciStorageService, OciStorageServiceProvider +``` + +--- + +## Common patterns when modifying this codebase + +### Adding a new operation to the interface +1. Add the method signature to `IStorageService.java` (add a `default` overload + if optional params are involved) +2. If the operation needs a new primitive, add the `abstract` method to + `AbstractStorageService.java` and implement it in all four CSP services +3. If orchestration logic is shared, implement it in `AbstractStorageService` + and call the abstract primitive +4. Add unit tests in `AbstractStorageServiceTest` using `InMemoryStorageService` +5. Add integration test coverage in `BaseStorageServiceIntegrationTest` + +### Adding a new CSP +1. Create a new Maven module `cloud-storage-sdk-/` +2. Add it to the parent POM `` list +3. Depend on `cloud-storage-sdk-api` +4. Extend `AbstractStorageService`, implement all 12 primitives +5. Implement `StorageServiceProvider`, register in `META-INF/services/` +6. Add `StorageType.` to the enum in `StorageConfig.java` +7. Write an integration test extending `BaseStorageServiceIntegrationTest` + +### Changing authentication logic +Each CSP's `resolveAuth(StorageConfig)` / auth setup is self-contained in the +CSP's service constructor. Do not share auth logic across CSPs. + +--- + +## Important design decisions + +- **No Scala, no jclouds** — complete clean break at v2.0.0 +- **OIDC first** — `AuthType.OIDC` is the intended production auth; `ACCESS_KEY` + is for local development and legacy environments +- **`Ceph S3` is handled in the AWS module** via `endpointOverride` (not a + separate module) +- **`IStorageService extends AutoCloseable`** — use try-with-resources; `close()` + shuts down the underlying SDK client +- **`DeleteTarget` replaces `(String, Boolean)` tuples** — use + `DeleteTarget.file(key)` or `DeleteTarget.directory(key)` +- **Thread-safety** — retry counters use local variables, not instance fields +- **Test-jar** — `cloud-storage-sdk-api` publishes a `-tests` classifier jar so + CSP modules can extend `BaseStorageServiceIntegrationTest` without duplication diff --git a/INTEGRATION_GUIDE.md b/INTEGRATION_GUIDE.md new file mode 100644 index 0000000..5c98690 --- /dev/null +++ b/INTEGRATION_GUIDE.md @@ -0,0 +1,1082 @@ +# Sunbird Cloud Storage SDK — Technical Integration Guide + +**Version:** 2.0.0  |  **Java:** 11  |  **Build:** Maven +**Frameworks:** Play Framework 2.x · Spring Boot 2.x / 3.x +**Supported CSPs:** Amazon Web Services (S3) · Microsoft Azure (Blob) · Google Cloud Storage · Oracle Cloud (OCI) + +--- + +## Table of Contents + +1. [Overview](#1-overview) +2. [Prerequisites](#2-prerequisites) +3. [Maven Dependency Setup](#3-maven-dependency-setup) + - [3.1 Spring Boot — pom.xml](#31-spring-boot--pomxml) + - [3.2 Play Framework 2.x — build.sbt](#32-play-framework-2x--buildsbt) + - [3.3 Transitive Dependencies per CSP](#33-transitive-dependencies-per-csp) +4. [Configuration](#4-configuration) + - [4.1 StorageConfig Builder](#41-storageconfig-builder) + - [4.2 AuthType Values](#42-authtype-values) + - [4.3 Configuration Examples](#43-configuration-examples) + - [4.4 Spring Boot — application.yml Integration](#44-spring-boot--applicationyml-integration) + - [4.5 Play Framework — application.conf Integration](#45-play-framework--applicationconf-integration) +5. [Usage Examples](#5-usage-examples) + - [5.1 Instantiation via ServiceLoader](#51-instantiation-via-serviceloader) + - [5.2 Upload Operations](#52-upload-operations) + - [5.3 Download Operations](#53-download-operations) + - [5.4 Signed URLs](#54-signed-urls) + - [5.5 List and Search Operations](#55-list-and-search-operations) + - [5.6 Delete Operations](#56-delete-operations) + - [5.7 Copy and Archive Operations](#57-copy-and-archive-operations) +6. [Dockerfile — Multi-Stage Builds per CSP](#6-dockerfile--multi-stage-builds-per-csp) + - [6.1 AWS S3 Dockerfile](#61-aws-s3-dockerfile) + - [6.2 Azure Blob Storage Dockerfile](#62-azure-blob-storage-dockerfile) + - [6.3 Google Cloud Storage Dockerfile](#63-google-cloud-storage-dockerfile) + - [6.4 Oracle Cloud (OCI) Dockerfile](#64-oracle-cloud-oci-dockerfile) + - [6.5 Play Framework Dockerfile (Any CSP)](#65-play-framework-dockerfile-any-csp) +7. [Recommended Docker Image Strategy](#7-recommended-docker-image-strategy) + - [7.1 Option A — Build-Time CSP Selection (Recommended)](#71-option-a--build-time-csp-selection-recommended) + - [7.2 Option B — Single Image with All CSP Modules](#72-option-b--single-image-with-all-csp-modules) + - [7.3 Comparison and Recommendation](#73-comparison-and-recommendation) +8. [Runtime Environment Variables](#8-runtime-environment-variables) +9. [Exception Handling](#9-exception-handling) +10. [Quick Reference](#10-quick-reference) + +--- + +## 1. Overview + +The Sunbird Cloud Storage SDK (v2.0.0) is a **multi-module Java 11 Maven SDK** providing a unified interface to major cloud object storage providers. It replaces the previous Scala/jclouds v1.x implementation with a clean, CSP-agnostic API backed by each provider's official SDK. + +### Architecture + +The SDK is organized as a Maven multi-module project with a strict separation between the API contract and provider implementations: + +| Module | Purpose | +|--------|---------| +| `cloud-storage-sdk-api` | Interface, models, shared orchestration. **No CSP dependencies.** This is the only compile-time dependency your application needs. | +| `cloud-storage-sdk-aws` | AWS S3 + Ceph S3-compatible (AWS SDK v2). Include as `runtime` for AWS/Ceph deployments. | +| `cloud-storage-sdk-azure` | Azure Blob Storage (Azure SDK for Java). Include as `runtime` for Azure deployments. | +| `cloud-storage-sdk-gcp` | Google Cloud Storage (Google Cloud Java SDK). Include as `runtime` for GCP deployments. | +| `cloud-storage-sdk-oci` | OCI Object Storage (OCI Java SDK). Include as `runtime` for OCI deployments. | + +The key design principle is that **your application code depends only on the API module at compile time**. The CSP implementation module is added as a `runtime` dependency and is auto-discovered via Java's `ServiceLoader` — no code changes are needed when switching providers. + +> **OIDC-First Design:** `AuthType.OIDC` workload identity is the intended production authentication method. `ACCESS_KEY` is provided for local development and legacy environments only. In Kubernetes and cloud-native deployments, always prefer OIDC to avoid managing long-lived credentials. + +--- + +## 2. Prerequisites + +| Requirement | Details | +|-------------|---------| +| Java | 11 or later (LTS recommended: 11, 17, 21) | +| Maven | 3.6.3 or later | +| SDK Artifact Repository | `sunbird-cloud-storage-sdk:2.0.0` published to your Maven repository (Nexus, Artifactory, GitHub Packages, etc.) | +| Framework | Play Framework 2.x (Scala/Java) or Spring Boot 2.x / 3.x | +| Docker | 20.10+ for building container images (optional but recommended) | + +--- + +## 3. Maven Dependency Setup + +The SDK uses a **compile/runtime split** pattern: your application declares a `compile` dependency on the API module and a `runtime` dependency on exactly one CSP implementation module. This keeps CSP-specific code off your compile classpath and ensures that only the intended provider is active at runtime. + +### 3.1 Spring Boot — pom.xml + +```xml + + + + + + + org.sunbird + cloud-storage-sdk-api + 2.0.0 + + + + + + + + + org.sunbird + cloud-storage-sdk-aws + 2.0.0 + runtime + + + + + + + + + + + + +``` + +### 3.2 Play Framework 2.x — build.sbt + +For Play 2 projects using SBT, the `% "runtime"` suffix is equivalent to Maven's `runtime` scope: + +```scala +libraryDependencies ++= Seq( + + // ── COMPILE: API contract ───────────────────────────────────────────────── + "org.sunbird" % "cloud-storage-sdk-api" % "2.0.0", + + // ── RUNTIME: Uncomment exactly ONE provider ─────────────────────────────── + + // Option A: AWS S3 / Ceph S3-compatible + "org.sunbird" % "cloud-storage-sdk-aws" % "2.0.0" % "runtime", + + // Option B: Azure Blob Storage + // "org.sunbird" % "cloud-storage-sdk-azure" % "2.0.0" % "runtime", + + // Option C: Google Cloud Storage + // "org.sunbird" % "cloud-storage-sdk-gcp" % "2.0.0" % "runtime", + + // Option D: OCI Object Storage + // "org.sunbird" % "cloud-storage-sdk-oci" % "2.0.0" % "runtime" +) +``` + +> **Why `runtime` scope?** The CSP module is loaded by Java's `ServiceLoader` at runtime. It must be on the classpath but must **not** be on the compile classpath — this prevents your application code from accidentally importing CSP-specific classes, keeping it portable across cloud providers. + +### 3.3 Transitive Dependencies per CSP + +Each runtime implementation module automatically pulls in the following dependencies. You do not need to declare these explicitly: + +| CSP Module | Key Transitive Dependencies | +|------------|----------------------------| +| `cloud-storage-sdk-aws` | `software.amazon.awssdk:s3:2.25.0`, `software.amazon.awssdk:sts:2.25.0` | +| `cloud-storage-sdk-azure` | `com.azure:azure-storage-blob:12.25.0`, `com.azure:azure-identity:1.11.0` | +| `cloud-storage-sdk-gcp` | `com.google.cloud:google-cloud-storage:2.30.0` | +| `cloud-storage-sdk-oci` | `com.oracle.oci.sdk:oci-java-sdk-objectstorage:3.30.0`, `com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey3:3.30.0` | + +--- + +## 4. Configuration + +All configuration is expressed through the `StorageConfig` builder. The builder requires a `StorageType` and optionally an `AuthType` plus additional parameters depending on your authentication strategy. + +### 4.1 StorageConfig Builder + +| Method | Description | +|--------|-------------| +| `.builder(StorageType)` | Starts the builder for the given CSP. **Required.** | +| `.authType(AuthType)` | Authentication method. Defaults to `ACCESS_KEY` if not set. | +| `.storageKey(String)` | Access key ID (`ACCESS_KEY`) or user OCID (OCI). | +| `.storageSecret(String)` | Secret access key, private key PEM (OCI), or service-account JSON (GCP). | +| `.region(String)` | Cloud region, e.g. `us-east-1` (AWS), `eastus` (Azure), `us-central1` (GCP), `us-ashburn-1` (OCI). | +| `.endPoint(String)` | Custom endpoint URL. **Required for Ceph S3** / S3-compatible stores. | +| `.build()` | Builds and returns an immutable `StorageConfig` instance. | + +### 4.2 AuthType Values + +| AuthType | Description | Supported CSPs | +|----------|-------------|---------------| +| `ACCESS_KEY` | Static credentials (key + secret). Use for local development and legacy environments. | All CSPs | +| `OIDC` | Workload Identity / Web Identity Token. **Preferred for Kubernetes deployments.** | All CSPs | +| `IAM` | Managed Identity / Application Default Credentials. For VMs and managed services. | AWS, Azure, GCP | +| `IAM_ROLE` | AWS STS assumed role. | AWS only | +| `INSTANCE_PROFILE` | EC2 instance profile or OCI Instance Principal. | AWS, OCI | + +### 4.3 Configuration Examples + +#### OIDC (Production — Kubernetes Workload Identity) + +```java +// AWS — OIDC via Web Identity Token (EKS IRSA) +StorageConfig awsConfig = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.OIDC) + .region("us-east-1") + .build(); + +// Azure — Workload Identity (AKS) +// Env vars required: AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE +StorageConfig azureConfig = StorageConfig.builder(StorageConfig.StorageType.AZURE) + .authType(StorageConfig.AuthType.OIDC) + .storageKey("my-storage-account") // account name + .region("eastus") + .build(); + +// GCP — Application Default Credentials (GKE Workload Identity) +StorageConfig gcpConfig = StorageConfig.builder(StorageConfig.StorageType.GCLOUD) + .authType(StorageConfig.AuthType.OIDC) + .region("us-central1") + .build(); + +// OCI — Instance Principal (OKE) +StorageConfig ociConfig = StorageConfig.builder(StorageConfig.StorageType.OCI) + .authType(StorageConfig.AuthType.INSTANCE_PROFILE) + .region("us-ashburn-1") + .build(); +``` + +#### ACCESS_KEY (Development / Legacy) + +```java +// AWS — Static credentials +StorageConfig awsConfig = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .storageKey(System.getenv("AWS_ACCESS_KEY_ID")) + .storageSecret(System.getenv("AWS_SECRET_ACCESS_KEY")) + .region("us-east-1") + .build(); + +// Ceph S3-compatible (uses AWS module with endpoint override) +StorageConfig cephConfig = StorageConfig.builder(StorageConfig.StorageType.CEPHS3) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .endPoint("https://ceph-rgw.internal.example.com") + .storageKey(System.getenv("CEPH_ACCESS_KEY")) + .storageSecret(System.getenv("CEPH_SECRET_KEY")) + .region("default") + .build(); + +// Azure — Shared Key +StorageConfig azureConfig = StorageConfig.builder(StorageConfig.StorageType.AZURE) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .storageKey("my-storage-account") // account name + .storageSecret(System.getenv("AZURE_ACCOUNT_KEY")) // account key + .region("eastus") + .build(); + +// GCP — Service Account JSON +StorageConfig gcpConfig = StorageConfig.builder(StorageConfig.StorageType.GCLOUD) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .storageSecret(System.getenv("GOOGLE_SA_JSON")) // full JSON string + .build(); + +// OCI — API Key (ACCESS_KEY) +// Also requires OCI_TENANCY_ID and OCI_FINGERPRINT env vars +StorageConfig ociConfig = StorageConfig.builder(StorageConfig.StorageType.OCI) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .storageKey(System.getenv("OCI_USER_OCID")) // user OCID + .storageSecret(System.getenv("OCI_PRIVATE_KEY_PEM")) // private key PEM + .region("us-ashburn-1") + .build(); +``` + +### 4.4 Spring Boot — application.yml Integration + +**application.yml:** + +```yaml +sunbird: + storage: + type: AWS # AWS | AZURE | GCLOUD | OCI | CEPHS3 + auth-type: OIDC # OIDC | ACCESS_KEY | IAM | IAM_ROLE | INSTANCE_PROFILE + region: us-east-1 + storage-key: ${STORAGE_KEY:} # Optional — leave blank for OIDC/IAM + storage-secret: ${STORAGE_SECRET:} + endpoint: ${STORAGE_ENDPOINT:} # Required only for Ceph S3 +``` + +**StorageProperties.java:** + +```java +@Configuration +@ConfigurationProperties(prefix = "sunbird.storage") +public class StorageProperties { + private String type; + private String authType; + private String region; + private String storageKey; + private String storageSecret; + private String endpoint; + // getters + setters ... +} +``` + +**StorageServiceConfig.java:** + +```java +@Configuration +public class StorageServiceConfig { + + @Bean + @Singleton + public IStorageService storageService(StorageProperties props) { + StorageConfig.Builder builder = StorageConfig + .builder(StorageConfig.StorageType.valueOf(props.getType())) + .authType(StorageConfig.AuthType.valueOf(props.getAuthType())); + + if (props.getRegion() != null) builder.region(props.getRegion()); + if (props.getStorageKey() != null) builder.storageKey(props.getStorageKey()); + if (props.getStorageSecret() != null) builder.storageSecret(props.getStorageSecret()); + if (props.getEndpoint() != null) builder.endPoint(props.getEndpoint()); + + return StorageServiceFactory.getStorageService(builder.build()); + } +} +``` + +### 4.5 Play Framework — application.conf Integration + +**application.conf:** + +```hocon +sunbird.storage { + type = "AWS" + auth-type = "OIDC" + region = "us-east-1" + storage-key = ${?STORAGE_KEY} + storage-secret = ${?STORAGE_SECRET} + endpoint = ${?STORAGE_ENDPOINT} +} +``` + +**StorageModule.java (Guice Module):** + +```java +public class StorageModule extends AbstractModule { + + private final Configuration config; + + public StorageModule(Environment env, Configuration config) { + this.config = config; + } + + @Override + protected void configure() { + String type = config.getString("sunbird.storage.type"); + String authType = config.getString("sunbird.storage.auth-type"); + String region = config.getString("sunbird.storage.region"); + String key = config.getString("sunbird.storage.storage-key"); + String secret = config.getString("sunbird.storage.storage-secret"); + String endpoint = config.getString("sunbird.storage.endpoint"); + + StorageConfig.Builder builder = StorageConfig + .builder(StorageConfig.StorageType.valueOf(type)) + .authType(StorageConfig.AuthType.valueOf(authType)); + + if (region != null) builder.region(region); + if (key != null) builder.storageKey(key); + if (secret != null) builder.storageSecret(secret); + if (endpoint != null) builder.endPoint(endpoint); + + IStorageService service = StorageServiceFactory.getStorageService(builder.build()); + bind(IStorageService.class).toInstance(service); + } +} +``` + +Register the module in `application.conf`: + +```hocon +play.modules.enabled += "modules.StorageModule" +``` + +--- + +## 5. Usage Examples + +All examples use the `IStorageService` interface. The same code works regardless of which CSP module is on the runtime classpath. + +### 5.1 Instantiation via ServiceLoader + +```java +// Factory auto-discovers the runtime CSP module via ServiceLoader +try (IStorageService service = StorageServiceFactory.getStorageService(config)) { + + // All operations here... + +} // close() called automatically — shuts down the underlying SDK client + +// Direct instantiation (bypasses ServiceLoader; only when one CSP is on classpath) +IStorageService service = new AwsStorageService(config); +``` + +> `IStorageService extends AutoCloseable` — always use try-with-resources to ensure the underlying SDK client is properly shut down. + +### 5.2 Upload Operations + +```java +// Upload a single file (with retry support) +String objectUrl = service.upload( + "my-bucket", // container / bucket name + "/local/path/file.csv",// local file path + "data/2026/file.csv", // remote object key + false, // not a directory + 1, // attempt number (start at 1) + 3, // max retries + null // TTL in seconds (null = no expiry metadata) +); + +// Convenience overload — no retries, no TTL +String objectUrl = service.upload("my-bucket", "/local/file.csv", "data/file.csv"); + +// Upload a directory recursively (async) +CompletableFuture> future = service.uploadFolder( + "my-bucket", + "/local/export-folder", + "exports/2026-02/", + false, // not public + null, // no TTL + 2, // retries + 1 // attempt +); +List uploadedKeys = future.get(); // block for result + +// Upload raw bytes +byte[] jsonBytes = objectMapper.writeValueAsBytes(payload); +service.put("my-bucket", jsonBytes, "events/event-001.json"); +``` + +### 5.3 Download Operations + +```java +// Download a single object +service.download("my-bucket", "data/2026/file.csv", "/tmp/downloads/"); + +// Download a directory recursively +service.download("my-bucket", "exports/2026-02/", "/tmp/export/", true); + +// Read object content as text lines +List lines = service.getObjectData("my-bucket", "config/settings.json"); + +// Fetch blob metadata (without payload) +Blob blob = service.getObject("my-bucket", "data/2026/file.csv"); +System.out.println("Size: " + blob.getContentLength()); +System.out.println("Modified: " + blob.getLastModified()); + +// Fetch blob metadata with content bytes +Blob blobWithPayload = service.getObject("my-bucket", "small-file.txt", true); +String content = new String(blobWithPayload.getPayload(), StandardCharsets.UTF_8); +``` + +### 5.4 Signed URLs + +```java +// Signed read URL — valid for 1 hour +String getUrl = service.getSignedURL( + "my-bucket", + "reports/annual-report.pdf", + 3600, // TTL in seconds + "r" // permission: "r" = read, "w" = write +); + +// Signed upload URL with content type (e.g. for direct browser upload) +String putUrl = service.getSignedURLV2( + "my-bucket", + "uploads/user-avatar.png", + 900, // 15-minute TTL + "w", // write permission + "image/png", // content type + Map.of("x-amz-acl", "private") // additional params (CSP-specific) +); +``` + +### 5.5 List and Search Operations + +```java +// List all object keys under a prefix +List keys = service.listObjectKeys("my-bucket", "data/2026/"); + +// List blobs with metadata +List blobs = service.listObjects("my-bucket", "data/2026/"); + +// Date-based search — finds objects in date-prefixed paths (e.g. telemetry/raw/2026-02-14/) +List recent = service.searchObjects( + "my-bucket", + "telemetry/raw/", + null, // fromDate — null means compute from toDate minus delta + "2026-02-20", // toDate + 7, // delta: go back 7 days + "yyyy-MM-dd" // date pattern used in path segments +); + +// Search returning only keys +List recentKeys = service.searchObjectKeys( + "my-bucket", "telemetry/raw/", null, "2026-02-20", 7, "yyyy-MM-dd" +); +``` + +### 5.6 Delete Operations + +```java +// Delete a single file +service.deleteObject("my-bucket", "data/old-file.csv"); + +// Delete a directory and all its contents +service.deleteObject("my-bucket", "exports/old-export/", true); + +// Delete multiple typed targets (mix of files and directories) +service.deleteObjects("my-bucket", List.of( + DeleteTarget.file("data/file-a.csv"), + DeleteTarget.file("data/file-b.csv"), + DeleteTarget.directory("temp/staging/") +)); +``` + +### 5.7 Copy and Archive Operations + +```java +// Copy within the same bucket +service.copyObjects("my-bucket", "src/file.csv", "my-bucket", "dst/file.csv"); + +// Cross-bucket copy of an entire directory +service.copyObjects( + "source-bucket", "archive/2025/", + "dest-bucket", "archive/2025/", + true // isDirectory +); + +// Extract a ZIP archive from cloud storage to a new cloud prefix +// Downloads the ZIP, extracts locally, then uploads all files to the destination key +service.extractArchive( + "my-bucket", + "zips/dataset.zip", // source ZIP object key + "datasets/extracted/" // destination prefix in the same bucket +); +``` + +--- + +## 6. Dockerfile — Multi-Stage Builds per CSP + +All Dockerfiles below use a **multi-stage build**: + +- **Stage 1 (`builder`):** Compiles the application using a full JDK + Maven/SBT image. +- **Stage 2 (runtime):** Contains only a JRE, the assembled JAR, and no build tools. + +This pattern minimises final image size, reduces the CVE attack surface, and prevents build credentials or source code from reaching production. + +### 6.1 AWS S3 Dockerfile + +For AWS deployments, the SDK reads credentials from the pod's OIDC token (EKS IRSA) or instance profile automatically. No credentials are baked into the image. + +```dockerfile +# ── Stage 1: Build ───────────────────────────────────────────────────────────── +FROM maven:3.9-eclipse-temurin-11 AS builder + +WORKDIR /build + +# Cache Maven dependencies before copying source (faster rebuilds) +COPY pom.xml . +RUN mvn dependency:go-offline -B + +COPY src ./src + +# Build fat-JAR; integration tests are skipped (they require real cloud access) +RUN mvn clean package -DskipTests -B + + +# ── Stage 2: Runtime ─────────────────────────────────────────────────────────── +FROM eclipse-temurin:11-jre-jammy + +# Security: run as non-root +RUN groupadd -r appuser && useradd -r -g appuser appuser + +WORKDIR /app + +COPY --from=builder /build/target/*.jar app.jar + +USER appuser + +# AWS SDK reads credentials from environment / IRSA token automatically. +# Supply these at runtime via your Kubernetes ServiceAccount annotation, +# ECS task role, or EC2 instance profile — do NOT hardcode them here. +# +# Required env vars (set via K8s secret or pod spec): +# AWS_REGION — e.g. us-east-1 +# AWS_ROLE_ARN — for IRSA (EKS workload identity) +# AWS_WEB_IDENTITY_TOKEN_FILE — injected automatically by EKS + +EXPOSE 9000 + +ENTRYPOINT ["java", "-jar", "/app/app.jar"] +``` + +### 6.2 Azure Blob Storage Dockerfile + +For AKS deployments, Azure Workload Identity injects `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and the federated token file automatically via the pod's service account. No credentials are baked into the image. + +```dockerfile +# ── Stage 1: Build ───────────────────────────────────────────────────────────── +FROM maven:3.9-eclipse-temurin-11 AS builder + +WORKDIR /build + +COPY pom.xml . +RUN mvn dependency:go-offline -B + +COPY src ./src +RUN mvn clean package -DskipTests -B + + +# ── Stage 2: Runtime ─────────────────────────────────────────────────────────── +FROM eclipse-temurin:11-jre-jammy + +RUN groupadd -r appuser && useradd -r -g appuser appuser + +WORKDIR /app + +COPY --from=builder /build/target/*.jar app.jar + +USER appuser + +# Azure Workload Identity injects these automatically on AKS: +# AZURE_CLIENT_ID +# AZURE_TENANT_ID +# AZURE_FEDERATED_TOKEN_FILE +# +# For ACCESS_KEY auth, inject AZURE_ACCOUNT_KEY via a K8s Secret. +# +# StorageConfig should be set with: +# .authType(StorageConfig.AuthType.OIDC) +# .storageKey("") + +EXPOSE 9000 + +ENTRYPOINT ["java", "-jar", "/app/app.jar"] +``` + +### 6.3 Google Cloud Storage Dockerfile + +On GKE with Workload Identity, the GCP SDK reads credentials from the node metadata server automatically — no environment variables are needed when using `AuthType.OIDC` or `IAM`. + +```dockerfile +# ── Stage 1: Build ───────────────────────────────────────────────────────────── +FROM maven:3.9-eclipse-temurin-11 AS builder + +WORKDIR /build + +COPY pom.xml . +RUN mvn dependency:go-offline -B + +COPY src ./src +RUN mvn clean package -DskipTests -B + + +# ── Stage 2: Runtime ─────────────────────────────────────────────────────────── +FROM eclipse-temurin:11-jre-jammy + +RUN groupadd -r appuser && useradd -r -g appuser appuser + +WORKDIR /app + +COPY --from=builder /build/target/*.jar app.jar + +USER appuser + +# GKE Workload Identity: no env vars required. +# Application Default Credentials (ADC) are automatically resolved via +# the GKE metadata server when the pod's KSA is annotated with a GSA. +# +# For service-account key auth (dev only), mount the JSON key file +# as a K8s Secret Volume and set: +# GOOGLE_APPLICATION_CREDENTIALS=/secrets/sa-key.json +# Never bake service account keys into the image. + +EXPOSE 9000 + +ENTRYPOINT ["java", "-jar", "/app/app.jar"] +``` + +### 6.4 Oracle Cloud (OCI) Dockerfile + +OCI deployments on OKE use Instance Principals (`INSTANCE_PROFILE` AuthType). The OCI SDK communicates with the instance identity service automatically. `OCI_TENANCY_ID` and `OCI_FINGERPRINT` are only required for `ACCESS_KEY` auth. + +```dockerfile +# ── Stage 1: Build ───────────────────────────────────────────────────────────── +FROM maven:3.9-eclipse-temurin-11 AS builder + +WORKDIR /build + +COPY pom.xml . +RUN mvn dependency:go-offline -B + +COPY src ./src +RUN mvn clean package -DskipTests -B + + +# ── Stage 2: Runtime ─────────────────────────────────────────────────────────── +FROM eclipse-temurin:11-jre-jammy + +RUN groupadd -r appuser && useradd -r -g appuser appuser + +WORKDIR /app + +COPY --from=builder /build/target/*.jar app.jar + +USER appuser + +# OKE Instance Principals: the OCI SDK automatically contacts the +# Instance Metadata Service (IMDS) on the node — no env vars needed. +# +# For ACCESS_KEY auth only (supply via K8s Secret): +# OCI_TENANCY_ID — tenancy OCID +# OCI_FINGERPRINT — API key fingerprint +# (storageKey and storageSecret come from StorageConfig) + +EXPOSE 9000 + +ENTRYPOINT ["java", "-jar", "/app/app.jar"] +``` + +### 6.5 Play Framework Dockerfile (Any CSP) + +Play 2 applications are packaged as a distribution directory (`sbt dist`) rather than a single fat JAR. The multi-stage build uses SBT to produce the `dist` package, then copies only the unpacked distribution into the JRE image. + +```dockerfile +# ── Stage 1: Build with SBT ──────────────────────────────────────────────────── +FROM sbtscala/scala-sbt:eclipse-temurin-11.0.21_9_1.9.8_2.13.12 AS builder + +WORKDIR /build + +# Cache SBT dependencies before copying source +COPY build.sbt . +COPY project ./project +RUN sbt update + +COPY . . + +# Build universal distribution ZIP +RUN sbt clean dist + +# Unzip the distribution +RUN cd target/universal \ + && unzip *.zip \ + && mv $(ls -d */ | head -1) /app-dist + + +# ── Stage 2: Runtime ─────────────────────────────────────────────────────────── +FROM eclipse-temurin:11-jre-jammy + +RUN groupadd -r appuser && useradd -r -g appuser appuser + +COPY --from=builder /app-dist /app +RUN chmod +x /app/bin/* + +USER appuser + +# Play application port +EXPOSE 9000 + +# APPLICATION_SECRET must be supplied at runtime via a K8s Secret. +# Never hardcode it here. +# -DAPPLICATION_SECRET=$(cat /run/secrets/play-secret) +ENTRYPOINT ["/app/bin/my-play-app", "-Dhttp.port=9000"] +``` + +--- + +## 7. Recommended Docker Image Strategy + +When containerising applications that use the Sunbird Cloud Storage SDK, there are two common approaches. + +### 7.1 Option A — Build-Time CSP Selection (Recommended) + +Use a Maven profile and Docker `--build-arg` to select the CSP at image build time. Each CI pipeline job produces a tagged image for its target cloud. + +**Dockerfile.dynamic:** + +```dockerfile +ARG CSP=aws + +# ── Stage 1: Build ───────────────────────────────────────────────────────────── +FROM maven:3.9-eclipse-temurin-11 AS builder +ARG CSP + +WORKDIR /build +COPY pom.xml . +RUN mvn dependency:go-offline -B -P${CSP} + +COPY src ./src +RUN mvn clean package -DskipTests -B -P${CSP} + +# ── Stage 2: Runtime ─────────────────────────────────────────────────────────── +FROM eclipse-temurin:11-jre-jammy +ARG CSP + +RUN groupadd -r appuser && useradd -r -g appuser appuser +WORKDIR /app +COPY --from=builder /build/target/*.jar app.jar + +LABEL org.sunbird.csp=${CSP} + +USER appuser +EXPOSE 9000 +ENTRYPOINT ["java", "-jar", "/app/app.jar"] +``` + +**Build commands:** + +```bash +docker build --build-arg CSP=aws -t myapp:2.0-aws . +docker build --build-arg CSP=azure -t myapp:2.0-azure . +docker build --build-arg CSP=gcp -t myapp:2.0-gcp . +docker build --build-arg CSP=oci -t myapp:2.0-oci . +``` + +**Maven profiles in pom.xml:** + +```xml + + + aws + true + + + org.sunbird + cloud-storage-sdk-aws + 2.0.0 + runtime + + + + + azure + + + org.sunbird + cloud-storage-sdk-azure + 2.0.0 + runtime + + + + + gcp + + + org.sunbird + cloud-storage-sdk-gcp + 2.0.0 + runtime + + + + + oci + + + org.sunbird + cloud-storage-sdk-oci + 2.0.0 + runtime + + + + +``` + +### 7.2 Option B — Single Image with All CSP Modules + +Include all four CSP modules in the image. The active provider is selected at runtime via `StorageConfig`. This maximises deployment flexibility but increases image size due to multiple large SDK JARs (~300–400 MB additional). + +```xml + + + + org.sunbird + cloud-storage-sdk-api + 2.0.0 + + + org.sunbird + cloud-storage-sdk-aws + 2.0.0 + runtime + + + org.sunbird + cloud-storage-sdk-azure + 2.0.0 + runtime + + + org.sunbird + cloud-storage-sdk-gcp + 2.0.0 + runtime + + + org.sunbird + cloud-storage-sdk-oci + 2.0.0 + runtime + + +``` + +### 7.3 Comparison and Recommendation + +| Criterion | Option A (CSP-tagged images) | Option B (All CSPs in one image) | +|-----------|------------------------------|----------------------------------| +| **Image size** | Lean — only the required CSP JARs are included | Larger — carries 4× the SDK JARs (~300–400 MB extra) | +| **Security surface** | Minimal — unused CSP SDKs and their CVEs are excluded | Broader — unused dependencies still present | +| **Deployment clarity** | Explicit CSP in image tag (`myapp:2.0-aws`) reduces misconfiguration | CSP determined at runtime via env var | +| **Multi-cloud flexibility** | Requires a rebuild to switch CSP | Switch CSP by changing an env var without a rebuild | +| **CI complexity** | Separate build job per CSP | Single build job | +| **Recommended for** | Single-cloud deployments (most enterprise use cases) | Multi-tenant SaaS where tenants use different CSPs | + +> **Recommendation:** Use **Option A** (Maven profiles + CSP-tagged images) for the vast majority of deployments. Option B is only warranted when your application must serve multiple CSPs from the same running instance — for example, a multi-tenant platform where each tenant brings their own cloud account. For all single-cloud deployments, Option A produces smaller, more auditable, and more secure images. + +--- + +## 8. Runtime Environment Variables + +Set these via Kubernetes Secrets, pod annotations, or ECS task definitions. **Never hardcode credentials in Dockerfiles or configuration files.** + +### AWS + +| Variable | Description | When Required | +|----------|-------------|--------------| +| `AWS_REGION` | Cloud region, e.g. `us-east-1`. Also accepted as `AWS_DEFAULT_REGION`. | Always | +| `AWS_ACCESS_KEY_ID` | Static access key ID. | `ACCESS_KEY` only | +| `AWS_SECRET_ACCESS_KEY` | Static secret access key. | `ACCESS_KEY` only | +| `AWS_ROLE_ARN` | IAM role ARN for EKS IRSA. | `OIDC` (EKS) | +| `AWS_WEB_IDENTITY_TOKEN_FILE` | Path to OIDC token file. Injected automatically by EKS. | `OIDC` (EKS) | + +### Azure + +| Variable | Description | When Required | +|----------|-------------|--------------| +| `AZURE_CLIENT_ID` | Managed identity or app registration client ID. | `OIDC` / `IAM` | +| `AZURE_TENANT_ID` | Azure Active Directory tenant ID. | `OIDC` / `IAM` | +| `AZURE_FEDERATED_TOKEN_FILE` | Path to K8s service account token. Injected by AKS Workload Identity. | `OIDC` (AKS) | +| `AZURE_ACCOUNT_KEY` | Storage account access key. Read this into `storageSecret` in your `StorageModule`. | `ACCESS_KEY` only | + +### GCP + +| Variable | Description | When Required | +|----------|-------------|--------------| +| `GOOGLE_APPLICATION_CREDENTIALS` | Path to a service account JSON key file. For dev/legacy use only. | `ACCESS_KEY` only | +| *(none for GKE)* | GKE Workload Identity binds a KSA to a GSA via pod annotation. No env vars required. | `OIDC` (GKE) | + +### OCI + +| Variable | Description | When Required | +|----------|-------------|--------------| +| `OCI_TENANCY_ID` | Tenancy OCID. | `ACCESS_KEY` only | +| `OCI_FINGERPRINT` | API key fingerprint. | `ACCESS_KEY` only | +| *(none for OKE)* | OKE Instance Principal auth uses the node's IMDS endpoint automatically. | `INSTANCE_PROFILE` | + +--- + +## 9. Exception Handling + +All `IStorageService` methods throw `StorageServiceException`, an unchecked `RuntimeException`. Wrap calls appropriately in your service layer to distinguish transient from permanent failures. + +```java +import org.sunbird.cloud.storage.exception.StorageServiceException; + +@Service +public class ContentStorageService { + + private final IStorageService storage; + + public ContentStorageService(IStorageService storage) { + this.storage = storage; + } + + public String uploadContent(String bucket, Path localFile, String remoteKey) { + try { + return storage.upload( + bucket, + localFile.toString(), + remoteKey, + false, // not a directory + 1, // first attempt + 3, // retry up to 3 times total + null // no TTL + ); + } catch (StorageServiceException e) { + log.error("Upload failed for key={}", remoteKey, e); + throw new ContentServiceException("Storage upload failed", e); + } + } +} +``` + +> **Built-in Retry:** `AbstractStorageService` provides automatic retry orchestration for `upload()` and `put()` operations. The `attempt` parameter tracks the current attempt (start at `1`) and `retryCount` sets the maximum. You do not need to implement your own retry loop. + +--- + +## 10. Quick Reference + +### CSP Comparison + +| CSP | Module Artifact | SDK Version | Recommended AuthType | HDFS Prefix Format | +|-----|----------------|-------------|---------------------|--------------------| +| AWS S3 | `cloud-storage-sdk-aws` | AWS SDK v2 `2.25.0` | `OIDC` (IRSA) | `s3n:///` | +| Azure Blob | `cloud-storage-sdk-azure` | Azure SDK `12.25.0` | `OIDC` (Workload Identity) | `wasb://@.blob.core.windows.net/` | +| GCP Storage | `cloud-storage-sdk-gcp` | Google Cloud Java `2.30.0` | `OIDC` (GKE WI) | `gs:///` | +| OCI Object Storage | `cloud-storage-sdk-oci` | OCI Java SDK `3.30.0` | `INSTANCE_PROFILE` | `oci://@/` | +| Ceph S3 | `cloud-storage-sdk-aws` | AWS SDK v2 `2.25.0` (via `endpointOverride`) | `ACCESS_KEY` | `s3n:///` | + +### Key Dependency Versions + +| Component | Version | +|-----------|---------| +| SDK | `2.0.0` | +| Java target | `11` | +| AWS SDK v2 | `2.25.0` | +| Azure Blob Storage | `12.25.0` | +| Azure Identity | `1.11.0` | +| GCP Cloud Storage | `2.30.0` | +| OCI Java SDK | `3.30.0` | +| Apache Tika | `2.9.0` | +| JUnit 5 | `5.10.1` | +| Mockito | `5.8.0` | + +### Build Commands + +```bash +# Full build with unit tests (integration tests auto-skip without env vars) +mvn clean verify + +# Build without tests +mvn clean package -DskipTests + +# Build with specific CSP profile +mvn clean package -DskipTests -Paws +mvn clean package -DskipTests -Pazure +mvn clean package -DskipTests -Pgcp +mvn clean package -DskipTests -Poci + +# Build a single module +mvn -pl cloud-storage-sdk-aws package -DskipTests + +# Run unit tests only (no cloud access needed) +mvn -pl cloud-storage-sdk-api test + +# Docker — build one image per CSP (Option A) +docker build --build-arg CSP=aws -t myapp:2.0-aws . +docker build --build-arg CSP=azure -t myapp:2.0-azure . +docker build --build-arg CSP=gcp -t myapp:2.0-gcp . +docker build --build-arg CSP=oci -t myapp:2.0-oci . +``` + +--- + +*Sunbird Cloud Storage SDK v2.0.0 — Generated February 2026* diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..9818ec4 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,545 @@ +# Implementation Plan: Multi-Module Java 11 Cloud Storage SDK + +## Overview + +Refactor the Sunbird Cloud Storage SDK from a single Scala/jclouds-based module into a **multi-module pure Java 11 Maven project** with CSP-specific implementations using official cloud SDKs. Replaces all existing Scala source code. + +### Key Decisions +- **Ceph S3**: Folded into the AWS module via configurable endpoint override (no separate module) +- **OCI**: Uses official OCI Java SDK (`oci-java-sdk-objectstorage`) for native API and Instance Principal auth +- **Existing code**: Replaced entirely — clean break at version 2.0.0 + +--- + +## 1. Maven Module Structure + +### Directory Layout +``` +sunbird-cloud-storage-sdk/ +├── pom.xml (parent POM, packaging=pom) +├── cloud-storage-sdk-api/ +│ ├── pom.xml +│ └── src/main/java/org/sunbird/cloud/storage/ +│ ├── IStorageService.java (main interface) +│ ├── StorageConfig.java (config with Builder pattern) +│ ├── StorageServiceFactory.java (ServiceLoader-based factory) +│ ├── StorageServiceProvider.java (SPI interface) +│ ├── AbstractStorageService.java (shared orchestration logic) +│ ├── model/ +│ │ ├── Blob.java (data model) +│ │ └── DeleteTarget.java (replaces Scala tuple) +│ ├── exception/ +│ │ └── StorageServiceException.java (runtime exception) +│ └── util/ +│ ├── DateRangeUtil.java (java.time replacement for Joda-Time) +│ └── FileUtil.java (file copy, zip extraction) +├── cloud-storage-sdk-aws/ +│ ├── pom.xml +│ └── src/ +│ ├── main/java/org/sunbird/cloud/storage/service/aws/ +│ │ ├── AwsStorageService.java (AWS S3 + Ceph S3 implementation) +│ │ └── AwsStorageServiceProvider.java (SPI registration) +│ └── main/resources/META-INF/services/ +│ └── org.sunbird.cloud.storage.StorageServiceProvider +├── cloud-storage-sdk-azure/ +│ ├── pom.xml +│ └── src/ +│ ├── main/java/org/sunbird/cloud/storage/service/azure/ +│ │ ├── AzureStorageService.java +│ │ └── AzureStorageServiceProvider.java +│ └── main/resources/META-INF/services/ +│ └── org.sunbird.cloud.storage.StorageServiceProvider +├── cloud-storage-sdk-gcp/ +│ ├── pom.xml +│ └── src/ +│ ├── main/java/org/sunbird/cloud/storage/service/gcp/ +│ │ ├── GcpStorageService.java +│ │ └── GcpStorageServiceProvider.java +│ └── main/resources/META-INF/services/ +│ └── org.sunbird.cloud.storage.StorageServiceProvider +└── cloud-storage-sdk-oci/ + ├── pom.xml + └── src/ + ├── main/java/org/sunbird/cloud/storage/service/oci/ + │ ├── OciStorageService.java + │ └── OciStorageServiceProvider.java + └── main/resources/META-INF/services/ + └── org.sunbird.cloud.storage.StorageServiceProvider +``` + +### Artifact Summary + +| Module | Artifact ID | Key Dependencies | +|--------|-------------|------------------| +| Parent | `cloud-storage-sdk-parent` | — (pom packaging) | +| API | `cloud-storage-sdk-api` | SLF4J, Tika Core, Jackson Databind | +| AWS | `cloud-storage-sdk-aws` | API module + AWS SDK v2 (s3, sts) | +| Azure | `cloud-storage-sdk-azure` | API module + azure-storage-blob, azure-identity | +| GCP | `cloud-storage-sdk-gcp` | API module + google-cloud-storage | +| OCI | `cloud-storage-sdk-oci` | API module + oci-java-sdk-objectstorage | + +**Group ID**: `org.sunbird` (unchanged) +**Version**: `2.0.0` (major bump — breaking change from Scala to Java) + +--- + +## 2. Parent POM Configuration + +```xml + + 11 + 11 + 11 + UTF-8 + + + 2.25.0 + 12.25.0 + 1.11.0 + 2.30.0 + 3.30.0 + 2.9.0 + 2.15.3 + 2.0.9 + 5.10.1 + 5.8.0 + +``` + +Uses `` with BOM imports for AWS (`software.amazon.awssdk:bom`), Azure (`com.azure:azure-sdk-bom`), and GCP (`com.google.cloud:libraries-bom`). + +--- + +## 3. Interface Design (Java) + +### 3.1 Scala-to-Java Type Mapping + +| Scala Type | Java Type | Notes | +|-----------|----------|-------| +| `Option[Boolean]` | `boolean` parameter + method overloads with defaults | Convenience `default` methods | +| `Option[Int]` | `Integer` (nullable) | Null = not specified | +| `Option[String]` | `String` (nullable) | Null = not specified | +| `List[Blob]` | `java.util.List` | Immutable via `List.of()` / `Collections.unmodifiableList()` | +| `List[(String, Boolean)]` | `java.util.List` | New typed record instead of tuple | +| `Future[List[String]]` | `CompletableFuture>` | Standard Java async | +| `Array[Byte]` | `byte[]` | Direct | +| `Map[String, AnyRef]` | `Map` | Standard Java | +| `Option[Array[Byte]]` | `byte[]` (nullable) | Null = no payload | + +### 3.2 IStorageService Interface + +```java +public interface IStorageService extends AutoCloseable { + + // Upload + String upload(String container, String file, String objectKey, + boolean isDirectory, int attempt, int retryCount, Integer ttl); + default String upload(String container, String file, String objectKey) { ... } + + CompletableFuture> uploadFolder(String container, String file, + String objectKey, boolean isPublic, Integer ttl, + Integer retryCount, int attempt); + default CompletableFuture> uploadFolder(String container, + String file, String objectKey) { ... } + + String put(String container, byte[] content, String objectKey, + boolean isPublic, boolean isDirectory, Integer ttl, Integer retryCount); + default String put(String container, byte[] content, String objectKey) { ... } + + // Signed URLs + String getSignedURL(String container, String objectKey, Integer ttl, String permission); + default String getSignedURL(String container, String objectKey) { ... } + + String getSignedURLV2(String container, String objectKey, Integer ttl, + String permission, String contentType, + Map additionalParams); + default String getSignedURLV2(String container, String objectKey) { ... } + + // Download + void download(String container, String objectKey, String localPath, boolean isDirectory); + default void download(String container, String objectKey, String localPath) { ... } + + // Delete + void deleteObject(String container, String objectKey, boolean isDirectory); + default void deleteObject(String container, String objectKey) { ... } + void deleteObjects(String container, List objectKeys); + + // Copy + void copyObjects(String fromContainer, String fromKey, + String toContainer, String toKey, boolean isDirectory); + default void copyObjects(String from, String fKey, String to, String tKey) { ... } + + // Archive + void extractArchive(String container, String objectKey, String toKey); + + // Object access + Blob getObject(String container, String objectKey, boolean withPayload); + default Blob getObject(String container, String objectKey) { ... } + List getObjectData(String container, String objectKey); + List listObjects(String container, String prefix, boolean withPayload); + default List listObjects(String container, String prefix) { ... } + List listObjectKeys(String container, String prefix); + + // Search + List searchObjects(String container, String prefix, + String fromDate, String toDate, + Integer delta, String pattern); + List searchObjectKeys(String container, String prefix, + String fromDate, String toDate, + Integer delta, String pattern); + + // Path operations + List getPaths(String container, List objects); + String getUri(String container, String prefix, boolean isDirectory); + default String getUri(String container, String prefix) { ... } + + // Lifecycle + @Override void close(); +} +``` + +### 3.3 StorageConfig (Builder pattern with enums) + +```java +public final class StorageConfig { + + public enum AuthType { + ACCESS_KEY, + OIDC, // NEW: primary auth for cloud-native deployments + IAM, + IAM_ROLE, + INSTANCE_PROFILE + } + + public enum StorageType { + AWS, AZURE, GCLOUD, OCI, CEPHS3 + } + + // Fields: type, storageKey, storageSecret, endPoint, region, authType + // Builder: StorageConfig.builder(StorageType.AWS).authType(AuthType.OIDC).build() +} +``` + +### 3.4 Models + +**Blob.java** — Immutable data class with: `key`, `contentLength`, `lastModified`, `metadata`, `payload` (nullable byte[]) + +**DeleteTarget.java** — Replaces `(String, Boolean)` tuple with: `objectKey`, `isDirectory`. Static factories: `DeleteTarget.file(key)`, `DeleteTarget.directory(key)` + +### 3.5 StorageServiceFactory (ServiceLoader discovery) + +```java +public final class StorageServiceFactory { + public static IStorageService getStorageService(StorageConfig config) { + ServiceLoader loader = + ServiceLoader.load(StorageServiceProvider.class); + for (StorageServiceProvider provider : loader) { + if (provider.supports(config.getType())) { + return provider.create(config); + } + } + throw new StorageServiceException("No provider found for: " + config.getType()); + } +} +``` + +### 3.6 StorageServiceProvider (SPI interface) + +```java +public interface StorageServiceProvider { + boolean supports(StorageConfig.StorageType type); + IStorageService create(StorageConfig config); +} +``` + +Each CSP module registers its provider via `META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider`. + +--- + +## 4. AbstractStorageService (Shared Logic in API Module) + +Migrates orchestration logic from `BaseStorageService.scala` to Java. This is ~50% of BaseStorageService. + +### Logic kept in abstract base (CSP-independent): +- `filesList()` — recursive file listing +- `uploadFolder()` — parallel upload via CompletableFuture + ForkJoinPool +- `upload()` — retry loop orchestration calling abstract `putObject()` +- `put()` — retry loop calling abstract `putContent()` +- `download()` — directory download orchestration calling abstract `downloadObject()` +- `deleteObject()` — delegates to `deleteObjects()` +- `searchObjects()` / `searchObjectKeys()` — date range iteration via `DateRangeUtil` +- `extractArchive()` — download → unzip → upload orchestration +- `listObjects()` — iterates `listObjectKeys` + `getObject` +- `copyObjects()` — directory copy orchestration calling abstract `copyObject()` + +### Abstract methods each CSP must implement: + +```java +// Container management +protected abstract void ensureContainerExists(String container); + +// Blob I/O +protected abstract String putObject(String container, String objectKey, File file); +protected abstract String putObject(String container, String objectKey, byte[] content); +protected abstract InputStream getObjectStream(String container, String objectKey); +protected abstract String getObjectUri(String container, String objectKey); +protected abstract BlobDetail getObjectDetail(String container, String objectKey); + +// Listing +protected abstract List listKeys(String container, String prefix); + +// Delete & Copy +protected abstract void removeObjects(String container, List objectKeys); +protected abstract void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey); + +// Signed URLs +protected abstract String generateSignedGetUrl(String container, String objectKey, + int ttlSeconds); +protected abstract String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams); + +// HDFS path prefix (e.g., "s3n://", "wasb://...", "gs://") +protected abstract String getHdfsPrefix(String container); +``` + +### Utility classes (in API module): + +**DateRangeUtil.java**: Replaces Joda-Time with `java.time.LocalDate` / `DateTimeFormatter` / `ChronoUnit`. + +**FileUtil.java**: `listFilesRecursive()`, `copyStream()`, `unZip()` — ported from `CommonUtil.scala`. + +--- + +## 5. CSP Module Implementations + +### 5.1 AWS Module (`cloud-storage-sdk-aws`) + +**SDK**: AWS SDK v2 (`software.amazon.awssdk:s3`, `software.amazon.awssdk:sts`) + +**Key class**: `AwsStorageService extends AbstractStorageService` + +**Credential resolution by AuthType**: +| AuthType | AWS Credential Provider | +|----------|------------------------| +| `ACCESS_KEY` | `StaticCredentialsProvider.create(AwsBasicCredentials.create(key, secret))` | +| `OIDC` | `StsWebIdentityTokenFileCredentialsProvider` (reads `AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_ROLE_ARN`) | +| `IAM` / `IAM_ROLE` / `INSTANCE_PROFILE` | `DefaultCredentialsProvider.create()` | + +**Ceph S3 support**: When `StorageType.CEPHS3`, builds `S3Client` with: +- `endpointOverride(URI.create(config.getEndPoint()))` +- `S3Configuration.builder().pathStyleAccessEnabled(true).build()` + +**Signed URLs**: Uses `S3Presigner` with `presignGetObject()` / `presignPutObject()` + +**HDFS prefix**: `s3n:///` + +**AwsStorageServiceProvider**: `supports()` returns `true` for `StorageType.AWS` and `StorageType.CEPHS3` + +### 5.2 Azure Module (`cloud-storage-sdk-azure`) + +**SDK**: `com.azure:azure-storage-blob`, `com.azure:azure-identity` + +Replaces the old `com.microsoft.azure:azure-storage:5.0.0` with the modern Azure SDK for Java. + +**Key class**: `AzureStorageService extends AbstractStorageService` + +**Credential resolution by AuthType**: +| AuthType | Azure Credential | +|----------|-----------------| +| `ACCESS_KEY` | `StorageSharedKeyCredential(accountName, accountKey)` | +| `OIDC` | `WorkloadIdentityCredentialBuilder` (reads `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_FEDERATED_TOKEN_FILE`) | +| `IAM` / `INSTANCE_PROFILE` | `ManagedIdentityCredentialBuilder` | + +**Signed URLs**: Uses SAS (Shared Access Signatures): +- Access key auth: `blobClient.generateSas(sasValues)` +- OIDC/IAM auth: User Delegation SAS via `blobServiceClient.getUserDelegationKey()` + `blobClient.generateUserDelegationSas()` + +**HDFS prefix**: `wasb://@.blob.core.windows.net/` + +### 5.3 GCP Module (`cloud-storage-sdk-gcp`) + +**SDK**: `com.google.cloud:google-cloud-storage` + +The current GCP implementation already works around jclouds by using native GCP SDK for signed URLs and raw HTTP for `listObjectKeys` and `download`. The new module eliminates jclouds entirely and uses native SDK for everything. + +**Key class**: `GcpStorageService extends AbstractStorageService` + +**Credential resolution by AuthType**: +| AuthType | GCP Credential | +|----------|---------------| +| `ACCESS_KEY` | `ServiceAccountCredentials.fromPkcs8()` using `additionalParams` from config, or `ServiceAccountCredentials.fromStream()` | +| `OIDC` | `ExternalAccountCredentials` via Workload Identity Federation (`GOOGLE_APPLICATION_CREDENTIALS` env var) | +| `IAM` / `INSTANCE_PROFILE` | `GoogleCredentials.getApplicationDefault()` / `ComputeEngineCredentials` | + +**Signed URLs** (ported from current `GcloudStorageService.getPutSignedURL()`): +```java +storage.signUrl(blobInfo, ttl, TimeUnit.SECONDS, + Storage.SignUrlOption.httpMethod(method), + Storage.SignUrlOption.withV4Signature()); +``` +Supports chunked/resumable uploads via `HttpMethod.POST` + `x-goog-resumable: start` header (matching current implementation). + +**HDFS prefix**: `gs:///` + +### 5.4 OCI Module (`cloud-storage-sdk-oci`) + +**SDK**: `com.oracle.oci.sdk:oci-java-sdk-objectstorage` (native OCI API) + +**Key class**: `OciStorageService extends AbstractStorageService` + +**Credential resolution by AuthType**: +| AuthType | OCI Credential Provider | +|----------|------------------------| +| `ACCESS_KEY` | `SimpleAuthenticationDetailsProvider` (API key-based auth with tenancy/user/fingerprint/key) | +| `OIDC` | `OkeWorkloadIdentityAuthenticationDetailsProvider` (for OKE Kubernetes workloads) | +| `IAM` / `INSTANCE_PROFILE` | `InstancePrincipalsAuthenticationDetailsProvider` | + +**Key differences from current S3-compatible approach**: +- Uses `ObjectStorageClient` instead of S3 client with endpoint override +- Native pre-authenticated request (PAR) API for signed URLs via `CreatePreauthenticatedRequestRequest` +- Uses `PutObjectRequest` with MD5 hash (matching current OCI behavior) +- Requires `namespace` parameter (OCI-specific) — added to `StorageConfig` as optional field or derived via `ObjectStorageClient.getNamespace()` + +**HDFS prefix**: `oci://@/` (OCI native) or configurable + +--- + +## 6. Authentication Architecture + +### OIDC as Primary Auth + +OIDC-based auth is the default/primary approach for cloud-native Kubernetes deployments: + +| CSP | OIDC Mechanism | Environment Variables | +|-----|---------------|----------------------| +| AWS | STS AssumeRoleWithWebIdentity | `AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_ROLE_ARN`, `AWS_ROLE_SESSION_NAME` | +| Azure | Workload Identity Federation | `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_FEDERATED_TOKEN_FILE` | +| GCP | Workload Identity Federation | `GOOGLE_APPLICATION_CREDENTIALS` (credential config JSON) | +| OCI | OKE Workload Identity | OCI-specific service account token | + +### Configuration Example + +```java +// OIDC auth (new primary approach — no keys needed) +StorageConfig config = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.OIDC) + .region("us-east-1") + .build(); + +// Access Key auth (configurable fallback) +StorageConfig config = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .storageKey("AKIA...") + .storageSecret("secret...") + .build(); + +// Factory creates the right service via ServiceLoader +IStorageService service = StorageServiceFactory.getStorageService(config); +``` + +--- + +## 7. Implementation Phases + +### Phase 1: Project Structure + API Module +1. Create parent POM with multi-module structure +2. Remove all existing Scala source code and Scala-related build configuration +3. Create `cloud-storage-sdk-api` module POM +4. Implement `IStorageService.java` interface +5. Implement `Blob.java`, `DeleteTarget.java`, `StorageServiceException.java` +6. Implement `StorageConfig.java` with Builder pattern and enums +7. Implement `StorageServiceProvider.java` SPI interface +8. Implement `StorageServiceFactory.java` with ServiceLoader discovery +9. Implement `DateRangeUtil.java` (port from Joda-Time to java.time) +10. Implement `FileUtil.java` (port from CommonUtil.scala) +11. Implement `AbstractStorageService.java` with shared orchestration logic + +### Phase 2: AWS Module +1. Create `cloud-storage-sdk-aws` module POM +2. Implement `AwsStorageService.java` with AWS SDK v2 S3Client / S3Presigner +3. Implement credential resolution (access key, OIDC via STS, IAM) +4. Handle Ceph S3 (endpoint override + path-style access) +5. Implement `AwsStorageServiceProvider.java` + META-INF/services registration +6. Write unit tests + +### Phase 3: Azure Module +1. Create `cloud-storage-sdk-azure` module POM +2. Implement `AzureStorageService.java` with BlobServiceClient +3. Implement credential resolution (shared key, workload identity, managed identity) +4. Implement SAS token generation for signed URLs +5. Implement `AzureStorageServiceProvider.java` + META-INF/services registration +6. Write unit tests + +### Phase 4: GCP Module +1. Create `cloud-storage-sdk-gcp` module POM +2. Implement `GcpStorageService.java` with native com.google.cloud.storage.Storage +3. Implement credential resolution (service account, WIF, application default) +4. Port V4 signed URL logic from current `GcloudStorageService.getPutSignedURL()` +5. Implement `GcpStorageServiceProvider.java` + META-INF/services registration +6. Write unit tests + +### Phase 5: OCI Module +1. Create `cloud-storage-sdk-oci` module POM +2. Implement `OciStorageService.java` with OCI ObjectStorageClient +3. Implement credential resolution (API key, instance principal, OKE workload identity) +4. Handle OCI-specific concerns (namespace, MD5 on upload, pre-authenticated requests) +5. Implement `OciStorageServiceProvider.java` + META-INF/services registration +6. Write unit tests + +### Phase 6: CI/CD and Release +1. Update `.github/workflows/maven-release.yml` for multi-module build +2. Configure individual module publishing to Maven Central +3. Update README with architecture overview and migration guide + +--- + +## 8. Consumer Migration Guide + +### Dependency Change + +**Before:** +```xml + + org.sunbird + cloud-store-sdk_2.13 + 1.4.10 + +``` + +**After (e.g., for AWS):** +```xml + + org.sunbird + cloud-storage-sdk-api + 2.0.0 + + + org.sunbird + cloud-storage-sdk-aws + 2.0.0 + runtime + +``` + +### Key API Changes + +| Before (Scala) | After (Java) | +|----------------|-------------| +| `Option(false)` parameters | `boolean` params with `default` method overloads | +| `service.closeContext()` | `service.close()` (AutoCloseable / try-with-resources) | +| `StorageConfig("aws", key, secret)` | `StorageConfig.builder(AWS).storageKey(key).storageSecret(secret).build()` | +| `List[(String, Boolean)]` | `List` | +| `searchObjectkeys` (lowercase k) | `searchObjectKeys` (camelCase) | +| `import o.s.c.s.factory.StorageServiceFactory` | `import o.s.c.s.StorageServiceFactory` (package changed) | + +### Scala consumers +Since the new modules are pure Java JARs, Scala consumers can use them directly without Scala version cross-compilation issues: +```scala +import org.sunbird.cloud.storage._ +val config = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.OIDC) + .build() +val service = StorageServiceFactory.getStorageService(config) +``` diff --git a/README.md b/README.md index 995bf01..8068060 100644 --- a/README.md +++ b/README.md @@ -1 +1,94 @@ -# sunbird-cloud-storage \ No newline at end of file +# Sunbird Cloud Storage SDK + +Multi-module Java 11 SDK providing cloud storage operations (upload, download, signed URLs, delete, copy, search, etc.) with CSP-specific implementations using official cloud SDKs. + +## Modules + +| Module | Artifact ID | Description | +|--------|-------------|-------------| +| API | `cloud-storage-sdk-api` | Interface, models, factory, and shared abstractions | +| AWS | `cloud-storage-sdk-aws` | AWS S3 + Ceph S3-compatible (AWS SDK v2) | +| Azure | `cloud-storage-sdk-azure` | Azure Blob Storage (Azure SDK for Java) | +| GCP | `cloud-storage-sdk-gcp` | Google Cloud Storage (Google Cloud Java SDK) | +| OCI | `cloud-storage-sdk-oci` | Oracle Cloud Object Storage (OCI Java SDK) | + +## Authentication + +OIDC-based authentication is the primary approach for cloud-native deployments. Access Key authentication is available as a configurable fallback. + +| Auth Type | Description | +|-----------|-------------| +| `OIDC` | Workload Identity Federation / Web Identity Token (primary) | +| `ACCESS_KEY` | Static access key and secret | +| `IAM` | IAM role / managed identity | +| `INSTANCE_PROFILE` | Cloud instance metadata credentials | + +## Usage + +### Maven Dependency + +Add the API module and one CSP-specific module: + +```xml + + org.sunbird + cloud-storage-sdk-api + 2.0.0 + + + + + org.sunbird + cloud-storage-sdk-aws + 2.0.0 + runtime + +``` + +### Java Example + +```java +import org.sunbird.cloud.storage.*; + +// OIDC auth (cloud-native, no keys needed) +StorageConfig config = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.OIDC) + .region("us-east-1") + .build(); + +// Access Key auth (fallback) +StorageConfig config = StorageConfig.builder(StorageConfig.StorageType.AWS) + .authType(StorageConfig.AuthType.ACCESS_KEY) + .storageKey("AKIA...") + .storageSecret("secret...") + .build(); + +// Factory auto-discovers the CSP module via ServiceLoader +try (IStorageService service = StorageServiceFactory.getStorageService(config)) { + // Upload + String url = service.upload("my-bucket", "/local/file.txt", "path/file.txt"); + + // Signed URL + String signedUrl = service.getSignedURL("my-bucket", "path/file.txt", 3600, "r"); + + // Download + service.download("my-bucket", "path/file.txt", "/local/download/"); + + // List + List keys = service.listObjectKeys("my-bucket", "path/"); +} +``` + +## Building + +```bash +mvn clean install +``` + +## Migration from v1.x + +See [PLAN.md](PLAN.md) for the detailed migration guide from the Scala/jclouds-based v1.x. + +## License + +[MIT License](LICENSE) diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..3daf178 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,285 @@ +# Testing Guide + +This document explains how to run tests for the Cloud Storage SDK, including +unit tests that run without any cloud credentials and integration tests that +require access to real cloud storage. + +--- + +## Unit Tests (no credentials required) + +The **cloud-storage-sdk-api** module contains pure unit tests that validate +shared logic (date utilities, file utilities, abstract orchestration) without +connecting to any cloud provider. They run with an in-memory storage +implementation. + +```bash +# Run API unit tests only +mvn -pl cloud-storage-sdk-api test + +# Run all unit tests across all modules (integration tests auto-skip when +# environment variables are absent) +mvn test +``` + +The unit tests always execute; no special configuration is needed. + +--- + +## Integration Tests + +Each CSP module contains an integration test class that exercises the full +storage operations (upload, download, signed URLs, copy, delete, search, etc.) +against a real cloud bucket. + +**Integration tests are skipped by default.** They only execute when specific +environment variables are set, making it safe to run `mvn test` in any +environment — CI pipelines, local laptops, or cloud VMs. + +### How the skip mechanism works + +Each CSP test uses JUnit 5 `assumeTrue()` guards. When the required +`*_STORAGE_TEST_ENABLED` environment variable is absent, JUnit marks +the tests as **skipped** (not failed), so CI pipelines remain green. + +--- + +### AWS S3 + +**Test class:** `cloud-storage-sdk-aws` → `AwsStorageServiceTest` + +| Variable | Required | Description | +|---|---|---| +| `AWS_STORAGE_TEST_ENABLED` | Yes | Any non-empty value (e.g. `true`) enables the tests | +| `AWS_STORAGE_TEST_CONTAINER` | Yes | Name of a pre-existing, writable S3 bucket | +| `AWS_STORAGE_TEST_AUTH_TYPE` | No | `ACCESS_KEY` (default), `IAM`, `IAM_ROLE`, or `INSTANCE_PROFILE` | +| `AWS_ACCESS_KEY_ID` | If ACCESS_KEY | Standard AWS access key | +| `AWS_SECRET_ACCESS_KEY` | If ACCESS_KEY | Standard AWS secret key | +| `AWS_REGION` | No | AWS region (default: `us-east-1`) | +| `AWS_STORAGE_TEST_ENDPOINT` | No | Custom endpoint for Ceph S3-compatible storage | +| `AWS_STORAGE_TEST_TYPE` | No | `AWS` (default) or `CEPHS3` | + +#### Example: EC2 instance with IAM role + +On an EC2 instance (or EKS pod) that has an IAM role with S3 permissions: + +```bash +export AWS_STORAGE_TEST_ENABLED=true +export AWS_STORAGE_TEST_CONTAINER=my-test-bucket +export AWS_STORAGE_TEST_AUTH_TYPE=INSTANCE_PROFILE +export AWS_REGION=us-east-1 + +mvn -pl cloud-storage-sdk-aws test +``` + +#### Example: Local development with access keys + +```bash +export AWS_STORAGE_TEST_ENABLED=true +export AWS_STORAGE_TEST_CONTAINER=my-test-bucket +export AWS_ACCESS_KEY_ID=AKIA... +export AWS_SECRET_ACCESS_KEY=wJal... +export AWS_REGION=us-east-1 + +mvn -pl cloud-storage-sdk-aws test +``` + +#### Example: Ceph S3-compatible storage + +```bash +export AWS_STORAGE_TEST_ENABLED=true +export AWS_STORAGE_TEST_CONTAINER=my-ceph-bucket +export AWS_STORAGE_TEST_TYPE=CEPHS3 +export AWS_STORAGE_TEST_ENDPOINT=https://ceph-rgw.example.com +export AWS_ACCESS_KEY_ID=ceph-key +export AWS_SECRET_ACCESS_KEY=ceph-secret +export AWS_REGION=us-east-1 + +mvn -pl cloud-storage-sdk-aws test +``` + +--- + +### Azure Blob Storage + +**Test class:** `cloud-storage-sdk-azure` → `AzureStorageServiceTest` + +| Variable | Required | Description | +|---|---|---| +| `AZURE_STORAGE_TEST_ENABLED` | Yes | Any non-empty value enables the tests | +| `AZURE_STORAGE_TEST_CONTAINER` | Yes | Name of a pre-existing, writable blob container | +| `AZURE_STORAGE_ACCOUNT_NAME` | Yes | Azure Storage account name | +| `AZURE_STORAGE_TEST_AUTH_TYPE` | No | `ACCESS_KEY` (default), `OIDC`, `IAM`, or `INSTANCE_PROFILE` | +| `AZURE_STORAGE_ACCOUNT_KEY` | If ACCESS_KEY | Storage account key | +| `AZURE_CLIENT_ID` | If OIDC | Service principal / managed identity client ID | +| `AZURE_TENANT_ID` | If OIDC | Azure AD tenant ID | +| `AZURE_FEDERATED_TOKEN_FILE` | If OIDC | Path to the federated token file (Kubernetes workload identity) | + +#### Example: Azure VM with Managed Identity + +On an Azure VM or AKS pod with a system-assigned or user-assigned managed +identity that has `Storage Blob Data Contributor` role: + +```bash +export AZURE_STORAGE_TEST_ENABLED=true +export AZURE_STORAGE_TEST_CONTAINER=my-test-container +export AZURE_STORAGE_ACCOUNT_NAME=mystorageaccount +export AZURE_STORAGE_TEST_AUTH_TYPE=IAM + +mvn -pl cloud-storage-sdk-azure test +``` + +#### Example: Local development with account key + +```bash +export AZURE_STORAGE_TEST_ENABLED=true +export AZURE_STORAGE_TEST_CONTAINER=my-test-container +export AZURE_STORAGE_ACCOUNT_NAME=mystorageaccount +export AZURE_STORAGE_ACCOUNT_KEY=base64encodedkey... + +mvn -pl cloud-storage-sdk-azure test +``` + +--- + +### Google Cloud Storage + +**Test class:** `cloud-storage-sdk-gcp` → `GcpStorageServiceTest` + +| Variable | Required | Description | +|---|---|---| +| `GCP_STORAGE_TEST_ENABLED` | Yes | Any non-empty value enables the tests | +| `GCP_STORAGE_TEST_CONTAINER` | Yes | Name of a pre-existing, writable GCS bucket | +| `GCP_STORAGE_TEST_AUTH_TYPE` | No | `ACCESS_KEY` (default), `OIDC`, or `IAM` | +| `GCP_SERVICE_ACCOUNT_KEY_JSON` | If ACCESS_KEY | Full JSON content of a service account key | +| `GCP_STORAGE_TEST_PROJECT` | No | GCP project ID | +| `GOOGLE_APPLICATION_CREDENTIALS` | If OIDC/IAM | Standard GCP env var pointing to a credentials file | + +#### Example: GCE VM with default service account + +On a Compute Engine instance (or GKE pod) with Workload Identity or the +default compute service account that has `roles/storage.objectAdmin`: + +```bash +export GCP_STORAGE_TEST_ENABLED=true +export GCP_STORAGE_TEST_CONTAINER=my-test-bucket +export GCP_STORAGE_TEST_AUTH_TYPE=IAM + +mvn -pl cloud-storage-sdk-gcp test +``` + +#### Example: Local development with service account key + +```bash +export GCP_STORAGE_TEST_ENABLED=true +export GCP_STORAGE_TEST_CONTAINER=my-test-bucket +export GCP_SERVICE_ACCOUNT_KEY_JSON='{"type":"service_account","project_id":"...","private_key":"..."}' + +mvn -pl cloud-storage-sdk-gcp test +``` + +--- + +### Oracle Cloud Infrastructure Object Storage + +**Test class:** `cloud-storage-sdk-oci` → `OciStorageServiceTest` + +| Variable | Required | Description | +|---|---|---| +| `OCI_STORAGE_TEST_ENABLED` | Yes | Any non-empty value enables the tests | +| `OCI_STORAGE_TEST_CONTAINER` | Yes | Name of a pre-existing, writable OCI bucket | +| `OCI_STORAGE_TEST_AUTH_TYPE` | No | `ACCESS_KEY` (default), `INSTANCE_PROFILE`, or `IAM` | +| `OCI_USER_ID` | If ACCESS_KEY | User OCID (mapped to `storageKey`) | +| `OCI_PRIVATE_KEY_PEM` | If ACCESS_KEY | PEM content of the API signing key (mapped to `storageSecret`) | +| `OCI_TENANCY_ID` | If ACCESS_KEY | Tenancy OCID | +| `OCI_FINGERPRINT` | If ACCESS_KEY | Key fingerprint | +| `OCI_REGION` | No | OCI region (default: `us-ashburn-1`) | + +#### Example: OCI Compute instance with Instance Principal + +On an OCI Compute instance or OKE pod with an Instance Principal dynamic group +policy granting `manage objects` and `manage buckets`: + +```bash +export OCI_STORAGE_TEST_ENABLED=true +export OCI_STORAGE_TEST_CONTAINER=my-test-bucket +export OCI_STORAGE_TEST_AUTH_TYPE=INSTANCE_PROFILE +export OCI_REGION=us-ashburn-1 + +mvn -pl cloud-storage-sdk-oci test +``` + +#### Example: Local development with API key + +```bash +export OCI_STORAGE_TEST_ENABLED=true +export OCI_STORAGE_TEST_CONTAINER=my-test-bucket +export OCI_USER_ID=ocid1.user.oc1..aaaa... +export OCI_TENANCY_ID=ocid1.tenancy.oc1..aaaa... +export OCI_FINGERPRINT=aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99 +export OCI_PRIVATE_KEY_PEM="$(cat ~/.oci/oci_api_key.pem)" +export OCI_REGION=us-ashburn-1 + +mvn -pl cloud-storage-sdk-oci test +``` + +--- + +## Running All Integration Tests + +To run integration tests for multiple (or all) providers at once, set the +relevant environment variables and run: + +```bash +mvn test +``` + +Only the CSPs whose `*_STORAGE_TEST_ENABLED` variable is set will actually +execute; all others are skipped. + +## Running a Single CSP Module + +```bash +# AWS only +mvn -pl cloud-storage-sdk-aws test + +# Azure only +mvn -pl cloud-storage-sdk-azure test + +# GCP only +mvn -pl cloud-storage-sdk-gcp test + +# OCI only +mvn -pl cloud-storage-sdk-oci test +``` + +## Running a Specific Test Method + +```bash +mvn -pl cloud-storage-sdk-aws test \ + -Dtest="AwsStorageServiceTest#testUploadAndDownload" +``` + +--- + +## CI/CD Notes + +- **Default behaviour:** `mvn test` runs unit tests and skips all integration + tests (since `*_STORAGE_TEST_ENABLED` is not set). This is safe for all CI + environments. +- **Integration gate:** To add an integration test gate in CI, set the required + environment variables as secrets in your CI system and enable the specific CSP + tests in a dedicated pipeline stage. +- **Cleanup:** Integration tests create objects under a unique UUID-prefixed + path and delete them in `@AfterAll`. If a test run is interrupted, you may + need to manually clean up objects matching the `test-/` prefix from the + test bucket. + +## Prerequisites + +| Provider | Minimum permissions | +|---|---| +| AWS | `s3:PutObject`, `s3:GetObject`, `s3:DeleteObject`, `s3:ListBucket` on the test bucket | +| Azure | `Storage Blob Data Contributor` role on the test container | +| GCP | `roles/storage.objectAdmin` on the test bucket | +| OCI | `manage objects` and `read buckets` in the test compartment | diff --git a/cloud-storage-sdk-api/pom.xml b/cloud-storage-sdk-api/pom.xml new file mode 100644 index 0000000..4d32bb0 --- /dev/null +++ b/cloud-storage-sdk-api/pom.xml @@ -0,0 +1,54 @@ + + 4.0.0 + + org.sunbird + cloud-storage-sdk-parent + 2.0.2 + + + cloud-storage-sdk-api + jar + Cloud Storage SDK API + Interface definitions, models, and shared abstractions for cloud storage operations. + + + + org.slf4j + slf4j-api + + + org.apache.tika + tika-core + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + test-jar + + test-jar + + + + + + + diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/AbstractStorageService.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/AbstractStorageService.java new file mode 100644 index 0000000..f2d50d2 --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/AbstractStorageService.java @@ -0,0 +1,445 @@ +package org.sunbird.cloud.storage; + +import org.apache.tika.Tika; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sunbird.cloud.storage.exception.StorageServiceException; +import org.sunbird.cloud.storage.model.Blob; +import org.sunbird.cloud.storage.model.DeleteTarget; +import org.sunbird.cloud.storage.util.DateRangeUtil; +import org.sunbird.cloud.storage.util.FileUtil; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +/** + * Abstract base implementation of {@link IStorageService} providing shared orchestration logic. + * + *

CSP-specific subclasses implement the abstract primitive methods to interact with + * the actual cloud storage APIs.

+ */ +public abstract class AbstractStorageService implements IStorageService { + + private static final Logger logger = LoggerFactory.getLogger(AbstractStorageService.class); + + protected final Tika tika = new Tika(); + protected int maxRetries = 2; + protected int maxSignedUrlTTL = 604800; + + // --- Abstract primitives that each CSP module must implement --- + + protected abstract void ensureContainerExists(String container); + + protected abstract String putObject(String container, String objectKey, File file); + + protected abstract String putObject(String container, String objectKey, byte[] content); + + protected abstract InputStream getObjectStream(String container, String objectKey); + + protected abstract String getObjectUri(String container, String objectKey); + + protected abstract BlobDetail getObjectDetail(String container, String objectKey); + + protected abstract List listKeys(String container, String prefix); + + protected abstract void removeObjects(String container, List objectKeys); + + protected abstract void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey); + + protected abstract String generateSignedGetUrl(String container, String objectKey, + int ttlSeconds); + + protected abstract String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams); + + /** + * Get the HDFS path prefix for this CSP (e.g., "s3n://", "gs://", "wasb://..."). + * + * @param container the container/bucket name + * @return the full HDFS prefix including the container + */ + protected abstract String getHdfsPrefix(String container); + + /** + * Metadata holder returned by {@link #getObjectDetail(String, String)}. + */ + protected static class BlobDetail { + public final String key; + public final long contentLength; + public final Date lastModified; + public final Map metadata; + public final String uri; + + public BlobDetail(String key, long contentLength, Date lastModified, + Map metadata, String uri) { + this.key = key; + this.contentLength = contentLength; + this.lastModified = lastModified; + this.metadata = metadata; + this.uri = uri; + } + } + + // --- Orchestration implementations --- + + @Override + public String upload(String container, String file, String objectKey, + boolean isDirectory, int attempt, int retryCount, Integer ttl) { + int maxAttempts = retryCount > 0 ? retryCount : maxRetries; + try { + if (isDirectory) { + File dir = new File(file); + List files = FileUtil.listFilesRecursive(dir); + List urls = new ArrayList<>(); + for (File f : files) { + String relativePath = f.getAbsolutePath() + .substring((dir.getAbsolutePath() + File.separator).length()); + String prefix = objectKey.endsWith("/") ? objectKey : objectKey + "/"; + String key = prefix + relativePath; + urls.add(upload(container, f.getAbsolutePath(), key, false, attempt, maxAttempts, ttl)); + } + return String.join(",", urls); + } else { + if (attempt >= maxAttempts) { + throw new StorageServiceException( + "Failed to upload. file: " + file + ", key: " + objectKey + + ", attempt: " + attempt + ", maxAttempts: " + maxAttempts + + ". Exceeded maximum number of retries"); + } + ensureContainerExists(container); + String uri = putObject(container, objectKey, new File(file)); + if (ttl != null) { + return getSignedURL(container, objectKey, ttl, "r"); + } + return uri; + } + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + logger.error("Upload failed for key: {}, attempt: {}", objectKey, attempt, e); + try { + Thread.sleep((long) attempt * 2000); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + int nextAttempt = attempt + 1; + if (nextAttempt <= maxAttempts) { + return upload(container, file, objectKey, isDirectory, nextAttempt, maxAttempts, ttl); + } + throw new StorageServiceException("Upload failed after " + attempt + " attempts: " + e.getMessage(), e); + } + } + + @Override + public CompletableFuture> uploadFolder(String container, String file, + String objectKey, boolean isPublic, + Integer ttl, Integer retryCount, + int attempt) { + return CompletableFuture.supplyAsync(() -> { + File dir = new File(file); + List files = FileUtil.listFilesRecursive(dir); + List> futures = new ArrayList<>(); + for (File f : files) { + String relativePath = f.getAbsolutePath() + .substring((dir.getAbsolutePath() + File.separator).length()); + String prefix = objectKey.endsWith("/") ? objectKey : objectKey + "/"; + String key = prefix + relativePath; + futures.add(CompletableFuture.supplyAsync(() -> + upload(container, f.getAbsolutePath(), key, false, attempt, + retryCount != null ? retryCount : 0, ttl))); + } + return futures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + }); + } + + @Override + public String put(String container, byte[] content, String objectKey, + boolean isPublic, boolean isDirectory, Integer ttl, Integer retryCount) { + int maxAttempts = retryCount != null ? retryCount : maxRetries; + int attempt = 0; + while (true) { + try { + if (attempt >= maxAttempts) { + throw new StorageServiceException( + "Failed to put. key: " + objectKey + + ", attempt: " + attempt + ", maxAttempts: " + maxAttempts + + ". Exceeded maximum number of retries"); + } + ensureContainerExists(container); + String uri = putObject(container, objectKey, content); + if (isPublic) { + return getSignedURL(container, objectKey, + ttl != null ? ttl : maxSignedUrlTTL, "r"); + } + return uri; + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + logger.error("Put failed for key: {}, attempt: {}", objectKey, attempt, e); + try { + Thread.sleep((long) attempt * 2000); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + attempt++; + if (attempt > maxAttempts) { + throw new StorageServiceException("Put failed: " + e.getMessage(), e); + } + } + } + } + + @Override + public String getSignedURL(String container, String objectKey, Integer ttl, String permission) { + int effectiveTtl = ttl != null ? ttl : maxSignedUrlTTL; + if ("w".equalsIgnoreCase(permission)) { + return generateSignedPutUrl(container, objectKey, effectiveTtl, null, null); + } + return generateSignedGetUrl(container, objectKey, effectiveTtl); + } + + @Override + public String getSignedURLV2(String container, String objectKey, Integer ttl, + String permission, String contentType, + Map additionalParams) { + int effectiveTtl = ttl != null ? ttl : maxSignedUrlTTL; + if ("w".equalsIgnoreCase(permission)) { + return generateSignedPutUrl(container, objectKey, effectiveTtl, contentType, additionalParams); + } + return generateSignedGetUrl(container, objectKey, effectiveTtl); + } + + @Override + public void download(String container, String objectKey, String localPath, boolean isDirectory) { + try { + if (isDirectory) { + List objects = listObjectKeys(container, objectKey); + for (String obj : objects) { + try (InputStream stream = getObjectStream(container, obj)) { + String relativePath = obj.startsWith(objectKey) ? obj.substring(objectKey.length()) : obj; + File targetFile = new File(localPath, relativePath); + File parentDir = targetFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + String fileName = targetFile.getName(); + String dirPath = parentDir != null ? parentDir.getAbsolutePath() + "/" : localPath; + FileUtil.copyStream(stream, dirPath, fileName); + } + } + } else { + try (InputStream stream = getObjectStream(container, objectKey)) { + String fileName = objectKey.contains("/") + ? objectKey.substring(objectKey.lastIndexOf('/') + 1) + : objectKey; + FileUtil.copyStream(stream, localPath, fileName); + } + } + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + throw new StorageServiceException("Download failed: " + e.getMessage(), e); + } + } + + @Override + public void deleteObject(String container, String objectKey, boolean isDirectory) { + deleteObjects(container, List.of(new DeleteTarget(objectKey, isDirectory))); + } + + @Override + public void deleteObjects(String container, List objectKeys) { + try { + for (DeleteTarget target : objectKeys) { + if (target.isDirectory()) { + List keys = listKeys(container, target.getObjectKey()); + if (!keys.isEmpty()) { + removeObjects(container, keys); + } + } else { + removeObjects(container, List.of(target.getObjectKey())); + } + } + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + throw new StorageServiceException("Delete failed: " + e.getMessage(), e); + } + } + + @Override + public Blob getObject(String container, String objectKey, boolean withPayload) { + try { + BlobDetail detail = getObjectDetail(container, objectKey); + byte[] payload = null; + if (withPayload) { + if (detail.contentLength > 100 * 1024 * 1024) { // 100MB limit + throw new StorageServiceException( + "Object too large for in-memory payload (" + detail.contentLength + " bytes). " + + "Use getObjectStream() for large objects."); + } + try (InputStream is = getObjectStream(container, objectKey)) { + payload = is.readAllBytes(); + } + } + return new Blob(objectKey, detail.contentLength, detail.lastModified, + detail.metadata, payload); + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + throw new StorageServiceException("Failed to get object: " + e.getMessage(), e); + } + } + + @Override + public List getObjectData(String container, String objectKey) { + try (InputStream is = getObjectStream(container, objectKey); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + return reader.lines().collect(Collectors.toList()); + } catch (IOException e) { + throw new StorageServiceException("Failed to read object data: " + e.getMessage(), e); + } + } + + @Override + public List listObjects(String container, String prefix, boolean withPayload) { + try { + List keys = listObjectKeys(container, prefix); + List blobs = new ArrayList<>(); + for (String key : keys) { + blobs.add(getObject(container, key, withPayload)); + } + return blobs; + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + throw new StorageServiceException("Failed to list objects: " + e.getMessage(), e); + } + } + + @Override + public List listObjectKeys(String container, String prefix) { + return listKeys(container, prefix); + } + + @Override + public List searchObjects(String container, String prefix, + String fromDate, String toDate, + Integer delta, String pattern) { + String from = (delta != null) ? DateRangeUtil.getStartDate(toDate, delta) : fromDate; + if (from != null && !from.isEmpty()) { + String[] dates = DateRangeUtil.getDatesBetween(from, toDate, pattern); + List results = new ArrayList<>(); + for (String date : dates) { + results.addAll(listObjects(container, prefix + date)); + } + return results; + } + return listObjects(container, prefix); + } + + @Override + public List searchObjectKeys(String container, String prefix, + String fromDate, String toDate, + Integer delta, String pattern) { + String from = (delta != null) ? DateRangeUtil.getStartDate(toDate, delta) : fromDate; + if (from != null && !from.isEmpty()) { + String[] dates = DateRangeUtil.getDatesBetween(from, toDate, pattern); + List results = new ArrayList<>(); + for (String date : dates) { + results.addAll(listObjectKeys(container, prefix + date)); + } + return results; + } + return listObjectKeys(container, prefix); + } + + @Override + public void copyObjects(String fromContainer, String fromKey, + String toContainer, String toKey, boolean isDirectory) { + if (isDirectory) { + String updatedFromKey = fromKey.endsWith("/") ? fromKey : fromKey + "/"; + String updatedToKey = toKey.endsWith("/") ? toKey : toKey + "/"; + List keys = listObjectKeys(fromContainer, updatedFromKey); + for (String key : keys) { + String objName = key.replace(updatedFromKey, ""); + copyObject(fromContainer, key, toContainer, updatedToKey + objName); + } + } else { + copyObject(fromContainer, fromKey, toContainer, toKey); + } + } + + @Override + public void extractArchive(String container, String objectKey, String toKey) { + String localExtractPath = System.getProperty("local_extract_path", + System.getenv().getOrDefault("local_extract_path", "/tmp/extract")); + try { + download(container, objectKey, localExtractPath, false); + String archiveName = objectKey.contains("/") + ? objectKey.substring(objectKey.lastIndexOf('/') + 1) + : objectKey; + String folderName = toKey.contains("/") + ? toKey.substring(toKey.lastIndexOf('/') + 1) + : toKey; + String localFolder = localExtractPath + "/" + folderName; + FileUtil.unZip(localExtractPath + "/" + archiveName, localFolder); + upload(container, localFolder, toKey, true, 1, 0, null); + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + throw new StorageServiceException("Extract archive failed: " + e.getMessage(), e); + } finally { + try { + java.nio.file.Files.walk(java.nio.file.Paths.get(localExtractPath)) + .sorted(java.util.Comparator.reverseOrder()) + .map(java.nio.file.Path::toFile) + .forEach(File::delete); + } catch (IOException cleanupEx) { + logger.warn("Failed to clean up extraction directory: {}", localExtractPath, cleanupEx); + } + } + } + + @Override + public List getPaths(String container, List objects) { + String hdfsPrefix = getHdfsPrefix(container); + List paths = new ArrayList<>(); + for (Blob blob : objects) { + paths.add(hdfsPrefix + blob.getKey()); + } + return paths; + } + + @Override + public String getUri(String container, String prefix, boolean isDirectory) { + List keys = listObjectKeys(container, prefix); + if (keys.isEmpty()) { + throw new StorageServiceException("The given prefix is incorrect: " + prefix); + } + String firstKey = keys.get(0); + String uri = getObjectUri(container, firstKey); + if (uri != null && !uri.isEmpty()) { + int idx = uri.indexOf(prefix); + if (idx >= 0) { + return uri.substring(0, idx) + prefix; + } + return uri; + } + throw new StorageServiceException("URI not available for the given prefix: " + prefix); + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/IStorageService.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/IStorageService.java new file mode 100644 index 0000000..c9ac4e9 --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/IStorageService.java @@ -0,0 +1,302 @@ +package org.sunbird.cloud.storage; + +import org.sunbird.cloud.storage.model.Blob; +import org.sunbird.cloud.storage.model.DeleteTarget; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +/** + * Cloud storage service interface providing operations for upload, download, + * signed URL generation, deletion, copy, search, and metadata retrieval. + * + *

Implementations are CSP-specific (AWS, Azure, GCP, OCI) and discovered + * via {@link StorageServiceFactory} using Java ServiceLoader.

+ */ +public interface IStorageService extends AutoCloseable { + + // --- Upload operations --- + + /** + * Upload a file or folder to cloud storage. + * + * @param container the container/bucket name + * @param file the local file path + * @param objectKey the destination key/path in cloud storage + * @param isDirectory whether to upload recursively as a directory + * @param attempt current attempt number (starting from 1) + * @param retryCount maximum number of retries before failing + * @param ttl time-to-live in seconds for the uploaded object (null for no expiry) + * @return the URL of the uploaded file + */ + String upload(String container, String file, String objectKey, + boolean isDirectory, int attempt, int retryCount, Integer ttl); + + default String upload(String container, String file, String objectKey) { + return upload(container, file, objectKey, false, 1, 0, null); + } + + /** + * Upload a folder asynchronously. + * + * @param container the container/bucket name + * @param file the local folder path + * @param objectKey the destination prefix in cloud storage + * @param isPublic whether uploaded files should have public read access + * @param ttl time-to-live in seconds (null for no expiry) + * @param retryCount maximum number of retries (null for default) + * @param attempt current attempt number + * @return future completing with a list of uploaded file URLs + */ + CompletableFuture> uploadFolder(String container, String file, + String objectKey, boolean isPublic, + Integer ttl, Integer retryCount, int attempt); + + default CompletableFuture> uploadFolder(String container, String file, + String objectKey) { + return uploadFolder(container, file, objectKey, false, null, null, 1); + } + + /** + * Put binary content directly as a blob in cloud storage. + * + * @param container the container/bucket name + * @param content the byte array payload + * @param objectKey the destination key/path + * @param isPublic whether the object should have public read access + * @param isDirectory whether the content represents a directory + * @param ttl time-to-live in seconds (null for no expiry) + * @param retryCount maximum number of retries (null for default) + * @return the URL of the uploaded object + */ + String put(String container, byte[] content, String objectKey, + boolean isPublic, boolean isDirectory, Integer ttl, Integer retryCount); + + default String put(String container, byte[] content, String objectKey) { + return put(container, content, objectKey, false, false, null, null); + } + + // --- Signed URL operations --- + + /** + * Get a pre-signed URL to access an object. + * + * @param container the container/bucket name + * @param objectKey the object key + * @param ttl time-to-live in seconds for the URL (null for default) + * @param permission "r" for read, "w" for write + * @return the pre-signed URL + */ + String getSignedURL(String container, String objectKey, Integer ttl, String permission); + + default String getSignedURL(String container, String objectKey) { + return getSignedURL(container, objectKey, null, "r"); + } + + /** + * Get a pre-signed URL with additional options (content type, extra params). + * + * @param container the container/bucket name + * @param objectKey the object key + * @param ttl time-to-live in seconds (null for default) + * @param permission "r" for read, "w" for write + * @param contentType the content type of the object + * @param additionalParams CSP-specific additional parameters (nullable) + * @return the pre-signed URL + */ + String getSignedURLV2(String container, String objectKey, Integer ttl, + String permission, String contentType, + Map additionalParams); + + default String getSignedURLV2(String container, String objectKey) { + return getSignedURLV2(container, objectKey, null, "r", "application/octet-stream", null); + } + + // --- Download operations --- + + /** + * Download a file or folder from cloud storage. + * + * @param container the container/bucket name + * @param objectKey the object key to download + * @param localPath the local destination path + * @param isDirectory whether to download recursively as a directory + */ + void download(String container, String objectKey, String localPath, boolean isDirectory); + + default void download(String container, String objectKey, String localPath) { + download(container, objectKey, localPath, false); + } + + // --- Delete operations --- + + /** + * Delete an object from cloud storage. + * + * @param container the container/bucket name + * @param objectKey the object key to delete + * @param isDirectory whether the object is a directory (recursive delete) + */ + void deleteObject(String container, String objectKey, boolean isDirectory); + + default void deleteObject(String container, String objectKey) { + deleteObject(container, objectKey, false); + } + + /** + * Delete multiple objects from cloud storage. + * + * @param container the container/bucket name + * @param objectKeys list of delete targets + */ + void deleteObjects(String container, List objectKeys); + + // --- Copy operations --- + + /** + * Copy objects between containers or within the same container. + * + * @param fromContainer source container + * @param fromKey source object key/prefix + * @param toContainer destination container + * @param toKey destination object key/prefix + * @param isDirectory whether to copy recursively as a directory + */ + void copyObjects(String fromContainer, String fromKey, + String toContainer, String toKey, boolean isDirectory); + + default void copyObjects(String fromContainer, String fromKey, + String toContainer, String toKey) { + copyObjects(fromContainer, fromKey, toContainer, toKey, false); + } + + // --- Archive operations --- + + /** + * Extract an archive file on cloud storage to a destination folder. + * + * @param container the container/bucket name + * @param objectKey the archive object key + * @param toKey the destination folder key + */ + void extractArchive(String container, String objectKey, String toKey); + + // --- Object retrieval --- + + /** + * Get blob object details. + * + * @param container the container/bucket name + * @param objectKey the object key + * @param withPayload whether to include the payload bytes + * @return the blob object with metadata + */ + Blob getObject(String container, String objectKey, boolean withPayload); + + default Blob getObject(String container, String objectKey) { + return getObject(container, objectKey, false); + } + + /** + * Get object data as lines of text. + * + * @param container the container/bucket name + * @param objectKey the object key + * @return the object content as a list of lines + */ + List getObjectData(String container, String objectKey); + + /** + * List blob objects for a given prefix. + * + * @param container the container/bucket name + * @param prefix the object prefix to list + * @param withPayload whether to include payloads + * @return list of blob objects + */ + List listObjects(String container, String prefix, boolean withPayload); + + default List listObjects(String container, String prefix) { + return listObjects(container, prefix, false); + } + + /** + * List object keys for a given prefix. + * + * @param container the container/bucket name + * @param prefix the object prefix + * @return list of object keys + */ + List listObjectKeys(String container, String prefix); + + // --- Search operations --- + + /** + * Search for objects by date-prefixed paths. + * + * @param container the container/bucket name + * @param prefix the base prefix + * @param fromDate start date string (nullable) + * @param toDate end date string (nullable) + * @param delta number of days delta from toDate (nullable) + * @param pattern date format pattern (e.g., "yyyy-MM-dd") + * @return list of matching blob objects + */ + List searchObjects(String container, String prefix, + String fromDate, String toDate, + Integer delta, String pattern); + + default List searchObjects(String container, String prefix) { + return searchObjects(container, prefix, null, null, null, "yyyy-MM-dd"); + } + + /** + * Search for object keys by date-prefixed paths. + * + * @param container the container/bucket name + * @param prefix the base prefix + * @param fromDate start date string (nullable) + * @param toDate end date string (nullable) + * @param delta number of days delta from toDate (nullable) + * @param pattern date format pattern (e.g., "yyyy-MM-dd") + * @return list of matching object keys + */ + List searchObjectKeys(String container, String prefix, + String fromDate, String toDate, + Integer delta, String pattern); + + default List searchObjectKeys(String container, String prefix) { + return searchObjectKeys(container, prefix, null, null, null, "yyyy-MM-dd"); + } + + // --- Path operations --- + + /** + * Get HDFS-compatible file paths for use with Spark etc. + * + * @param container the container/bucket name + * @param objects list of blob objects + * @return HDFS-compatible paths + */ + List getPaths(String container, List objects); + + /** + * Get the URI for a given prefix. + * + * @param container the container/bucket name + * @param prefix the object prefix + * @param isDirectory whether the prefix represents a directory + * @return the URI string + */ + String getUri(String container, String prefix, boolean isDirectory); + + default String getUri(String container, String prefix) { + return getUri(container, prefix, false); + } + + // --- Lifecycle --- + + @Override + void close(); +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageConfig.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageConfig.java new file mode 100644 index 0000000..a2aa210 --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageConfig.java @@ -0,0 +1,106 @@ +package org.sunbird.cloud.storage; + +/** + * Configuration for cloud storage service initialization. + * Use the {@link Builder} pattern for construction. + */ +public final class StorageConfig { + + public enum AuthType { + ACCESS_KEY, + OIDC, + IAM, + IAM_ROLE, + INSTANCE_PROFILE + } + + public enum StorageType { + AWS, AZURE, GCLOUD, OCI, CEPHS3 + } + + private final StorageType type; + private final String storageKey; + private final String storageSecret; + private final String endPoint; + private final String region; + private final AuthType authType; + + private StorageConfig(Builder builder) { + this.type = builder.type; + this.storageKey = builder.storageKey; + this.storageSecret = builder.storageSecret; + this.endPoint = builder.endPoint; + this.region = builder.region; + this.authType = builder.authType; + } + + public StorageType getType() { + return type; + } + + public String getStorageKey() { + return storageKey; + } + + public String getStorageSecret() { + return storageSecret; + } + + public String getEndPoint() { + return endPoint; + } + + public String getRegion() { + return region; + } + + public AuthType getAuthType() { + return authType; + } + + public static Builder builder(StorageType type) { + return new Builder(type); + } + + public static class Builder { + private final StorageType type; + private String storageKey = ""; + private String storageSecret = ""; + private String endPoint; + private String region; + private AuthType authType = AuthType.ACCESS_KEY; + + private Builder(StorageType type) { + this.type = type; + } + + public Builder storageKey(String storageKey) { + this.storageKey = storageKey; + return this; + } + + public Builder storageSecret(String storageSecret) { + this.storageSecret = storageSecret; + return this; + } + + public Builder endPoint(String endPoint) { + this.endPoint = endPoint; + return this; + } + + public Builder region(String region) { + this.region = region; + return this; + } + + public Builder authType(AuthType authType) { + this.authType = authType; + return this; + } + + public StorageConfig build() { + return new StorageConfig(this); + } + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageServiceFactory.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageServiceFactory.java new file mode 100644 index 0000000..e943499 --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageServiceFactory.java @@ -0,0 +1,39 @@ +package org.sunbird.cloud.storage; + +import org.sunbird.cloud.storage.exception.StorageServiceException; + +import java.util.ServiceLoader; + +/** + * Factory for creating {@link IStorageService} instances. + * Uses Java {@link ServiceLoader} to discover CSP-specific implementations at runtime. + * + *

Consumers add the appropriate CSP module JAR to their classpath and the factory + * automatically discovers and uses the matching provider.

+ */ +public final class StorageServiceFactory { + + private StorageServiceFactory() { + } + + /** + * Create a storage service for the given configuration. + * The appropriate provider is discovered via ServiceLoader based on the storage type. + * + * @param config the storage configuration + * @return a new storage service instance + * @throws StorageServiceException if no provider is found for the given type + */ + public static IStorageService getStorageService(StorageConfig config) { + ServiceLoader loader = + ServiceLoader.load(StorageServiceProvider.class); + for (StorageServiceProvider provider : loader) { + if (provider.supports(config.getType())) { + return provider.create(config); + } + } + throw new StorageServiceException( + "No StorageService provider found for type: " + config.getType() + + ". Ensure the appropriate CSP module JAR is on the classpath."); + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageServiceProvider.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageServiceProvider.java new file mode 100644 index 0000000..e91b66e --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/StorageServiceProvider.java @@ -0,0 +1,25 @@ +package org.sunbird.cloud.storage; + +/** + * Service Provider Interface for cloud storage implementations. + * Each CSP module provides an implementation registered via + * {@code META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider}. + */ +public interface StorageServiceProvider { + + /** + * Check whether this provider supports the given storage type. + * + * @param type the storage type + * @return true if this provider can create a service for the given type + */ + boolean supports(StorageConfig.StorageType type); + + /** + * Create a new storage service instance for the given configuration. + * + * @param config the storage configuration + * @return a new storage service instance + */ + IStorageService create(StorageConfig config); +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/exception/StorageServiceException.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/exception/StorageServiceException.java new file mode 100644 index 0000000..75ff16d --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/exception/StorageServiceException.java @@ -0,0 +1,15 @@ +package org.sunbird.cloud.storage.exception; + +/** + * Runtime exception for cloud storage operations. + */ +public class StorageServiceException extends RuntimeException { + + public StorageServiceException(String message) { + super(message); + } + + public StorageServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/model/Blob.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/model/Blob.java new file mode 100644 index 0000000..0a81ebb --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/model/Blob.java @@ -0,0 +1,69 @@ +package org.sunbird.cloud.storage.model; + +import java.util.Collections; +import java.util.Date; +import java.util.Map; + +/** + * Represents a cloud storage blob object with metadata and optional payload. + */ +public final class Blob { + + private final String key; + private final long contentLength; + private final Date lastModified; + private final Map metadata; + private final byte[] payload; + + public Blob(String key, long contentLength, Date lastModified, + Map metadata, byte[] payload) { + this.key = key; + this.contentLength = contentLength; + this.lastModified = lastModified != null ? new Date(lastModified.getTime()) : null; + this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); + this.payload = payload != null ? payload.clone() : null; + } + + public Blob(String key, long contentLength, Date lastModified, Map metadata) { + this(key, contentLength, lastModified, metadata, null); + } + + public String getKey() { + return key; + } + + public long getContentLength() { + return contentLength; + } + + public Date getLastModified() { + return lastModified != null ? new Date(lastModified.getTime()) : null; + } + + public Map getMetadata() { + return metadata; + } + + public byte[] getPayload() { + return payload != null ? payload.clone() : null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Blob blob = (Blob) o; + return java.util.Objects.equals(key, blob.key); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(key); + } + + @Override + public String toString() { + return "Blob{key='" + key + "', contentLength=" + contentLength + + ", lastModified=" + lastModified + "}"; + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/model/DeleteTarget.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/model/DeleteTarget.java new file mode 100644 index 0000000..5189b1f --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/model/DeleteTarget.java @@ -0,0 +1,49 @@ +package org.sunbird.cloud.storage.model; + +/** + * Represents a target for deletion in cloud storage, combining object key with directory flag. + */ +public final class DeleteTarget { + + private final String objectKey; + private final boolean directory; + + public DeleteTarget(String objectKey, boolean directory) { + this.objectKey = objectKey; + this.directory = directory; + } + + public static DeleteTarget file(String objectKey) { + return new DeleteTarget(objectKey, false); + } + + public static DeleteTarget directory(String objectKey) { + return new DeleteTarget(objectKey, true); + } + + public String getObjectKey() { + return objectKey; + } + + public boolean isDirectory() { + return directory; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DeleteTarget that = (DeleteTarget) o; + return directory == that.directory && java.util.Objects.equals(objectKey, that.objectKey); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(objectKey, directory); + } + + @Override + public String toString() { + return "DeleteTarget{key='" + objectKey + "', directory=" + directory + "}"; + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/util/DateRangeUtil.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/util/DateRangeUtil.java new file mode 100644 index 0000000..37be05f --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/util/DateRangeUtil.java @@ -0,0 +1,56 @@ +package org.sunbird.cloud.storage.util; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; + +/** + * Date range utility for computing date-based prefixes used in object search operations. + * Replaces Joda-Time usage with java.time API. + */ +public final class DateRangeUtil { + + private static final DateTimeFormatter DEFAULT_FORMAT = + DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private DateRangeUtil() { + } + + /** + * Calculate the start date by subtracting delta days from the end date. + * + * @param endDate end date string (nullable — defaults to today) + * @param delta number of days to subtract + * @return the start date as a string in yyyy-MM-dd format + */ + public static String getStartDate(String endDate, int delta) { + LocalDate to = (endDate != null && !endDate.isEmpty()) + ? LocalDate.parse(endDate, DEFAULT_FORMAT) + : LocalDate.now(); + return to.minusDays(delta).toString(); + } + + /** + * Get all dates between fromDate and toDate (inclusive) formatted with the given pattern. + * + * @param fromDate start date string + * @param toDate end date string (nullable — defaults to today) + * @param pattern date format pattern + * @return array of formatted date strings + */ + public static String[] getDatesBetween(String fromDate, String toDate, String pattern) { + DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern); + LocalDate to = (toDate != null && !toDate.isEmpty()) + ? LocalDate.parse(toDate, df) + : LocalDate.now(); + LocalDate from = LocalDate.parse(fromDate, df); + long daysBetween = ChronoUnit.DAYS.between(from, to); + List dates = new ArrayList<>(); + for (long i = 0; i <= daysBetween; i++) { + dates.add(from.plusDays(i).format(df)); + } + return dates.toArray(new String[0]); + } +} diff --git a/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/util/FileUtil.java b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/util/FileUtil.java new file mode 100644 index 0000000..01eaa77 --- /dev/null +++ b/cloud-storage-sdk-api/src/main/java/org/sunbird/cloud/storage/util/FileUtil.java @@ -0,0 +1,107 @@ +package org.sunbird.cloud.storage.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * File utility methods for recursive listing, stream copy, and zip extraction. + */ +public final class FileUtil { + + private FileUtil() { + } + + /** + * Recursively list all files under the given directory. + * + * @param file the directory or file to list + * @return list of files (not directories) + */ + public static List listFilesRecursive(File file) { + List result = new ArrayList<>(); + if (file.exists() && file.isDirectory()) { + File[] children = file.listFiles(); + if (children != null) { + for (File child : children) { + if (child.isDirectory()) { + result.addAll(listFilesRecursive(child)); + } else { + result.add(child); + } + } + } + } else if (file.exists() && file.isFile()) { + result.add(file); + } + return result; + } + + /** + * Copy an input stream to a file at the given path. + * + * @param from the source input stream + * @param path the destination directory + * @param fileName the destination file name + * @throws IOException if an I/O error occurs + */ + public static void copyStream(InputStream from, String path, String fileName) throws IOException { + Files.createDirectories(Paths.get(path)); + Files.copy(from, Paths.get(path, fileName), StandardCopyOption.REPLACE_EXISTING); + } + + /** + * Extract a ZIP archive to the specified output folder. + * Skips hidden files (starting with '.'), underscore-prefixed files, and blank file names. + * + * @param zipFile path to the ZIP file + * @param outputFolder path to the output directory + * @throws IOException if an I/O error occurs + */ + public static void unZip(String zipFile, String outputFolder) throws IOException { + File folder = new File(outputFolder); + if (!folder.exists()) { + folder.mkdirs(); + } + + byte[] buffer = new byte[4096]; + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String entryName = entry.getName(); + String simpleName = Paths.get(entryName).getFileName().toString(); + + if (simpleName.startsWith(".") || simpleName.startsWith("_") || simpleName.isEmpty()) { + continue; + } + + File newFile = new File(outputFolder + File.separator + entryName); + String canonicalDestDir = folder.getCanonicalPath(); + String canonicalNewFile = newFile.getCanonicalPath(); + if (!canonicalNewFile.startsWith(canonicalDestDir + File.separator)) { + throw new IOException("Zip entry is outside of the target dir: " + entryName); + } + File parent = newFile.getParentFile(); + if (parent != null && !parent.exists()) { + parent.mkdirs(); + } + + try (FileOutputStream fos = new FileOutputStream(newFile)) { + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + } + } +} diff --git a/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/AbstractStorageServiceTest.java b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/AbstractStorageServiceTest.java new file mode 100644 index 0000000..14a0e18 --- /dev/null +++ b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/AbstractStorageServiceTest.java @@ -0,0 +1,494 @@ +package org.sunbird.cloud.storage; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sunbird.cloud.storage.exception.StorageServiceException; +import org.sunbird.cloud.storage.model.Blob; +import org.sunbird.cloud.storage.model.DeleteTarget; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for AbstractStorageService orchestration logic using an in-memory + * storage implementation — no cloud credentials required. + */ +class AbstractStorageServiceTest { + + @TempDir + Path tempDir; + + private InMemoryStorageService service; + + @BeforeEach + void setUp() { + service = new InMemoryStorageService(); + } + + // ── Upload / Put ────────────────────────────────────────────────────────── + + @Test + void upload_singleFile_returnsUri() throws IOException { + Path file = tempDir.resolve("file.txt"); + Files.writeString(file, "hello"); + + String uri = service.upload("bucket", file.toString(), "prefix/file.txt"); + + assertEquals("mem://bucket/prefix/file.txt", uri); + assertTrue(service.exists("bucket", "prefix/file.txt")); + } + + @Test + void upload_directory_uploadsAllFiles() throws IOException { + Path dir = tempDir.resolve("data"); + Files.createDirectory(dir); + Files.writeString(dir.resolve("a.txt"), "aaa"); + Files.writeString(dir.resolve("b.txt"), "bbb"); + + String result = service.upload("bucket", dir.toString(), "prefix/data", true, 1, 0, null); + + assertTrue(result.contains("prefix/data/a.txt")); + assertTrue(result.contains("prefix/data/b.txt")); + assertTrue(service.exists("bucket", "prefix/data/a.txt")); + assertTrue(service.exists("bucket", "prefix/data/b.txt")); + } + + @Test + void upload_directory_withTrailingSlash_noDoubleSlash() throws IOException { + Path dir = tempDir.resolve("data-slash"); + Files.createDirectory(dir); + Files.writeString(dir.resolve("a.txt"), "aaa"); + Files.writeString(dir.resolve("b.txt"), "bbb"); + + // objectKey already ends with '/' — must not produce "prefix/data//a.txt" + String result = service.upload("bucket", dir.toString(), "prefix/data/", true, 1, 0, null); + + assertTrue(result.contains("prefix/data/a.txt"), "Expected prefix/data/a.txt, got: " + result); + assertTrue(result.contains("prefix/data/b.txt"), "Expected prefix/data/b.txt, got: " + result); + assertFalse(pathOf(result).contains("//"), "Keys must not contain double slashes: " + result); + assertTrue(service.exists("bucket", "prefix/data/a.txt")); + assertTrue(service.exists("bucket", "prefix/data/b.txt")); + } + + @Test + void upload_directory_withoutTrailingSlash_addsSingleSlash() throws IOException { + Path dir = tempDir.resolve("data-noslash"); + Files.createDirectory(dir); + Files.writeString(dir.resolve("c.txt"), "ccc"); + + // objectKey has no trailing '/' — must add exactly one + String result = service.upload("bucket", dir.toString(), "prefix/data", true, 1, 0, null); + + assertTrue(result.contains("prefix/data/c.txt"), "Expected prefix/data/c.txt, got: " + result); + assertFalse(pathOf(result).contains("//"), "Keys must not contain double slashes: " + result); + assertTrue(service.exists("bucket", "prefix/data/c.txt")); + } + + @Test + void uploadFolder_withTrailingSlash_noDoubleSlash() throws ExecutionException, InterruptedException, IOException { + Path dir = tempDir.resolve("async-slash"); + Files.createDirectory(dir); + Files.writeString(dir.resolve("x.txt"), "xxx"); + Files.writeString(dir.resolve("y.txt"), "yyy"); + + // async uploadFolder — same prefix logic at line 157-158 + List urls = service.uploadFolder("bucket", dir.toString(), "async/data/") + .get(); + + assertTrue(urls.stream().anyMatch(u -> u.contains("async/data/x.txt")), + "Expected async/data/x.txt in: " + urls); + assertTrue(urls.stream().anyMatch(u -> u.contains("async/data/y.txt")), + "Expected async/data/y.txt in: " + urls); + urls.forEach(u -> assertFalse(pathOf(u).contains("//"), + "URL must not contain double slashes: " + u)); + } + + @Test + void uploadFolder_withoutTrailingSlash_addsSingleSlash() throws ExecutionException, InterruptedException, IOException { + Path dir = tempDir.resolve("async-noslash"); + Files.createDirectory(dir); + Files.writeString(dir.resolve("z.txt"), "zzz"); + + List urls = service.uploadFolder("bucket", dir.toString(), "async/data") + .get(); + + assertTrue(urls.stream().anyMatch(u -> u.contains("async/data/z.txt")), + "Expected async/data/z.txt in: " + urls); + urls.forEach(u -> assertFalse(pathOf(u).contains("//"), + "URL must not contain double slashes: " + u)); + } + + @Test + void upload_exceedsMaxRetries_throwsException() { + // Non-existent file forces an exception, and retry count 1 means fail immediately + assertThrows(StorageServiceException.class, () -> + service.upload("bucket", "/no/such/file.txt", "key", false, 1, 1, null)); + } + + @Test + void put_bytesContent_returnsUri() { + byte[] content = "data bytes".getBytes(StandardCharsets.UTF_8); + String uri = service.put("bucket", content, "obj/data.bin"); + + assertEquals("mem://bucket/obj/data.bin", uri); + assertTrue(service.exists("bucket", "obj/data.bin")); + } + + // ── List / Get ──────────────────────────────────────────────────────────── + + @Test + void listObjectKeys_returnsMatchingKeys() { + service.putDirect("bucket", "logs/2024-01-01.txt", "a".getBytes()); + service.putDirect("bucket", "logs/2024-01-02.txt", "b".getBytes()); + service.putDirect("bucket", "other/file.txt", "c".getBytes()); + + List keys = service.listObjectKeys("bucket", "logs/"); + + assertEquals(2, keys.size()); + assertTrue(keys.contains("logs/2024-01-01.txt")); + assertTrue(keys.contains("logs/2024-01-02.txt")); + } + + @Test + void listObjects_returnsBlobs() { + service.putDirect("bucket", "data/file.txt", "content".getBytes()); + + List blobs = service.listObjects("bucket", "data/"); + + assertEquals(1, blobs.size()); + assertEquals("data/file.txt", blobs.get(0).getKey()); + } + + @Test + void getObject_returnsMetadata() { + service.putDirect("bucket", "key.txt", "hello".getBytes()); + + Blob blob = service.getObject("bucket", "key.txt"); + + assertEquals("key.txt", blob.getKey()); + assertEquals(5L, blob.getContentLength()); + } + + @Test + void getObject_withPayload_returnsBytes() { + service.putDirect("bucket", "data.txt", "payload".getBytes(StandardCharsets.UTF_8)); + + Blob blob = service.getObject("bucket", "data.txt", true); + + assertNotNull(blob.getPayload()); + assertEquals("payload", new String(blob.getPayload(), StandardCharsets.UTF_8)); + } + + @Test + void getObjectData_returnsLines() { + service.putDirect("bucket", "lines.txt", "line1\nline2\nline3".getBytes()); + + List lines = service.getObjectData("bucket", "lines.txt"); + + assertEquals(3, lines.size()); + assertEquals("line1", lines.get(0)); + } + + // ── Delete ──────────────────────────────────────────────────────────────── + + @Test + void deleteObject_removesFile() { + service.putDirect("bucket", "delete-me.txt", "x".getBytes()); + assertTrue(service.exists("bucket", "delete-me.txt")); + + service.deleteObject("bucket", "delete-me.txt"); + + assertFalse(service.exists("bucket", "delete-me.txt")); + } + + @Test + void deleteObjects_directory_removesAllKeysWithPrefix() { + service.putDirect("bucket", "dir/a.txt", "a".getBytes()); + service.putDirect("bucket", "dir/b.txt", "b".getBytes()); + service.putDirect("bucket", "other.txt", "o".getBytes()); + + service.deleteObjects("bucket", List.of(DeleteTarget.directory("dir/"))); + + assertFalse(service.exists("bucket", "dir/a.txt")); + assertFalse(service.exists("bucket", "dir/b.txt")); + assertTrue(service.exists("bucket", "other.txt")); + } + + @Test + void deleteObjects_multipleTargets() { + service.putDirect("bucket", "a.txt", "a".getBytes()); + service.putDirect("bucket", "b.txt", "b".getBytes()); + + service.deleteObjects("bucket", List.of( + DeleteTarget.file("a.txt"), + DeleteTarget.file("b.txt"))); + + assertFalse(service.exists("bucket", "a.txt")); + assertFalse(service.exists("bucket", "b.txt")); + } + + // ── Copy ───────────────────────────────────────────────────────────────── + + @Test + void copyObjects_singleFile_copiesCorrectly() { + service.putDirect("src-bucket", "original.txt", "data".getBytes()); + + service.copyObjects("src-bucket", "original.txt", "dst-bucket", "copy.txt"); + + assertTrue(service.exists("dst-bucket", "copy.txt")); + assertTrue(service.exists("src-bucket", "original.txt")); // original still there + } + + @Test + void copyObjects_directory_copiesAllFiles() { + service.putDirect("src", "folder/x.txt", "x".getBytes()); + service.putDirect("src", "folder/y.txt", "y".getBytes()); + + service.copyObjects("src", "folder/", "dst", "backup/", true); + + assertTrue(service.exists("dst", "backup/x.txt")); + assertTrue(service.exists("dst", "backup/y.txt")); + } + + // ── Search ──────────────────────────────────────────────────────────────── + + @Test + void searchObjects_withDateRange_returnsMatchingBlobs() { + service.putDirect("bucket", "logs/2024-03-01/data.txt", "a".getBytes()); + service.putDirect("bucket", "logs/2024-03-02/data.txt", "b".getBytes()); + service.putDirect("bucket", "logs/2024-03-03/data.txt", "c".getBytes()); + service.putDirect("bucket", "logs/2024-04-01/data.txt", "d".getBytes()); + + List results = service.searchObjects( + "bucket", "logs/", "2024-03-01", "2024-03-02", null, "yyyy-MM-dd"); + + assertEquals(2, results.size()); + } + + @Test + void searchObjectKeys_withDelta_returnsKeys() { + service.putDirect("bucket", "events/2024-05-08/file.txt", "x".getBytes()); + service.putDirect("bucket", "events/2024-05-09/file.txt", "y".getBytes()); + service.putDirect("bucket", "events/2024-05-10/file.txt", "z".getBytes()); + + List keys = service.searchObjectKeys( + "bucket", "events/", null, "2024-05-10", 2, "yyyy-MM-dd"); + + // delta=2 from 2024-05-10 means from 2024-05-08 to 2024-05-10 (inclusive) + assertEquals(3, keys.size()); + } + + @Test + void searchObjects_noDatesOrDelta_listsAllWithPrefix() { + service.putDirect("bucket", "prefix/a.txt", "a".getBytes()); + service.putDirect("bucket", "prefix/b.txt", "b".getBytes()); + service.putDirect("bucket", "other/c.txt", "c".getBytes()); + + List results = service.searchObjects( + "bucket", "prefix/", null, null, null, "yyyy-MM-dd"); + + assertEquals(2, results.size()); + } + + // ── URI / Paths ─────────────────────────────────────────────────────────── + + @Test + void getPaths_returnsHdfsPrefixedPaths() { + Blob b1 = new Blob("key1.txt", 10, new Date(), Map.of()); + Blob b2 = new Blob("key2.txt", 20, new Date(), Map.of()); + + List paths = service.getPaths("bucket", List.of(b1, b2)); + + assertEquals(List.of("mem://bucket/key1.txt", "mem://bucket/key2.txt"), paths); + } + + @Test + void getUri_returnsBaseUri() { + service.putDirect("bucket", "prefix/file.txt", "x".getBytes()); + + String uri = service.getUri("bucket", "prefix/"); + + assertEquals("mem://bucket/prefix/", uri); + } + + @Test + void getUri_emptyPrefix_throwsException() { + assertThrows(StorageServiceException.class, () -> + service.getUri("bucket", "nonexistent/prefix/")); + } + + // ── Download ────────────────────────────────────────────────────────────── + + @Test + void download_singleFile_writesToLocalPath() throws IOException { + service.putDirect("bucket", "remote/data.txt", "file-content".getBytes()); + String localDir = tempDir.toString() + "/"; + + service.download("bucket", "remote/data.txt", localDir); + + Path downloaded = tempDir.resolve("data.txt"); + assertTrue(Files.exists(downloaded)); + assertEquals("file-content", Files.readString(downloaded)); + } + + @Test + void download_directory_downloadsAllFiles() throws IOException { + service.putDirect("bucket", "dir/sub/a.txt", "aaa".getBytes()); + service.putDirect("bucket", "dir/sub/b.txt", "bbb".getBytes()); + String localDir = tempDir.toString() + "/"; + + service.download("bucket", "dir/", localDir, true); + + // Files should appear relative to localDir + assertTrue(Files.exists(tempDir.resolve("sub/a.txt")) || + Files.exists(tempDir.resolve("a.txt"))); + } + + // ── Signed URLs ─────────────────────────────────────────────────────────── + + @Test + void getSignedURL_read_returnsUrl() { + String url = service.getSignedURL("bucket", "key.txt", 3600, "r"); + assertEquals("mem://signed/r/bucket/key.txt?ttl=3600", url); + } + + @Test + void getSignedURL_write_returnsUrl() { + String url = service.getSignedURL("bucket", "key.txt", 3600, "w"); + assertEquals("mem://signed/w/bucket/key.txt?ttl=3600", url); + } + + @Test + void getSignedURLV2_defaultsToRead() { + String url = service.getSignedURLV2("bucket", "key.txt"); + assertTrue(url.contains("bucket") && url.contains("key.txt")); + } + + // ── Inner in-memory implementation ─────────────────────────────────────── + + static class InMemoryStorageService extends AbstractStorageService { + + /** container -> (key -> bytes) */ + private final Map> store = new HashMap<>(); + + boolean exists(String container, String key) { + return store.containsKey(container) && store.get(container).containsKey(key); + } + + void putDirect(String container, String key, byte[] bytes) { + store.computeIfAbsent(container, k -> new HashMap<>()).put(key, bytes); + } + + @Override + protected void ensureContainerExists(String container) { + store.computeIfAbsent(container, k -> new HashMap<>()); + } + + @Override + protected String putObject(String container, String objectKey, java.io.File file) { + try { + byte[] bytes = Files.readAllBytes(file.toPath()); + store.computeIfAbsent(container, k -> new HashMap<>()).put(objectKey, bytes); + return "mem://" + container + "/" + objectKey; + } catch (IOException e) { + throw new StorageServiceException("Failed to put from file: " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, byte[] content) { + store.computeIfAbsent(container, k -> new HashMap<>()).put(objectKey, content); + return "mem://" + container + "/" + objectKey; + } + + @Override + protected InputStream getObjectStream(String container, String objectKey) { + Map bucket = store.get(container); + if (bucket == null || !bucket.containsKey(objectKey)) { + throw new StorageServiceException("Not found: " + container + "/" + objectKey); + } + return new ByteArrayInputStream(bucket.get(objectKey)); + } + + @Override + protected String getObjectUri(String container, String objectKey) { + return "mem://" + container + "/" + objectKey; + } + + @Override + protected BlobDetail getObjectDetail(String container, String objectKey) { + Map bucket = store.get(container); + if (bucket == null || !bucket.containsKey(objectKey)) { + throw new StorageServiceException("Not found: " + container + "/" + objectKey); + } + byte[] bytes = bucket.get(objectKey); + Map meta = new HashMap<>(); + meta.put("uri", "mem://" + container + "/" + objectKey); + return new BlobDetail(objectKey, bytes.length, new Date(), meta, + "mem://" + container + "/" + objectKey); + } + + @Override + protected List listKeys(String container, String prefix) { + Map bucket = store.getOrDefault(container, Map.of()); + return bucket.keySet().stream() + .filter(k -> k.startsWith(prefix)) + .sorted() + .collect(Collectors.toList()); + } + + @Override + protected void removeObjects(String container, List objectKeys) { + Map bucket = store.get(container); + if (bucket != null) { + objectKeys.forEach(bucket::remove); + } + } + + @Override + protected void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey) { + Map src = store.get(fromContainer); + if (src == null || !src.containsKey(fromKey)) { + throw new StorageServiceException("Not found: " + fromContainer + "/" + fromKey); + } + store.computeIfAbsent(toContainer, k -> new HashMap<>()).put(toKey, src.get(fromKey)); + } + + @Override + protected String generateSignedGetUrl(String container, String objectKey, int ttlSeconds) { + return "mem://signed/r/" + container + "/" + objectKey + "?ttl=" + ttlSeconds; + } + + @Override + protected String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams) { + return "mem://signed/w/" + container + "/" + objectKey + "?ttl=" + ttlSeconds; + } + + @Override + protected String getHdfsPrefix(String container) { + return "mem://" + container + "/"; + } + + @Override + public void close() { + store.clear(); + } + } + + /** Removes all scheme separators ("://") so that double-slash checks only apply + * to path segments, not the scheme — handles both single and multi-URL strings. */ + private static String pathOf(String url) { + return url.replace("://", ""); + } +} diff --git a/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/BaseStorageServiceIntegrationTest.java b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/BaseStorageServiceIntegrationTest.java new file mode 100644 index 0000000..a4a140f --- /dev/null +++ b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/BaseStorageServiceIntegrationTest.java @@ -0,0 +1,290 @@ +package org.sunbird.cloud.storage; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.io.TempDir; +import org.sunbird.cloud.storage.model.Blob; +import org.sunbird.cloud.storage.model.DeleteTarget; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Base class for CSP integration tests. + * + *

Subclasses must override {@link #createService()} to provide a configured + * {@link IStorageService} and {@link #getContainer()} to return a pre-existing + * writable bucket/container name.

+ * + *

Tests are organised to be self-cleaning: each creates objects under a unique + * per-run prefix and removes them in {@code @AfterAll}.

+ */ +public abstract class BaseStorageServiceIntegrationTest { + + @TempDir + protected static Path tempDir; + + protected static IStorageService service; + + /** Unique prefix for this test run to avoid collisions. */ + protected static String testPrefix; + + /** Return a configured storage service. Called once before all tests. */ + protected abstract IStorageService createService(); + + /** Return the name of a pre-existing, writable container/bucket. */ + protected abstract String getContainer(); + + @BeforeAll + void setUpService() { + service = createService(); + testPrefix = "sdk-integration-test/" + UUID.randomUUID() + "/"; + } + + @AfterAll + void tearDown() { + if (service != null) { + try { + // Best-effort cleanup: delete everything under the test prefix + List keys = service.listObjectKeys(getContainer(), testPrefix); + if (!keys.isEmpty()) { + service.deleteObjects(getContainer(), + keys.stream() + .map(DeleteTarget::file) + .collect(Collectors.toList())); + } + } finally { + service.close(); + } + } + } + + // ── Upload & list ───────────────────────────────────────────────────────── + + @Test + @DisplayName("Upload single file and verify it appears in listing") + void uploadAndList() throws IOException { + Path file = tempDir.resolve("upload-test.txt"); + Files.writeString(file, "integration test content"); + String key = testPrefix + "upload-test.txt"; + + String url = service.upload(getContainer(), file.toString(), key); + + assertNotNull(url, "Upload should return a URL"); + assertFalse(url.isBlank()); + List keys = service.listObjectKeys(getContainer(), testPrefix); + assertTrue(keys.contains(key), "Listed keys should contain the uploaded key"); + } + + @Test + @DisplayName("Upload folder recursively") + void uploadFolder() throws IOException { + Path dir = tempDir.resolve("upload-dir"); + Files.createDirectory(dir); + Files.writeString(dir.resolve("a.txt"), "aaa"); + Files.writeString(dir.resolve("b.txt"), "bbb"); + String folderPrefix = testPrefix + "folder/"; + + service.upload(getContainer(), dir.toString(), folderPrefix, true, 1, 0, null); + + List keys = service.listObjectKeys(getContainer(), folderPrefix); + assertTrue(keys.size() >= 2); + } + + // ── put() (bytes) ───────────────────────────────────────────────────────── + + @Test + @DisplayName("Put bytes directly and verify retrieval") + void putAndGetData() { + String key = testPrefix + "bytes-object.txt"; + byte[] content = "line1\nline2\nline3".getBytes(StandardCharsets.UTF_8); + + service.put(getContainer(), content, key); + + List lines = service.getObjectData(getContainer(), key); + assertEquals(3, lines.size()); + assertEquals("line1", lines.get(0)); + assertEquals("line3", lines.get(2)); + } + + // ── getObject / metadata ────────────────────────────────────────────────── + + @Test + @DisplayName("Get object metadata") + void getObjectMetadata() throws IOException { + Path file = tempDir.resolve("meta-test.txt"); + String content = "metadata test"; + Files.writeString(file, content); + String key = testPrefix + "meta-test.txt"; + service.upload(getContainer(), file.toString(), key); + + Blob blob = service.getObject(getContainer(), key); + + assertEquals(key, blob.getKey()); + assertEquals(content.getBytes().length, blob.getContentLength()); + assertNotNull(blob.getLastModified()); + } + + @Test + @DisplayName("Get object with payload") + void getObjectWithPayload() throws IOException { + Path file = tempDir.resolve("payload-test.txt"); + Files.writeString(file, "payload data"); + String key = testPrefix + "payload-test.txt"; + service.upload(getContainer(), file.toString(), key); + + Blob blob = service.getObject(getContainer(), key, true); + + assertNotNull(blob.getPayload(), "Payload should be present"); + assertTrue(blob.getPayload().length > 0); + } + + // ── Download ────────────────────────────────────────────────────────────── + + @Test + @DisplayName("Download file and verify content") + void downloadFile() throws IOException { + String expected = "download me"; + String key = testPrefix + "download-test.txt"; + service.put(getContainer(), expected.getBytes(StandardCharsets.UTF_8), key); + + Path downloadDir = tempDir.resolve("downloads"); + Files.createDirectories(downloadDir); + service.download(getContainer(), key, downloadDir.toString() + "/"); + + Path downloaded = downloadDir.resolve("download-test.txt"); + assertTrue(Files.exists(downloaded)); + assertEquals(expected, Files.readString(downloaded)); + } + + // ── Signed URLs ─────────────────────────────────────────────────────────── + + @Test + @DisplayName("Generate read signed URL — URL must be non-blank") + void getSignedReadUrl() throws IOException { + Path file = tempDir.resolve("signed-read.txt"); + Files.writeString(file, "signed url test"); + String key = testPrefix + "signed-read.txt"; + service.upload(getContainer(), file.toString(), key); + + String url = service.getSignedURL(getContainer(), key, 3600, "r"); + + assertNotNull(url); + assertFalse(url.isBlank()); + // URL should reference the container and key in some form + assertTrue(url.contains(getContainer()) || url.contains(key) || url.startsWith("https://"), + "Signed URL should be a valid HTTPS URL"); + } + + @Test + @DisplayName("Generate write signed URL — URL must be non-blank") + void getSignedWriteUrl() { + String key = testPrefix + "signed-write.txt"; + String url = service.getSignedURL(getContainer(), key, 3600, "w"); + + assertNotNull(url); + assertFalse(url.isBlank()); + } + + @Test + @DisplayName("getSignedURLV2 — supports content type and additional params") + void getSignedUrlV2() throws IOException { + Path file = tempDir.resolve("signed-v2.txt"); + Files.writeString(file, "v2 test"); + String key = testPrefix + "signed-v2.txt"; + service.upload(getContainer(), file.toString(), key); + + String url = service.getSignedURLV2(getContainer(), key, 3600, "r", + "text/plain", null); + + assertNotNull(url); + assertFalse(url.isBlank()); + } + + // ── Copy ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("Copy single object within the same container") + void copyObject() throws IOException { + Path file = tempDir.resolve("copy-src.txt"); + Files.writeString(file, "copy source"); + String srcKey = testPrefix + "copy-src.txt"; + String dstKey = testPrefix + "copy-dst.txt"; + service.upload(getContainer(), file.toString(), srcKey); + + service.copyObjects(getContainer(), srcKey, getContainer(), dstKey); + + List keys = service.listObjectKeys(getContainer(), testPrefix); + assertTrue(keys.contains(srcKey), "Source key should still exist"); + assertTrue(keys.contains(dstKey), "Destination key should exist"); + } + + // ── Delete ──────────────────────────────────────────────────────────────── + + @Test + @DisplayName("Delete object and verify removal") + void deleteObject() throws IOException { + Path file = tempDir.resolve("delete-test.txt"); + Files.writeString(file, "to be deleted"); + String key = testPrefix + "delete-test.txt"; + service.upload(getContainer(), file.toString(), key); + assertTrue(service.listObjectKeys(getContainer(), key).contains(key)); + + service.deleteObject(getContainer(), key); + + List keys = service.listObjectKeys(getContainer(), key); + assertFalse(keys.contains(key), "Deleted key should not appear in listing"); + } + + // ── Search ──────────────────────────────────────────────────────────────── + + @Test + @DisplayName("Search objects by date prefix") + void searchObjectsByDate() { + String datePrefix = testPrefix + "dated/"; + service.put(getContainer(), "d1".getBytes(), datePrefix + "2024-06-01/file.txt"); + service.put(getContainer(), "d2".getBytes(), datePrefix + "2024-06-02/file.txt"); + service.put(getContainer(), "d3".getBytes(), datePrefix + "2024-06-10/file.txt"); + + List results = service.searchObjects( + getContainer(), datePrefix, "2024-06-01", "2024-06-02", null, "yyyy-MM-dd"); + + assertEquals(2, results.size()); + } + + @Test + @DisplayName("Search object keys by delta") + void searchObjectKeysByDelta() { + String datePrefix = testPrefix + "delta/"; + service.put(getContainer(), "d1".getBytes(), datePrefix + "2024-07-08/file.txt"); + service.put(getContainer(), "d2".getBytes(), datePrefix + "2024-07-09/file.txt"); + service.put(getContainer(), "d3".getBytes(), datePrefix + "2024-07-10/file.txt"); + service.put(getContainer(), "d4".getBytes(), datePrefix + "2024-07-20/file.txt"); + + List keys = service.searchObjectKeys( + getContainer(), datePrefix, null, "2024-07-10", 2, "yyyy-MM-dd"); + + assertEquals(3, keys.size()); + } + + // ── HDFS paths ──────────────────────────────────────────────────────────── + + @Test + @DisplayName("getPaths returns HDFS-prefixed paths") + void getPaths() { + Blob blob = new Blob(testPrefix + "some-file.txt", 100, null, java.util.Map.of()); + List paths = service.getPaths(getContainer(), List.of(blob)); + + assertEquals(1, paths.size()); + String path = paths.get(0); + assertTrue(path.contains(getContainer()), "Path should contain the container name"); + assertTrue(path.endsWith(testPrefix + "some-file.txt"), + "Path should end with the blob key"); + } +} diff --git a/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/util/DateRangeUtilTest.java b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/util/DateRangeUtilTest.java new file mode 100644 index 0000000..625f6a6 --- /dev/null +++ b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/util/DateRangeUtilTest.java @@ -0,0 +1,69 @@ +package org.sunbird.cloud.storage.util; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class DateRangeUtilTest { + + @Test + void getStartDate_subtractsDeltaFromEndDate() { + String result = DateRangeUtil.getStartDate("2024-05-10", 5); + assertEquals("2024-05-05", result); + } + + @Test + void getStartDate_nullEndDateUsesToday() { + // Just assert it returns a valid date string without throwing + String result = DateRangeUtil.getStartDate(null, 3); + assertNotNull(result); + assertTrue(result.matches("\\d{4}-\\d{2}-\\d{2}")); + } + + @Test + void getStartDate_emptyEndDateUsesToday() { + String result = DateRangeUtil.getStartDate("", 7); + assertNotNull(result); + assertTrue(result.matches("\\d{4}-\\d{2}-\\d{2}")); + } + + @Test + void getDatesBetween_inclusiveDateRange() { + String[] dates = DateRangeUtil.getDatesBetween("2024-03-01", "2024-03-05", "yyyy-MM-dd"); + assertEquals(5, dates.length); + assertEquals("2024-03-01", dates[0]); + assertEquals("2024-03-05", dates[4]); + } + + @Test + void getDatesBetween_singleDayRange() { + String[] dates = DateRangeUtil.getDatesBetween("2024-06-15", "2024-06-15", "yyyy-MM-dd"); + assertEquals(1, dates.length); + assertEquals("2024-06-15", dates[0]); + } + + @Test + void getDatesBetween_nullToDateUsesToday() { + // fromDate = yesterday relative to today; result should have >= 1 element + String yesterday = DateRangeUtil.getStartDate(null, 1); + String[] dates = DateRangeUtil.getDatesBetween(yesterday, null, "yyyy-MM-dd"); + assertTrue(dates.length >= 1); + } + + @Test + void getDatesBetween_customPattern() { + String[] dates = DateRangeUtil.getDatesBetween("01/03/2024", "03/03/2024", "dd/MM/yyyy"); + assertEquals(3, dates.length); + assertEquals("01/03/2024", dates[0]); + assertEquals("03/03/2024", dates[2]); + } + + @Test + void getDatesBetween_containsConsecutiveDates() { + String[] dates = DateRangeUtil.getDatesBetween("2024-01-30", "2024-02-02", "yyyy-MM-dd"); + assertEquals(4, dates.length); + assertEquals("2024-01-30", dates[0]); + assertEquals("2024-01-31", dates[1]); + assertEquals("2024-02-01", dates[2]); + assertEquals("2024-02-02", dates[3]); + } +} diff --git a/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/util/FileUtilTest.java b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/util/FileUtilTest.java new file mode 100644 index 0000000..9512c9d --- /dev/null +++ b/cloud-storage-sdk-api/src/test/java/org/sunbird/cloud/storage/util/FileUtilTest.java @@ -0,0 +1,124 @@ +package org.sunbird.cloud.storage.util; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.*; + +class FileUtilTest { + + @TempDir + Path tempDir; + + @Test + void listFilesRecursive_returnsEmptyForNonExistentDir() { + List files = FileUtil.listFilesRecursive(new File("/nonexistent/path")); + assertTrue(files.isEmpty()); + } + + @Test + void listFilesRecursive_returnsSingleFile() throws IOException { + File f = tempDir.resolve("file.txt").toFile(); + f.createNewFile(); + List files = FileUtil.listFilesRecursive(f); + assertEquals(1, files.size()); + assertEquals(f, files.get(0)); + } + + @Test + void listFilesRecursive_recursesSubdirectories() throws IOException { + Path sub = tempDir.resolve("sub"); + Files.createDirectory(sub); + Files.writeString(tempDir.resolve("root.txt"), "root"); + Files.writeString(sub.resolve("child.txt"), "child"); + + List files = FileUtil.listFilesRecursive(tempDir.toFile()); + assertEquals(2, files.size()); + assertTrue(files.stream().anyMatch(f -> f.getName().equals("root.txt"))); + assertTrue(files.stream().anyMatch(f -> f.getName().equals("child.txt"))); + } + + @Test + void listFilesRecursive_ignoresDirectoriesThemselves() throws IOException { + Path sub = Files.createDirectory(tempDir.resolve("emptydir")); + // directory exists but no files + List files = FileUtil.listFilesRecursive(tempDir.toFile()); + assertTrue(files.isEmpty()); + } + + @Test + void copyStream_writesFileToDestination() throws IOException { + String content = "hello cloud storage"; + InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + String destDir = tempDir.toString() + "/"; + + FileUtil.copyStream(is, destDir, "output.txt"); + + Path output = tempDir.resolve("output.txt"); + assertTrue(Files.exists(output)); + assertEquals(content, Files.readString(output)); + } + + @Test + void copyStream_createsDirectoryIfAbsent() throws IOException { + String content = "test content"; + InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + String newDir = tempDir.resolve("newsubdir").toString() + "/"; + + FileUtil.copyStream(is, newDir, "file.txt"); + + assertTrue(Files.exists(Path.of(newDir, "file.txt"))); + } + + @Test + void unZip_extractsFilesCorrectly() throws IOException { + // Create a test zip + Path zipFile = tempDir.resolve("test.zip"); + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile.toFile()))) { + zos.putNextEntry(new ZipEntry("data/file1.txt")); + zos.write("content1".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + zos.putNextEntry(new ZipEntry("data/file2.txt")); + zos.write("content2".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + } + + Path outputDir = tempDir.resolve("extracted"); + FileUtil.unZip(zipFile.toString(), outputDir.toString()); + + assertTrue(Files.exists(outputDir.resolve("data/file1.txt"))); + assertTrue(Files.exists(outputDir.resolve("data/file2.txt"))); + assertEquals("content1", Files.readString(outputDir.resolve("data/file1.txt"))); + } + + @Test + void unZip_skipsHiddenAndUnderscoreFiles() throws IOException { + Path zipFile = tempDir.resolve("test.zip"); + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile.toFile()))) { + zos.putNextEntry(new ZipEntry(".hidden")); + zos.write("hidden".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + zos.putNextEntry(new ZipEntry("_metadata")); + zos.write("meta".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + zos.putNextEntry(new ZipEntry("real.txt")); + zos.write("real".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + } + + Path outputDir = tempDir.resolve("out"); + FileUtil.unZip(zipFile.toString(), outputDir.toString()); + + assertFalse(Files.exists(outputDir.resolve(".hidden"))); + assertFalse(Files.exists(outputDir.resolve("_metadata"))); + assertTrue(Files.exists(outputDir.resolve("real.txt"))); + } +} diff --git a/cloud-storage-sdk-aws/pom.xml b/cloud-storage-sdk-aws/pom.xml new file mode 100644 index 0000000..7ce8d77 --- /dev/null +++ b/cloud-storage-sdk-aws/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + + org.sunbird + cloud-storage-sdk-parent + 2.0.2 + + + cloud-storage-sdk-aws + jar + Cloud Storage SDK - AWS + AWS S3 and Ceph S3-compatible storage implementation using AWS SDK v2. + + + + org.sunbird + cloud-storage-sdk-api + + + software.amazon.awssdk + s3 + + + software.amazon.awssdk + sts + + + org.sunbird + cloud-storage-sdk-api + tests + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + diff --git a/cloud-storage-sdk-aws/src/main/java/org/sunbird/cloud/storage/service/aws/AwsStorageService.java b/cloud-storage-sdk-aws/src/main/java/org/sunbird/cloud/storage/service/aws/AwsStorageService.java new file mode 100644 index 0000000..6d9bca7 --- /dev/null +++ b/cloud-storage-sdk-aws/src/main/java/org/sunbird/cloud/storage/service/aws/AwsStorageService.java @@ -0,0 +1,373 @@ +package org.sunbird.cloud.storage.service.aws; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sunbird.cloud.storage.AbstractStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.exception.StorageServiceException; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException; +import software.amazon.awssdk.services.s3.model.CopyObjectRequest; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.Delete; +import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.HeadBucketRequest; +import software.amazon.awssdk.services.s3.model.HeadObjectRequest; +import software.amazon.awssdk.services.s3.model.HeadObjectResponse; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.NoSuchBucketException; +import software.amazon.awssdk.services.s3.model.ObjectIdentifier; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.S3Object; +import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; + +import java.io.File; +import java.io.InputStream; +import java.net.URI; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * AWS S3 storage service implementation using AWS SDK v2. + * + *

Supports standard AWS S3, Ceph S3-compatible, and OCI S3-compatible storage + * endpoints. Authentication can be configured via access keys, OIDC, IAM roles, + * or instance profiles.

+ */ +public class AwsStorageService extends AbstractStorageService { + + private static final Logger logger = LoggerFactory.getLogger(AwsStorageService.class); + + private final StorageConfig config; + private final S3Client s3Client; + private final S3Presigner s3Presigner; + + public AwsStorageService(StorageConfig config) { + this.config = config; + + Region region = (config.getRegion() != null && !config.getRegion().isEmpty()) + ? Region.of(config.getRegion()) + : Region.US_EAST_1; + + boolean useCustomEndpoint = isCustomEndpoint(config); + String endPoint = config.getEndPoint(); + + // Build S3Client + S3ClientBuilder clientBuilder = S3Client.builder().region(region); + S3Presigner.Builder presignerBuilder = S3Presigner.builder().region(region); + + // Configure credentials based on auth type + switch (config.getAuthType()) { + case ACCESS_KEY: + StaticCredentialsProvider staticCreds = StaticCredentialsProvider.create( + AwsBasicCredentials.create(config.getStorageKey(), config.getStorageSecret())); + clientBuilder.credentialsProvider(staticCreds); + presignerBuilder.credentialsProvider(staticCreds); + break; + case OIDC: + case IAM: + case IAM_ROLE: + case INSTANCE_PROFILE: + DefaultCredentialsProvider defaultCreds = DefaultCredentialsProvider.create(); + clientBuilder.credentialsProvider(defaultCreds); + presignerBuilder.credentialsProvider(defaultCreds); + break; + default: + throw new StorageServiceException("Unsupported auth type: " + config.getAuthType()); + } + + // Configure custom endpoint and path-style access for non-AWS S3 implementations + if (useCustomEndpoint && endPoint != null && !endPoint.isEmpty()) { + URI endpointUri = URI.create(endPoint); + clientBuilder.endpointOverride(endpointUri); + presignerBuilder.endpointOverride(endpointUri); + + S3Configuration s3Config = S3Configuration.builder() + .pathStyleAccessEnabled(true) + .build(); + clientBuilder.serviceConfiguration(s3Config); + presignerBuilder.serviceConfiguration(s3Config); + } + + this.s3Client = clientBuilder.build(); + this.s3Presigner = presignerBuilder.build(); + + logger.info("Initialized AwsStorageService for type={}, region={}, customEndpoint={}", + config.getType(), region, useCustomEndpoint); + } + + /** + * Determines whether a custom endpoint should be configured. + * Custom endpoints are used for CEPHS3, OCI, or when an explicit endpoint is set. + */ + private boolean isCustomEndpoint(StorageConfig config) { + if (config.getType() == StorageConfig.StorageType.CEPHS3 + || config.getType() == StorageConfig.StorageType.OCI) { + return true; + } + return config.getEndPoint() != null && !config.getEndPoint().isEmpty(); + } + + @Override + protected void ensureContainerExists(String container) { + try { + s3Client.headBucket(HeadBucketRequest.builder().bucket(container).build()); + } catch (NoSuchBucketException e) { + try { + s3Client.createBucket(CreateBucketRequest.builder().bucket(container).build()); + logger.info("Created bucket: {}", container); + } catch (BucketAlreadyOwnedByYouException ignored) { + // Bucket was created concurrently; safe to proceed + logger.debug("Bucket already owned by us: {}", container); + } catch (Exception ex) { + throw new StorageServiceException( + "Failed to create bucket: " + container + " - " + ex.getMessage(), ex); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to check bucket existence: " + container + " - " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, File file) { + try { + String contentType = tika.detect(file); + PutObjectRequest request = PutObjectRequest.builder() + .bucket(container) + .key(objectKey) + .contentType(contentType) + .build(); + s3Client.putObject(request, RequestBody.fromFile(file)); + return getObjectUri(container, objectKey); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to put object from file: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, byte[] content) { + try { + PutObjectRequest request = PutObjectRequest.builder() + .bucket(container) + .key(objectKey) + .contentType(tika.detect(new java.io.ByteArrayInputStream(content), objectKey)) + .build(); + s3Client.putObject(request, RequestBody.fromBytes(content)); + return getObjectUri(container, objectKey); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to put object from bytes: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected InputStream getObjectStream(String container, String objectKey) { + try { + GetObjectRequest request = GetObjectRequest.builder() + .bucket(container) + .key(objectKey) + .build(); + return s3Client.getObject(request); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object stream: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getObjectUri(String container, String objectKey) { + String endPoint = config.getEndPoint(); + if (endPoint != null && !endPoint.isEmpty()) { + // Custom endpoint: // + String base = endPoint.endsWith("/") ? endPoint.substring(0, endPoint.length() - 1) : endPoint; + return base + "/" + container + "/" + objectKey; + } + // Standard AWS S3: https://.s3.amazonaws.com/ + return "https://" + container + ".s3.amazonaws.com/" + objectKey; + } + + @Override + protected BlobDetail getObjectDetail(String container, String objectKey) { + try { + HeadObjectRequest request = HeadObjectRequest.builder() + .bucket(container) + .key(objectKey) + .build(); + HeadObjectResponse response = s3Client.headObject(request); + + long contentLength = response.contentLength() != null ? response.contentLength() : 0L; + Date lastModified = response.lastModified() != null + ? Date.from(response.lastModified()) + : null; + + Map metadata = new HashMap<>(); + if (response.metadata() != null) { + metadata.putAll(response.metadata()); + } + if (response.contentType() != null) { + metadata.put("Content-Type", response.contentType()); + } + if (response.eTag() != null) { + metadata.put("ETag", response.eTag()); + } + + String uri = getObjectUri(container, objectKey); + return new BlobDetail(objectKey, contentLength, lastModified, metadata, uri); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object detail: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected List listKeys(String container, String prefix) { + try { + ListObjectsV2Request request = ListObjectsV2Request.builder() + .bucket(container) + .prefix(prefix) + .build(); + ListObjectsV2Iterable pages = s3Client.listObjectsV2Paginator(request); + + List keys = new ArrayList<>(); + for (var page : pages) { + for (S3Object s3Object : page.contents()) { + keys.add(s3Object.key()); + } + } + return keys; + } catch (Exception e) { + throw new StorageServiceException( + "Failed to list keys with prefix: " + prefix + " - " + e.getMessage(), e); + } + } + + @Override + protected void removeObjects(String container, List objectKeys) { + try { + if (objectKeys == null || objectKeys.isEmpty()) { + return; + } + + // S3 DeleteObjects supports up to 1000 keys per request + int batchSize = 1000; + for (int i = 0; i < objectKeys.size(); i += batchSize) { + List batch = objectKeys.subList(i, Math.min(i + batchSize, objectKeys.size())); + List identifiers = batch.stream() + .map(key -> ObjectIdentifier.builder().key(key).build()) + .collect(Collectors.toList()); + + DeleteObjectsRequest request = DeleteObjectsRequest.builder() + .bucket(container) + .delete(Delete.builder().objects(identifiers).quiet(true).build()) + .build(); + s3Client.deleteObjects(request); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to remove objects from container: " + container + " - " + e.getMessage(), e); + } + } + + @Override + protected void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey) { + try { + CopyObjectRequest request = CopyObjectRequest.builder() + .sourceBucket(fromContainer) + .sourceKey(fromKey) + .destinationBucket(toContainer) + .destinationKey(toKey) + .build(); + s3Client.copyObject(request); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to copy object from " + fromContainer + "/" + fromKey + + " to " + toContainer + "/" + toKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedGetUrl(String container, String objectKey, int ttlSeconds) { + try { + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(container) + .key(objectKey) + .build(); + GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() + .signatureDuration(Duration.ofSeconds(ttlSeconds)) + .getObjectRequest(getObjectRequest) + .build(); + return s3Presigner.presignGetObject(presignRequest).url().toString(); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate signed GET URL for: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams) { + try { + PutObjectRequest.Builder putRequestBuilder = PutObjectRequest.builder() + .bucket(container) + .key(objectKey); + if (contentType != null && !contentType.isEmpty()) { + putRequestBuilder.contentType(contentType); + } + if (additionalParams != null && !additionalParams.isEmpty()) { + putRequestBuilder.metadata(additionalParams); + } + + PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() + .signatureDuration(Duration.ofSeconds(ttlSeconds)) + .putObjectRequest(putRequestBuilder.build()) + .build(); + return s3Presigner.presignPutObject(presignRequest).url().toString(); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate signed PUT URL for: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getHdfsPrefix(String container) { + return "s3n://" + container + "/"; + } + + @Override + public void close() { + try { + if (s3Client != null) { + s3Client.close(); + } + } catch (Exception e) { + logger.warn("Error closing S3Client", e); + } + try { + if (s3Presigner != null) { + s3Presigner.close(); + } + } catch (Exception e) { + logger.warn("Error closing S3Presigner", e); + } + } +} diff --git a/cloud-storage-sdk-aws/src/main/java/org/sunbird/cloud/storage/service/aws/AwsStorageServiceProvider.java b/cloud-storage-sdk-aws/src/main/java/org/sunbird/cloud/storage/service/aws/AwsStorageServiceProvider.java new file mode 100644 index 0000000..214f8b0 --- /dev/null +++ b/cloud-storage-sdk-aws/src/main/java/org/sunbird/cloud/storage/service/aws/AwsStorageServiceProvider.java @@ -0,0 +1,22 @@ +package org.sunbird.cloud.storage.service.aws; + +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.StorageServiceProvider; + +/** + * SPI provider for AWS S3 and Ceph S3-compatible storage. + */ +public class AwsStorageServiceProvider implements StorageServiceProvider { + + @Override + public boolean supports(StorageConfig.StorageType type) { + return type == StorageConfig.StorageType.AWS + || type == StorageConfig.StorageType.CEPHS3; + } + + @Override + public IStorageService create(StorageConfig config) { + return new AwsStorageService(config); + } +} diff --git a/cloud-storage-sdk-aws/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider b/cloud-storage-sdk-aws/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider new file mode 100644 index 0000000..1a8c547 --- /dev/null +++ b/cloud-storage-sdk-aws/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider @@ -0,0 +1 @@ +org.sunbird.cloud.storage.service.aws.AwsStorageServiceProvider diff --git a/cloud-storage-sdk-aws/src/test/java/org/sunbird/cloud/storage/service/aws/AwsStorageServiceTest.java b/cloud-storage-sdk-aws/src/test/java/org/sunbird/cloud/storage/service/aws/AwsStorageServiceTest.java new file mode 100644 index 0000000..bc7556d --- /dev/null +++ b/cloud-storage-sdk-aws/src/test/java/org/sunbird/cloud/storage/service/aws/AwsStorageServiceTest.java @@ -0,0 +1,89 @@ +package org.sunbird.cloud.storage.service.aws; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.sunbird.cloud.storage.BaseStorageServiceIntegrationTest; +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * AWS S3 integration tests. + * + *

Tests are skipped by default and only execute when the following environment + * variables are set:

+ *
    + *
  • {@code AWS_STORAGE_TEST_ENABLED} — any non-null value enables the tests
  • + *
  • {@code AWS_STORAGE_TEST_CONTAINER} — name of a pre-existing, writable S3 bucket
  • + *
+ * + *

Authentication is determined by {@code AWS_STORAGE_TEST_AUTH_TYPE}:

+ *
    + *
  • {@code ACCESS_KEY} (default) — reads {@code AWS_ACCESS_KEY_ID} and + * {@code AWS_SECRET_ACCESS_KEY}
  • + *
  • {@code IAM} / {@code IAM_ROLE} / {@code INSTANCE_PROFILE} — uses the + * default AWS credential chain (instance profile, task role, etc.)
  • + *
+ * + *

Optional: {@code AWS_REGION} (default: {@code us-east-1})

+ * + *

For Ceph S3-compatible endpoints, also set:

+ *
    + *
  • {@code AWS_STORAGE_TEST_ENDPOINT} — custom endpoint URL
  • + *
  • {@code AWS_STORAGE_TEST_TYPE=CEPHS3}
  • + *
+ */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class AwsStorageServiceTest extends BaseStorageServiceIntegrationTest { + + @Override + protected IStorageService createService() { + assumeTrue(System.getenv("AWS_STORAGE_TEST_ENABLED") != null, + "Skipping AWS integration tests: set AWS_STORAGE_TEST_ENABLED=true to enable"); + + String authTypeStr = getEnv("AWS_STORAGE_TEST_AUTH_TYPE", "ACCESS_KEY"); + StorageConfig.AuthType authType = StorageConfig.AuthType.valueOf(authTypeStr.toUpperCase()); + + String storageTypeStr = getEnv("AWS_STORAGE_TEST_TYPE", "AWS"); + StorageConfig.StorageType storageType = StorageConfig.StorageType.valueOf(storageTypeStr.toUpperCase()); + + StorageConfig.Builder builder = StorageConfig.builder(storageType) + .authType(authType) + .region(getEnv("AWS_REGION", "us-east-1")); + + if (authType == StorageConfig.AuthType.ACCESS_KEY) { + String key = System.getenv("AWS_ACCESS_KEY_ID"); + String secret = System.getenv("AWS_SECRET_ACCESS_KEY"); + assumeTrue(key != null && secret != null, + "Skipping: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set for ACCESS_KEY auth"); + builder.storageKey(key).storageSecret(secret); + } + + String endpoint = System.getenv("AWS_STORAGE_TEST_ENDPOINT"); + if (endpoint != null && !endpoint.isBlank()) { + builder.endPoint(endpoint); + } + + return new AwsStorageService(builder.build()); + } + + @Override + protected String getContainer() { + String container = System.getenv("AWS_STORAGE_TEST_CONTAINER"); + assumeTrue(container != null && !container.isBlank(), + "Skipping: AWS_STORAGE_TEST_CONTAINER must specify a pre-existing writable bucket"); + return container; + } + + @BeforeAll + static void checkAwsEnabled() { + assumeTrue(System.getenv("AWS_STORAGE_TEST_ENABLED") != null, + "Skipping AWS integration tests: set AWS_STORAGE_TEST_ENABLED=true to enable"); + } + + private static String getEnv(String name, String defaultValue) { + String value = System.getenv(name); + return (value != null && !value.isBlank()) ? value : defaultValue; + } +} diff --git a/cloud-storage-sdk-azure/pom.xml b/cloud-storage-sdk-azure/pom.xml new file mode 100644 index 0000000..8e3c3c5 --- /dev/null +++ b/cloud-storage-sdk-azure/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + + org.sunbird + cloud-storage-sdk-parent + 2.0.2 + + + cloud-storage-sdk-azure + jar + Cloud Storage SDK - Azure + Azure Blob Storage implementation using Azure SDK for Java. + + + + org.sunbird + cloud-storage-sdk-api + + + com.azure + azure-storage-blob + + + com.azure + azure-core-http-netty + + + + + com.azure + azure-identity + + + com.azure + azure-core-http-netty + + + + + com.azure + azure-core-http-okhttp + + + org.sunbird + cloud-storage-sdk-api + tests + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + diff --git a/cloud-storage-sdk-azure/src/main/java/org/sunbird/cloud/storage/service/azure/AzureStorageService.java b/cloud-storage-sdk-azure/src/main/java/org/sunbird/cloud/storage/service/azure/AzureStorageService.java new file mode 100644 index 0000000..0eaeb88 --- /dev/null +++ b/cloud-storage-sdk-azure/src/main/java/org/sunbird/cloud/storage/service/azure/AzureStorageService.java @@ -0,0 +1,318 @@ +package org.sunbird.cloud.storage.service.azure; + +import com.azure.identity.DefaultAzureCredentialBuilder; +import com.azure.identity.ManagedIdentityCredentialBuilder; +import com.azure.identity.WorkloadIdentityCredentialBuilder; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.models.BlobHttpHeaders; +import com.azure.storage.blob.models.BlobItem; +import com.azure.storage.blob.models.BlobListDetails; +import com.azure.storage.blob.models.BlobProperties; +import com.azure.storage.blob.models.ListBlobsOptions; +import com.azure.storage.blob.options.BlobParallelUploadOptions; +import com.azure.storage.blob.options.BlobUploadFromFileOptions; +import com.azure.storage.blob.sas.BlobSasPermission; +import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; +import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.core.http.okhttp.OkHttpAsyncHttpClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sunbird.cloud.storage.AbstractStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.exception.StorageServiceException; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Azure Blob Storage implementation using Azure SDK for Java. + */ +public class AzureStorageService extends AbstractStorageService { + + private static final Logger logger = LoggerFactory.getLogger(AzureStorageService.class); + + private final StorageConfig config; + private final BlobServiceClient blobServiceClient; + private final boolean useSharedKey; + + public AzureStorageService(StorageConfig config) { + this.config = config; + + String accountName = config.getStorageKey(); + String endpoint = "https://" + accountName + ".blob.core.windows.net"; + + // OkHttp avoids Netty's request-line bitmask bug that rejects uppercase chars in percent-encoded blob names. + BlobServiceClientBuilder builder = new BlobServiceClientBuilder() + .endpoint(endpoint) + .httpClient(new OkHttpAsyncHttpClientBuilder().build()); + + switch (config.getAuthType()) { + case ACCESS_KEY: + StorageSharedKeyCredential sharedKey = + new StorageSharedKeyCredential(accountName, config.getStorageSecret()); + builder.credential(sharedKey); + this.useSharedKey = true; + break; + case OIDC: + String clientId = System.getenv("AZURE_CLIENT_ID"); + String tenantId = System.getenv("AZURE_TENANT_ID"); + String tokenFile = System.getenv("AZURE_FEDERATED_TOKEN_FILE"); + if (clientId != null && tenantId != null && tokenFile != null) { + builder.credential(new WorkloadIdentityCredentialBuilder() + .clientId(clientId) + .tenantId(tenantId) + .tokenFilePath(tokenFile) + .build()); + } else { + builder.credential(new DefaultAzureCredentialBuilder().build()); + } + this.useSharedKey = false; + break; + case IAM: + case IAM_ROLE: + case INSTANCE_PROFILE: + builder.credential(new ManagedIdentityCredentialBuilder().build()); + this.useSharedKey = false; + break; + default: + throw new StorageServiceException("Unsupported auth type: " + config.getAuthType()); + } + + this.blobServiceClient = builder.buildClient(); + logger.info("Initialized AzureStorageService for account={}, authType={}", + accountName, config.getAuthType()); + } + + private BlobContainerClient getContainerClient(String container) { + return blobServiceClient.getBlobContainerClient(container); + } + + private BlobClient getBlobClient(String container, String objectKey) { + return getContainerClient(container).getBlobClient(objectKey); + } + + /** + * The Azure SDK's getBlobUrl() encodes '/' in blob names as '%2F'. + * This replaces only %2F with '/', leaving all other percent-encoded characters + */ + private String decodeBlobUrl(String url) { + try { + java.net.URI uri = new java.net.URI(url); + String path = uri.getRawPath().replace("%2F", "/").replace("%2f", "/"); + String result = uri.getScheme() + "://" + uri.getRawAuthority() + path; + return uri.getRawQuery() != null ? result + "?" + uri.getRawQuery() : result; + } catch (java.net.URISyntaxException e) { + return url; + } + } + + @Override + protected void ensureContainerExists(String container) { + try { + BlobContainerClient containerClient = getContainerClient(container); + if (!containerClient.exists()) { + containerClient.create(); + logger.info("Created container: {}", container); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to ensure container exists: " + container + " - " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, File file) { + try { + BlobClient blobClient = getBlobClient(container, objectKey); + String contentType = tika.detect(file); + blobClient.uploadFromFileWithResponse( + new BlobUploadFromFileOptions(file.getAbsolutePath()) + .setHeaders(new BlobHttpHeaders().setContentType(contentType)), + null, null); + return decodeBlobUrl(blobClient.getBlobUrl()); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to put object from file: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, byte[] content) { + try { + BlobClient blobClient = getBlobClient(container, objectKey); + String contentType = tika.detect(new ByteArrayInputStream(content), objectKey); + blobClient.uploadWithResponse( + new BlobParallelUploadOptions(new ByteArrayInputStream(content), content.length) + .setHeaders(new BlobHttpHeaders().setContentType(contentType)), + null, null); + return decodeBlobUrl(blobClient.getBlobUrl()); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to put object from bytes: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected InputStream getObjectStream(String container, String objectKey) { + try { + BlobClient blobClient = getBlobClient(container, objectKey); + return blobClient.openInputStream(); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object stream: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getObjectUri(String container, String objectKey) { + return decodeBlobUrl(getBlobClient(container, objectKey).getBlobUrl()); + } + + @Override + protected BlobDetail getObjectDetail(String container, String objectKey) { + try { + BlobClient blobClient = getBlobClient(container, objectKey); + BlobProperties props = blobClient.getProperties(); + + Map metadata = new HashMap<>(); + if (props.getMetadata() != null) { + metadata.putAll(props.getMetadata()); + } + String blobUrl = decodeBlobUrl(blobClient.getBlobUrl()); + metadata.put("uri", blobUrl); + metadata.put("Content-Type", props.getContentType()); + metadata.put("ETag", props.getETag()); + + Date lastModified = props.getLastModified() != null + ? Date.from(props.getLastModified().toInstant()) + : null; + + return new BlobDetail(objectKey, props.getBlobSize(), lastModified, + metadata, blobUrl); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object detail: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected List listKeys(String container, String prefix) { + try { + BlobContainerClient containerClient = getContainerClient(container); + ListBlobsOptions options = new ListBlobsOptions() + .setPrefix(prefix) + .setDetails(new BlobListDetails().setRetrieveMetadata(false)); + + List keys = new ArrayList<>(); + for (BlobItem item : containerClient.listBlobs(options, null)) { + keys.add(item.getName()); + } + return keys; + } catch (Exception e) { + throw new StorageServiceException( + "Failed to list keys with prefix: " + prefix + " - " + e.getMessage(), e); + } + } + + @Override + protected void removeObjects(String container, List objectKeys) { + try { + for (String key : objectKeys) { + BlobClient blobClient = getBlobClient(container, key); + blobClient.deleteIfExists(); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to remove objects: " + e.getMessage(), e); + } + } + + @Override + protected void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey) { + try { + BlobClient sourceBlobClient = getBlobClient(fromContainer, fromKey); + BlobClient destBlobClient = getBlobClient(toContainer, toKey); + destBlobClient.copyFromUrl(decodeBlobUrl(sourceBlobClient.getBlobUrl())); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to copy object from " + fromContainer + "/" + fromKey + + " to " + toContainer + "/" + toKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedGetUrl(String container, String objectKey, int ttlSeconds) { + try { + BlobClient blobClient = getBlobClient(container, objectKey); + BlobSasPermission permission = new BlobSasPermission().setReadPermission(true); + BlobServiceSasSignatureValues values = new BlobServiceSasSignatureValues( + OffsetDateTime.now().plusSeconds(ttlSeconds), permission); + + String baseUrl = decodeBlobUrl(blobClient.getBlobUrl()); + if (useSharedKey) { + return baseUrl + "?" + blobClient.generateSas(values); + } else { + var delegationKey = blobServiceClient.getUserDelegationKey( + OffsetDateTime.now().minusMinutes(5), + OffsetDateTime.now().plusSeconds(ttlSeconds)); + return baseUrl + "?" + blobClient.generateUserDelegationSas(values, delegationKey); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate signed GET URL for: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams) { + try { + BlobClient blobClient = getBlobClient(container, objectKey); + BlobSasPermission permission = new BlobSasPermission() + .setWritePermission(true) + .setCreatePermission(true); + BlobServiceSasSignatureValues values = new BlobServiceSasSignatureValues( + OffsetDateTime.now().plusSeconds(ttlSeconds), permission); + if (contentType != null) { + values.setContentType(contentType); + } + + String baseUrl = decodeBlobUrl(blobClient.getBlobUrl()); + if (useSharedKey) { + return baseUrl + "?" + blobClient.generateSas(values); + } else { + var delegationKey = blobServiceClient.getUserDelegationKey( + OffsetDateTime.now().minusMinutes(5), + OffsetDateTime.now().plusSeconds(ttlSeconds)); + return baseUrl + "?" + blobClient.generateUserDelegationSas(values, delegationKey); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate signed PUT URL for: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getHdfsPrefix(String container) { + return "wasb://" + container + "@" + config.getStorageKey() + ".blob.core.windows.net/"; + } + + @Override + public void close() { + // BlobServiceClient manages its own HTTP connection pool internally + // and does not implement Closeable. No explicit cleanup is needed. + logger.info("AzureStorageService closed"); + } +} diff --git a/cloud-storage-sdk-azure/src/main/java/org/sunbird/cloud/storage/service/azure/AzureStorageServiceProvider.java b/cloud-storage-sdk-azure/src/main/java/org/sunbird/cloud/storage/service/azure/AzureStorageServiceProvider.java new file mode 100644 index 0000000..5008a3c --- /dev/null +++ b/cloud-storage-sdk-azure/src/main/java/org/sunbird/cloud/storage/service/azure/AzureStorageServiceProvider.java @@ -0,0 +1,21 @@ +package org.sunbird.cloud.storage.service.azure; + +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.StorageServiceProvider; + +/** + * SPI provider for Azure Blob Storage. + */ +public class AzureStorageServiceProvider implements StorageServiceProvider { + + @Override + public boolean supports(StorageConfig.StorageType type) { + return type == StorageConfig.StorageType.AZURE; + } + + @Override + public IStorageService create(StorageConfig config) { + return new AzureStorageService(config); + } +} diff --git a/cloud-storage-sdk-azure/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider b/cloud-storage-sdk-azure/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider new file mode 100644 index 0000000..89e50d6 --- /dev/null +++ b/cloud-storage-sdk-azure/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider @@ -0,0 +1 @@ +org.sunbird.cloud.storage.service.azure.AzureStorageServiceProvider diff --git a/cloud-storage-sdk-azure/src/test/java/org/sunbird/cloud/storage/service/azure/AzureStorageServiceTest.java b/cloud-storage-sdk-azure/src/test/java/org/sunbird/cloud/storage/service/azure/AzureStorageServiceTest.java new file mode 100644 index 0000000..ad130d6 --- /dev/null +++ b/cloud-storage-sdk-azure/src/test/java/org/sunbird/cloud/storage/service/azure/AzureStorageServiceTest.java @@ -0,0 +1,78 @@ +package org.sunbird.cloud.storage.service.azure; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.sunbird.cloud.storage.BaseStorageServiceIntegrationTest; +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Azure Blob Storage integration tests. + * + *

Tests are skipped by default and only execute when the following environment + * variables are set:

+ *
    + *
  • {@code AZURE_STORAGE_TEST_ENABLED} — any non-null value enables the tests
  • + *
  • {@code AZURE_STORAGE_TEST_CONTAINER} — name of a pre-existing, writable container
  • + *
  • {@code AZURE_STORAGE_ACCOUNT_NAME} — storage account name
  • + *
+ * + *

Authentication is determined by {@code AZURE_STORAGE_TEST_AUTH_TYPE}:

+ *
    + *
  • {@code ACCESS_KEY} (default) — reads {@code AZURE_STORAGE_ACCOUNT_KEY}
  • + *
  • {@code OIDC} — uses Workload Identity; requires {@code AZURE_CLIENT_ID}, + * {@code AZURE_TENANT_ID}, {@code AZURE_FEDERATED_TOKEN_FILE} or falls back to + * {@code DefaultAzureCredential}
  • + *
  • {@code IAM} / {@code INSTANCE_PROFILE} — uses Managed Identity
  • + *
+ */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class AzureStorageServiceTest extends BaseStorageServiceIntegrationTest { + + @Override + protected IStorageService createService() { + assumeTrue(System.getenv("AZURE_STORAGE_TEST_ENABLED") != null, + "Skipping Azure integration tests: set AZURE_STORAGE_TEST_ENABLED=true to enable"); + + String accountName = System.getenv("AZURE_STORAGE_ACCOUNT_NAME"); + assumeTrue(accountName != null && !accountName.isBlank(), + "Skipping: AZURE_STORAGE_ACCOUNT_NAME must be set"); + + String authTypeStr = getEnv("AZURE_STORAGE_TEST_AUTH_TYPE", "ACCESS_KEY"); + StorageConfig.AuthType authType = StorageConfig.AuthType.valueOf(authTypeStr.toUpperCase()); + + StorageConfig.Builder builder = StorageConfig.builder(StorageConfig.StorageType.AZURE) + .authType(authType) + .storageKey(accountName); + + if (authType == StorageConfig.AuthType.ACCESS_KEY) { + String accountKey = System.getenv("AZURE_STORAGE_ACCOUNT_KEY"); + assumeTrue(accountKey != null && !accountKey.isBlank(), + "Skipping: AZURE_STORAGE_ACCOUNT_KEY must be set for ACCESS_KEY auth"); + builder.storageSecret(accountKey); + } + + return new AzureStorageService(builder.build()); + } + + @Override + protected String getContainer() { + String container = System.getenv("AZURE_STORAGE_TEST_CONTAINER"); + assumeTrue(container != null && !container.isBlank(), + "Skipping: AZURE_STORAGE_TEST_CONTAINER must specify a pre-existing writable container"); + return container; + } + + @BeforeAll + static void checkAzureEnabled() { + assumeTrue(System.getenv("AZURE_STORAGE_TEST_ENABLED") != null, + "Skipping Azure integration tests: set AZURE_STORAGE_TEST_ENABLED=true to enable"); + } + + private static String getEnv(String name, String defaultValue) { + String value = System.getenv(name); + return (value != null && !value.isBlank()) ? value : defaultValue; + } +} diff --git a/cloud-storage-sdk-gcp/pom.xml b/cloud-storage-sdk-gcp/pom.xml new file mode 100644 index 0000000..6be746d --- /dev/null +++ b/cloud-storage-sdk-gcp/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + org.sunbird + cloud-storage-sdk-parent + 2.0.2 + + + cloud-storage-sdk-gcp + jar + Cloud Storage SDK - GCP + Google Cloud Storage implementation using Google Cloud Java SDK. + + + + org.sunbird + cloud-storage-sdk-api + + + com.google.cloud + google-cloud-storage + + + org.sunbird + cloud-storage-sdk-api + tests + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + diff --git a/cloud-storage-sdk-gcp/src/main/java/org/sunbird/cloud/storage/service/gcp/GcpStorageService.java b/cloud-storage-sdk-gcp/src/main/java/org/sunbird/cloud/storage/service/gcp/GcpStorageService.java new file mode 100644 index 0000000..d35ff2e --- /dev/null +++ b/cloud-storage-sdk-gcp/src/main/java/org/sunbird/cloud/storage/service/gcp/GcpStorageService.java @@ -0,0 +1,336 @@ +package org.sunbird.cloud.storage.service.gcp; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.ServiceAccountCredentials; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.HttpMethod; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sunbird.cloud.storage.AbstractStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.exception.StorageServiceException; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Google Cloud Storage implementation using the Google Cloud Java SDK. + */ +public class GcpStorageService extends AbstractStorageService { + + private static final Logger logger = LoggerFactory.getLogger(GcpStorageService.class); + private static final String GCS_HOST = "https://storage.googleapis.com/"; + + private final StorageConfig config; + private final Storage storage; + private Storage signingStorage; + + public GcpStorageService(StorageConfig config) { + this.config = config; + this.storage = buildStorage(config); + logger.info("Initialized GcpStorageService with authType={}", config.getAuthType()); + } + + private Storage buildStorage(StorageConfig config) { + try { + StorageOptions.Builder builder = StorageOptions.newBuilder(); + + switch (config.getAuthType()) { + case ACCESS_KEY: + // For access key auth, storageKey is the project ID and storageSecret + // is a service account JSON key. Parse as JSON credentials. + if (config.getStorageSecret() != null && !config.getStorageSecret().isEmpty()) { + logger.warn("Using service account JSON from config. For production, prefer GOOGLE_APPLICATION_CREDENTIALS env var."); + GoogleCredentials credentials = GoogleCredentials.fromStream( + new ByteArrayInputStream(config.getStorageSecret().getBytes())); + builder.setCredentials(credentials); + } + if (config.getStorageKey() != null && !config.getStorageKey().isEmpty()) { + builder.setProjectId(config.getStorageKey()); + } + break; + case OIDC: + case IAM: + case IAM_ROLE: + case INSTANCE_PROFILE: + // Use application default credentials (handles WIF, GKE Workload Identity, + // metadata server, GOOGLE_APPLICATION_CREDENTIALS) + builder.setCredentials(GoogleCredentials.getApplicationDefault()); + break; + default: + throw new StorageServiceException("Unsupported auth type: " + config.getAuthType()); + } + + return builder.build().getService(); + } catch (IOException e) { + throw new StorageServiceException("Failed to initialize GCP Storage: " + e.getMessage(), e); + } + } + + @Override + protected void ensureContainerExists(String container) { + try { + if (storage.get(container) == null) { + storage.create(com.google.cloud.storage.BucketInfo.of(container)); + logger.info("Created bucket: {}", container); + } + } catch (Exception e) { + // Bucket may already exist; log and continue + logger.debug("Bucket check/create for {}: {}", container, e.getMessage()); + } + } + + @Override + protected String putObject(String container, String objectKey, File file) { + try { + String contentType = tika.detect(file); + BlobId blobId = BlobId.of(container, objectKey); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId) + .setContentType(contentType) + .build(); + try (java.nio.channels.WritableByteChannel writer = storage.writer(blobInfo); + java.io.FileInputStream fis = new java.io.FileInputStream(file); + java.nio.channels.ReadableByteChannel reader = java.nio.channels.Channels.newChannel(fis)) { + java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(64 * 1024); + while (reader.read(buffer) > 0) { + buffer.flip(); + writer.write(buffer); + buffer.clear(); + } + } + return GCS_HOST + container + "/" + objectKey; + } catch (IOException e) { + throw new StorageServiceException( + "Failed to put object from file: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, byte[] content) { + BlobId blobId = BlobId.of(container, objectKey); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build(); + storage.create(blobInfo, content); + return GCS_HOST + container + "/" + objectKey; + } + + @Override + protected InputStream getObjectStream(String container, String objectKey) { + try { + com.google.cloud.ReadChannel reader = storage.reader(BlobId.of(container, objectKey)); + return java.nio.channels.Channels.newInputStream(reader); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object stream: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getObjectUri(String container, String objectKey) { + return GCS_HOST + container + "/" + objectKey; + } + + @Override + protected BlobDetail getObjectDetail(String container, String objectKey) { + try { + com.google.cloud.storage.Blob blob = storage.get(BlobId.of(container, objectKey)); + if (blob == null) { + throw new StorageServiceException("Object not found: " + container + "/" + objectKey); + } + + Map metadata = new HashMap<>(); + if (blob.getMetadata() != null) { + metadata.putAll(blob.getMetadata()); + } + metadata.put("uri", GCS_HOST + container + "/" + objectKey); + metadata.put("publicUri", GCS_HOST + container + "/" + objectKey); + metadata.put("name", blob.getName()); + metadata.put("Content-Type", blob.getContentType()); + + Long size = blob.getSize(); + long contentLength = size != null ? size : 0L; + Date lastModified = blob.getUpdateTimeOffsetDateTime() != null + ? Date.from(blob.getUpdateTimeOffsetDateTime().toInstant()) + : null; + + return new BlobDetail(objectKey, contentLength, lastModified, metadata, + GCS_HOST + container + "/" + objectKey); + } catch (StorageServiceException e) { + throw e; + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object detail: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected List listKeys(String container, String prefix) { + try { + List keys = new ArrayList<>(); + com.google.api.gax.paging.Page blobs = + storage.list(container, Storage.BlobListOption.prefix(prefix)); + for (com.google.cloud.storage.Blob blob : blobs.iterateAll()) { + keys.add(blob.getName()); + } + return keys; + } catch (Exception e) { + throw new StorageServiceException( + "Failed to list keys with prefix: " + prefix + " - " + e.getMessage(), e); + } + } + + @Override + protected void removeObjects(String container, List objectKeys) { + try { + List blobIds = new ArrayList<>(); + for (String key : objectKeys) { + blobIds.add(BlobId.of(container, key)); + } + if (!blobIds.isEmpty()) { + storage.delete(blobIds); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to remove objects: " + e.getMessage(), e); + } + } + + @Override + protected void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey) { + try { + Storage.CopyRequest request = Storage.CopyRequest.newBuilder() + .setSource(BlobId.of(fromContainer, fromKey)) + .setTarget(BlobId.of(toContainer, toKey)) + .build(); + storage.copy(request).getResult(); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to copy object from " + fromContainer + "/" + fromKey + + " to " + toContainer + "/" + toKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedGetUrl(String container, String objectKey, int ttlSeconds) { + try { + int effectiveTtl = Math.min(ttlSeconds, maxSignedUrlTTL); + BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(container, objectKey)).build(); + URL url = storage.signUrl(blobInfo, effectiveTtl, TimeUnit.SECONDS, + Storage.SignUrlOption.httpMethod(HttpMethod.GET), + Storage.SignUrlOption.withV4Signature()); + return url.toString(); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate signed GET URL for: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams) { + try { + int effectiveTtl = Math.min(ttlSeconds, maxSignedUrlTTL); + BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(container, objectKey)).build(); + + // Check for chunked upload mode + boolean isChunkedUpload = additionalParams != null + && "true".equalsIgnoreCase(additionalParams.get("chunked")); + + // If additionalParams contains service account credentials, use them + Storage signingStorage = resolveSigningStorage(additionalParams); + + if (isChunkedUpload) { + Map extensionHeaders = new HashMap<>(); + extensionHeaders.put("Content-Type", + contentType != null ? contentType : "application/octet-stream"); + extensionHeaders.put("x-goog-resumable", "start"); + URL url = signingStorage.signUrl(blobInfo, effectiveTtl, TimeUnit.SECONDS, + Storage.SignUrlOption.httpMethod(HttpMethod.POST), + Storage.SignUrlOption.withV4Signature(), + Storage.SignUrlOption.withExtHeaders(extensionHeaders)); + return url.toString(); + } else { + URL url = signingStorage.signUrl(blobInfo, effectiveTtl, TimeUnit.SECONDS, + Storage.SignUrlOption.httpMethod(HttpMethod.PUT), + Storage.SignUrlOption.withV4Signature()); + return url.toString(); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate signed PUT URL for: " + objectKey + " - " + e.getMessage(), e); + } + } + + /** + * If additionalParams contains GCP service account credentials, build a dedicated + * Storage client for signing. Otherwise fall back to the main storage client. + */ + private Storage resolveSigningStorage(Map additionalParams) { + if (additionalParams == null || additionalParams.isEmpty()) { + return this.storage; + } + String clientId = additionalParams.get("clientId"); + String clientEmail = additionalParams.get("clientEmail"); + String privateKeyPkcs8 = additionalParams.get("privateKeyPkcs8"); + String privateKeyId = additionalParams.get("privateKeyId"); + String projectId = additionalParams.get("projectId"); + + if (clientEmail != null && privateKeyPkcs8 != null && privateKeyId != null) { + if (this.signingStorage != null) { + return this.signingStorage; + } + try { + ServiceAccountCredentials credentials = ServiceAccountCredentials.fromPkcs8( + clientId, clientEmail, privateKeyPkcs8, privateKeyId, + new ArrayList<>()); + this.signingStorage = StorageOptions.newBuilder() + .setProjectId(projectId) + .setCredentials(credentials) + .build() + .getService(); + return this.signingStorage; + } catch (IOException e) { + throw new StorageServiceException( + "Failed to create signing credentials from additionalParams: " + e.getMessage(), e); + } + } + return this.storage; + } + + @Override + protected String getHdfsPrefix(String container) { + return "gs://" + container + "/"; + } + + @Override + public String getUri(String container, String prefix, boolean isDirectory) { + List keys = listObjectKeys(container, prefix); + if (keys.isEmpty()) { + throw new StorageServiceException("The given prefix is incorrect: " + prefix); + } + return GCS_HOST + container + "/" + prefix; + } + + @Override + public void close() { + try { + storage.close(); + } catch (Exception e) { + logger.warn("Error closing GCP Storage client", e); + } + } +} diff --git a/cloud-storage-sdk-gcp/src/main/java/org/sunbird/cloud/storage/service/gcp/GcpStorageServiceProvider.java b/cloud-storage-sdk-gcp/src/main/java/org/sunbird/cloud/storage/service/gcp/GcpStorageServiceProvider.java new file mode 100644 index 0000000..76d5c4d --- /dev/null +++ b/cloud-storage-sdk-gcp/src/main/java/org/sunbird/cloud/storage/service/gcp/GcpStorageServiceProvider.java @@ -0,0 +1,21 @@ +package org.sunbird.cloud.storage.service.gcp; + +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.StorageServiceProvider; + +/** + * SPI provider for Google Cloud Storage. + */ +public class GcpStorageServiceProvider implements StorageServiceProvider { + + @Override + public boolean supports(StorageConfig.StorageType type) { + return type == StorageConfig.StorageType.GCLOUD; + } + + @Override + public IStorageService create(StorageConfig config) { + return new GcpStorageService(config); + } +} diff --git a/cloud-storage-sdk-gcp/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider b/cloud-storage-sdk-gcp/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider new file mode 100644 index 0000000..aa35ab4 --- /dev/null +++ b/cloud-storage-sdk-gcp/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider @@ -0,0 +1 @@ +org.sunbird.cloud.storage.service.gcp.GcpStorageServiceProvider diff --git a/cloud-storage-sdk-gcp/src/test/java/org/sunbird/cloud/storage/service/gcp/GcpStorageServiceTest.java b/cloud-storage-sdk-gcp/src/test/java/org/sunbird/cloud/storage/service/gcp/GcpStorageServiceTest.java new file mode 100644 index 0000000..96cb637 --- /dev/null +++ b/cloud-storage-sdk-gcp/src/test/java/org/sunbird/cloud/storage/service/gcp/GcpStorageServiceTest.java @@ -0,0 +1,78 @@ +package org.sunbird.cloud.storage.service.gcp; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.sunbird.cloud.storage.BaseStorageServiceIntegrationTest; +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Google Cloud Storage integration tests. + * + *

Tests are skipped by default and only execute when the following environment + * variables are set:

+ *
    + *
  • {@code GCP_STORAGE_TEST_ENABLED} — any non-null value enables the tests
  • + *
  • {@code GCP_STORAGE_TEST_CONTAINER} — name of a pre-existing, writable GCS bucket
  • + *
+ * + *

Authentication is determined by {@code GCP_STORAGE_TEST_AUTH_TYPE}:

+ *
    + *
  • {@code ACCESS_KEY} (default) — reads {@code GCP_SERVICE_ACCOUNT_KEY_JSON} + * (the full JSON content of a service account key file)
  • + *
  • {@code OIDC} / {@code IAM} — uses Application Default Credentials + * ({@code GOOGLE_APPLICATION_CREDENTIALS} or GCE metadata)
  • + *
+ * + *

Optional: {@code GCP_STORAGE_TEST_PROJECT} — GCP project ID (used for some operations)

+ */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class GcpStorageServiceTest extends BaseStorageServiceIntegrationTest { + + @Override + protected IStorageService createService() { + assumeTrue(System.getenv("GCP_STORAGE_TEST_ENABLED") != null, + "Skipping GCP integration tests: set GCP_STORAGE_TEST_ENABLED=true to enable"); + + String authTypeStr = getEnv("GCP_STORAGE_TEST_AUTH_TYPE", "ACCESS_KEY"); + StorageConfig.AuthType authType = StorageConfig.AuthType.valueOf(authTypeStr.toUpperCase()); + + StorageConfig.Builder builder = StorageConfig.builder(StorageConfig.StorageType.GCLOUD) + .authType(authType); + + if (authType == StorageConfig.AuthType.ACCESS_KEY) { + String serviceAccountJson = System.getenv("GCP_SERVICE_ACCOUNT_KEY_JSON"); + assumeTrue(serviceAccountJson != null && !serviceAccountJson.isBlank(), + "Skipping: GCP_SERVICE_ACCOUNT_KEY_JSON must contain the service account key JSON for ACCESS_KEY auth"); + builder.storageSecret(serviceAccountJson); + } + + String project = System.getenv("GCP_STORAGE_TEST_PROJECT"); + if (project != null && !project.isBlank()) { + builder.storageKey(project); + } + + return new GcpStorageService(builder.build()); + } + + @Override + protected String getContainer() { + String container = System.getenv("GCP_STORAGE_TEST_CONTAINER"); + assumeTrue(container != null && !container.isBlank(), + "Skipping: GCP_STORAGE_TEST_CONTAINER must specify a pre-existing writable GCS bucket"); + return container; + } + + @BeforeAll + static void checkGcpEnabled() { + assumeTrue(System.getenv("GCP_STORAGE_TEST_ENABLED") != null, + "Skipping GCP integration tests: set GCP_STORAGE_TEST_ENABLED=true to enable"); + } + + private static String getEnv(String name, String defaultValue) { + String value = System.getenv(name); + return (value != null && !value.isBlank()) ? value : defaultValue; + } +} diff --git a/cloud-storage-sdk-oci/pom.xml b/cloud-storage-sdk-oci/pom.xml new file mode 100644 index 0000000..a60d651 --- /dev/null +++ b/cloud-storage-sdk-oci/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + org.sunbird + cloud-storage-sdk-parent + 2.0.2 + + + cloud-storage-sdk-oci + jar + Cloud Storage SDK - OCI + Oracle Cloud Infrastructure Object Storage implementation using OCI Java SDK. + + + + org.sunbird + cloud-storage-sdk-api + + + com.oracle.oci.sdk + oci-java-sdk-objectstorage + + + com.oracle.oci.sdk + oci-java-sdk-common-httpclient-jersey3 + runtime + + + org.sunbird + cloud-storage-sdk-api + tests + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + diff --git a/cloud-storage-sdk-oci/src/main/java/org/sunbird/cloud/storage/service/oci/OciStorageService.java b/cloud-storage-sdk-oci/src/main/java/org/sunbird/cloud/storage/service/oci/OciStorageService.java new file mode 100644 index 0000000..3461354 --- /dev/null +++ b/cloud-storage-sdk-oci/src/main/java/org/sunbird/cloud/storage/service/oci/OciStorageService.java @@ -0,0 +1,376 @@ +package org.sunbird.cloud.storage.service.oci; + +import com.oracle.bmc.Region; +import com.oracle.bmc.auth.AuthenticationDetailsProvider; +import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider; +import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider; +import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider; +import com.oracle.bmc.objectstorage.ObjectStorageClient; +import com.oracle.bmc.objectstorage.model.CreateBucketDetails; +import com.oracle.bmc.objectstorage.model.CreatePreauthenticatedRequestDetails; +import com.oracle.bmc.objectstorage.model.ListObjects; +import com.oracle.bmc.objectstorage.model.ObjectSummary; +import com.oracle.bmc.objectstorage.requests.*; +import com.oracle.bmc.objectstorage.responses.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sunbird.cloud.storage.AbstractStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.exception.StorageServiceException; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Oracle Cloud Infrastructure Object Storage implementation using the OCI Java SDK. + */ +public class OciStorageService extends AbstractStorageService { + + private static final Logger logger = LoggerFactory.getLogger(OciStorageService.class); + + private final StorageConfig config; + private final ObjectStorageClient objectStorageClient; + private final String namespace; + + public OciStorageService(StorageConfig config) { + this.config = config; + + BasicAuthenticationDetailsProvider authProvider = resolveAuth(config); + + ObjectStorageClient.Builder clientBuilder = ObjectStorageClient.builder(); + + if (config.getEndPoint() != null && !config.getEndPoint().isEmpty()) { + clientBuilder.endpoint(config.getEndPoint()); + } else if (config.getRegion() != null && !config.getRegion().isEmpty()) { + clientBuilder.region(Region.fromRegionId(config.getRegion())); + } + + ObjectStorageClient tempClient = clientBuilder.build(authProvider); + try { + GetNamespaceResponse namespaceResponse = tempClient.getNamespace( + GetNamespaceRequest.builder().build()); + this.namespace = namespaceResponse.getValue(); + this.objectStorageClient = tempClient; + } catch (Exception e) { + tempClient.close(); + throw new StorageServiceException( + "Failed to initialize OCI storage - could not get namespace: " + e.getMessage(), e); + } + + logger.info("Initialized OciStorageService, namespace={}, authType={}", + namespace, config.getAuthType()); + } + + private BasicAuthenticationDetailsProvider resolveAuth(StorageConfig config) { + switch (config.getAuthType()) { + case ACCESS_KEY: + // For API Key auth, storageKey is the user OCID, storageSecret is the private key PEM + // This requires additional config values typically set via environment or system properties + String tenancyId = System.getenv().getOrDefault("OCI_TENANCY_ID", + System.getProperty("oci.tenancy.id", "")); + String userId = config.getStorageKey(); + String fingerprint = System.getenv().getOrDefault("OCI_FINGERPRINT", + System.getProperty("oci.fingerprint", "")); + String privateKey = config.getStorageSecret(); + String region = config.getRegion() != null ? config.getRegion() : "us-ashburn-1"; + + return SimpleAuthenticationDetailsProvider.builder() + .tenantId(tenancyId) + .userId(userId) + .fingerprint(fingerprint) + .privateKeySupplier(() -> new ByteArrayInputStream(privateKey.getBytes())) + .region(Region.fromRegionId(region)) + .build(); + + case OIDC: + case IAM: + case IAM_ROLE: + case INSTANCE_PROFILE: + return InstancePrincipalsAuthenticationDetailsProvider.builder().build(); + + default: + throw new StorageServiceException("Unsupported auth type: " + config.getAuthType()); + } + } + + @Override + protected void ensureContainerExists(String container) { + try { + objectStorageClient.headBucket(HeadBucketRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .build()); + } catch (com.oracle.bmc.model.BmcException e) { + if (e.getStatusCode() == 404) { + try { + objectStorageClient.createBucket(CreateBucketRequest.builder() + .namespaceName(namespace) + .createBucketDetails(CreateBucketDetails.builder() + .name(container) + .compartmentId(System.getenv().getOrDefault( + "OCI_COMPARTMENT_ID", + System.getProperty("oci.compartment.id", ""))) + .build()) + .build()); + logger.info("Created bucket: {}", container); + } catch (Exception ex) { + logger.debug("Bucket creation for {}: {}", container, ex.getMessage()); + } + } + } + } + + @Override + protected String putObject(String container, String objectKey, File file) { + try { + String contentType = tika.detect(file); + byte[] data = Files.readAllBytes(file.toPath()); + String md5 = computeMd5Base64(data); + + PutObjectRequest request = PutObjectRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .objectName(objectKey) + .contentType(contentType) + .contentLength((long) data.length) + .contentMD5(md5) + .putObjectBody(new ByteArrayInputStream(data)) + .build(); + objectStorageClient.putObject(request); + return buildObjectUri(container, objectKey); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to put object from file: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String putObject(String container, String objectKey, byte[] content) { + try { + String md5 = computeMd5Base64(content); + PutObjectRequest request = PutObjectRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .objectName(objectKey) + .contentLength((long) content.length) + .contentMD5(md5) + .putObjectBody(new ByteArrayInputStream(content)) + .build(); + objectStorageClient.putObject(request); + return buildObjectUri(container, objectKey); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to put object from bytes: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected InputStream getObjectStream(String container, String objectKey) { + try { + GetObjectResponse response = objectStorageClient.getObject( + GetObjectRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .objectName(objectKey) + .build()); + return response.getInputStream(); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object stream: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getObjectUri(String container, String objectKey) { + return buildObjectUri(container, objectKey); + } + + @Override + protected BlobDetail getObjectDetail(String container, String objectKey) { + try { + HeadObjectResponse response = objectStorageClient.headObject( + HeadObjectRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .objectName(objectKey) + .build()); + + Map metadata = new HashMap<>(); + if (response.getOpcMeta() != null) { + metadata.putAll(response.getOpcMeta()); + } + metadata.put("uri", buildObjectUri(container, objectKey)); + metadata.put("Content-Type", response.getContentType()); + metadata.put("ETag", response.getETag()); + + long contentLength = response.getContentLength() != null ? response.getContentLength() : 0L; + Date lastModified = response.getLastModified() != null + ? Date.from(response.getLastModified().toInstant()) + : null; + + return new BlobDetail(objectKey, contentLength, lastModified, metadata, + buildObjectUri(container, objectKey)); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to get object detail: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected List listKeys(String container, String prefix) { + try { + List keys = new ArrayList<>(); + String nextStart = null; + + do { + ListObjectsRequest.Builder requestBuilder = ListObjectsRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .prefix(prefix); + if (nextStart != null) { + requestBuilder.start(nextStart); + } + + ListObjectsResponse response = objectStorageClient.listObjects(requestBuilder.build()); + ListObjects listObjects = response.getListObjects(); + for (ObjectSummary summary : listObjects.getObjects()) { + keys.add(summary.getName()); + } + nextStart = listObjects.getNextStartWith(); + } while (nextStart != null); + + return keys; + } catch (Exception e) { + throw new StorageServiceException( + "Failed to list keys with prefix: " + prefix + " - " + e.getMessage(), e); + } + } + + @Override + protected void removeObjects(String container, List objectKeys) { + try { + for (String key : objectKeys) { + objectStorageClient.deleteObject(DeleteObjectRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .objectName(key) + .build()); + } + } catch (Exception e) { + throw new StorageServiceException( + "Failed to remove objects: " + e.getMessage(), e); + } + } + + @Override + protected void copyObject(String fromContainer, String fromKey, + String toContainer, String toKey) { + try { + objectStorageClient.copyObject(CopyObjectRequest.builder() + .namespaceName(namespace) + .bucketName(fromContainer) + .copyObjectDetails(com.oracle.bmc.objectstorage.model.CopyObjectDetails.builder() + .sourceObjectName(fromKey) + .destinationNamespace(namespace) + .destinationBucket(toContainer) + .destinationObjectName(toKey) + .destinationRegion(config.getRegion() != null ? config.getRegion() : "us-ashburn-1") + .build()) + .build()); + } catch (Exception e) { + throw new StorageServiceException( + "Failed to copy object from " + fromContainer + "/" + fromKey + + " to " + toContainer + "/" + toKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String generateSignedGetUrl(String container, String objectKey, int ttlSeconds) { + return generatePreauthenticatedRequest(container, objectKey, ttlSeconds, + CreatePreauthenticatedRequestDetails.AccessType.ObjectRead); + } + + @Override + protected String generateSignedPutUrl(String container, String objectKey, + int ttlSeconds, String contentType, + Map additionalParams) { + return generatePreauthenticatedRequest(container, objectKey, ttlSeconds, + CreatePreauthenticatedRequestDetails.AccessType.ObjectWrite); + } + + private String generatePreauthenticatedRequest(String container, String objectKey, + int ttlSeconds, + CreatePreauthenticatedRequestDetails.AccessType accessType) { + try { + Date expiry = Date.from(java.time.Instant.now().plusSeconds(ttlSeconds)); + + CreatePreauthenticatedRequestDetails details = CreatePreauthenticatedRequestDetails.builder() + .name("par-" + objectKey + "-" + System.currentTimeMillis()) + .objectName(objectKey) + .accessType(accessType) + .timeExpires(expiry) + .build(); + + CreatePreauthenticatedRequestResponse response = + objectStorageClient.createPreauthenticatedRequest( + CreatePreauthenticatedRequestRequest.builder() + .namespaceName(namespace) + .bucketName(container) + .createPreauthenticatedRequestDetails(details) + .build()); + + String accessUri = response.getPreauthenticatedRequest().getAccessUri(); + String endpoint = config.getEndPoint() != null ? config.getEndPoint() + : "https://objectstorage." + (config.getRegion() != null ? config.getRegion() : "us-ashburn-1") + + ".oraclecloud.com"; + return endpoint + accessUri; + } catch (Exception e) { + throw new StorageServiceException( + "Failed to generate pre-authenticated request for: " + objectKey + " - " + e.getMessage(), e); + } + } + + @Override + protected String getHdfsPrefix(String container) { + return "oci://" + container + "@" + namespace + "/"; + } + + @Override + public void close() { + try { + objectStorageClient.close(); + } catch (Exception e) { + logger.warn("Error closing OCI ObjectStorageClient", e); + } + } + + private String buildObjectUri(String container, String objectKey) { + String endpoint = config.getEndPoint(); + if (endpoint != null && !endpoint.isEmpty()) { + String base = endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint; + return base + "/n/" + namespace + "/b/" + container + "/o/" + objectKey; + } + String region = config.getRegion() != null ? config.getRegion() : "us-ashburn-1"; + return "https://objectstorage." + region + ".oraclecloud.com/n/" + + namespace + "/b/" + container + "/o/" + objectKey; + } + + private String computeMd5Base64(byte[] content) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(content); + return Base64.getEncoder().encodeToString(md.digest()); + } catch (Exception e) { + throw new StorageServiceException("Failed to compute MD5: " + e.getMessage(), e); + } + } +} diff --git a/cloud-storage-sdk-oci/src/main/java/org/sunbird/cloud/storage/service/oci/OciStorageServiceProvider.java b/cloud-storage-sdk-oci/src/main/java/org/sunbird/cloud/storage/service/oci/OciStorageServiceProvider.java new file mode 100644 index 0000000..b28801e --- /dev/null +++ b/cloud-storage-sdk-oci/src/main/java/org/sunbird/cloud/storage/service/oci/OciStorageServiceProvider.java @@ -0,0 +1,21 @@ +package org.sunbird.cloud.storage.service.oci; + +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; +import org.sunbird.cloud.storage.StorageServiceProvider; + +/** + * SPI provider for Oracle Cloud Infrastructure Object Storage. + */ +public class OciStorageServiceProvider implements StorageServiceProvider { + + @Override + public boolean supports(StorageConfig.StorageType type) { + return type == StorageConfig.StorageType.OCI; + } + + @Override + public IStorageService create(StorageConfig config) { + return new OciStorageService(config); + } +} diff --git a/cloud-storage-sdk-oci/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider b/cloud-storage-sdk-oci/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider new file mode 100644 index 0000000..dc9c44b --- /dev/null +++ b/cloud-storage-sdk-oci/src/main/resources/META-INF/services/org.sunbird.cloud.storage.StorageServiceProvider @@ -0,0 +1 @@ +org.sunbird.cloud.storage.service.oci.OciStorageServiceProvider diff --git a/cloud-storage-sdk-oci/src/test/java/org/sunbird/cloud/storage/service/oci/OciStorageServiceTest.java b/cloud-storage-sdk-oci/src/test/java/org/sunbird/cloud/storage/service/oci/OciStorageServiceTest.java new file mode 100644 index 0000000..cd19029 --- /dev/null +++ b/cloud-storage-sdk-oci/src/test/java/org/sunbird/cloud/storage/service/oci/OciStorageServiceTest.java @@ -0,0 +1,89 @@ +package org.sunbird.cloud.storage.service.oci; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.sunbird.cloud.storage.BaseStorageServiceIntegrationTest; +import org.sunbird.cloud.storage.IStorageService; +import org.sunbird.cloud.storage.StorageConfig; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Oracle Cloud Infrastructure Object Storage integration tests. + * + *

Tests are skipped by default and only execute when the following environment + * variables are set:

+ *
    + *
  • {@code OCI_STORAGE_TEST_ENABLED} — any non-null value enables the tests
  • + *
  • {@code OCI_STORAGE_TEST_CONTAINER} — name of a pre-existing, writable OCI bucket
  • + *
+ * + *

Authentication is determined by {@code OCI_STORAGE_TEST_AUTH_TYPE}:

+ *
    + *
  • {@code ACCESS_KEY} (default) — API Key auth; requires: + *
      + *
    • {@code OCI_TENANCY_ID} — tenancy OCID
    • + *
    • {@code OCI_USER_ID} — user OCID (mapped to storageKey)
    • + *
    • {@code OCI_FINGERPRINT} — key fingerprint
    • + *
    • {@code OCI_PRIVATE_KEY_PEM} — PEM content of the private key (mapped to storageSecret)
    • + *
    • {@code OCI_REGION} — OCI region identifier (e.g. {@code us-ashburn-1})
    • + *
    + *
  • + *
  • {@code INSTANCE_PROFILE} / {@code IAM} — uses Instance Principals + * (for OCI Compute instances or OKE pods)
  • + *
+ */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OciStorageServiceTest extends BaseStorageServiceIntegrationTest { + + @Override + protected IStorageService createService() { + assumeTrue(System.getenv("OCI_STORAGE_TEST_ENABLED") != null, + "Skipping OCI integration tests: set OCI_STORAGE_TEST_ENABLED=true to enable"); + + String authTypeStr = getEnv("OCI_STORAGE_TEST_AUTH_TYPE", "ACCESS_KEY"); + StorageConfig.AuthType authType = StorageConfig.AuthType.valueOf(authTypeStr.toUpperCase()); + + StorageConfig.Builder builder = StorageConfig.builder(StorageConfig.StorageType.OCI) + .authType(authType) + .region(getEnv("OCI_REGION", "us-ashburn-1")); + + if (authType == StorageConfig.AuthType.ACCESS_KEY) { + String userId = System.getenv("OCI_USER_ID"); + String privateKeyPem = System.getenv("OCI_PRIVATE_KEY_PEM"); + + assumeTrue(userId != null && !userId.isBlank(), + "Skipping: OCI_USER_ID must be set for ACCESS_KEY auth"); + assumeTrue(privateKeyPem != null && !privateKeyPem.isBlank(), + "Skipping: OCI_PRIVATE_KEY_PEM must contain the private key PEM for ACCESS_KEY auth"); + assumeTrue(System.getenv("OCI_TENANCY_ID") != null, + "Skipping: OCI_TENANCY_ID must be set for ACCESS_KEY auth"); + assumeTrue(System.getenv("OCI_FINGERPRINT") != null, + "Skipping: OCI_FINGERPRINT must be set for ACCESS_KEY auth"); + + // storageKey = user OCID, storageSecret = PEM content (as expected by OciStorageService) + builder.storageKey(userId).storageSecret(privateKeyPem); + } + + return new OciStorageService(builder.build()); + } + + @Override + protected String getContainer() { + String container = System.getenv("OCI_STORAGE_TEST_CONTAINER"); + assumeTrue(container != null && !container.isBlank(), + "Skipping: OCI_STORAGE_TEST_CONTAINER must specify a pre-existing writable bucket"); + return container; + } + + @BeforeAll + static void checkOciEnabled() { + assumeTrue(System.getenv("OCI_STORAGE_TEST_ENABLED") != null, + "Skipping OCI integration tests: set OCI_STORAGE_TEST_ENABLED=true to enable"); + } + + private static String getEnv(String name, String defaultValue) { + String value = System.getenv(name); + return (value != null && !value.isBlank()) ? value : defaultValue; + } +} diff --git a/pom.xml b/pom.xml index da71cc7..4bf20b2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,214 +1,290 @@ - + 4.0.0 org.sunbird - cloud-store-sdk_2.12 - 1.4.6 - jar - Cloud Store SDK - cloud-store-sdk provides client APIs to handle cloud store (AWS S3, Azure Blobstore, GCP, OCI). + cloud-storage-sdk-parent + 2.0.2 + pom + Cloud Storage SDK Parent + Multi-module cloud storage SDK providing CSP-specific implementations using official cloud SDKs. https://www.sunbird.org/ + - 1.7 - 1.7 + 11 + 11 + 11 UTF-8 - 1.1.1 - 2.12 - 2.12.11 + + + 2.25.0 + 12.25.0 + 1.11.0 + + 1.11.0 + 2.30.0 + 3.30.0 + 2.9.0 + 2.15.3 + 2.0.9 + 5.10.1 + 5.8.0 + + + cloud-storage-sdk-api + cloud-storage-sdk-aws + cloud-storage-sdk-azure + cloud-storage-sdk-gcp + cloud-storage-sdk-oci + + - - MIT License - http://www.opensource.org/licenses/mit-license.php - - - - - Sunbird - info@sunbird.org - Sunbird - https://www.sunbird.org/ - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - scm:git:git://github.com/project-sunbird/sunbird-cloud-storage-sdk.git - scm:git:ssh://github.com/project-sunbird/sunbird-cloud-storage-sdk.git - https://github.com/project-sunbird/sunbird-cloud-storage-sdk/tree/master - - - - org.scala-lang - scala-library - ${scala.version} - provided - - - com.google.guava - guava - 19.0 - - - junit - junit - 3.8.1 - test - - - org.apache.jclouds - jclouds-all - 2.2.1 - - - com.microsoft.azure - azure-storage - 5.0.0 - - - org.apache.hadoop - hadoop-azure - 2.7.3 - - - com.typesafe - config - 1.3.0 - - - joda-time - joda-time - 2.8.1 - - - org.scalatest - scalatest_${scala.maj.version} - 3.0.8 - test - - - com.fasterxml.jackson.core - jackson-databind - 2.7.8 - - - com.fasterxml.jackson.module - jackson-module-scala_${scala.maj.version}.0-RC1 - 2.7.8 - - - org.apache.tika - tika-core - 1.18 - - + + MIT License + http://www.opensource.org/licenses/mit-license.php + + + + + Sunbird + info@sunbird.org + Sunbird + https://www.sunbird.org/ + + + + scm:git:git://github.com/project-sunbird/sunbird-cloud-storage-sdk.git + scm:git:ssh://github.com/project-sunbird/sunbird-cloud-storage-sdk.git + https://github.com/project-sunbird/sunbird-cloud-storage-sdk/tree/master + + + + + central + https://repo1.maven.org/maven2 + + + + + central + https://repo1.maven.org/maven2 + + + + + + + + org.sunbird + cloud-storage-sdk-api + ${project.version} + + + org.sunbird + cloud-storage-sdk-api + ${project.version} + tests + test + + + + + software.amazon.awssdk + s3 + ${aws.sdk.version} + + + software.amazon.awssdk + sts + ${aws.sdk.version} + + + + + org.apache.tika + tika-core + ${tika.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + com.azure + azure-storage-blob + ${azure.storage.blob.version} + + + com.azure + azure-identity + ${azure.identity.version} + + + com.azure + azure-core-http-okhttp + ${azure.core.http.okhttp.version} + + + + + com.google.cloud + google-cloud-storage + ${gcp.storage.version} + + + + + com.oracle.oci.sdk + oci-java-sdk-objectstorage + ${oci.sdk.version} + + + com.oracle.oci.sdk + oci-java-sdk-common-httpclient-jersey3 + ${oci.sdk.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + + - src/main/scala - src/test/scala - - - - net.alchim31.maven - scala-maven-plugin - 3.2.2 - - - - compile - testCompile - - - - -dependencyfile - ${project.build.directory}/.scala_dependencies - - - - - attach-javadocs - - doc-jar - - - - - - org.scalatest - scalatest-maven-plugin - 1.0 - - - test - test - - test - - - - - - org.scoverage - scoverage-maven-plugin - ${scoverage.plugin.version} - - ${scala.version} - true - true - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - deploy - - sign - - - - - --pinentry-mode - loopback - - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - - - - - - + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${java.version} + ${java.version} + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.3 + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.1.0 + + + sign-artifacts + deploy + + sign + + + + --pinentry-mode + loopback + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 + true + + central + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + + release + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.sonatype.central + central-publishing-maven-plugin + + + + + + diff --git a/src/main/scala/org/sunbird/cloud/storage/BaseStorageService.scala b/src/main/scala/org/sunbird/cloud/storage/BaseStorageService.scala deleted file mode 100644 index 8743e8f..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/BaseStorageService.scala +++ /dev/null @@ -1,330 +0,0 @@ -package org.sunbird.cloud.storage - -import org.jclouds.blobstore._ -import com.google.common.io._ -import java.io._ - -import org.apache.commons.io.FilenameUtils -import org.apache.tika.Tika -import org.sunbird.cloud.storage.exception.StorageServiceException -import org.sunbird.cloud.storage.util.{CommonUtil, JSONUtils} - -import collection.JavaConverters._ -import org.jclouds.blobstore.options.ListContainerOptions.Builder.{afterMarker, prefix, recursive} -import org.sunbird.cloud.storage.Model.Blob -import org.jclouds.blobstore.options.{CopyOptions, PutOptions} -import org.sunbird.cloud.storage.conf.AppConf - -import scala.collection.mutable.ListBuffer -import scala.concurrent.{ExecutionContext, Future} - -trait BaseStorageService extends IStorageService { - - var context: BlobStoreContext - var blobStore: BlobStore - var maxRetries: Int = 2 - var maxSignedurlTTL: Int = 604800 - var attempt = 0 - var maxContentLength = 0 - val tika = new Tika() - - def filesList(file: File): List[File] = { - if (file.exists && file.isDirectory) { - val files = file.listFiles.filter(_.isDirectory).flatMap(f => filesList(f)).toList - files ++ file.listFiles.filter(_.isFile).toList; - } else if (file.exists && file.isFile) { - List[File](file) - } else { - List[File](); - } - } - - override def uploadFolder(container: String, file: String, objectKey: String, isPublic: Option[Boolean] = Option(false), ttl: Option[Int] = None, retryCount: Option[Int] = None, attempt: Int = 1) (implicit execution: ExecutionContext) : Future[List[String]] = { - val d = new File(file) - val files = filesList(d) - val futures = files.map {f => - Future { - val key = objectKey + f.getAbsolutePath.split(d.getAbsolutePath + File.separator).last - upload(container, f.getAbsolutePath, key, Option(false), Option(attempt), retryCount, ttl) - } - }; - Future.sequence(futures); - } - - def createContainerInLocation(container: String): Unit = { - blobStore.createContainerInLocation(null, container) - } - - def putBlob(objectKey: String, file: File, container: String): Unit = { - val payload = Files.asByteSource(file) - val contentType = tika.detect(file) - val blob = blobStore.blobBuilder(objectKey).payload(payload).contentType(contentType).contentEncoding("UTF-8").contentLength(payload.size()).build() - blobStore.putBlob(container, blob, new PutOptions().multipart()) - } - - override def upload(container: String, file: String, objectKey: String, isDirectory: Option[Boolean] = Option(false), attempt: Option[Int] = Option(1), retryCount: Option[Int] = None, ttl: Option[Int] = None): String = { - try { - if(isDirectory.get) { - val d = new File(file) - val files = filesList(d) - val list = files.map {f => - val key = objectKey + f.getAbsolutePath.split(d.getAbsolutePath + File.separator).last - upload(container, f.getAbsolutePath, key, Option(false), attempt, retryCount, ttl) - } - list.mkString(",") - } - else { - if (attempt.getOrElse(1) >= retryCount.getOrElse(maxRetries)) { - val message = s"Failed to upload. file: $file, key: $objectKey, attempt: $attempt, maxAttempts: $retryCount. Exceeded maximum number of retries" - throw new StorageServiceException(message) - } - - createContainerInLocation(container) - val fileObj = new File(file) - putBlob(objectKey, fileObj, container) - if (ttl.isDefined) { - getSignedURL(container, objectKey, Option(ttl.get)) - } else - blobStore.blobMetadata(container, objectKey).getUri.toString - } - } - catch { - case e: Exception => { - e.printStackTrace() - Thread.sleep(attempt.getOrElse(1)*2000) - val uploadAttempt = attempt.getOrElse(1) + 1 - if (uploadAttempt <= retryCount.getOrElse(maxRetries)) { - upload(container, file, objectKey, isDirectory, Option(uploadAttempt), retryCount, ttl) - } else { - throw e; - } - } - } - } - - override def put(container: String, content: Array[Byte], objectKey: String, isPublic: Option[Boolean] = Option(false), isDirectory: Option[Boolean] = Option(false), ttl: Option[Int] = None, retryCount: Option[Int] = None): String = { - - try { - - if (attempt == retryCount.getOrElse(maxRetries)) { - val message = s"Failed to upload. key: $objectKey, attempt: $attempt, maxAttempts: $retryCount. Exceeded maximum number of retries" - throw new StorageServiceException(message) - } - - createContainerInLocation(container) - val blob = blobStore.blobBuilder(objectKey).payload(content).contentLength(content.length).build() - blobStore.putBlob(container, blob, new PutOptions().multipart()) - if(isPublic.get) { - getSignedURL(container, objectKey, Option(ttl.getOrElse(maxSignedurlTTL))) - } - else blobStore.getBlob(container, objectKey).getMetadata.getUri.toString - } - catch { - case e: Exception => { - Thread.sleep(attempt*2000) - attempt += 1 - put(container, content, objectKey, isPublic, isDirectory, ttl, retryCount) - } - } - } - - override def getSignedURL(container: String, objectKey: String, ttl: Option[Int] = None, permission: Option[String] = Option("r")): String = { - if (context.getBlobStore.toString.contains("google")) { - throw new StorageServiceException("getSignedURL method is not supported for GCP. Please use getPutSignedURL with contentType.", new Exception()) - } - else { - if (permission.getOrElse("").equalsIgnoreCase("w")) { - context.getSigner.signPutBlob(container, blobStore.blobBuilder(objectKey).forSigning().contentLength(maxContentLength).build(), ttl.getOrElse(maxSignedurlTTL).asInstanceOf[Number].longValue()).getEndpoint.toString - } else { - context.getSigner.signGetBlob(container, objectKey, ttl.getOrElse(maxSignedurlTTL)).getEndpoint.toString - } - } - } - - override def getSignedURLV2(container: String, objectKey: String, ttl: Option[Int] = None, permission: Option[String] = Option("r"), contentType: Option[String] = Option("text/plain")): String = { - if (context.getBlobStore.toString.contains("google")) { - getPutSignedURL(container, objectKey, Option(maxSignedurlTTL), None, contentType) - } else { - getSignedURL(container, objectKey, ttl, permission) - } - } - - def getPutSignedURL(container: String, objectKey: String, ttl: Option[Int] = None, permission: Option[String] = Option("r"), contentType: Option[String] = Option("text/plain")): String = { - if (permission.getOrElse("").equalsIgnoreCase("w")) { - context.getSigner.signPutBlob(container, blobStore.blobBuilder(objectKey).forSigning().contentLength(maxContentLength).contentType(contentType.get).build(), ttl.getOrElse(maxSignedurlTTL).asInstanceOf[Number].longValue()).getEndpoint.toString - } else { - context.getSigner.signGetBlob(container, objectKey, ttl.getOrElse(maxSignedurlTTL)).getEndpoint.toString - } - } - - override def download(container: String, objectKey: String, localPath: String, isDirectory: Option[Boolean] = Option(false)) = { - try { - if(isDirectory.get) { - val objects = listObjectKeys(container, objectKey) - for (obj <- objects) { - val file = FilenameUtils.getName(obj); - val fileObj = blobStore.getBlob(container, obj) - val downloadPath = localPath + FilenameUtils.getPath(obj).split("/").last + "/"; - CommonUtil.copyFile(fileObj.getPayload.getInput, downloadPath.replaceAll("//", "/"), file); - } - } - else { - val inStream = blobStore.getBlob(container, objectKey).getPayload.getInput - val fileName = objectKey.split("/").last - CommonUtil.copyFile(inStream, localPath, fileName); - } - } catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def deleteObject(container: String, objectKey: String, isDirectory: Option[Boolean] = Option(false)) = { - try { - deleteObjects(container, List((objectKey, isDirectory.get))) - } catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def deleteObjects(container: String, objectKeys: List[(String, Boolean)]) = { - try { - for (obj <- objectKeys) { - if(obj._2) { - val objList = blobStore.list(container, prefix(obj._1).recursive()).asScala.map(f => f.getName).toList - blobStore.removeBlobs(container, objList.asJavaCollection) - } - else { - blobStore.removeBlobs(container, List(obj._1).asJavaCollection) - } - } - } catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def getObject(container: String, objectKey: String, withPayload: Option[Boolean] = Option(false)): Blob = { - try { - val blob = blobStore.getBlob(container, objectKey) - val objData = blob.getMetadata - val metaData = JSONUtils.deserialize[Map[String, AnyRef]](JSONUtils.serialize(objData)) - val payload = if(withPayload.get) Option(blob.getPayload.getContentMetadata.getContentMD5AsHashCode.asBytes()) else None - Blob(objectKey, objData.getContentMetadata.getContentLength, objData.getLastModified, metaData, payload) - } catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def listObjects(container: String, prefix: String, withPayload: Option[Boolean] = Option(false)): List[Blob] = { - try { - val objects = listObjectKeys(container, prefix) - objects.map { obj => - getObject(container, obj, withPayload) - } - } - catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def listObjectKeys(container: String, _prefix: String): List[String] = { - val fileNames = ListBuffer[String]() - val containerOpts = recursive().prefix(_prefix) - var marker: Option[String] = None - do { - if (marker.exists(_.trim.nonEmpty)) containerOpts.afterMarker(marker.getOrElse("")) - val pageSet = blobStore.list(container, containerOpts) - fileNames ++= pageSet.asScala.map(f => f.getName).toList - marker = Option(pageSet.getNextMarker) - } while (marker.isDefined) - fileNames.toList - } - - override def searchObjects(container: String, prefix: String, fromDate: Option[String] = None, toDate: Option[String] = None, delta: Option[Int] = None, pattern: String = "yyyy-MM-dd"): List[Blob] = { - val from = if (delta.nonEmpty) CommonUtil.getStartDate(toDate, delta.get) else fromDate; - if (from.nonEmpty) { - val dates = CommonUtil.getDatesBetween(from.get, toDate, pattern) - val paths = for (date <- dates) yield { - listObjects(container, prefix + date) - } - paths.flatMap { x => x.map { x => x } }.toList - } else { - listObjects(container, prefix) - } - } - - override def searchObjectkeys(container: String, prefix: String, fromDate: Option[String] = None, toDate: Option[String] = None, delta: Option[Int] = None, pattern: String = "yyyy-MM-dd"): List[String] = { -// val objectList = searchObjects(container, prefix, fromDate, toDate, delta, pattern) -// getPaths(container, objectList); - val from = if (delta.nonEmpty) CommonUtil.getStartDate(toDate, delta.get) else fromDate; - if (from.nonEmpty) { - val dates = CommonUtil.getDatesBetween(from.get, toDate, pattern); - val paths = for (date <- dates) yield { - listObjectKeys(container, prefix + date) - } - paths.flatMap { x => x.map { x => x } }.toList - } else { - listObjectKeys(container, prefix) - } - } - - override def copyObjects(fromContainer: String, fromKey: String, toContainer: String, toKey: String, isDirectory: Option[Boolean] = Option(false)): Unit = { - if(isDirectory.get) { - val updatedFromKey = if(fromKey.endsWith("/")) fromKey else fromKey+"/" - val updatedToKey = if(toKey.endsWith("/")) toKey else toKey+"/" - val objectKeys = listObjectKeys(fromContainer, updatedFromKey) - for (obj <- objectKeys) { - val objName = obj.replace(updatedFromKey, "") - blobStore.copyBlob(fromContainer, obj, toContainer, updatedToKey+objName, CopyOptions.NONE) - } - } - else blobStore.copyBlob(fromContainer, fromKey, toContainer, toKey, CopyOptions.NONE) - } - - override def extractArchive(container: String, objectKey: String, toKey: String): Unit = { - try { - val localPath = AppConf.getConfig("local_extract_path") - download(container, objectKey, localPath, Option(false)) - val localFolder = localPath + "/" + toKey.split("/").last - CommonUtil.unZip(localPath + "/" + objectKey.split("/").last, localFolder) - upload(container, localFolder, toKey, Option(true)) - } - catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def getObjectData(container: String, objectKey: String): Array[String] = { - - try { - val inStream = blobStore.getBlob(container, objectKey).getPayload.openStream() - scala.io.Source.fromInputStream(inStream).getLines().toArray - } catch { - case e: Exception => - throw new StorageServiceException(e.getMessage, e) - } - } - - override def getUri(container: String, _prefix: String, isDirectory: Option[Boolean] = Option(false)): String = { - val keys = listObjectKeys(container, _prefix); - if (keys.isEmpty) - throw new StorageServiceException("The given _prefix is incorrect: " + _prefix) - val prefix = keys.head - val blob = getObject(container, prefix, Option(false)) - val uri = blob.metadata.get("uri") - if (!uri.isEmpty) { - uri.get.asInstanceOf[String].split(_prefix).head + _prefix - } else - throw new StorageServiceException("uri not available for the given prefix: "+ _prefix) - } - - def closeContext() = { - context.close() - } - -} diff --git a/src/main/scala/org/sunbird/cloud/storage/IStorageService.scala b/src/main/scala/org/sunbird/cloud/storage/IStorageService.scala deleted file mode 100644 index 3c8d450..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/IStorageService.scala +++ /dev/null @@ -1,213 +0,0 @@ -package org.sunbird.cloud.storage - -import org.sunbird.cloud.storage.Model.Blob - -import scala.concurrent.{ExecutionContext, Future} - -trait IStorageService { - - def uploadFolder(container: String, file: String, objectKey: String, isPublic: Option[Boolean] = Option(false), ttl: Option[Int] = None, retryCount: Option[Int] = None, attempt: Int = 1)(implicit execution: ExecutionContext) : Future[List[String]]; - - /** - * - * Upload a file/folder to cloud - * - * @param container String - The container/bucket to upload the file to. - * @param file String - The file path. - * @param objectKey String - The destination key/path to upload the file to. If the path exists it will be overwritten. - * @param isDirectory Option[Boolean] - Whether the file is a directory and need to upload folder recursively? - * @param attempt - * @param retryCount Option[Int] - Number of times the upload will be retried before failing. Defaults to global configuration "max.retries" - * @param ttl Option[Int] - The ttl/expiry for the file. Optional and default is never expires - * @return String - The url of the file/folder uploaded - */ - def upload(container: String, file: String, objectKey: String, isDirectory: Option[Boolean] = Option(false), attempt: Option[Int] = Option(1), retryCount: Option[Int] = None, ttl: Option[Int] = None): String - - - /** - * Put a blob in the cloud with the given content data. The difference between this and upload() method is that this method takes in byte array and sets is payload for the object. - * - * @param container String - The container/bucket to upload the file to. - * @param content String - The byte array of an object. - * @param objectKey String - The destination key/path to upload the file to. If the path exists it will be overwritten. - * @param isPublic Option[Boolean] - Whether the file should have public read access? Optional and defaults to false. - * @param isDirectory Option[Boolean] - Whether the file is a directory and need to upload folder recursively? Optional and defaults to false. - * @param ttl Option[Int] - The ttl/expiry for the file. Optional and default is never expires - * @param retryCount Option[Int] - Number of times the upload will be retried before failing. Defaults to "max.retries" defined in global configuration - * - * @return String - The url of the file/folder uploaded - */ - def put(container: String, content: Array[Byte], objectKey: String, isPublic: Option[Boolean] = Option(false), isDirectory: Option[Boolean] = Option(false), - ttl: Option[Int] = None, retryCount: Option[Int] = None): String - - /** - * Get pre-signed URL to access an object in the cloud store. - * - * @param container String - The container/bucket of the file - * @param objectKey String - The key/path of the file to pre-sign - * @param ttl Option[Int] - The ttl/expiry for the pre-signed URL. Defaults to "max.signedurl.ttl" defined in global configuration* - * @param permission String - The permission of pre-signed url the values are w (write), r (read). Defaults to "read". - * - * @return String - The pre-signed url - */ - def getSignedURL(container: String, objectKey: String, ttl: Option[Int] = None, permission: Option[String] = Option("r")): String - - /** - * Get pre-signed URL to access an object in the cloud store. - * - * @param container String - The container/bucket of the file - * @param objectKey String - The key/path of the file to pre-sign - * @param ttl Option[Int] - The ttl/expiry for the pre-signed URL. Defaults to "max.signedurl.ttl" defined in global configuration* - * @param permission String - The permission of pre-signed url the values are w (write), r (read). Defaults to "read". - * @param contentType String - The object type of the content. - * - * @return String - The pre-signed url - */ - def getSignedURLV2(container: String, objectKey: String, ttl: Option[Int] = None, permission: Option[String] = Option("r"), contentType: Option[String] = Option("text/plain")): String - - /** - * Download file/folder from cloud storage - * - * @param container String - The container/bucket of the file - * @param objectKey String - The key/path of the file to download from - * @param localPath String - The local destination path to download to - * @param isDirectory Option[Boolean] - Whether the file is a directory and need to be downloaded recursively? Optional and defaults to false. - */ - def download(container: String, objectKey: String, localPath: String, isDirectory: Option[Boolean] = Option(false)) - - /** - * Delete an object from the cloud store - * - * @param container String - The container/bucket of the object. - * @param objectKey String - The object key to delete. - * @param isDirectory Option[Boolean] - Whether the object is a directory and need to be deleted recursively? Optional and defaults to false. - */ - def deleteObject(container: String, objectKey: String, isDirectory: Option[Boolean] = Option(false)) - - /** - * Delete objects from the cloud store - * - * @param container String - The container/bucket of the objects - * @param objectKeys List[(String, Boolean)] - The objects to delete. The tuple contains the object key to be deleted and whether the object is a folder so that recursive delete is triggered. - */ - def deleteObjects(container: String, objectKeys: List[(String, Boolean)]) - - /** - * Copy objects from one container to another container or between different folders within the same container. This assumes that the credentials provided have access to both containers. - * - * @param fromContainer String - The container to copy the object from. - * @param fromKey String - The object prefix to copy from. - * @param toContainer String - The container to copy the object to. - * @param toKey String - The object prefix to copy to. - * @param isDirectory Option[Boolean] - Whether the copy is a file or folder? Defaults to false i.e copy one file. - */ - def copyObjects(fromContainer: String, fromKey: String, toContainer: String, toKey: String, isDirectory: Option[Boolean] = Option(false)) - - - /** - * Remote extract a archived file on cloud storage to a given folder within the same container. - * - * @param container String - The container/bucket of the archive file. - * @param objectKey String - The blob object archive file - * @param toKey String - The destination folder on the bucket/container to extract to. - */ - def extractArchive(container: String, objectKey: String, toKey: String) - - /** - * Get the blob object details - * - * @param container String - The container/bucket of the file - * @param objectKey String - The key/path of the blob object - * @param withPayload Option[Boolean] - Get payload as well while fetching details. Defaults to false. - * - * @return Blob - The blob object. - */ - def getObject(container: String, objectKey: String, withPayload: Option[Boolean] = Option(false)): Blob - - /** - * List objects from cloud storage for a given prefix. - * - * @param container String - The container/bucket - * @param prefix String - The object prefix to list objects. The prefix can be folder or pattern. - * @param withPayload Option[Boolean] - Does the listing of objects include payload as well? Defaults to false - * - * @return List[Blob] - The blob objects for the given prefix. - */ - def listObjects(container: String, prefix: String, withPayload: Option[Boolean] = Option(false)): List[Blob] - - /** - * List object keys from cloud storage for a given prefix. Similar to listObjects() - * - * @param container String - The container/bucket - * @param _prefix String - The object prefix to list objects. The prefix can be folder or pattern. - * - * @return List[Blob] - The blob objects for the given prefix. - */ - def listObjectKeys(container: String, _prefix: String): List[String] - - /** - * Search for objects for a given prefix and return only keys. Specifically used for telemetry files as the files are prefixed by sync date. - * By design the payload is not included in the search. If payload is required use getObject() method after the search is complete. - * - * // TODO: Provide examples of all combinations of fromDate, toDate and delta - * - * @param container String - The container/bucket. - * @param prefix String - The prefix to search on. - * @param fromDate Option[String] - The date to search from. Optional - * @param toDate Option[String] - The date to search to. Optional - * @param delta Option[Int] - The delta to search from given a from date or to date. Optional. If delta is provided and both fromDate and toDate are empty, the toDate will be defaulted to current date - * @param pattern String - The date pattern of from and to date. Defaulst to "yyyy-MM-dd" - * - * @return List[Blob] - The object keys - */ - def searchObjects(container: String, prefix: String, fromDate: Option[String] = None, toDate: Option[String] = None, delta: Option[Int] = None, - pattern: String = "yyyy-MM-dd"): List[Blob] - - /** - * Similar to searchObjects(). The only difference is that this method only returns the object keys instead of full blob objects. - * - * @param container String - The container/bucket. - * @param prefix String - The prefix to search on. - * @param fromDate Option[String] - The date to search from. Optional - * @param toDate Option[String] - The date to search to. Optional - * @param delta Option[Int] - The delta to search from given a from date or to date. Optional. If delta is provided and both fromDate and toDate are empty, the toDate will be defaulted to current date - * @param pattern String - The date pattern of from and to date. Defaulst to "yyyy-MM-dd" - * - * @return List[String] - The object keys - */ - def searchObjectkeys(container: String, prefix: String, fromDate: Option[String] = None, toDate: Option[String] = None, delta: Option[Int] = None, - pattern: String = "yyyy-MM-dd"): List[String] - - /** - * Get HDFS compatible file paths to be used in tech stack like Spark. - * For ex: for S3 the file path is prefixed with s3n:/// and for Azure blob storage it would be wasbs://@.blob.core.windows.net// - * - * @param container String - The container/bucket of the objects - * @param objects List[Blob] - The Blob objects in the given container - * - * @return List[String] - HDFS compatible file paths. - */ - def getPaths(container: String, objects: List[Blob]): List[String] - - /** - * Get the blob object data. - * - * @param container String - The container/bucket of the file - * @param objectKey String - The key/path of the blob object - * - * @return Array[String] - object data - */ - def getObjectData(container: String, objectKey: String): Array[String] - - - /** - * Get the URI of the given prefix - * - * @param container - * @param _prefix - * @param isDirectory - * @return String - URI of the given prefix - */ - def getUri(container: String, _prefix: String, isDirectory: Option[Boolean] = Option(false)): String - -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/Model.scala b/src/main/scala/org/sunbird/cloud/storage/Model.scala deleted file mode 100644 index 5f8739d..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/Model.scala +++ /dev/null @@ -1,8 +0,0 @@ -package org.sunbird.cloud.storage - -import java.util.Date - -object Model { - - case class Blob(key: String, contentLength: Long, lastModified: Date, metadata: Map[String, AnyRef], payload: Option[Array[Byte]]); -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/conf/AppConf.scala b/src/main/scala/org/sunbird/cloud/storage/conf/AppConf.scala deleted file mode 100644 index 7aa4394..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/conf/AppConf.scala +++ /dev/null @@ -1,38 +0,0 @@ -package org.sunbird.cloud.storage.conf - -import com.typesafe.config.ConfigFactory -import com.typesafe.config.Config - -object AppConf { - - lazy val defaultConf = ConfigFactory.load(); - lazy val envConf = ConfigFactory.systemEnvironment(); - lazy val conf = envConf.withFallback(defaultConf); - - def getConfig(key: String): String = { - if (conf.hasPath(key)) - conf.getString(key); - else ""; - } - - def getConfig: Config = conf - - - def getStorageType: String = getConfig("cloud_storage_type") - - def getStorageKey: String = getConfig("cloud_storage_key") - - def getStorageSecret: String = getConfig("cloud_storage_secret") - - def getRegion: Option[String] = { - if (getStorageType.equals("oci")) - Option(getConfig("cloud_storage_region")) - else Option("") - } - - def getEndPoint: Option[String] = { - if (getStorageType.equals("oci")) - Option(getConfig("cloud_storage_endpoint")) - else Option("") - } -} diff --git a/src/main/scala/org/sunbird/cloud/storage/conf/package.scala b/src/main/scala/org/sunbird/cloud/storage/conf/package.scala deleted file mode 100644 index 6d0bd94..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/conf/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.sunbird.cloud.storage -/** - * Provides the configuration handling APIs. - * @since 1.0 - */ -package object conf { -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/exception/StorageServiceException.scala b/src/main/scala/org/sunbird/cloud/storage/exception/StorageServiceException.scala deleted file mode 100644 index d13cef7..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/exception/StorageServiceException.scala +++ /dev/null @@ -1,5 +0,0 @@ -package org.sunbird.cloud.storage.exception - -class StorageServiceException(msg: String, ex: Exception = null) extends Exception(msg, ex) { - -} diff --git a/src/main/scala/org/sunbird/cloud/storage/exception/package.scala b/src/main/scala/org/sunbird/cloud/storage/exception/package.scala deleted file mode 100644 index 8606aab..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/exception/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.sunbird.cloud.storage -/** - * Provides the Exception implementation for cloud-storage sdk. - * @since 1.0 - */ -package object exception { -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/factory/StorageServiceFactory.scala b/src/main/scala/org/sunbird/cloud/storage/factory/StorageServiceFactory.scala deleted file mode 100644 index b0e18c0..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/factory/StorageServiceFactory.scala +++ /dev/null @@ -1,32 +0,0 @@ -package org.sunbird.cloud.storage.factory - -import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.cloud.storage.exception.StorageServiceException -import org.sunbird.cloud.storage.service.{AzureStorageService, CephS3StorageService, GcloudStorageService, OCIS3StorageService, S3StorageService} - -case class StorageConfig(`type`: String, storageKey: String, storageSecret: String, endPoint: Option[String] = None, region: Option[String] = Option("")) - -object StorageServiceFactory { - - /** - * Creates StorageService based on the given config - * @param config - * @return - */ - def getStorageService(config: StorageConfig): BaseStorageService = { - config.`type`.toLowerCase() match { - case "aws" => - new S3StorageService(config); - case "azure" => - new AzureStorageService(config); - case "cephs3" => - new CephS3StorageService(config); - case "gcloud" => - new GcloudStorageService(config); - case "oci" => - new OCIS3StorageService(config); - case _ => - throw new StorageServiceException("Unknown storage type found"); - } - } -} diff --git a/src/main/scala/org/sunbird/cloud/storage/factory/package.scala b/src/main/scala/org/sunbird/cloud/storage/factory/package.scala deleted file mode 100644 index 1791df4..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/factory/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.sunbird.cloud.storage -/** - * Provides the factory implementation of StorageService. - * @since 1.0 - */ -package object factory { -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/package.scala b/src/main/scala/org/sunbird/cloud/storage/package.scala deleted file mode 100644 index f79eb1c..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/package.scala +++ /dev/null @@ -1,8 +0,0 @@ -package org.sunbird.cloud -/** -* Provides the interface and base implementation of StorageService. -* @since 1.0 -* -**/ -package object storage { -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/service/AzureStorageService.scala b/src/main/scala/org/sunbird/cloud/storage/service/AzureStorageService.scala deleted file mode 100644 index 5fc428a..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/service/AzureStorageService.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.jclouds.ContextBuilder -import org.jclouds.blobstore.BlobStoreContext -import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.cloud.storage.Model.Blob -import org.sunbird.cloud.storage.factory.StorageConfig - -class AzureStorageService(config: StorageConfig) extends BaseStorageService { - - var context = ContextBuilder.newBuilder("azureblob").credentials(config.storageKey, config.storageSecret).buildView(classOf[BlobStoreContext]) - var blobStore = context.getBlobStore - - override def getPaths(container: String, objects: List[Blob]): List[String] = { - objects.map{f => "wasb://" + container + "@" + config.storageKey + ".blob.core.windows.net/" + f.key} - } -} diff --git a/src/main/scala/org/sunbird/cloud/storage/service/CephS3StorageService.scala b/src/main/scala/org/sunbird/cloud/storage/service/CephS3StorageService.scala deleted file mode 100644 index 05d9976..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/service/CephS3StorageService.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.jclouds.ContextBuilder -import org.jclouds.blobstore.BlobStoreContext -import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.cloud.storage.Model.Blob -import org.sunbird.cloud.storage.factory.StorageConfig - -import java.util.Properties - -class CephS3StorageService(config: StorageConfig) extends BaseStorageService { - - var context = ContextBuilder.newBuilder("s3").endpoint(config.endPoint.get).credentials(config.storageKey, config.storageSecret).buildView(classOf[BlobStoreContext]) - var blobStore = context.getBlobStore - - override def getPaths(container: String, objects: List[Blob]): List[String] = { - objects.map{f => "s3n://" + container + "/" + f.key} - } -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/service/GcloudStorageService.scala b/src/main/scala/org/sunbird/cloud/storage/service/GcloudStorageService.scala deleted file mode 100644 index c6cceed..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/service/GcloudStorageService.scala +++ /dev/null @@ -1,88 +0,0 @@ -package org.sunbird.cloud.storage.service - -import com.google.common.io.Files -import org.jclouds.ContextBuilder -import org.jclouds.blobstore.BlobStoreContext -import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.cloud.storage.Model.Blob -import org.sunbird.cloud.storage.exception.StorageServiceException -import org.sunbird.cloud.storage.factory.StorageConfig - -import java.io.File - -class GcloudStorageService(config: StorageConfig) extends BaseStorageService { - - var context = ContextBuilder.newBuilder("google-cloud-storage").credentials(config.storageKey, config.storageSecret).buildView(classOf[BlobStoreContext]) - var blobStore = context.getBlobStore - - override def getPaths(container: String, objects: List[Blob]): List[String] = { - objects.map{f => "gs://" + container + "/" + f.key} - } - - // Overriding upload methos since multipart upload not working for GCP - override def upload(container: String, file: String, objectKey: String, isDirectory: Option[Boolean] = Option(false), attempt: Option[Int] = Option(1), retryCount: Option[Int] = None, ttl: Option[Int] = None): String = { - try { - if(isDirectory.get) { - val d = new File(file) - val files = filesList(d) - val list = files.map {f => - val key = objectKey + f.getAbsolutePath.split(d.getAbsolutePath + File.separator).last - upload(container, f.getAbsolutePath, key, Option(false), attempt, retryCount, ttl) - } - list.mkString(",") - } - else { - if (attempt.getOrElse(1) >= retryCount.getOrElse(maxRetries)) { - val message = s"Failed to upload. file: $file, key: $objectKey, attempt: $attempt, maxAttempts: $retryCount. Exceeded maximum number of retries" - throw new StorageServiceException(message) - } - - blobStore.createContainerInLocation(null, container) - val fileObj = new File(file) - val payload = Files.asByteSource(fileObj) - val contentType = tika.detect(fileObj) - val blob = blobStore.blobBuilder(objectKey).payload(payload).contentType(contentType).contentEncoding("UTF-8").contentLength(payload.size()).build() - blobStore.putBlob(container, blob) - if (ttl.isDefined) { - getPutSignedURL(container, objectKey, Option(ttl.get), None, Option(contentType)) - } else { - val host = "https://storage.googleapis.com/" - val name = blobStore.blobMetadata(container, objectKey).getName - host + container + "/" + name - } - } - } - catch { - case e: Exception => { - e.printStackTrace() - Thread.sleep(attempt.getOrElse(1)*2000) - val uploadAttempt = attempt.getOrElse(1) + 1 - if (uploadAttempt <= retryCount.getOrElse(maxRetries)) { - upload(container, file, objectKey, isDirectory, Option(uploadAttempt), retryCount, ttl) - } else { - throw e; - } - } - } - } - - override def getUri(container: String, _prefix: String, isDirectory: Option[Boolean] = Option(false)): String = { - val keys = listObjectKeys(container, _prefix); - if (keys.isEmpty) - throw new StorageServiceException("The given _prefix is incorrect: " + _prefix) - val prefix = keys.head - val blob = getObject(container, prefix, Option(false)) - val uri = blob.metadata.get("publicUri") - if (!uri.isEmpty) { - if(isDirectory.get){ - throw new StorageServiceException("getUri for directory is not supported for GCP. The given _prefix is incorrect: " + _prefix) - } - else{ - val host = "https://storage.googleapis.com/" - val name = blob.metadata.get("name").get.asInstanceOf[String] - host + container + "/" + name - } - } else - throw new StorageServiceException("uri not available for the given prefix: "+ _prefix) - } -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/service/OCIS3StorageService.scala b/src/main/scala/org/sunbird/cloud/storage/service/OCIS3StorageService.scala deleted file mode 100644 index b64d9e7..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/service/OCIS3StorageService.scala +++ /dev/null @@ -1,59 +0,0 @@ -package org.sunbird.cloud.storage.service - -import com.google.common.io.Files -import com.google.common.hash -import org.jclouds.ContextBuilder -import org.jclouds.blobstore.BlobStoreContext - -import org.sunbird.cloud.storage.{BaseStorageService, Model} -import org.sunbird.cloud.storage.factory.StorageConfig - -import java.io.File -import java.util.Properties - -class OCIS3StorageService (config: StorageConfig) extends BaseStorageService { - - - val overrides = new Properties() - - overrides.setProperty("jclouds.provider", "s3") - overrides.setProperty("jclouds.endpoint", config.endPoint.get) - overrides.setProperty("jclouds.s3.virtual-host-buckets", "false") - overrides.setProperty("jclouds.strip-expect-header", "true") - overrides.setProperty("jclouds.regions", config.region.get) - overrides.setProperty("jclouds.s3.signer-version", "4") - - var context = ContextBuilder.newBuilder("aws-s3") - .credentials(config.storageKey, config.storageSecret) - .overrides(overrides) - .endpoint(config.endPoint.get).buildView(classOf[BlobStoreContext]) - var blobStore = context.getBlobStore - - /** - * Get HDFS compatible file paths to be used in tech stack like Spark. - * For ex: for S3 the file path is prefixed with s3n:/// and for Azure blob storage it would be wasbs://@.blob.core.windows.net// - * - * @param container String - The container/bucket of the objects - * @param objects List[Blob] - The Blob objects in the given container - * @return List[String] - HDFS compatible file paths. - */ - override def getPaths(container: String, objects: List[Model.Blob]): List[String] = { - objects.map{f => "s3n://" + container + "/" + f.key} - } - - override def createContainerInLocation(container: String): Unit = { - if (!( blobStore.containerExists(container))) { - blobStore.createContainerInLocation(null, container) - } - } - - override def putBlob(objectKey: String, file: File, container: String): Unit = { - val payload = Files.asByteSource(file) - val payloadSize = payload.size() - val payloadMD5 = payload.hash(hash.Hashing.md5()) - val contentType = tika.detect(file) - val blob = blobStore.blobBuilder(objectKey).payload(payload).contentType(contentType).contentEncoding("UTF-8").contentLength(payloadSize).contentMD5(payloadMD5).build() - blobStore.putBlob(container, blob) - } - -} diff --git a/src/main/scala/org/sunbird/cloud/storage/service/S3StorageService.scala b/src/main/scala/org/sunbird/cloud/storage/service/S3StorageService.scala deleted file mode 100644 index 00260c7..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/service/S3StorageService.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.jclouds.ContextBuilder -import org.jclouds.blobstore.BlobStoreContext -import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.cloud.storage.Model.Blob -import org.sunbird.cloud.storage.factory.StorageConfig - -class S3StorageService(config: StorageConfig) extends BaseStorageService { - - var context = ContextBuilder.newBuilder("aws-s3").credentials(config.storageKey, config.storageSecret).buildView(classOf[BlobStoreContext]) - var blobStore = context.getBlobStore - - override def getPaths(container: String, objects: List[Blob]): List[String] = { - objects.map{f => "s3n://" + container + "/" + f.key} - } -} diff --git a/src/main/scala/org/sunbird/cloud/storage/service/package.scala b/src/main/scala/org/sunbird/cloud/storage/service/package.scala deleted file mode 100644 index d660632..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/service/package.scala +++ /dev/null @@ -1,8 +0,0 @@ -package org.sunbird.cloud.storage -/** -* Provides the implementation of StorageService. -* @since 1.0 -* -**/ -package object service { -} \ No newline at end of file diff --git a/src/main/scala/org/sunbird/cloud/storage/util/CommonUtil.scala b/src/main/scala/org/sunbird/cloud/storage/util/CommonUtil.scala deleted file mode 100644 index f91c8f0..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/util/CommonUtil.scala +++ /dev/null @@ -1,98 +0,0 @@ -package org.sunbird.cloud.storage.util - -import org.joda.time.DateTime -import org.joda.time.DateTimeZone -import org.joda.time.Days -import org.joda.time.LocalDate -import org.joda.time.Weeks -import org.joda.time.Years -import org.joda.time.format.DateTimeFormat -import org.joda.time.format.DateTimeFormatter -import java.util.Date -import java.io.InputStream -import java.nio.file.Files -import java.nio.file.Paths -import java.nio.file.Paths.get -import java.nio.file.StandardCopyOption -import java.io.{File, FileInputStream, FileOutputStream, IOException} -import java.util.zip.{ZipEntry, ZipInputStream} - -import org.apache.commons.io.FilenameUtils -import org.apache.commons.lang3.StringUtils - -object CommonUtil { - - @transient val dateFormat: DateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZoneUTC(); - - def getStartDate(endDate: Option[String], delta: Int): Option[String] = { - val to = if (endDate.nonEmpty) dateFormat.parseLocalDate(endDate.get) else LocalDate.fromDateFields(new Date); - Option(to.minusDays(delta).toString()); - } - - def getDatesBetween(fromDate: String, toDate: Option[String], pattern: String): Array[String] = { - val df: DateTimeFormatter = DateTimeFormat.forPattern(pattern).withZoneUTC(); - val to = if (toDate.nonEmpty) df.parseLocalDate(toDate.get) else LocalDate.fromDateFields(new Date); - val from = df.parseLocalDate(fromDate); - val dates = datesBetween(from, to); - dates.map { x => df.print(x) }.toArray; - } - - def getDatesBetween(fromDate: String, toDate: Option[String]): Array[String] = { - getDatesBetween(fromDate, toDate, "yyyy-MM-dd"); - } - - def datesBetween(from: LocalDate, to: LocalDate): IndexedSeq[LocalDate] = { - val numberOfDays = Days.daysBetween(from, to).getDays() - for (f <- 0 to numberOfDays) yield from.plusDays(f) - } - - def createDirectory(dir: String) { - val path = get(dir); - Files.createDirectories(path); - } - - def copyFile(from: InputStream, path: String, fileName: String) = { - createDirectory(path); - Files.copy(from, Paths.get(path + fileName), StandardCopyOption.REPLACE_EXISTING); - } - - def unZip(zipFile: String, outputFolder: String): Unit = { - val buffer = new Array[Byte](1024) - try { - //output directory - val folder = new File(outputFolder); - if (!folder.exists()) { - folder.mkdir(); - } - //zip file content - val zis: ZipInputStream = new ZipInputStream(new FileInputStream(zipFile)); - //get the zipped file list entry - var ze: ZipEntry = zis.getNextEntry(); - - while (ze != null) { - val fileName = ze.getName(); - if (!(FilenameUtils.getName(fileName).startsWith(".") | fileName.startsWith("_") | StringUtils.isBlank(FilenameUtils.getName(fileName)))) { - val newFile = new File(outputFolder + File.separator + fileName); - //create folders - new File(newFile.getParent()).mkdirs(); - val fos = new FileOutputStream(newFile); - var len: Int = zis.read(buffer); - while (len > 0) { - fos.write(buffer, 0, len) - len = zis.read(buffer) - } - fos.close() - } - ze = zis.getNextEntry() - } - zis.closeEntry() - zis.close() - - } catch { - case e: IOException => - e.printStackTrace() - println("exception caught: " + e.getMessage) - } - - } -} diff --git a/src/main/scala/org/sunbird/cloud/storage/util/JSONUtils.scala b/src/main/scala/org/sunbird/cloud/storage/util/JSONUtils.scala deleted file mode 100644 index 9dc999e..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/util/JSONUtils.scala +++ /dev/null @@ -1,45 +0,0 @@ -package org.sunbird.cloud.storage.util - -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import com.fasterxml.jackson.core.`type`.TypeReference -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.fasterxml.jackson.annotation.JsonInclude.Include -import com.fasterxml.jackson.databind.SerializationFeature - -object JSONUtils { - - @transient val mapper = new ObjectMapper(); - mapper.registerModule(DefaultScalaModule); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - mapper.setSerializationInclusion(Include.NON_NULL); - - @throws(classOf[Exception]) - def serialize(obj: AnyRef): String = { - mapper.writeValueAsString(obj); - } - - @throws(classOf[Exception]) - def deserialize[T: Manifest](value: String): T = mapper.readValue(value, typeReference[T]); - - private[this] def typeReference[T: Manifest] = new TypeReference[T] { - override def getType = typeFromManifest(manifest[T]) - } - - - private[this] def typeFromManifest(m: Manifest[_]): Type = { - if (m.typeArguments.isEmpty) { m.runtimeClass } - // $COVERAGE-OFF$Disabling scoverage as this code is impossible to test - else new ParameterizedType { - def getRawType = m.runtimeClass - def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray - def getOwnerType = null - } - // $COVERAGE-ON$ - } - -} diff --git a/src/main/scala/org/sunbird/cloud/storage/util/package.scala b/src/main/scala/org/sunbird/cloud/storage/util/package.scala deleted file mode 100644 index b835b9d..0000000 --- a/src/main/scala/org/sunbird/cloud/storage/util/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.sunbird.cloud.storage -/** - * Provides utility APIs. - * @since 1.0 - */ -package object util { -} \ No newline at end of file diff --git a/src/test/resources/1234/part-00000 b/src/test/resources/1234/part-00000 deleted file mode 100644 index 40bc409..0000000 --- a/src/test/resources/1234/part-00000 +++ /dev/null @@ -1,286 +0,0 @@ -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:04.305+0530","ets":1462426984305,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:cover_sound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:28+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.699Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:11.891+0530","ets":1462426991891,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.699Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:12.103+0530","ets":1462426992103,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:scene1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.699Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:14.053+0530","ets":1462426994053,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.699Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:14.260+0530","ets":1462426994260,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.699Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:14.270+0530","ets":1462426994270,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:scene1_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.699Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:15.241+0530","ets":1462426995241,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:15.412+0530","ets":1462426995412,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:15.424+0530","ets":1462426995424,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:16.040+0530","ets":1462426996040,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:16.190+0530","ets":1462426996190,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:scene4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:16.198+0530","ets":1462426996198,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene4","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:16.973+0530","ets":1462426996973,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.700Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:17.308+0530","ets":1462426997308,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:scene5_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:17.318+0530","ets":1462426997318,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:scene4_sound","stageid":"scene5","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:18.028+0530","ets":1462426998028,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:18.175+0530","ets":1462426998175,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:scene6_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:18.185+0530","ets":1462426998185,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:scene5_sound","stageid":"scene6","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:18.918+0530","ets":1462426998918,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:19.091+0530","ets":1462426999091,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene7:scene7_sound","stageid":"scene7","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:19.099+0530","ets":1462426999099,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:scene6_sound","stageid":"scene7","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.701Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:19.864+0530","ets":1462426999864,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene7","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:20.044+0530","ets":1462427000044,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene8:scene8_sound","stageid":"scene8","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:20.050+0530","ets":1462427000050,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene7:scene7_sound","stageid":"scene8","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:23.734+0530","ets":1462427003734,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene8","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:23.897+0530","ets":1462427003897,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:scene9_sound","stageid":"scene9","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:23.908+0530","ets":1462427003908,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene8:scene8_sound","stageid":"scene9","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:24.755+0530","ets":1462427004755,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene9","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.702Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:24.923+0530","ets":1462427004923,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:scene10_sound","stageid":"scene10","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:24.932+0530","ets":1462427004932,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:scene9_sound","stageid":"scene10","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:25.661+0530","ets":1462427005661,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene10","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:25.827+0530","ets":1462427005827,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:scene11_sound","stageid":"scene11","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:25.836+0530","ets":1462427005836,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:scene10_sound","stageid":"scene11","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:26.637+0530","ets":1462427006637,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene11","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:26.795+0530","ets":1462427006795,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:scene12_sound","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:26.809+0530","ets":1462427006809,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:scene11_sound","stageid":"scene12","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.703Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:27.501+0530","ets":1462427007501,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.704Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:27.630+0530","ets":1462427007630,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene13:scene13_sound","stageid":"scene13","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.704Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:27.639+0530","ets":1462427007639,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:scene12_sound","stageid":"scene13","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.704Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:28.309+0530","ets":1462427008309,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene13","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.704Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:28.439+0530","ets":1462427008439,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14:scene14_sound","stageid":"scene14","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.704Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:28.445+0530","ets":1462427008445,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene13:scene13_sound","stageid":"scene14","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.704Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:29.091+0530","ets":1462427009091,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene14","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:29.238+0530","ets":1462427009238,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:scene15_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:29.245+0530","ets":1462427009245,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14:scene14_sound","stageid":"scene15","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:30.026+0530","ets":1462427010026,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:30.198+0530","ets":1462427010198,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene16:scene16_sound","stageid":"scene16","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:30.206+0530","ets":1462427010206,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:scene15_sound","stageid":"scene16","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:30.989+0530","ets":1462427010989,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene16","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.705Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:31.142+0530","ets":1462427011142,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene17:scene17_sound","stageid":"scene17","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:31.149+0530","ets":1462427011149,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene16:scene16_sound","stageid":"scene17","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:31.864+0530","ets":1462427011864,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene17","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:32.025+0530","ets":1462427012025,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene18:scene18_sound","stageid":"scene18","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:32.031+0530","ets":1462427012032,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene17:scene17_sound","stageid":"scene18","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:34.111+0530","ets":1462427014111,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene18","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:34.255+0530","ets":1462427014255,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene19:scene19_sound","stageid":"scene19","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.706Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:34.265+0530","ets":1462427014265,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene18:scene18_sound","stageid":"scene19","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:35.128+0530","ets":1462427015128,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene19","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:35.317+0530","ets":1462427015317,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene20:learning10_sound","stageid":"scene20","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:35.323+0530","ets":1462427015323,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene19:scene19_sound","stageid":"scene20","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:43.122+0530","ets":1462427023122,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene20","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:44.703+0530","ets":1462427024703,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene20","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:45.697+0530","ets":1462427025697,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene20","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.707Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:45.864+0530","ets":1462427025864,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene20:learning12_sound","stageid":"scene20","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.708Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:47.502+0530","ets":1462427027502,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene20","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.708Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:48.091+0530","ets":1462427028091,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene21","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.708Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:55.898+0530","ets":1462427035898,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:cover_sound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.708Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:59.441+0530","ets":1462427039441,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:cover_sound","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.709Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:13:59.918+0530","ets":1462427039918,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:cover_sound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.709Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:14:05.353+0530","ets":1462427045353,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:cover_sound","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.709Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:14:41.878+0530","ets":1462427081878,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:cover_sound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.710Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:14:48.214+0530","ets":1462427088214,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"splash_audio","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.710Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:14:48.258+0530","ets":1462427088258,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:splash_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.710Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:14:50.279+0530","ets":1462427090279,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.haircut","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previousContainer","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.710Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:32.743+0530","ets":1462427132743,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.711Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:36.731+0530","ets":1462427136731,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.711Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:37.053+0530","ets":1462427137053,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.711Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:37.068+0530","ets":1462427137068,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.711Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:47.194+0530","ets":1462427147194,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.711Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:47.351+0530","ets":1462427147351,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:47.360+0530","ets":1462427147360,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:49.287+0530","ets":1462427149287,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:49.416+0530","ets":1462427149416,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:15:49.426+0530","ets":1462427149426,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:09.959+0530","ets":1462427169959,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:10.067+0530","ets":1462427170067,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.712Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:10.488+0530","ets":1462427170488,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:11.989+0530","ets":1462427171989,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:12.469+0530","ets":1462427172469,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:12.609+0530","ets":1462427172609,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:13.070+0530","ets":1462427173070,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:13.380+0530","ets":1462427173380,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:13.504+0530","ets":1462427173504,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:13.519+0530","ets":1462427173519,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.713Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:15.040+0530","ets":1462427175040,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"scene1_audio","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:15.058+0530","ets":1462427175058,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PAUSE","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:17.189+0530","ets":1462427177189,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:17.353+0530","ets":1462427177353,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:17.370+0530","ets":1462427177370,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:34.007+0530","ets":1462427194007,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:34.019+0530","ets":1462427194019,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:34.580+0530","ets":1462427194580,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:34.750+0530","ets":1462427194750,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.714Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:34.758+0530","ets":1462427194758,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:36.072+0530","ets":1462427196072,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"scene1_audio","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:36.081+0530","ets":1462427196081,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PAUSE","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:37.581+0530","ets":1462427197581,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:37.930+0530","ets":1462427197930,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:38.174+0530","ets":1462427198174,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:39.711+0530","ets":1462427199711,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:40.171+0530","ets":1462427200171,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:45.760+0530","ets":1462427205760,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.715Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:45.940+0530","ets":1462427205940,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previousContainer","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.716Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:46.132+0530","ets":1462427206132,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.716Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:16:46.145+0530","ets":1462427206145,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.joy","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.716Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:01.281+0530","ets":1462427221281,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:splash_sound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.717Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:01.326+0530","ets":1462427221326,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:delayed_intro_sound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.717Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:07.760+0530","ets":1462427227760,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.717Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:07.977+0530","ets":1462427227977,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:hs_s1_dial_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.717Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:08.106+0530","ets":1462427228107,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:fair_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.717Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:08.118+0530","ets":1462427228118,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:delayed_intro_sound","stageid":"scene1","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.717Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:13.633+0530","ets":1462427233633,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:13.851+0530","ets":1462427233851,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:13.859+0530","ets":1462427233859,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:fair_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:17.386+0530","ets":1462427237386,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:17.631+0530","ets":1462427237631,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:17.696+0530","ets":1462427237696,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:wind_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:17.700+0530","ets":1462427237700,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"HIDE","extype":"","id":"flying_cap1","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:17.704+0530","ets":1462427237704,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"flying_cap2","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:29+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:17.726+0530","ets":1462427237726,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.718Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:21.753+0530","ets":1462427241753,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:21.927+0530","ets":1462427241927,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:21.935+0530","ets":1462427241935,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:29.392+0530","ets":1462427249392,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:29.545+0530","ets":1462427249545,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:29.561+0530","ets":1462427249561,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:wind_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:29.565+0530","ets":1462427249565,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"HIDE","extype":"","id":"flying_cap1","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:29.569+0530","ets":1462427249569,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"flying_cap2","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.719Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:29.584+0530","ets":1462427249584,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:30.785+0530","ets":1462427250785,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:30.964+0530","ets":1462427250964,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:30.974+0530","ets":1462427250974,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:30.981+0530","ets":1462427250981,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:wind_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:40.745+0530","ets":1462427260745,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:40.937+0530","ets":1462427260937,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:40.957+0530","ets":1462427260957,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:wind_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.720Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:40.994+0530","ets":1462427260994,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"HIDE","extype":"","id":"flying_cap1","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:40.997+0530","ets":1462427260997,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"flying_cap2","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:47.730+0530","ets":1462427267730,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:47.919+0530","ets":1462427267919,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:scene4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:48.111+0530","ets":1462427268111,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:leaves_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:48.119+0530","ets":1462427268119,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_sound","stageid":"scene4","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:52.574+0530","ets":1462427272574,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:52.769+0530","ets":1462427272769,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:scene5_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.721Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:52.845+0530","ets":1462427272845,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:crying_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:52.854+0530","ets":1462427272854,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:scene4_sound","stageid":"scene5","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:17:52.861+0530","ets":1462427272861,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:leaves_sound","stageid":"scene5","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:07.127+0530","ets":1462427287127,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:07.327+0530","ets":1462427287327,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:scene6_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:07.422+0530","ets":1462427287422,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:evening_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:15.389+0530","ets":1462427295389,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.722Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:15.572+0530","ets":1462427295572,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene7:scene7_sound","stageid":"scene7","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:22.414+0530","ets":1462427302414,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene7","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:22.644+0530","ets":1462427302644,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene8:scene8_sound","stageid":"scene8","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:22.658+0530","ets":1462427302658,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene7:scene7_sound","stageid":"scene8","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:31.837+0530","ets":1462427311837,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene8","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:32.042+0530","ets":1462427312042,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:scene9_sound","stageid":"scene9","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:32.296+0530","ets":1462427312297,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:outro_sound","stageid":"scene9","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.723Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:39.746+0530","ets":1462427319746,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene9","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:39.909+0530","ets":1462427319909,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:scene10_sound","stageid":"scene10","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:40.151+0530","ets":1462427320151,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:intro_sound","stageid":"scene10","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:40.180+0530","ets":1462427320180,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:outro_sound","stageid":"scene10","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:43.584+0530","ets":1462427323584,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene10","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:43.810+0530","ets":1462427323810,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:ft_1_sound","stageid":"scene11","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:43.817+0530","ets":1462427323817,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:intro_sound","stageid":"scene11","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.724Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:49.101+0530","ets":1462427329101,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_sun","stageid":"scene11","uri":"","subtype":"","pos":[{"x":190.82899475097656,"y":250.24398803710938},{"x":616.7849731445312,"y":420.0}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.725Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:50.489+0530","ets":1462427330489,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_moon","stageid":"scene11","uri":"","subtype":"","pos":[{"x":405.510986328125,"y":223.90199279785156},{"x":431.0679931640625,"y":427.02398681640625}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.725Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:54.182+0530","ets":1462427334182,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_tree","stageid":"scene11","uri":"","subtype":"","pos":[{"x":979.7000122070312,"y":365.85400390625},{"x":151.64100646972656,"y":446.927001953125}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.725Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:54.580+0530","ets":1462427334580,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene11","uri":"","subtype":"","pos":[{"x":644.0469970703125,"y":420.0},{"x":793.9829711914062,"y":449.26800537109375}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.725Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:18:57.630+0530","ets":1462427337630,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene11","uri":"","subtype":"","pos":[{"x":584.4130249023438,"y":218.63400268554688},{"x":824.6519775390625,"y":416.4880065917969}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.725Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:00.061+0530","ets":1462427340061,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_tree","stageid":"scene11","uri":"","subtype":"","pos":[{"x":811.02197265625,"y":301.7560119628906},{"x":182.3090057373047,"y":456.2929992675781}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:02.595+0530","ets":1462427342595,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene11","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:03.197+0530","ets":1462427343197,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:03.462+0530","ets":1462427343462,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene13:ft_4_sound","stageid":"scene13","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:05.521+0530","ets":1462427345521,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene13","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:14.098+0530","ets":1462427354098,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:14.262+0530","ets":1462427354262,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene13:ft_4_sound","stageid":"scene13","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.726Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:17.003+0530","ets":1462427357003,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene13","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.727Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:17.464+0530","ets":1462427357464,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.727Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:17.653+0530","ets":1462427357653,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:ft_1_sound","stageid":"scene11","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.727Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:19.547+0530","ets":1462427359547,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene11","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.727Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:19.721+0530","ets":1462427359721,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:ft_1_sound","stageid":"scene12","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.727Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:20.770+0530","ets":1462427360770,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.728Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:20.921+0530","ets":1462427360921,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:ft_1_sound","stageid":"scene11","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.728Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:23.758+0530","ets":1462427363758,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_tree","stageid":"scene11","uri":"","subtype":"","pos":[{"x":811.02197265625,"y":267.80499267578125},{"x":194.23599243164062,"y":429.95098876953125}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.728Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:25.825+0530","ets":1462427365825,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene11","uri":"","subtype":"","pos":[{"x":579.0,"y":276.0},{"x":831.468017578125,"y":445.7560119628906}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.728Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:27.689+0530","ets":1462427367689,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_sun","stageid":"scene11","uri":"","subtype":"","pos":[{"x":243.64700317382812,"y":230.17100524902344},{"x":642.343017578125,"y":415.3169860839844}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.728Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:29.319+0530","ets":1462427369319,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_moon","stageid":"scene11","uri":"","subtype":"","pos":[{"x":414.0299987792969,"y":273.65899658203125},{"x":419.1409912109375,"y":460.3900146484375}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:31.219+0530","ets":1462427371219,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene11","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:38.130+0530","ets":1462427378130,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ft_3_map_sound","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:38.177+0530","ets":1462427378177,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:ft_3_map_sound","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:38.356+0530","ets":1462427378356,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ft_3_map_sound","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:38.361+0530","ets":1462427378361,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:ft_3_map_sound","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:38.866+0530","ets":1462427378866,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ft_3_map_sound","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:38.869+0530","ets":1462427378869,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:ft_3_map_sound","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:42.123+0530","ets":1462427382123,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ft_3_noon_sound","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.729Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:42.176+0530","ets":1462427382176,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:ft_3_noon_sound","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:42.349+0530","ets":1462427382349,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ft_3_goon_sound","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:42.390+0530","ets":1462427382390,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:ft_3_goon_sound","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:44.566+0530","ets":1462427384566,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:44.832+0530","ets":1462427384832,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene13:ft_4_sound","stageid":"scene13","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:50.757+0530","ets":1462427390757,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene13","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:50.925+0530","ets":1462427390925,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_1:ws_3_sound","stageid":"scene14_1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:51.161+0530","ets":1462427391161,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene14_1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.730Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:51.302+0530","ets":1462427391302,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_sound1","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:51.312+0530","ets":1462427391312,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_1:ws_3_sound","stageid":"scene14_2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:54.356+0530","ets":1462427394356,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:54.675+0530","ets":1462427394675,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:54.679+0530","ets":1462427394679,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:54.685+0530","ets":1462427394685,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"glow_redcap","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:57.493+0530","ets":1462427397493,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:57.516+0530","ets":1462427397516,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.731Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:58.591+0530","ets":1462427398591,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:58.597+0530","ets":1462427398597,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:58.747+0530","ets":1462427398747,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:58.750+0530","ets":1462427398750,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:59.208+0530","ets":1462427399208,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:59.236+0530","ets":1462427399236,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:59.828+0530","ets":1462427399828,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:19:59.831+0530","ets":1462427399831,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.097+0530","ets":1462427400097,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.099+0530","ets":1462427400099,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.732Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.324+0530","ets":1462427400324,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.328+0530","ets":1462427400328,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.537+0530","ets":1462427400537,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.551+0530","ets":1462427400551,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.790+0530","ets":1462427400790,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:00.796+0530","ets":1462427400796,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:01.006+0530","ets":1462427401007,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:01.012+0530","ets":1462427401012,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:01.215+0530","ets":1462427401215,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.733Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:01.218+0530","ets":1462427401218,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:01.877+0530","ets":1462427401877,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:01.880+0530","ets":1462427401880,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:02.085+0530","ets":1462427402085,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:02.100+0530","ets":1462427402100,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:02.266+0530","ets":1462427402266,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:02.268+0530","ets":1462427402268,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_2:ws_3_redcap_sound","stageid":"scene14_2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:04.141+0530","ets":1462427404141,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene14_2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:04.322+0530","ets":1462427404322,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene14_3:ws_3_sound2","stageid":"scene14_3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.734Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:05.543+0530","ets":1462427405543,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene14_3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.735Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:05.873+0530","ets":1462427405873,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_8_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.735Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:14.593+0530","ets":1462427414593,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.735Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:14.822+0530","ets":1462427414822,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.735Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:16.296+0530","ets":1462427416296,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.735Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:16.404+0530","ets":1462427416404,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.736Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:17.835+0530","ets":1462427417835,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.736Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:19.979+0530","ets":1462427419979,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.736Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:21.723+0530","ets":1462427421723,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.736Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:27.521+0530","ets":1462427427521,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.736Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:27.713+0530","ets":1462427427713,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.736Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:31.164+0530","ets":1462427431164,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"A","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.737Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:37.270+0530","ets":1462427437270,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.737Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:37.427+0530","ets":1462427437427,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.737Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:40.303+0530","ets":1462427440303,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"U","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:30+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.737Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:51.643+0530","ets":1462427451643,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"U","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.738Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:53.143+0530","ets":1462427453143,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"U","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.738Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:56.651+0530","ets":1462427456651,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.738Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:20:56.937+0530","ets":1462427456937,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_5_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.738Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:01.997+0530","ets":1462427461997,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Fair","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.738Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:06.083+0530","ets":1462427466083,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.738Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:06.313+0530","ets":1462427466313,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_4_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:09.920+0530","ets":1462427469920,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"ws_red","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:15.332+0530","ets":1462427475332,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:15.585+0530","ets":1462427475585,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_7_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:18.498+0530","ets":1462427478498,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:18.818+0530","ets":1462427478818,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_4_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:20.393+0530","ets":1462427480393,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"ws_blue","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.739Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:22.987+0530","ets":1462427482987,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.740Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:23.066+0530","ets":1462427483066,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_7_sound","stageid":"scene15","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.740Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:23.214+0530","ets":1462427483214,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_7_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.740Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:30.303+0530","ets":1462427490303,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Bright","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.740Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:35.337+0530","ets":1462427495337,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Candy","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.740Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:37.601+0530","ets":1462427497601,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Bright","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.741Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:45.019+0530","ets":1462427505019,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Candy","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.741Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:52.389+0530","ets":1462427512389,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Strong","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.741Z"} diff --git a/src/test/resources/1234/part-00001 b/src/test/resources/1234/part-00001 deleted file mode 100644 index 03f45af..0000000 --- a/src/test/resources/1234/part-00001 +++ /dev/null @@ -1,277 +0,0 @@ -{"eid":"OE_INTERACT","ts":"2016-05-05T11:21:58.867+0530","ets":1462427518867,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Old","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.741Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:02.598+0530","ets":1462427522598,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.742Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:02.828+0530","ets":1462427522828,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.742Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:06.039+0530","ets":1462427526039,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"E","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.742Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:06.627+0530","ets":1462427526627,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"E","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.742Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:07.306+0530","ets":1462427527306,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"E","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.742Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:10.870+0530","ets":1462427530870,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.743Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:11.239+0530","ets":1462427531239,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_1_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.743Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:14.125+0530","ets":1462427534125,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.743Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:14.347+0530","ets":1462427534347,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.743Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:22.999+0530","ets":1462427542999,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"E","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.743Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:23.957+0530","ets":1462427543957,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"E","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.743Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:26.792+0530","ets":1462427546792,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.744Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:27.000+0530","ets":1462427547000,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_1_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.744Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:30.336+0530","ets":1462427550336,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_candy","stageid":"scene15","uri":"","subtype":"","pos":[{"x":509.4440002441406,"y":229.17100524902344},{"x":819.541015625,"y":407.12200927734375}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.744Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:33.246+0530","ets":1462427553246,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_fancyglasses","stageid":"scene15","uri":"","subtype":"","pos":[{"x":853.6170043945312,"y":274.8290100097656},{"x":224.90499877929688,"y":452.7799987792969}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.744Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:34.525+0530","ets":1462427554525,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene15","uri":"","subtype":"","pos":[{"x":193.25999450683594,"y":302.0060119628906},{"x":511.14801025390625,"y":459.80499267578125}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.744Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:38.804+0530","ets":1462427558804,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_fancyglasses","stageid":"scene15","uri":"","subtype":"","pos":[{"x":270.9079895019531,"y":420.5849914550781},{"x":845.0980224609375,"y":211.61000061035156}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.745Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:38.867+0530","ets":1462427558867,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_candy","stageid":"scene15","uri":"","subtype":"","pos":[{"x":479.4620056152344,"y":235.61000061035156},{"x":538.4089965820312,"y":235.61000061035156}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.745Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:39.682+0530","ets":1462427559682,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene15","uri":"","subtype":"","pos":[{"x":282.8349914550781,"y":211.61000061035156},{"x":250.46299743652344,"y":216.8780059814453}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.745Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:41.551+0530","ets":1462427561551,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene15","uri":"","subtype":"","pos":[{"x":836.5789794921875,"y":242.0489959716797},{"x":250.46299743652344,"y":216.8780059814453}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.745Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:42.425+0530","ets":1462427562425,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_fancyglasses","stageid":"scene15","uri":"","subtype":"","pos":[{"x":221.4969940185547,"y":317.5610046386719},{"x":231.72000122070312,"y":374.34100341796875}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.745Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:42.945+0530","ets":1462427562945,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_fancyglasses","stageid":"scene15","uri":"","subtype":"","pos":[{"x":157.02699279785156,"y":226.09300231933594},{"x":231.72000122070312,"y":374.34100341796875}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.746Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:43.302+0530","ets":1462427563302,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_fancyglasses","stageid":"scene15","uri":"","subtype":"","pos":[{"x":188.2729949951172,"y":202.53700256347656},{"x":185.7169952392578,"y":203.4149932861328}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.746Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:45.046+0530","ets":1462427565046,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"DROP","extype":"","id":"ws_bluecap","stageid":"scene15","uri":"","subtype":"","pos":[{"x":812.7249755859375,"y":259.6099853515625},{"x":511.14801025390625,"y":455.7070007324219}],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.746Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:47.473+0530","ets":1462427567473,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.746Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:47.717+0530","ets":1462427567717,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_6_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.746Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:52.393+0530","ets":1462427572393,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Pipal tree","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.746Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:59.106+0530","ets":1462427579106,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Moon","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.747Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:22:59.211+0530","ets":1462427579211,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Moon","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.747Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:00.285+0530","ets":1462427580285,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Banana tree","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.747Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:05.400+0530","ets":1462427585400,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.747Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:05.622+0530","ets":1462427585622,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:07.177+0530","ets":1462427587177,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:07.322+0530","ets":1462427587322,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_6_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:10.297+0530","ets":1462427590297,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Banana tree","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:13.477+0530","ets":1462427593477,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:13.564+0530","ets":1462427593564,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:13.717+0530","ets":1462427593717,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene15:ws_2_sound","stageid":"scene15","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.748Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:16.424+0530","ets":1462427596424,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"O","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.749Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:17.128+0530","ets":1462427597128,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"O","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.749Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:23.889+0530","ets":1462427603889,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene15","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.749Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:30.066+0530","ets":1462427610066,"ver":"2.0","gdata":{"id":"org.ekstep.lit.moon_cap.story","ver":"12"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"theme","stageid":"endScreen","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.749Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:49.425+0530","ets":1462427629425,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.750Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:57.544+0530","ets":1462427637544,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.750Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:23:57.872+0530","ets":1462427637872,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.750Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:05.603+0530","ets":1462427645603,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.750Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:05.838+0530","ets":1462427645838,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_audio","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.750Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:09.573+0530","ets":1462427649573,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.751Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:09.753+0530","ets":1462427649753,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.751Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:09.769+0530","ets":1462427649769,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.751Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:18.155+0530","ets":1462427658155,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.751Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:18.405+0530","ets":1462427658405,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.751Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:23.655+0530","ets":1462427663655,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.751Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:23.803+0530","ets":1462427663803,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:23.817+0530","ets":1462427663817,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:scene1_audio","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:28.372+0530","ets":1462427668372,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:28.607+0530","ets":1462427668607,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:scene3_audio","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:28.618+0530","ets":1462427668618,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:scene2_audio","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:38.075+0530","ets":1462427678075,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:38.299+0530","ets":1462427678299,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:scene4_audio","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.752Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:43.005+0530","ets":1462427683005,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.783Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:43.224+0530","ets":1462427683224,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:scene5_audio","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.783Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:43.232+0530","ets":1462427683232,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:scene4_audio","stageid":"scene5","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.784Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:49.640+0530","ets":1462427689640,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.784Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:49.893+0530","ets":1462427689893,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:scene6_audio","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.784Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:52.601+0530","ets":1462427692601,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.784Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:52.825+0530","ets":1462427692825,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene7:scene7_audio","stageid":"scene7","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.784Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:52.833+0530","ets":1462427692833,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:scene6_audio","stageid":"scene7","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.784Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:59.459+0530","ets":1462427699459,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene7","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:59.689+0530","ets":1462427699689,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene8:scene8_audio","stageid":"scene8","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:24:59.698+0530","ets":1462427699698,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene7:scene7_audio","stageid":"scene8","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:02.790+0530","ets":1462427702790,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene8","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:02.995+0530","ets":1462427702995,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:scene9_audio","stageid":"scene9","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:03.003+0530","ets":1462427703003,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene8:scene8_audio","stageid":"scene9","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:06.337+0530","ets":1462427706337,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene9","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:06.570+0530","ets":1462427706570,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:scene10_audio","stageid":"scene10","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.785Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:06.578+0530","ets":1462427706578,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene9:scene9_audio","stageid":"scene10","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:09.861+0530","ets":1462427709861,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene10","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:10.105+0530","ets":1462427710105,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:scene11_audio","stageid":"scene11","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:10.122+0530","ets":1462427710122,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene10:scene10_audio","stageid":"scene11","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:14.648+0530","ets":1462427714648,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene11","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:14.866+0530","ets":1462427714866,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene12:scene12_audio","stageid":"scene12","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:14.874+0530","ets":1462427714874,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene11:scene11_audio","stageid":"scene12","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.786Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:19.049+0530","ets":1462427719049,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene12","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.787Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:24.227+0530","ets":1462427724227,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.colours","ver":"1"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"theme","stageid":"scene13","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.787Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:57.576+0530","ets":1462427757576,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"activity","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.788Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:25:57.822+0530","ets":1462427757822,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:titleSound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.788Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:04.573+0530","ets":1462427764573,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.788Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:04.775+0530","ets":1462427764775,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.788Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:14.538+0530","ets":1462427774538,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.788Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:14.750+0530","ets":1462427774750,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.788Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:31.702+0530","ets":1462427791702,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:32.104+0530","ets":1462427792105,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:40.904+0530","ets":1462427800904,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:41.102+0530","ets":1462427801102,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:42.162+0530","ets":1462427802162,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:42.335+0530","ets":1462427802335,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:42.348+0530","ets":1462427802348,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene1","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.789Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:43.072+0530","ets":1462427803072,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:43.273+0530","ets":1462427803273,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:titleSound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:43.285+0530","ets":1462427803285,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"splash","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:55.795+0530","ets":1462427815795,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:26:55.979+0530","ets":1462427815979,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:04.213+0530","ets":1462427824213,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:04.393+0530","ets":1462427824393,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:04.418+0530","ets":1462427824418,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.790Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:21.576+0530","ets":1462427841576,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:21.805+0530","ets":1462427841805,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:22.373+0530","ets":1462427842373,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:22.638+0530","ets":1462427842638,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:22.653+0530","ets":1462427842653,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene4","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:27.458+0530","ets":1462427847458,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:27.698+0530","ets":1462427847698,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.791Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:27.717+0530","ets":1462427847717,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:28.733+0530","ets":1462427848733,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:28.932+0530","ets":1462427848932,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:28.948+0530","ets":1462427848948,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:36.506+0530","ets":1462427856506,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:36.868+0530","ets":1462427856868,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:36.886+0530","ets":1462427856886,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.792Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:44.504+0530","ets":1462427864504,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:44.729+0530","ets":1462427864729,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:51.771+0530","ets":1462427871771,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:52.007+0530","ets":1462427872007,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:page5_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:31+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:59.594+0530","ets":1462427879594,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:27:59.683+0530","ets":1462427879683,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:00.100+0530","ets":1462427880100,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:page6_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.793Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:05.977+0530","ets":1462427885977,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:08.452+0530","ets":1462427888452,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_replay_button","stageid":"endScreen","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:08.896+0530","ets":1462427888896,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:18.765+0530","ets":1462427898765,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:19.185+0530","ets":1462427899185,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:26.485+0530","ets":1462427906485,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:27.058+0530","ets":1462427907058,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.794Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:27.073+0530","ets":1462427907073,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:44.519+0530","ets":1462427924519,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:45.073+0530","ets":1462427925073,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:50.724+0530","ets":1462427930724,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:51.290+0530","ets":1462427931290,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:page5_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:28:59.575+0530","ets":1462427939575,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:00.129+0530","ets":1462427940129,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:page6_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.795Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:06.619+0530","ets":1462427946619,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:14.408+0530","ets":1462427954408,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"endScreen","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:17.770+0530","ets":1462427957770,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:38.132+0530","ets":1462427978132,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:42.423+0530","ets":1462427982423,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:44.326+0530","ets":1462427984326,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:45.925+0530","ets":1462427985925,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:51.195+0530","ets":1462427991195,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.796Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:29:52.041+0530","ets":1462427992041,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_replay_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.797Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:30:09.430+0530","ets":1462428009430,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.797Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:30:10.047+0530","ets":1462428010047,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.797Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:30:10.363+0530","ets":1462428010363,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.797Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:30:10.594+0530","ets":1462428010594,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.797Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:04.497+0530","ets":1462428064497,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"activity","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.798Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:04.748+0530","ets":1462428064748,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:titleSound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.798Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:07.612+0530","ets":1462428067612,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.798Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:08.456+0530","ets":1462428068456,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.798Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:08.476+0530","ets":1462428068476,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:titleSound","stageid":"scene1","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.799Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:31.106+0530","ets":1462428091106,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.799Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:31.890+0530","ets":1462428091890,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.799Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:33.242+0530","ets":1462428093242,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.799Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:34.185+0530","ets":1462428094185,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.799Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:34.204+0530","ets":1462428094204,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.799Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:35.299+0530","ets":1462428095299,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:35.886+0530","ets":1462428095886,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:35.907+0530","ets":1462428095907,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene4","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:37.000+0530","ets":1462428097000,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:37.569+0530","ets":1462428097569,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:page5_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:37.588+0530","ets":1462428097588,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene5","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:43.858+0530","ets":1462428103858,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:44.361+0530","ets":1462428104361,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:page6_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.800Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:44.373+0530","ets":1462428104373,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:page5_sound","stageid":"scene6","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.801Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:46.884+0530","ets":1462428106884,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next_button","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.801Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:48.615+0530","ets":1462428108615,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"endScreen","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.801Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:49.394+0530","ets":1462428109394,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.801Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:31:49.667+0530","ets":1462428109667,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:mtf_bear_sound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.801Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:00.052+0530","ets":1462428120052,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"bear","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.801Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:01.648+0530","ets":1462428121648,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"bear","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.802Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:02.493+0530","ets":1462428122493,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"bear","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.802Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:04.145+0530","ets":1462428124145,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:goodJobSound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.802Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:04.151+0530","ets":1462428124151,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"goodjobDialog","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.802Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:04.723+0530","ets":1462428124723,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:goodJobSound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.802Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:11.252+0530","ets":1462428131252,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:goodJobSound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:11.974+0530","ets":1462428131974,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:12.316+0530","ets":1462428132316,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:mtf_lion_sound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:12.661+0530","ets":1462428132661,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:12.987+0530","ets":1462428132987,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:mtf_yummy_sound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:17.896+0530","ets":1462428137896,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"enjoyingfood","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:19.326+0530","ets":1462428139326,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:19.585+0530","ets":1462428139585,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound1","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.803Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:29.833+0530","ets":1462428149833,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Saree","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:31.347+0530","ets":1462428151347,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:31.607+0530","ets":1462428151607,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound2","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:31.980+0530","ets":1462428151980,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:32.226+0530","ets":1462428152227,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound3","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:34.910+0530","ets":1462428154910,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:34.994+0530","ets":1462428154994,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound2","stageid":"worksheet","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:35.109+0530","ets":1462428155109,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound2","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:42.790+0530","ets":1462428162790,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Trumpet","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.804Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:45.452+0530","ets":1462428165452,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:goodJobSound","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:45.464+0530","ets":1462428165464,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"goodjobDialog","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:48.126+0530","ets":1462428168126,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:48.212+0530","ets":1462428168212,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound3","stageid":"worksheet","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:48.390+0530","ets":1462428168390,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound3","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:48.646+0530","ets":1462428168646,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:48.843+0530","ets":1462428168843,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet:fitb_sound4","stageid":"worksheet","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.805Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:58.333+0530","ets":1462428178333,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Jungle King","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.806Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:32:59.324+0530","ets":1462428179324,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.806Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:03.724+0530","ets":1462428183724,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"endscreen_forward_button","stageid":"endScreen2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.806Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:04.191+0530","ets":1462428184191,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:tickWord","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.806Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:07.968+0530","ets":1462428187968,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Lamp ","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.806Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:08.939+0530","ets":1462428188939,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.806Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:09.139+0530","ets":1462428189139,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:tickWord","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:11.077+0530","ets":1462428191077,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Celebrating","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:11.688+0530","ets":1462428191688,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:11.765+0530","ets":1462428191765,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:tickWord","stageid":"worksheet2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:11.917+0530","ets":1462428191917,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:tickWord","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:14.930+0530","ets":1462428194930,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Trumpet ","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:15.585+0530","ets":1462428195585,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:15.799+0530","ets":1462428195799,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:tickWord","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.807Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:23.483+0530","ets":1462428203483,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Played ","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.808Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:23.853+0530","ets":1462428203853,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Played ","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.808Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:26.657+0530","ets":1462428206657,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"Played ","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.808Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:27.523+0530","ets":1462428207523,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:goodJobSound","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.808Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:27.527+0530","ets":1462428207527,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"SHOW","extype":"","id":"goodjobDialog","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.808Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:31.051+0530","ets":1462428211051,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"goodjob_forward_button","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.808Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:31.501+0530","ets":1462428211501,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:38.487+0530","ets":1462428218487,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"P","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:40.289+0530","ets":1462428220289,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:40.524+0530","ets":1462428220524,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:51.665+0530","ets":1462428231665,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"P","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:56.887+0530","ets":1462428236887,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:57.097+0530","ets":1462428237097,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.809Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:57.642+0530","ets":1462428237642,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:57.705+0530","ets":1462428237705,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:33:57.846+0530","ets":1462428237846,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:00.278+0530","ets":1462428240278,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"previous","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:00.349+0530","ets":1462428240349,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:00.514+0530","ets":1462428240514,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:02.609+0530","ets":1462428242609,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"P","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:04.802+0530","ets":1462428244802,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.810Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:05.010+0530","ets":1462428245010,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"worksheet2:taponP","stageid":"worksheet2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.811Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:07.629+0530","ets":1462428247629,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"CHOOSE","extype":"","id":"P","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.811Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:09.748+0530","ets":1462428249748,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"worksheet2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.811Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:11.604+0530","ets":1462428251604,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"endScreen3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.811Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:28.832+0530","ets":1462428268832,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"activity","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.812Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:29.091+0530","ets":1462428269091,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:titleSound","stageid":"splash","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.812Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:29.865+0530","ets":1462428269865,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"splash","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.812Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:30.023+0530","ets":1462428270023,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene1","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.812Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:30.055+0530","ets":1462428270055,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"splash:titleSound","stageid":"scene1","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.812Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:31.527+0530","ets":1462428271527,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene1","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.812Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:31.738+0530","ets":1462428271738,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene2","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.813Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:31.750+0530","ets":1462428271750,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene1:page1_sound","stageid":"scene2","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.813Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:33.085+0530","ets":1462428273085,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene2","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.813Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:33.285+0530","ets":1462428273285,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.813Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:33.312+0530","ets":1462428273312,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene2:page2_sound","stageid":"scene3","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.813Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:56.470+0530","ets":1462428296470,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"story_audio_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:56.495+0530","ets":1462428296495,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:56.872+0530","ets":1462428296872,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"story_audio_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:56.890+0530","ets":1462428296890,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PAUSE","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:57.144+0530","ets":1462428297144,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"story_audio_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:57.156+0530","ets":1462428297156,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:57.336+0530","ets":1462428297336,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"story_audio_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:57.344+0530","ets":1462428297344,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PAUSE","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:57.594+0530","ets":1462428297594,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"story_audio_button","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:34:57.600+0530","ets":1462428297600,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene3","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.814Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:01.225+0530","ets":1462428301225,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene3","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:01.596+0530","ets":1462428301596,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene4:page4_sound","stageid":"scene4","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:01.612+0530","ets":1462428301612,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene3:page3_sound","stageid":"scene4","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:07.135+0530","ets":1462428307135,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene4","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:07.432+0530","ets":1462428307432,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:page5_sound","stageid":"scene5","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:08.442+0530","ets":1462428308442,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene5","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:08.625+0530","ets":1462428308625,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene6:page6_sound","stageid":"scene6","uri":"","subtype":"PLAY","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:08.637+0530","ets":1462428308637,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"LISTEN","extype":"","id":"scene5:page5_sound","stageid":"scene6","uri":"","subtype":"STOP","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:32+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.815Z"} -{"eid":"OE_INTERACT","ts":"2016-05-05T11:35:10.088+0530","ets":1462428310088,"ver":"2.0","gdata":{"id":"org.ekstep.story.en.elephant","ver":"20"},"sid":"2b927be8-6a74-460b-aa20-0c991bcf57f6","uid":"40550853-c88c-4f6b-8d33-88d0f47c32f4","did":"d601e461a64b06f8828886e2f740e1688491a0a8","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"TOUCH","extype":"","id":"next","stageid":"scene6","uri":"","subtype":"","pos":[],"values":[],"tid":"","rating":0.0}},"tags":[{"genie":["becb887fe82f24c644482eb30041da6d88bd8150"]}],"metadata":{"sync_timestamp":"2016-11-19T23:12:33+00:00","public":"true"},"@timestamp":"2016-11-09T08:16:35.816Z"} diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf deleted file mode 100644 index bfb1b9d..0000000 --- a/src/test/resources/application.conf +++ /dev/null @@ -1,21 +0,0 @@ -local_extract_path="/tmp/extract/" -cloud_storage_type="aws" - -# AWS S3 Configuration -aws_storage_container="ekstep-dev-data-store" -aws_storage_key="" -aws_storage_secret="" - -# Azure configuration -azure_storage_container="test-container" - -#Gcloud configuration -gcloud_storage_container="test-obsrv-data-store" - -# OCI Configuration -oci_storage_container="" -oci_storage_key="" -oci_storage_secret="" -oci_region="ap-hyderabad-1" -# sample endpoint url: https://.compat.objectstorage..oraclecloud.com -oci_storage_endpoint="https://xyz.compat.objectstorage.ap-hyderabad-1.oraclecloud.com" diff --git a/src/test/resources/test-data.log b/src/test/resources/test-data.log deleted file mode 100644 index fe67b2a..0000000 --- a/src/test/resources/test-data.log +++ /dev/null @@ -1,18 +0,0 @@ -{"tags":[],"uid":"","sid":"","ts":"2016-05-21T15:42:47+05:30","edata":{"eks":{"dspec":{"camera":",","cap":[],"cpu":"abi: armeabi-v7a ARMv7 Processor rev 4 (v7l) ","dlocname":"","dname":"","edisk":12.95,"id":"1ea46b9d75036b5d","idisk":294.54,"make":"PANASONIC Panasonic P55 Novo","mem":-1.0,"os":"Android 4.4.2","scrn":4.59,"sims":1.0},"loc":""}},"did":"3418a40a297e46e8f61037a438e6985da39ca328","ver":"1.0","type":"events","eid":"GE_GENIE_START","@version":"1","gdata":{"id":"genie.android","ver":"4.2.61.4"},"@timestamp":"2016-05-25T08:45:52.493Z","uuid":"49efdece-e843-4725-a824-0796bfb8c97e3","key":"","metadata":{"checksum":"e478aba8a2096685e555d3b587fdf5361d5acc08"}} -{"tags":[],"uid":"","sid":"","ts":"2016-05-21T16:34:08+05:30","edata":{"eks":{"length":3080.0}},"did":"3418a40a297e46e8f61037a438e6985da39ca328","ver":"1.0","type":"events","eid":"GE_GENIE_END","@version":"1","gdata":{"id":"genie.android","ver":"4.2.61.4"},"@timestamp":"2016-05-25T08:45:52.532Z","uuid":"49efdece-e843-4725-a824-0796bfb8c97e95","key":"","metadata":{"checksum":"767e8e1b879b187ad615cc1aeaf8e7a0ca790e15"}} -{"tags":[],"uid":"","sid":"","ts":"2016-05-21T16:34:08+05:30","edata":{"eks":{"length":3080.0}},"did":"3418a40a297e46e8f61037a438e6985da39ca321","ver":"1.0","type":"events","eid":"GE_GENIE_END","@version":"1","gdata":{"id":"genie.android","ver":"4.2.61.4"},"@timestamp":"2016-05-25T08:45:52.532Z","uuid":"49efdece-e843-4725-a824-0796bfb8c97e95","key":"","metadata":{"checksum":"767e8e1b879b187ad615cc1aeaf8e7a0ca790e15"}} -{"tags":[],"eid":"GE_GENIE_START","ts":"2016-03-12T19:14:45+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.361Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"dspec":{"dlocname":"","dname":"","scrn":4.34,"camera":"5.0,1.0","idisk":217.95,"os":"Android 4.4.2","id":"4279a476a5040f3e","sims":1.0,"mem":-1.0,"cpu":"abi: armeabi-v7a ARMv7 Processor rev 4 (v7l) ","edisk":2.18,"make":"Samsung SM-G355H","cap":[]},"loc":"","score":0,"atmpts":0,"failedatmpts":0}}} -{"tags":[],"eid":"GE_GAME_END","ts":"2016-03-12T19:14:46+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.361Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"length":261.0,"atmpts":0,"failedatmpts":0,"gid":"org.ekstep.num.scrn.basic"}}} -{"tags":[],"eid":"GE_GENIE_END","ts":"2016-03-12T19:14:50+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.361Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"length":4.0,"atmpts":0,"failedatmpts":0}}} -{"tags":[],"eid":"GE_INTERRUPT","ts":"2016-03-12T19:14:53+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.361Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"BACKGROUND","id":""},"ext":{}}} -{"tags":[],"eid":"GE_INTERRUPT","ts":"2016-03-12T20:33:01+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.362Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"BACKGROUND","id":""},"ext":{}}} -{"tags":[],"eid":"GE_INTERRUPT","ts":"2016-03-13T18:52:58+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.362Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"BACKGROUND","id":""},"ext":{}}} -{"tags":[],"eid":"GE_GENIE_RESUME","ts":"2016-03-13T18:54:45+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.362Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"loc":"","score":0,"atmpts":0,"failedatmpts":0}}} -{"tags":[],"eid":"GE_INTERRUPT","ts":"2016-03-13T18:55:39+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.362Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"BACKGROUND","id":""},"ext":{}}} -{"tags":[],"eid":"GE_GENIE_RESUME","ts":"2016-03-13T18:55:59+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.362Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"loc":"","score":0,"atmpts":0,"failedatmpts":0}}} -{"tags":[],"eid":"GE_INTERRUPT","ts":"2016-03-13T18:56:24+05:30","ets":0,"@timestamp":"2016-04-25T06:38:30.363Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"70307295-4fbb-49a2-aceb-561625d7855c","uid":"927defc9-4da1-40ad-a780-8c92bf070ab8","did":"3fdf3dfc1f004ffa3762fdadded8f44208c8d06c","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"BACKGROUND","id":""},"ext":{}}} -{"tags":[{"partnerid":"org.ekstep.partner.pratham"}],"eid":"GE_ERROR","ts":"2016-03-17T13:22:27+05:30","ets":0,"@timestamp":"2016-04-25T05:29:05.357Z","ver":"1.0","gdata":{"id":"genieservice.android","ver":"1.0.229-production"},"sid":"","uid":"","did":"5c7567479e8515e740eaa2d21157f610bf057831","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"type":"GENIESERVICES","id":"getCurrentUser"}}} -{"tags":[{"partnerid":"org.ekstep.partner.pratham"}],"eid":"GE_SERVICE_API_CALL","ts":"2016-03-17T13:22:27+05:30","ets":0,"@timestamp":"2016-04-25T05:29:05.358Z","ver":"1.0","gdata":{"id":"genieservice.android","ver":"1.0.229-production"},"sid":"","uid":"","did":"5c7567479e8515e740eaa2d21157f610bf057831","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0}}} -{"tags":[{"partnerid":"org.ekstep.partner.pratham"}],"eid":"GE_GENIE_START","ts":"2016-03-17T13:22:30+05:30","ets":0,"@timestamp":"2016-04-25T05:29:05.358Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"","uid":"","did":"5c7567479e8515e740eaa2d21157f610bf057831","edata":{"eks":{"dspec":{"dlocname":"","dname":"","scrn":6.99,"camera":"4.0,2.0","idisk":1.01,"os":"Android 4.4.2","id":"740487ab0a9a663f","sims":-1.0,"mem":-1.0,"cpu":"abi: armeabi-v7a ARMv7 Processor rev 3 (v7l) ","edisk":4.85,"make":"Samsung SM-T231","cap":[]},"loc":"","score":0,"atmpts":0,"failedatmpts":0}}} -{"tags":[{"partnerid":"org.ekstep.partner.pratham"}],"eid":"GE_SESSION_START","ts":"2016-03-17T13:22:41+05:30","ets":0,"@timestamp":"2016-04-25T05:29:05.358Z","ver":"1.0","gdata":{"id":"genieservice.android","ver":"1.0.229-production"},"sid":"4e46572f-4d0b-4e96-a1a1-ec8c52e1e6b6","uid":"6eddac9b-ccdb-47aa-be06-a44b06f7fe62","did":"5c7567479e8515e740eaa2d21157f610bf057831","edata":{"eks":{"loc":"","score":0,"atmpts":0,"failedatmpts":0}}} -{"tags":[{"partnerid":"org.ekstep.partner.pratham"}],"eid":"GE_LAUNCH_GAME","ts":"2016-03-17T13:23:41+05:30","ets":0,"@timestamp":"2016-04-25T05:29:05.358Z","ver":"1.0","gdata":{"id":"genie.android","ver":"4.2.78.6"},"sid":"4e46572f-4d0b-4e96-a1a1-ec8c52e1e6b6","uid":"6eddac9b-ccdb-47aa-be06-a44b06f7fe62","did":"5c7567479e8515e740eaa2d21157f610bf057831","edata":{"eks":{"score":0,"atmpts":0,"failedatmpts":0,"gid":"org.ekstep.num.scrn.basic"}}} \ No newline at end of file diff --git a/src/test/resources/test-extract.zip b/src/test/resources/test-extract.zip deleted file mode 100644 index afbbc43..0000000 Binary files a/src/test/resources/test-extract.zip and /dev/null differ diff --git a/src/test/scala/org/sunbird/cloud/storage/service/TestAzureStorageService.scala b/src/test/scala/org/sunbird/cloud/storage/service/TestAzureStorageService.scala deleted file mode 100644 index 553aad1..0000000 --- a/src/test/scala/org/sunbird/cloud/storage/service/TestAzureStorageService.scala +++ /dev/null @@ -1,70 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.sunbird.cloud.storage.factory.StorageConfig -import org.sunbird.cloud.storage.factory.StorageServiceFactory -import org.scalatest._ -import org.sunbird.cloud.storage.conf.AppConf -import org.sunbird.cloud.storage.exception.StorageServiceException - -class TestAzureStorageService extends FlatSpec with Matchers { - - it should "test for azure storage" in { - - val azureService = StorageServiceFactory.getStorageService(StorageConfig("azure", AppConf.getStorageKey, AppConf.getStorageSecret)) - - val storageContainer = AppConf.getConfig("cloud_storage_container") - - azureService.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-blob.log", Option(false), Option(1), Option(2), None) - // azureService.download(storageContainer, "testUpload/test-blob.log", "src/test/resources/test-azure/") - - // upload directory - println("url of folder", azureService.upload(storageContainer, "src/test/resources/1234/", "testUpload/1234/", Option(true), Option(1), Option(2), None)) - - // downlaod directory - azureService.download(storageContainer, "testUpload/1234/", "src/test/resources/test-azure/", Option(true)) - - println("azure signed url", azureService.getSignedURL(storageContainer, "testUpload/test-blob.log", Option(600))) - - val blob = azureService.getObject(storageContainer, "testUpload/test-blob.log") - println("blob details: ", blob) - - val folderUri = azureService.getUri(storageContainer, "testUpload", Option(true)) - println("Folder URI: ", folderUri) - val fileUri = azureService.getUri(storageContainer, "testUpload/test-blob.log", Option(false)) - println("File URI: ", fileUri) - -// println("upload public url", azureService.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-data-public.log", Option(true))) -// println("upload public with expiry url", azureService.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-data-with-expiry.log", Option(true), Option(false), Option(600))) -// println("signed path to upload from external client", azureService.getSignedURL(storageContainer, "testUpload/test-data-public1.log", Option(600), Option("w"))) - - val keys = azureService.searchObjectkeys(storageContainer, "testUpload/1234/") - keys.foreach(f => println(f)) - val blobs = azureService.searchObjects(storageContainer, "testUpload/1234/") - blobs.foreach(f => println(f)) - - val objData = azureService.getObjectData(storageContainer, "testUpload/test-blob.log") - objData.length should be(18) - - // delete directory -// azureService.deleteObject(storageContainer, "testUpload/1234/", Option(true)) - //azureService.deleteObject(storageContainer, "testUpload/test-blob.log") - //azureUtil.deleteObject(storageContainer, "testUpload/test-data-public.log") - //azureUtil.deleteObject(storageContainer, "testUpload/test-data-with-expiry.log") - //azureService.deleteObject(storageContainer, "testDuplicate/1234/", Option(true)) - - //azureService.upload(storageContainer, "src/test/resources/test-extract.zip", "testUpload/test-extract.zip") - //println(azureService.upload(storageContainer, "src/test/resources/110Mb_File.zip", "testUpload/110Mb_File.zip", Option(false),Option(false),None, Option(3), 1)) - //azureService.copyObjects(storageContainer, "testUpload/test-extract.zip", storageContainer, "testDuplicate/test-extract.zip") - //azureService.copyObjects(storageContainer, "testUpload/1234/", storageContainer, "testDuplicate/1234/", Option(true)) - - //azureService.extractArchive(storageContainer, "testUpload/test-extract.zip", "testUpload/test-extract/") - - val caught = - intercept[StorageServiceException]{ - azureService.upload(storageContainer, "src/test/resources/1234/test-blob.log", "testUpload/1234/", Option(false), Option(5),Option(2), None) - } - assert(caught.getMessage.contains("Failed to upload.")) - - azureService.closeContext() - } -} diff --git a/src/test/scala/org/sunbird/cloud/storage/service/TestGcloudStorageService.scala b/src/test/scala/org/sunbird/cloud/storage/service/TestGcloudStorageService.scala deleted file mode 100644 index 31c308a..0000000 --- a/src/test/scala/org/sunbird/cloud/storage/service/TestGcloudStorageService.scala +++ /dev/null @@ -1,69 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.scalatest.{FlatSpec, Matchers} -import org.sunbird.cloud.storage.conf.AppConf -import org.sunbird.cloud.storage.exception.StorageServiceException -import org.sunbird.cloud.storage.factory.{StorageConfig, StorageServiceFactory} - -class TestGcloudStorageService extends FlatSpec with Matchers { - - it should "test for gcloud storage" in { - - val gsService = StorageServiceFactory.getStorageService(StorageConfig("gcloud", AppConf.getStorageKey, AppConf.getStorageSecret)) - - val storageContainer = AppConf.getConfig("cloud_storage_container") - -// gsService.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-blob.log", Option(false), Option(1), Option(2), None) -// gsService.upload(storageContainer, "src/test/resources/test-extract.zip", "testUpload/test-extract.zip", Option(false), Option(1), Option(2), None) -// -// // upload directory -// println("url of folder", gsService.upload(storageContainer, "src/test/resources/1234/", "testUpload/1234/", Option(true), Option(1), Option(2), None)) -// -// // downlaod directory -// gsService.download(storageContainer, "testUpload/1234/", "src/test/resources/test-gcloud/", Option(true)) -// -// println("gcloud signed url", gsService.getSignedURL(storageContainer, "testUpload/test-blob.log", Option(600))) -// -// val blob = gsService.getObject(storageContainer, "testUpload/test-blob.log") -// println("blob details: ", blob) -// -// val folderUri = gsService.getUri(storageContainer, "testUpload", Option(true)) -// println("Folder URI: ", folderUri) -// val fileUri = gsService.getUri(storageContainer, "testUpload/test-blob.log", Option(false)) -// println("File URI: ", fileUri) -// -// val keys = gsService.searchObjectkeys(storageContainer, "testUpload/1234/") -// keys.foreach(f => println(f)) -// val blobs = gsService.searchObjects(storageContainer, "testUpload/1234/") -// blobs.foreach(f => println(f)) -// -// val objData = gsService.getObjectData(storageContainer, "testUpload/test-blob.log") -// objData.length should be(18) - -// println("signed path to upload from external client", gsService.getPutSignedURL(storageContainer, "testUpload/test-data-public1.log", Option(600), Option("w"), Option("text/plain"))) - -// gsService.copyObjects(storageContainer, "testUpload/1234/", storageContainer, "testDuplicate/1234/", Option(true)) -// gsService.extractArchive(storageContainer, "testUpload/test-extract.zip", "testUpload/test-extract/") - - // delete directory -// gsService.deleteObject(storageContainer, "testUpload/1234/", Option(true)) -// gsService.deleteObject(storageContainer, "testUpload/test-blob.log") -// gsService.deleteObject(storageContainer, "testUpload/test-extract.zip") -// gsService.deleteObject(storageContainer, "testDuplicate/", Option(true)) -// gsService.deleteObject(storageContainer, "testUpload/test-extract/", Option(true)) - - val caught1 = - intercept[StorageServiceException]{ - gsService.getSignedURL(storageContainer, "testUpload/test-data-public1.log", Option(600), Option("w")) - } - assert(caught1.getMessage.contains("getSignedURL method is not supported for GCP. Please use getPutSignedURL with contentType.")) - - val caught2 = - intercept[StorageServiceException]{ - gsService.upload(storageContainer, "src/test/resources/1234/test-blob.log", "testUpload/1234/", Option(false), Option(5),Option(2), None) - } - assert(caught2.getMessage.contains("Failed to upload.")) - - gsService.closeContext() - } -} diff --git a/src/test/scala/org/sunbird/cloud/storage/service/TestOCIS3StorageService.scala b/src/test/scala/org/sunbird/cloud/storage/service/TestOCIS3StorageService.scala deleted file mode 100644 index cd36866..0000000 --- a/src/test/scala/org/sunbird/cloud/storage/service/TestOCIS3StorageService.scala +++ /dev/null @@ -1,67 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.scalatest.{FlatSpec, Matchers} -import org.sunbird.cloud.storage.conf.AppConf -import org.sunbird.cloud.storage.exception.StorageServiceException -import org.sunbird.cloud.storage.factory.{StorageConfig, StorageServiceFactory} - -class TestOCIS3StorageService extends FlatSpec with Matchers { - - it should "test for OCIS3 storage" in { - - val storageConfig = StorageConfig("oci", AppConf.getStorageKey, AppConf.getStorageSecret, Option("https://xyz.compat.objectstorage.ap-hyderabad-1.oraclecloud.com"), AppConf.getRegion) - val ociS3Service = StorageServiceFactory.getStorageService(storageConfig) - - val storageContainer = AppConf.getConfig("cloud_storage_container") - - // Use this exception block to execute the test cases successfully when it has invalid configuration. - val caught = - intercept[StorageServiceException]{ - ociS3Service.upload(storageContainer, "src/test/resources/1234/test-blob.log", "testUpload/1234/", Option(false),Option(5), Option(2), None) - } - assert(caught.getMessage.contains("Failed to upload.")) - - /** - * Use the below complete block when we have the valid configuration and - * to test the OCI functionality. - */ - /** - ociS3Service.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-blob.log") - ociS3Service.download(storageContainer, "testUpload/test-blob.log", "src/test/resources/test-s3/") - - // upload directory - println("url of folder", ociS3Service.upload(storageContainer, "src/test/resources/1234/", "testUpload/1234/", Option(true))) - - // downlaod directory - ociS3Service.download(storageContainer, "testUpload/1234/", "src/test/resources/test-s3/", Option(true)) - - println("OCI S3 signed url", ociS3Service.getSignedURL(storageContainer, "testUpload/test-blob.log", Option(600))) - - val blob = ociS3Service.getObject(storageContainer, "testUpload/test-blob.log") - println("blob details: ", blob) - - println("upload public url", ociS3Service.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-data-public.log", Option(true))) - println("upload public with expiry url", ociS3Service.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-data-with-expiry.log", Option(false))) - println("signed path to upload from external client", ociS3Service.getSignedURL(storageContainer, "testUpload/test-data-public1.log", Option(600), Option("w"))) - - val keys = ociS3Service.searchObjectkeys(storageContainer, "testUpload/1234/") - keys.foreach(f => println(f)) - val blobs = ociS3Service.searchObjects(storageContainer, "testUpload/1234/") - blobs.foreach(f => println(f)) - - val objData = ociS3Service.getObjectData(storageContainer, "testUpload/test-blob.log") - objData.length should be(18) - - // delete directory - ociS3Service.deleteObject(storageContainer, "testUpload/1234/", Option(true)) - ociS3Service.deleteObject(storageContainer, "testUpload/test-blob.log") - - ociS3Service.upload(storageContainer, "src/test/resources/test-extract.zip", "testUpload/test-extract.zip") - ociS3Service.copyObjects(storageContainer, "testUpload/test-extract.zip", storageContainer, "testDuplicate/test-extract.zip") - - ociS3Service.extractArchive(storageContainer, "testUpload/test-extract.zip", "testUpload/test-extract/") - */ - ociS3Service.closeContext() - } - -} diff --git a/src/test/scala/org/sunbird/cloud/storage/service/TestS3StorageService.scala b/src/test/scala/org/sunbird/cloud/storage/service/TestS3StorageService.scala deleted file mode 100644 index 96522c4..0000000 --- a/src/test/scala/org/sunbird/cloud/storage/service/TestS3StorageService.scala +++ /dev/null @@ -1,66 +0,0 @@ -package org.sunbird.cloud.storage.service - -import org.scalatest.FlatSpec -import org.scalatest._ -import org.sunbird.cloud.storage.conf.AppConf -import org.sunbird.cloud.storage.exception.StorageServiceException -import org.sunbird.cloud.storage.factory.{StorageConfig, StorageServiceFactory} - -class TestS3StorageService extends FlatSpec with Matchers { - - it should "test for s3 storage" in { - - val s3Service = StorageServiceFactory.getStorageService(StorageConfig("aws", AppConf.getStorageKey, AppConf.getStorageSecret)) - - val storageContainer = AppConf.getConfig("cloud_storage_container") - - //println(s3Service.upload(storageContainer, "src/test/resources/110Mb_File.zip", "testUpload/110Mb_File.zip", Option(false),Option(false),None, Option(3), 1)) - val caught = - intercept[StorageServiceException]{ - s3Service.upload(storageContainer, "src/test/resources/1234/test-blob.log", "testUpload/1234/", Option(false),Option(5), Option(2), None) - } - assert(caught.getMessage.contains("Failed to upload.")) - /* - s3Service.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-blob.log") - s3Service.download(storageContainer, "testUpload/test-blob.log", "src/test/resources/test-s3/") - // upload directory - println("url of folder", s3Service.upload(storageContainer, "src/test/resources/1234/", "testUpload/1234/", None, Option(true))) - // downlaod directory - s3Service.download(storageContainer, "testUpload/1234/", "src/test/resources/test-s3/", Option(true)) - println("aws signed url", s3Service.getSignedURL(storageContainer, "testUpload/test-blob.log", Option(600))) - val blob = s3Service.getObject(storageContainer, "testUpload/test-blob.log") - println("blob details: ", blob) - println("upload public url", s3Service.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-data-public.log", Option(true))) - println("upload public with expiry url", s3Service.upload(storageContainer, "src/test/resources/test-data.log", "testUpload/test-data-with-expiry.log", Option(true), Option(false), Option(600))) - println("signed path to upload from external client", s3Service.getSignedURL(storageContainer, "testUpload/test-data-public1.log", Option(600), Option("w"))) - val keys = s3Service.searchObjectkeys(storageContainer, "testUpload/1234/") - keys.foreach(f => println(f)) - val blobs = s3Service.searchObjects(storageContainer, "testUpload/1234/") - blobs.foreach(f => println(f)) - val objData = s3Service.getObjectData(storageContainer, "testUpload/test-blob.log") - objData.length should be(18) - // delete directory - s3Service.deleteObject(storageContainer, "testUpload/1234/", Option(true)) - s3Service.deleteObject(storageContainer, "testUpload/test-blob.log") - //s3Service.deleteObject(storageContainer, "testUpload/test-data-public.log") - //s3Service.deleteObject(storageContainer, "testUpload/test-data-with-expiry.log") - s3Service.upload(storageContainer, "src/test/resources/test-extract.zip", "testUpload/test-extract.zip") - s3Service.copyObjects(storageContainer, "testUpload/test-extract.zip", storageContainer, "testDuplicate/test-extract.zip") - s3Service.extractArchive(storageContainer, "testUpload/test-extract.zip", "testUpload/test-extract/") - */ - - // Test if the listObjectKeys lists all objects inside a directory when the number of objects count - // is greater than threshold value 1000 - /* val objectsListInsideADirectory = s3Service.listObjectKeys("ekstep-public-dev", _prefix = "content/h5p/do_112620111166709760183-snapshot/content") - objectsListInsideADirectory.size should be (1200) - // Test if the listObjectKeys lists individual files if the prefix is not a directory - val fileObject = s3Service.listObjectKeys("ekstep-public-dev", _prefix = "content/h5p/do_112620111166709760183-snapshot/index.html") - fileObject.size should be (1) - fileObject.head should be ("content/h5p/do_112620111166709760183-snapshot/index.html") - // Test if the listObjectKeys lists no files if the filename is not found - val noFilesFound = s3Service.listObjectKeys("ekstep-public-dev", _prefix = "random_file_name_which_is_not_present") - noFilesFound.size should be (0)*/ - - s3Service.closeContext() - } -} \ No newline at end of file