/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.iosched.server.schedule.model;
import static com.google.samples.apps.iosched.server.schedule.model.DataModelHelper.get;
import static com.google.samples.apps.iosched.server.schedule.model.DataModelHelper.getAsArray;
import static com.google.samples.apps.iosched.server.schedule.model.DataModelHelper.getMapValue;
import static com.google.samples.apps.iosched.server.schedule.model.DataModelHelper.isHashtag;
import static com.google.samples.apps.iosched.server.schedule.model.DataModelHelper.set;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.samples.apps.iosched.server.schedule.Config;
import com.google.samples.apps.iosched.server.schedule.model.validator.Converters;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
/**
* Encapsulation of the rules that maps Vendor data sources to the IOSched data sources.
*
*/
public class DataExtractor {
public static final String TRACK = "TRACK";
public static final String ANDROID_TRACK = "ANDROID";
public static final String ANDROID_TRACK_COLOR = "#AED581";
public static final String MOBILEWEB_TRACK = "MOBILEWEB";
public static final String MOBILEWEB_TRACK_COLOR = "#FFF176";
public static final String CLOUD_TRACK = "CLOUD";
public static final String DESIGN_TRACK = "DESIGN";
public static final String FIREBASE_TRACK = "FIREBASE";
public static final String GAMES_TRACK = "GAMES";
public static final String IOT_TRACK = "IOT";
public static final String LOCATION_AND_MAPS_TRACK = "LOCATION&MAPS";
public static final String PLAY_TRACK = "PLAY";
public static final String SEARCH_TRACK = "SEARCH";
public static final String TV_AND_LIVINGROOM_TRACK = "TV&LIVINGROOM";
public static final String VR_TRACK = "VR";
public static final String MISC_TRACK = "MISC";
public static final String ANDROIDSTUDIO_TRACK = "ANDROIDSTUDIO";
public static final String AUTO_TRACK = "AUTO";
public static final String MONETIZATION_TRACK = "MONETIZATION";
public static final String WEAR_TRACK = "WEAR";
public static final String CLOUD_TRACK_COLOR = "#80CBC4";
public static final String DESIGN_TRACK_COLOR = "#F8BBD0";
public static final String FIREBASE_TRACK_COLOR = "#FFD54F";
public static final String GAMES_TRACK_COLOR = "#DCE775";
public static final String IOT_TRACK_COLOR = "#BCAAA4";
public static final String LOCATION_AND_MAPS_TRACK_COLOR = "#EF9A9A";
public static final String PLAY_TRACK_COLOR = "#CE93D8";
public static final String SEARCH_TRACK_COLOR = "#90CAF9";
public static final String TV_AND_LIVINGROOM_TRACK_COLOR = "#B3E5FC";
public static final String VR_TRACK_COLOR = "#FF8A65";
public static final String MISC_TRACK_COLOR = "#C5C9E9";
public static final String ANDROIDSTUDIO_TRACK_COLOR = "#C4E2A2";
public static final String AUTO_TRACK_COLOR = "#CFD8DC";
public static final String MONETIZATION_TRACK_COLOR = "#A4D7A5";
public static final String WEAR_TRACK_COLOR = "#FFCD7A";
public static final String ADS_TRACK = "ADS";
public static final String ADS_TRACK_COLOR = "#B0BEC5";
private HashMap<String, JsonObject> videoSessionsById;
private HashMap<String, JsonObject> speakersById;
private HashMap<String, JsonObject> categoryToTagMap;
private HashSet<String> usedSpeakers, usedTags;
private JsonElement mainCategory;
private boolean obfuscate;
public DataExtractor(boolean obfuscate) {
this.obfuscate = obfuscate;
}
public JsonObject extractFromDataSources(JsonDataSources sources) {
usedTags = new HashSet<String>();
usedSpeakers = new HashSet<String>();
JsonObject result = new JsonObject();
result.add(OutputJsonKeys.MainTypes.rooms.name(), extractRooms(sources));
JsonArray speakers = extractSpeakers(sources);
JsonArray tags = extractTags(sources);
result.add(OutputJsonKeys.MainTypes.video_library.name(), extractVideoSessions(sources));
result.add(OutputJsonKeys.MainTypes.sessions.name(), extractSessions(sources));
// Remove tags that are not used on any session (b/14419126)
Iterator<JsonElement> tagsIt = tags.iterator();
while (tagsIt.hasNext()) {
JsonElement tag = tagsIt.next();
String tagName = get(tag.getAsJsonObject(), OutputJsonKeys.Tags.tag).getAsString();
if (!usedTags.contains(tagName)) {
tagsIt.remove();
}
}
// Remove speakers that are not used on any session:
Iterator<JsonElement> it = speakers.iterator();
while (it.hasNext()) {
JsonElement el = it.next();
String id = get(el.getAsJsonObject(), OutputJsonKeys.Speakers.id).getAsString();
if (!usedSpeakers.contains(id)) {
it.remove();
}
}
result.add(OutputJsonKeys.MainTypes.speakers.name(), speakers);
result.add(OutputJsonKeys.MainTypes.tags.name(), tags);
return result;
}
public JsonArray extractRooms(JsonDataSources sources) {
HashSet<String> ids = new HashSet<String>();
JsonArray result = new JsonArray();
JsonDataSource source = sources.getSource(InputJsonKeys.VendorAPISource.MainTypes.rooms.name());
if (source != null) {
for (JsonObject origin: source) {
JsonObject dest = new JsonObject();
JsonElement originalId = get(origin, InputJsonKeys.VendorAPISource.Rooms.Id);
String id = Config.ROOM_MAPPING.getRoomId(originalId.getAsString());
if (!ids.contains(id)) {
String title = Config.ROOM_MAPPING.getTitle(id, get(origin, InputJsonKeys.VendorAPISource.Rooms.Name).getAsString());
set(new JsonPrimitive(id), dest, OutputJsonKeys.Rooms.id);
set(originalId, dest, OutputJsonKeys.Rooms.original_id);
set(new JsonPrimitive(title), dest, OutputJsonKeys.Rooms.name);
result.add(dest);
ids.add(id);
}
}
}
if (Config.DEBUG_FIX_DATA) {
DebugDataExtractorHelper.changeRooms(result);
}
return result;
}
public JsonArray extractTags(JsonDataSources sources) {
JsonArray result = new JsonArray();
JsonDataSource source = sources.getSource(InputJsonKeys.VendorAPISource.MainTypes.categories.name());
JsonDataSource tagCategoryMappingSource = sources.getSource(InputJsonKeys.ExtraSource.MainTypes
.tag_category_mapping.name());
JsonDataSource tagsConfSource = sources.getSource(InputJsonKeys.ExtraSource.MainTypes.tag_conf.name());
categoryToTagMap = new HashMap<String, JsonObject>();
// Only for checking duplicates.
HashSet<String> originalTagNames = new HashSet<String>();
if (source != null) {
for (JsonObject origin: source) {
JsonObject dest = new JsonObject();
// set tag category, looking for parentid in the tag_category_mapping data source
JsonElement parentId = get(origin, InputJsonKeys.VendorAPISource.Categories.ParentId);
// Ignore categories with null parents, because they are roots (tag categories).
if (parentId != null && !parentId.getAsString().equals("")) {
JsonElement category = null;
if (tagCategoryMappingSource != null) {
JsonObject categoryMapping = tagCategoryMappingSource.getElementById(parentId.getAsString());
if (categoryMapping != null) {
category = get(categoryMapping, InputJsonKeys.ExtraSource.CategoryTagMapping.tag_name);
JsonPrimitive isDefault = (JsonPrimitive) get(categoryMapping, InputJsonKeys.ExtraSource.CategoryTagMapping.is_default);
if ( isDefault != null && isDefault.getAsBoolean() ) {
mainCategory = category;
}
}
set(category, dest, OutputJsonKeys.Tags.category);
}
// Ignore categories unrecognized parents (no category)
if (category == null) {
continue;
}
// Tag name is by convention: "TAGCATEGORY_TAGNAME"
JsonElement name = get(origin, InputJsonKeys.VendorAPISource.Categories.Name);
JsonElement tagName = new JsonPrimitive(category.getAsString() + "_" +
Converters.TAG_NAME.convert(name).getAsString());
JsonElement originalTagName = tagName;
if (obfuscate) {
name = Converters.OBFUSCATE.convert(name);
tagName = new JsonPrimitive(category.getAsString() + "_" +
Converters.TAG_NAME.convert(name).getAsString());
}
set(tagName, dest, OutputJsonKeys.Tags.tag);
set(name, dest, OutputJsonKeys.Tags.name);
set(origin, InputJsonKeys.VendorAPISource.Categories.Id, dest, OutputJsonKeys.Tags.original_id);
set(origin, InputJsonKeys.VendorAPISource.Categories.Description, dest, OutputJsonKeys.Tags._abstract, obfuscate ? Converters.OBFUSCATE : null);
if (tagsConfSource != null) {
JsonObject tagConf = tagsConfSource.getElementById(originalTagName.getAsString());
if (tagConf != null) {
set(tagConf, InputJsonKeys.ExtraSource.TagConf.order_in_category, dest, OutputJsonKeys.Tags.order_in_category);
set(tagConf, InputJsonKeys.ExtraSource.TagConf.color, dest, OutputJsonKeys.Tags.color);
set(tagConf, InputJsonKeys.ExtraSource.TagConf.hashtag, dest, OutputJsonKeys.Tags.hashtag);
}
}
// TODO: Directly associate Track images with Tags.
// Track images were retrieved from session images. This required that sessions on the
// same track have the same image. Thus here, we needed to check the Track of each
// session even if the corresponding Track was already assigned an image. If all sessions
// in the same track are going to have the same image then it would make more sense for
// the images to be attached to the Tag/Track rather than the session.
if (tagName.getAsString().startsWith(TRACK)) {
// Extract photo urls from topics for TRACK tags.
String objectId = extractTrackPhotoObjectId(sources,
dest.get(OutputJsonKeys.Tags.original_id.name()).getAsString());
if (!objectId.isEmpty()) {
dest.addProperty(OutputJsonKeys.Tags.photoUrl.name(),
Converters.SESSION_PHOTO_URL.convert(new JsonPrimitive(objectId))
.getAsString());
}
// Add background colors for TRACK tags.
String trackColor = getTrackColor(tagName.getAsString().substring(6));
if (!trackColor.isEmpty()) {
dest.addProperty(OutputJsonKeys.Tags.color.name(), trackColor);
}
}
categoryToTagMap.put(get(origin, InputJsonKeys.VendorAPISource.Categories.Id).getAsString(), dest);
if (originalTagNames.add(originalTagName.getAsString())) {
result.add(dest);
}
}
}
}
if (Config.DEBUG_FIX_DATA) {
DebugDataExtractorHelper.changeCategories(categoryToTagMap, result);
}
return result;
}
public JsonArray extractSpeakers(JsonDataSources sources) {
speakersById = new HashMap<String, JsonObject>();
JsonArray result = new JsonArray();
JsonDataSource source = sources.getSource(InputJsonKeys.VendorAPISource.MainTypes.speakers.name());
if (source != null) {
for (JsonObject origin: source) {
JsonObject dest = new JsonObject();
JsonElement id = get(origin, InputJsonKeys.VendorAPISource.Speakers.Id);
set(id, dest, OutputJsonKeys.Speakers.id);
set(origin, InputJsonKeys.VendorAPISource.Speakers.Name, dest, OutputJsonKeys.Speakers.name, obfuscate?Converters.OBFUSCATE:null);
set(origin, InputJsonKeys.VendorAPISource.Speakers.Bio, dest, OutputJsonKeys.Speakers.bio, obfuscate?Converters.OBFUSCATE:null);
set(origin, InputJsonKeys.VendorAPISource.Speakers.CompanyName, dest, OutputJsonKeys.Speakers.company, obfuscate?Converters.OBFUSCATE:null);
JsonElement originalPhoto = get(origin, InputJsonKeys.VendorAPISource.Speakers.Photo);
if (originalPhoto != null && !"".equals(originalPhoto.getAsString())) {
// Note that the input for SPEAKER_PHOTO_ID converter is the entity ID. We simply ignore the original
// photo URL, because that will be processed by an offline cron script, resizing the
// photos and saving them to a known location with the entity ID as its base name.
set(origin, InputJsonKeys.VendorAPISource.Speakers.Id, dest, OutputJsonKeys.Speakers.thumbnailUrl, Converters.SPEAKER_PHOTO_URL);
}
JsonElement info = origin.get(InputJsonKeys.VendorAPISource.Speakers.Info.name());
JsonPrimitive plusUrl = getMapValue(info, InputJsonKeys.VendorAPISource.Speakers.INFO_PUBLIC_PLUS_ID, Converters.GPLUS_URL, null);
if (plusUrl != null) {
set(plusUrl, dest, OutputJsonKeys.Speakers.plusoneUrl);
}
JsonPrimitive twitter = getMapValue(info, InputJsonKeys.VendorAPISource.Speakers.INFO_PUBLIC_TWITTER, Converters.TWITTER_URL, null);
if (twitter != null) {
set(twitter, dest, OutputJsonKeys.Speakers.twitterUrl);
}
result.add(dest);
speakersById.put(id.getAsString(), dest);
}
}
return result;
}
public JsonArray extractSessions(JsonDataSources sources) {
if (videoSessionsById == null) {
throw new IllegalStateException("You need to extract video sessions before attempting to extract sessions");
}
if (categoryToTagMap == null) {
throw new IllegalStateException("You need to extract tags before attempting to extract sessions");
}
JsonArray result = new JsonArray();
JsonDataSource source = sources.getSource(InputJsonKeys.VendorAPISource.MainTypes.topics.name());
if (source != null) {
for (JsonObject origin: source) {
if (isVideoSession(origin)) {
// Sessions with the Video tag are processed as video library content
continue;
}
if (isHiddenSession(origin)) {
// Sessions with a "Hidden from schedule" flag should be ignored
continue;
}
JsonElement title = get(origin, InputJsonKeys.VendorAPISource.Topics.Title);
// Since the CMS returns an empty keynote as a session, we need to ignore it
if (title != null && title.isJsonPrimitive() && "keynote".equalsIgnoreCase(title.getAsString())) {
continue;
}
JsonObject dest = new JsonObject();
// Some sessions require a special ID, so we replace it here...
if (title != null && title.isJsonPrimitive() && "after hours".equalsIgnoreCase(title.getAsString())) {
set(new JsonPrimitive("__afterhours__"), dest, OutputJsonKeys.Sessions.id);
} else {
set(origin, InputJsonKeys.VendorAPISource.Topics.Id, dest, OutputJsonKeys.Sessions.id);
}
set(origin, InputJsonKeys.VendorAPISource.Topics.Id, dest, OutputJsonKeys.Sessions.url, Converters.SESSION_URL);
set(origin, InputJsonKeys.VendorAPISource.Topics.Title, dest, OutputJsonKeys.Sessions.title, obfuscate?Converters.OBFUSCATE:null);
set(origin, InputJsonKeys.VendorAPISource.Topics.Description, dest, OutputJsonKeys.Sessions.description, obfuscate?Converters.OBFUSCATE:null);
set(origin, InputJsonKeys.VendorAPISource.Topics.Start, dest, OutputJsonKeys.Sessions.startTimestamp, Converters.DATETIME);
set(origin, InputJsonKeys.VendorAPISource.Topics.Finish, dest, OutputJsonKeys.Sessions.endTimestamp, Converters.DATETIME);
set(new JsonPrimitive(isFeatured(origin)), dest, OutputJsonKeys.Sessions.isFeatured);
JsonElement documents = get(origin, InputJsonKeys.VendorAPISource.Topics.Documents);
if (documents != null && documents.isJsonArray() && documents.getAsJsonArray().size()>0) {
// Note that the input for SessionPhotoURL is the entity ID. We simply ignore the original
// photo URL, because that will be processed by an offline cron script, resizing the
// photos and saving them to a known location with the entity ID as its base name.
set(origin, InputJsonKeys.VendorAPISource.Topics.Id, dest, OutputJsonKeys.Sessions.photoUrl, Converters.SESSION_PHOTO_URL);
}
setVideoPropertiesInSession(origin, dest);
setRelatedContent(origin, dest);
JsonElement mainTag = null;
JsonElement hashtag = null;
JsonElement mainTagColor = null;
JsonArray categories= origin.getAsJsonArray(InputJsonKeys.VendorAPISource.Topics.CategoryIds.name());
JsonArray tags = new JsonArray();
for (JsonElement category: categories) {
JsonObject tag = categoryToTagMap.get(category.getAsString());
if (tag != null) {
JsonElement tagName = get(tag, OutputJsonKeys.Tags.tag);
tags.add(tagName);
usedTags.add(tagName.getAsString());
if (mainTag == null) {
// check if the tag is from a "default" category. For example, if THEME is the default
// category, all sessions will have a "mainTag" property set to the first tag of type THEME
JsonElement tagCategory = get(tag, OutputJsonKeys.Tags.category); // THEME, TYPE or TOPIC
if (tagCategory.equals(mainCategory)) {
mainTag = tagName;
mainTagColor = get(tag, OutputJsonKeys.Tags.color);
}
if (hashtag == null && isHashtag(tag)) {
hashtag = get(tag, OutputJsonKeys.Tags.hashtag);
if (hashtag == null || hashtag.getAsString() == null || hashtag.getAsString().isEmpty()) {
// If no hashtag set in the tagsconf file, we will convert the tagname to find one:
hashtag = new JsonPrimitive(get(tag, OutputJsonKeys.Tags.name, Converters.TAG_NAME)
.getAsString().toLowerCase());
}
}
}
}
}
set(tags, dest, OutputJsonKeys.Sessions.tags);
if (mainTag != null) {
set(mainTag, dest, OutputJsonKeys.Sessions.mainTag);
}
if (mainTagColor != null) {
set(mainTagColor, dest, OutputJsonKeys.Sessions.color);
}
if (hashtag != null) {
set(hashtag, dest, OutputJsonKeys.Sessions.hashtag);
}
JsonArray speakers = getAsArray(origin, InputJsonKeys.VendorAPISource.Topics.SpeakerIds);
if (speakers != null) for (JsonElement speaker: speakers) {
String speakerId = speaker.getAsString();
usedSpeakers.add(speakerId);
}
set(speakers, dest, OutputJsonKeys.Sessions.speakers);
JsonArray sessions= origin.getAsJsonArray(InputJsonKeys.VendorAPISource.Topics.Sessions.name());
if (sessions != null && sessions.size()>0) {
String roomId = get(sessions.get(0).getAsJsonObject(), InputJsonKeys.VendorAPISource.Sessions.RoomId).getAsString();
roomId = Config.ROOM_MAPPING.getRoomId(roomId);
set(new JsonPrimitive(roomId), dest, OutputJsonKeys.Sessions.room);
// captions URL is set based on the session room, so keep it here.
String captionsURL = Config.ROOM_MAPPING.getCaptions(roomId);
if (captionsURL != null) {
set(new JsonPrimitive(captionsURL), dest, OutputJsonKeys.Sessions.captionsUrl);
}
}
if (Config.DEBUG_FIX_DATA) {
DebugDataExtractorHelper.changeSession(dest, usedTags);
}
result.add(dest);
}
}
return result;
}
public JsonArray extractVideoSessions(JsonDataSources sources) {
videoSessionsById = new HashMap<String, JsonObject>();
if (categoryToTagMap == null) {
throw new IllegalStateException("You need to extract tags before attempting to extract video sessions");
}
if (speakersById == null) {
throw new IllegalStateException("You need to extract speakers before attempting to extract video sessions");
}
JsonArray result = new JsonArray();
JsonDataSource source = sources.getSource(InputJsonKeys.VendorAPISource.MainTypes.topics.name());
if (source != null) {
for (JsonObject origin: source) {
if (!isVideoSession(origin)) {
continue;
}
if (isHiddenSession(origin)) {
// Sessions with a "Hidden from schedule" flag should be ignored
continue;
}
JsonObject dest = new JsonObject();
JsonPrimitive vid = setVideoForVideoSession(origin, dest);
JsonElement id = get(origin, InputJsonKeys.VendorAPISource.Topics.Id);
// video library id must be the Youtube video id
set(vid, dest, OutputJsonKeys.VideoLibrary.id);
set(origin, InputJsonKeys.VendorAPISource.Topics.Title, dest, OutputJsonKeys.VideoLibrary.title, obfuscate?Converters.OBFUSCATE:null);
set(origin, InputJsonKeys.VendorAPISource.Topics.Description, dest, OutputJsonKeys.VideoLibrary.desc, obfuscate?Converters.OBFUSCATE:null);
set(new JsonPrimitive(Config.CONFERENCE_YEAR), dest, OutputJsonKeys.VideoLibrary.year);
JsonElement videoTopic = null;
JsonArray categories= origin.getAsJsonArray(InputJsonKeys.VendorAPISource.Topics.CategoryIds.name());
for (JsonElement category: categories) {
JsonObject tag = categoryToTagMap.get(category.getAsString());
if (tag != null) {
if (isHashtag(tag)) {
videoTopic = get(tag, OutputJsonKeys.Tags.name);
// by definition, the first tag that can be a hashtag (usually a TOPIC) is considered the video tag
break;
}
}
}
if (videoTopic != null) {
set(videoTopic, dest, OutputJsonKeys.VideoLibrary.topic);
}
// Concatenate speakers:
JsonArray speakers = getAsArray(origin, InputJsonKeys.VendorAPISource.Topics.SpeakerIds);
StringBuilder sb = new StringBuilder();
if (speakers != null) for (int i=0; i<speakers.size(); i++) {
String speakerId = speakers.get(i).getAsString();
usedSpeakers.add(speakerId);
JsonObject speaker = speakersById.get(speakerId);
if (speaker != null) {
sb.append(get(speaker, OutputJsonKeys.Speakers.name).getAsString());
if (i<speakers.size()-1) sb.append(", ");
}
}
set(new JsonPrimitive(sb.toString()), dest, OutputJsonKeys.VideoLibrary.speakers);
videoSessionsById.put(id.getAsString(), dest);
result.add(dest);
}
}
return result;
}
private boolean isVideoSession(JsonObject sessionObj) {
JsonArray tags= sessionObj.getAsJsonArray(InputJsonKeys.VendorAPISource.Topics.CategoryIds.name());
for (JsonElement category: tags) {
if (Config.VIDEO_CATEGORY.equals(category.getAsString())) {
return true;
}
}
return false;
}
private boolean isHiddenSession(JsonObject sessionObj) {
JsonPrimitive hide = getMapValue(
get(sessionObj, InputJsonKeys.VendorAPISource.Topics.Info),
InputJsonKeys.VendorAPISource.Topics.INFO_HIDDEN_SESSION,
Converters.BOOLEAN, null);
if (hide != null && hide.isBoolean() && hide.getAsBoolean()) {
return true;
}
return false;
}
private boolean isLivestreamed(JsonObject sessionObj) {
// data generated after the end of the conference should never have livestream URLs
long endOfConference = Config.CONFERENCE_DAYS[Config.CONFERENCE_DAYS.length-1][1];
if (System.currentTimeMillis() > endOfConference ) {
return false;
}
JsonPrimitive livestream = getMapValue(
get(sessionObj, InputJsonKeys.VendorAPISource.Topics.Info),
InputJsonKeys.VendorAPISource.Topics.INFO_IS_LIVE_STREAM,
null, null);
return livestream != null && "true".equalsIgnoreCase(livestream.getAsString());
}
/** Check whether a session is featured or not.
*
* @param sessionObj Session to check
* @return True if featured, false otherwise.
*/
private boolean isFeatured(JsonObject sessionObj) {
// Extract "Featured Session" flag from EventPoint "info" block.
JsonPrimitive featured = getMapValue(
get(sessionObj, InputJsonKeys.VendorAPISource.Topics.Info),
InputJsonKeys.VendorAPISource.Topics.INFO_FEATURED_SESSION,
Converters.BOOLEAN, null);
if (featured != null && featured.isBoolean() && featured.getAsBoolean()) {
return true;
}
return false;
}
private void setVideoPropertiesInSession(JsonObject origin, JsonObject dest) {
boolean isLivestream = isLivestreamed(origin);
set(new JsonPrimitive(isLivestream), dest, OutputJsonKeys.Sessions.isLivestream);
JsonPrimitive vid = null;
if (isLivestream) {
vid = getVideoFromTopicInfo(origin, InputJsonKeys.VendorAPISource.Topics.INFO_STREAM_VIDEO_ID,
Config.VIDEO_LIVESTREAMURL_FOR_EMPTY);
} else {
vid = getMapValue(
get(origin, InputJsonKeys.VendorAPISource.Topics.Info), InputJsonKeys.VendorAPISource.Topics.INFO_VIDEO_URL,
Converters.YOUTUBE_URL, null);
}
if (vid != null && !vid.getAsString().isEmpty()) {
set(vid, dest, OutputJsonKeys.Sessions.youtubeUrl);
}
}
private JsonPrimitive getVideoFromTopicInfo(JsonObject origin, String sourceInfoKey, String defaultVideoUrl) {
JsonPrimitive result = null;
if (!obfuscate) {
JsonPrimitive vid = getMapValue(
get(origin, InputJsonKeys.VendorAPISource.Topics.Info), sourceInfoKey, null, defaultVideoUrl);
if (vid != null && !vid.getAsString().isEmpty()) {
result = vid;
}
}
return (result == null && defaultVideoUrl != null) ? new JsonPrimitive(defaultVideoUrl) : result;
}
private JsonPrimitive setVideoForVideoSession(JsonObject origin, JsonObject dest) {
JsonPrimitive vid = getVideoFromTopicInfo(origin,
InputJsonKeys.VendorAPISource.Topics.INFO_VIDEO_URL, null);
if (vid != null) {
set(vid, dest, OutputJsonKeys.VideoLibrary.vid);
JsonPrimitive thumbnail = new JsonPrimitive("http://img.youtube.com/vi/" + vid.getAsString() + "/hqdefault.jpg");
set(thumbnail, dest, OutputJsonKeys.VideoLibrary.thumbnailUrl);
}
return vid;
}
@Deprecated
private void setRelatedVideos(JsonObject origin, JsonObject dest) {
JsonArray related = getAsArray(origin, InputJsonKeys.VendorAPISource.Topics.Related);
if (related == null) {
return;
}
for (JsonElement el: related) {
if (!el.isJsonObject()) {
continue;
}
JsonObject obj = el.getAsJsonObject();
if (!obj.has("name") || !obj.has("values")) {
continue;
}
if (InputJsonKeys.VendorAPISource.Topics.RELATED_NAME_VIDEO.equals(
obj.getAsJsonPrimitive("name").getAsString())) {
JsonElement values = obj.get("values");
if (!values.isJsonArray()) {
continue;
}
// As per the data specification, related content is formatted as
// "video1 title1\nvideo2 title2\n..."
StringBuilder relatedContentStr = new StringBuilder();
for (JsonElement value: values.getAsJsonArray()) {
String relatedSessionId = value.getAsString();
JsonObject relatedVideo = videoSessionsById.get(relatedSessionId);
if (relatedVideo != null) {
JsonElement vid = get(relatedVideo, OutputJsonKeys.VideoLibrary.vid);
JsonElement title = get(relatedVideo, OutputJsonKeys.VideoLibrary.title);
if (vid != null && title != null) {
relatedContentStr.append(vid.getAsString()).append(" ")
.append(title.getAsString()).append("\n");
}
}
}
set(new JsonPrimitive(relatedContentStr.toString()),
dest, OutputJsonKeys.Sessions.relatedContent);
}
}
}
private void setRelatedContent(JsonObject origin, JsonObject dest) {
JsonArray related = getAsArray(origin, InputJsonKeys.VendorAPISource.Topics.Related);
JsonArray outputArray = new JsonArray();
if (related == null) {
return;
}
for (JsonElement el: related) {
if (!el.isJsonObject()) {
continue;
}
JsonObject obj = el.getAsJsonObject();
if (!obj.has("name") || !obj.has("values")) {
continue;
}
if (InputJsonKeys.VendorAPISource.Topics.RELATED_NAME_SESSIONS.equals(
obj.getAsJsonPrimitive("name").getAsString())) {
JsonElement values = obj.get("topics");
if (!values.isJsonArray()) {
continue;
}
// As per the data specification, related content is formatted as
// "video1 title1\nvideo2 title2\n..."
for (JsonElement topic : values.getAsJsonArray()) {
if (!topic.isJsonObject()) {
continue;
}
JsonObject topicObj = topic.getAsJsonObject();
String id = get(topicObj, InputJsonKeys.VendorAPISource.RelatedTopics.Id).getAsString();
String title = get(topicObj, InputJsonKeys.VendorAPISource.RelatedTopics.Title).getAsString();
if (id != null && title != null) {
JsonObject outputObj = new JsonObject();
set(new JsonPrimitive(id), outputObj, OutputJsonKeys.RelatedContent.id);
set(new JsonPrimitive(title), outputObj, OutputJsonKeys.RelatedContent.title);
outputArray.add(outputObj);
}
}
set(outputArray, dest, OutputJsonKeys.Sessions.relatedContent);
}
}
}
/**
* Extract the ObjectId (used to generate the photo url of the track) from the first topic that
* contains the given trackId.
*
* @param sources The full JSON object retrieved from the CMS.
* @param trackId Track id used to filter the topics, so the correct artwork can be retrieved.
* @return ObjectId of the topic that contains the trackId.
*/
private String extractTrackPhotoObjectId(JsonDataSources sources, String trackId) {
JsonDataSource source = sources.getSource(InputJsonKeys.VendorAPISource.MainTypes.topics.name());
for (JsonObject topic : source) {
JsonElement documents = get(topic, InputJsonKeys.VendorAPISource.Topics.Documents);
JsonArray categories = topic.getAsJsonArray(InputJsonKeys.VendorAPISource.Topics.CategoryIds.name());
for (int i = 0; i < categories.size(); i++) {
String categoryId = categories.get(i).getAsString();
if (categoryId.equals(trackId) && documents.getAsJsonArray().size() > 0) {
return documents.getAsJsonArray().get(0).getAsJsonObject().get("ObjectId").getAsString();
}
}
}
// If no topic is found to have this trackId contained in its categories array then an empty
// String is returned.
return "";
}
// TODO: improve the association of colors with tracks.
// Track and corresponding track colors were hard coded. These values should be defined at best
// as part of a config file retrieved from the application server, or at least as a config file
// within the application.
/**
* Provides the appropriate color given the track name.
*
* @param trackName Name of the track requiring a color.
* @return Color associated with track name.
*/
private String getTrackColor(String trackName) {
switch (trackName) {
// Known tracks
case ANDROID_TRACK:
return ANDROID_TRACK_COLOR;
case MOBILEWEB_TRACK:
return MOBILEWEB_TRACK_COLOR;
case CLOUD_TRACK:
return CLOUD_TRACK_COLOR;
case DESIGN_TRACK:
return DESIGN_TRACK_COLOR;
case FIREBASE_TRACK:
return FIREBASE_TRACK_COLOR;
case GAMES_TRACK:
return GAMES_TRACK_COLOR;
case IOT_TRACK:
return IOT_TRACK_COLOR;
case LOCATION_AND_MAPS_TRACK:
return LOCATION_AND_MAPS_TRACK_COLOR;
case PLAY_TRACK:
return PLAY_TRACK_COLOR;
case SEARCH_TRACK:
return SEARCH_TRACK_COLOR;
case TV_AND_LIVINGROOM_TRACK:
return TV_AND_LIVINGROOM_TRACK_COLOR;
case VR_TRACK:
return VR_TRACK_COLOR;
case MISC_TRACK:
return MISC_TRACK_COLOR;
case ADS_TRACK:
return ADS_TRACK_COLOR;
// other tracks
case ANDROIDSTUDIO_TRACK:
return ANDROIDSTUDIO_TRACK_COLOR;
case AUTO_TRACK:
return AUTO_TRACK_COLOR;
case MONETIZATION_TRACK:
return MONETIZATION_TRACK_COLOR;
case WEAR_TRACK:
return WEAR_TRACK_COLOR;
default:
return "";
}
}
}