package org.atlasapi.persistence.content.mongo;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.filter;
import static com.metabroadcast.common.persistence.mongo.MongoBuilders.where;
import static org.atlasapi.persistence.media.entity.ContainerTranslator.CHILDREN_KEY;
import java.util.List;
import java.util.Set;
import org.atlasapi.media.entity.Brand;
import org.atlasapi.media.entity.ChildRef;
import org.atlasapi.media.entity.Container;
import org.atlasapi.media.entity.Episode;
import org.atlasapi.media.entity.Item;
import org.atlasapi.media.entity.Series;
import org.atlasapi.media.entity.SeriesRef;
import org.atlasapi.persistence.content.ContentCategory;
import org.atlasapi.persistence.media.entity.ContainerTranslator;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.metabroadcast.common.base.Maybe;
import com.metabroadcast.common.ids.SubstitutionTableNumberCodec;
import com.metabroadcast.common.persistence.mongo.DatabasedMongo;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
public class ChildRefWriter {
private static final Logger log = LoggerFactory.getLogger(ChildRefWriter.class);
private final DBCollection containers;
private final DBCollection programmeGroups;
private final ContainerTranslator containerTranslator;
public ChildRefWriter(DatabasedMongo mongo) {
MongoContentTables mongoTables = new MongoContentTables(mongo);
this.containers = mongoTables.collectionFor(ContentCategory.CONTAINER);
this.programmeGroups = mongoTables.collectionFor(ContentCategory.PROGRAMME_GROUP);
this.containerTranslator = new ContainerTranslator(new SubstitutionTableNumberCodec());
}
public void includeEpisodeInSeriesAndBrand(Episode episode) {
if (Boolean.TRUE.equals(episode.getGenericDescription())
&& episode.getEpisodeNumber() == null
&& episode.getSeriesNumber() == null) {
log.debug("Not including episode " + episode.getCanonicalUri() + "in series and brand.");
return;
}
ChildRef childRef = episode.childRef();
if (episode.getSeriesRef() == null) { // just ensure item in container.
includeChildRefInContainer(episode, childRef, containers, CHILDREN_KEY, episode.isActivelyPublished());
return;
}
// otherwise retrieve the child references for both series and brand, if
// either are missing, change nothing and error out.
String brandUri = episode.getContainer().getUri();
String seriesUri = episode.getSeriesRef().getUri();
Maybe<Container> maybeBrand = getContainer(brandUri, containers);
Maybe<Container> maybeSeries = getContainer(seriesUri, programmeGroups);
if (maybeBrand.isNothing() || maybeSeries.isNothing()) {
throw new IllegalStateException(
String.format("Container %s or series %s not found for episode %s",
brandUri, seriesUri, episode.getCanonicalUri()));
}
episode.setContainer(maybeBrand.requireValue());
episode.setSeries((Series)maybeSeries.requireValue());
addChildRef(childRef, containers, maybeBrand.requireValue(), episode.isActivelyPublished());
addChildRef(childRef, programmeGroups, maybeSeries.requireValue(), episode.isActivelyPublished());
}
public void includeSeriesInTopLevelContainer(Series series) {
String containerUri = series.getParent().getUri();
Maybe<Container> maybeContainer = getContainer(containerUri, containers);
if (!maybeContainer.hasValue()) {
throw new IllegalStateException(String.format("Container %s not found in %s for series child ref %s", containerUri, containers.getName(), series.getCanonicalUri()));
}
Container container = maybeContainer.requireValue();
if(!(container instanceof Brand)) {
throw new IllegalStateException(String.format("Container %s for series child ref %s is not brand", containerUri, series.getCanonicalUri()));
}
Brand brand = (Brand) container;
List<SeriesRef> merged = mergeSeriesRefs(series.seriesRef(), brand.getSeriesRefs(), series.isActivelyPublished());
brand.setSeriesRefs(merged);
brand.setThisOrChildLastUpdated(laterOf(brand.getThisOrChildLastUpdated(), series.getThisOrChildLastUpdated()));
save(brand, containers);
series.setParent(brand);
}
private void save(Container container, DBCollection collection) {
if (!container.hashChanged(containerTranslator.hashCodeOf(container, true))) {
log.debug("Container {} hash not changed. Not writing.", container.getCanonicalUri());
return;
}
log.debug("Container {} hash changed so writing to db. There are {} ChildRefs",
container.getCanonicalUri(), container.getChildRefs().size());
collection.save(containerTranslator.toDBO(container, true));
}
private DateTime laterOf(DateTime left, DateTime right) {
if (left == null) {
return right;
}
if (right == null) {
return left;
}
return left.isAfter(right) ? left : right;
}
public void includeItemInTopLevelContainer(Item item) {
includeChildRefInContainer(item, item.childRef(), containers, CHILDREN_KEY, item.isActivelyPublished());
}
private void includeChildRefInContainer(Item item, ChildRef ref, DBCollection collection, String key, boolean activelyPublished) {
String containerUri = item.getContainer().getUri();
Maybe<Container> maybeContainer = getContainer(containerUri, collection);
if (maybeContainer.hasValue()) {
Container container = maybeContainer.requireValue();
addChildRef(ref, collection, container, item.isActivelyPublished());
item.setContainer(container);
} else {
throw new IllegalStateException(String.format("Container %s not found in %s for child ref %s", containerUri, collection.getName(), ref.getUri()));
}
}
private void addChildRef(ChildRef ref, DBCollection collection, Container container, boolean activelyPublished) {
List<ChildRef> merged = mergeChildRefs(ref, container.getChildRefs(), activelyPublished);
container.setThisOrChildLastUpdated(laterOf(container.getThisOrChildLastUpdated(), ref.getUpdated()));
container.setChildRefs(merged);
save(container, collection);
}
private List<ChildRef> mergeChildRefs(ChildRef newChildRef, Iterable<ChildRef> currentChildRefs, boolean activelyPublished) {
// filter out new refs so they can be overwritten.
currentChildRefs = filter(currentChildRefs, not(equalTo(newChildRef)));
if (activelyPublished) {
return ChildRef.dedupeAndSort(ImmutableList.<ChildRef> builder().addAll(currentChildRefs).add(newChildRef).build());
} else {
return ChildRef.dedupeAndSort(ImmutableList.<ChildRef> builder().addAll(currentChildRefs).build());
}
}
private List<SeriesRef> mergeSeriesRefs(SeriesRef newSeriesRef, Iterable<SeriesRef> currentSeriesRefs, Boolean activelyPublished) {
// filter out new refs so they can be overwritten.
currentSeriesRefs = filter(currentSeriesRefs, not(equalTo(newSeriesRef)));
if(activelyPublished) {
return SeriesRef.dedupeAndSort(ImmutableList.<SeriesRef>builder().addAll(currentSeriesRefs).add(newSeriesRef).build());
} else {
return SeriesRef.dedupeAndSort(ImmutableList.<SeriesRef>builder().addAll(currentSeriesRefs).build());
}
}
private Maybe<Container> getContainer(String canonicalUri, DBCollection collection) {
DBObject dbo = collection.findOne(where().idEquals(canonicalUri).build());
if (dbo == null) {
return Maybe.nothing();
}
return Maybe.<Container> fromPossibleNullValue(containerTranslator.fromDB(dbo, true));
}
}