package org.atlasapi.persistence.output; import static com.metabroadcast.common.persistence.mongo.MongoBuilders.select; import static com.metabroadcast.common.persistence.mongo.MongoBuilders.where; import static com.metabroadcast.common.persistence.translator.TranslatorUtils.toDBObjectList; import static com.metabroadcast.common.persistence.translator.TranslatorUtils.toDateTime; import java.util.Collection; import com.metabroadcast.applications.client.model.internal.Application; import org.atlasapi.media.entity.ChildRef; import org.atlasapi.media.entity.Container; import org.atlasapi.media.entity.EntityType; import org.atlasapi.media.entity.Item; import org.atlasapi.media.entity.LookupRef; import org.atlasapi.media.entity.Person; import org.atlasapi.media.entity.Publisher; import org.atlasapi.persistence.content.ContentCategory; import org.atlasapi.persistence.media.entity.DescribedTranslator; import org.atlasapi.persistence.media.entity.IdentifiedTranslator; import org.joda.time.DateTime; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.metabroadcast.common.base.MorePredicates; import com.metabroadcast.common.persistence.mongo.DatabasedMongo; import com.metabroadcast.common.persistence.mongo.MongoConstants; import com.metabroadcast.common.persistence.translator.TranslatorUtils; import com.metabroadcast.common.time.Clock; import com.metabroadcast.common.time.SystemClock; import com.mongodb.DBCollection; import com.mongodb.DBObject; public class MongoUpcomingItemsResolver implements UpcomingItemsResolver { private final String versions = "versions"; private final String broadcasts = "broadcasts"; private final String transmissionEndTime = "transmissionEndTime"; private final String transmissionEndTimeKey = Joiner.on(".").join(versions, broadcasts, transmissionEndTime); private final String containerKey = "container"; private final DBObject fields = select().fields(ImmutableSet.of(IdentifiedTranslator.TYPE, IdentifiedTranslator.OPAQUE_ID, DescribedTranslator.PUBLISHER_KEY, IdentifiedTranslator.LAST_UPDATED, transmissionEndTimeKey)) .build(); private final DBCollection children; private final DBCollection topLevelItems; private final Clock clock; public MongoUpcomingItemsResolver(DatabasedMongo db) { this(db, new SystemClock()); } public MongoUpcomingItemsResolver(DatabasedMongo db, Clock clock) { this.children = db.collection(ContentCategory.CHILD_ITEM.tableName()); this.topLevelItems = db.collection(ContentCategory.TOP_LEVEL_ITEM.tableName()); this.clock = clock; } @Override public Iterable<ChildRef> upcomingItemsFor(Container container) { return upcomingItemsByPublisherFor(container).values(); } @Override public Multimap<Publisher, ChildRef> upcomingItemsByPublisherFor(Container container) { final DateTime now = clock.now(); return filterToChildRef(now, broadcastEndsForChildrenOf(container, now)); } @Override public Multimap<Publisher, ChildRef> upcomingItemsByPublisherFor(Item item, Application application) { final DateTime now = clock.now(); return filterToChildRef(now, broadcastEndsForEquivalentTo(item, application)); } private Iterable<DBObject> broadcastEndsForEquivalentTo(Item item, Application application) { final DateTime now = clock.now(); DBObject query = where() .idIn(FluentIterable.from(item.getEquivalentTo()) .filter(sourceFilter(application.getConfiguration().getEnabledReadSources())) .transform(LookupRef.TO_URI) ) .fieldAfter(transmissionEndTime, now) .build(); return Iterables.concat(children.find(query,fields), topLevelItems.find(query,fields)); } @Override public Iterable<ChildRef> upcomingItemsFor(Person person) { final DateTime now = clock.now(); return filterToChildRef(now, broadcastEndsForItemsOf(person, now)).values(); } private Multimap<Publisher, ChildRef> filterToChildRef( final DateTime now, Iterable<DBObject> broadcastEnds ) { Builder<Publisher, ChildRef> builder = ImmutableMultimap.builder(); for (DBObject dbo : broadcastEnds) { for (DBObject version : toDBObjectList(dbo, versions)) { for (DBObject broadcast : toDBObjectList(version, broadcasts)) { if (after(toDateTime(broadcast, transmissionEndTime), now)) { String uri = TranslatorUtils.toString(dbo, MongoConstants.ID); Long aid = TranslatorUtils.toLong(dbo, IdentifiedTranslator.OPAQUE_ID); String type = TranslatorUtils.toString(dbo, IdentifiedTranslator.TYPE); DateTime lastUpdated = TranslatorUtils.toDateTime(dbo, IdentifiedTranslator.LAST_UPDATED); Publisher publisher = Publisher.fromKey(TranslatorUtils.toString(dbo, DescribedTranslator.PUBLISHER_KEY)).requireValue(); builder.put(publisher, new ChildRef(aid, uri, "", lastUpdated, EntityType.from(type))); } } } } return builder.build(); } private boolean after(DateTime dateTime, DateTime now) { return dateTime != null && dateTime.isAfter(now); } private Iterable<DBObject> broadcastEndsForChildrenOf(Container container, DateTime time) { DBObject query = where() .fieldEquals(containerKey, container.getCanonicalUri()) .fieldAfter(transmissionEndTimeKey, time) .build(); return children.find(query,fields); } private Iterable<DBObject> broadcastEndsForItemsOf(Person person, DateTime time) { DBObject query = where() .idIn(Iterables.transform(person.getContents(), ChildRef.TO_URI)) .fieldAfter(transmissionEndTimeKey, time) .build(); return Iterables.concat(children.find(query,fields), topLevelItems.find(query,fields)); } private Predicate<LookupRef> sourceFilter(Collection<Publisher> sources) { return MorePredicates.transformingPredicate(LookupRef.TO_SOURCE, Predicates.in(sources)); } }