/** * Mad-Advertisement * Copyright (C) 2011 Thorsten Marx <thmarx@gmx.net> * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package net.mad.ads.server.utils.selection.impl; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Collections2; import net.mad.ads.base.api.track.Criterion; import net.mad.ads.base.api.track.events.EventType; import net.mad.ads.db.definition.BannerDefinition; import net.mad.ads.db.definition.condition.ViewExpirationConditionDefinition; import net.mad.ads.db.enums.ConditionDefinitions; import net.mad.ads.db.enums.ExpirationResolution; import net.mad.ads.server.utils.RuntimeContext; import net.mad.ads.server.utils.context.AdContext; import net.mad.ads.server.utils.selection.BannerSelector; /** * Das Banner wird aufgrund der prozentualen Anzeige-Häufigkeit ausgewählt. * * 1. ermitteln, wie viel Prozent der Impressions schon erreicht wurde * 2. das Banner wählen, das prozentual am wenigsten angezeigt wurde * * Verhalten für Banner ohne Beschränkung der Impressionen: * 1. der kleinste ermittelte Prozentwert wird verwendet * * @author thmarx * */ public class ImpressionPercentageSingleBannerSelector implements BannerSelector { private static final Logger logger = LoggerFactory.getLogger(ImpressionPercentageSingleBannerSelector.class); private static final BannerSelector RANDOM_SELECTOR = new RandomSingleBannerSelector(); @Override public BannerDefinition selectBanner(List<BannerDefinition> banners, AdContext context) { List<BannerDecorator> decorators = new ArrayList<ImpressionPercentageSingleBannerSelector.BannerDecorator>(); for (BannerDefinition def : banners) { decorators.add(new BannerDecorator(def)); } // Sortieren Collections.sort(decorators, new Comparator<BannerDecorator>() { @Override public int compare(BannerDecorator o1, BannerDecorator o2) { float p1 = getPercentage(o1); float p2 = getPercentage(o2); o1.setPercentage(p1); o2.setPercentage(p2); if (p1 > p2) { return -1; } else if (p2 > p1) { return 1; } return 0; } }); Collections.reverse(decorators); /* * Alle Banner ermittel die in Frage kommen, also die, die noch nicht so häufig angezeigt wurden * */ List<BannerDefinition> bannerList = new ArrayList<BannerDefinition>(); /* * the smallest percentage view value used to select banner to choose the delivered banner * * The to steps to detect the smallest value and select all possible banners can be done in one step * because the list of banners is ordered by the view-percentage */ float sma = -2f; for (BannerDecorator bd : decorators) { // Every banner without expiration has -1 as percentage if (bd.getPercentage() == -1f) { bannerList.add(bd.getBanner()); } else if (sma == -2f) { // first banner with view expiration is used as minimum sma = bd.getPercentage(); bannerList.add(bd.getBanner()); } else if (bd.getPercentage() <= sma) { // take all banners smaller or equals to the minimum bannerList.add(bd.getBanner()); } else if (bd.getPercentage() > sma) { // reached the bigger banners break; } } return RANDOM_SELECTOR.selectBanner(bannerList, context); } private float getPercentage(BannerDecorator decorator) { try { BannerDefinition o1 = decorator.getBanner(); if (decorator.hasPercentage()) { return decorator.getPercentage(); } if (o1.hasConditionDefinition(ConditionDefinitions.VIEW_EXPIRATION)) { ViewExpirationConditionDefinition def = (ViewExpirationConditionDefinition) o1.getConditionDefinition(ConditionDefinitions.VIEW_EXPIRATION); if (def.getViewExpirations().containsKey(ExpirationResolution.DAY)) { Calendar from = Calendar.getInstance(Locale.GERMANY); from.set(Calendar.HOUR_OF_DAY, 0); from.set(Calendar.MINUTE, 0); from.set(Calendar.SECOND, 0); from.set(Calendar.MILLISECOND, 0); Calendar to = Calendar.getInstance(Locale.GERMANY); to.set(Calendar.HOUR_OF_DAY, 0); to.set(Calendar.MINUTE, 0); to.set(Calendar.SECOND, 0); to.set(Calendar.MILLISECOND, 0); to.add(Calendar.DAY_OF_WEEK, 1); int maxViewCount = def.getViewExpirations().get(ExpirationResolution.DAY); long viewCount = RuntimeContext.getTrackService().count(new Criterion(Criterion.Criteria.Banner, o1.getId()), EventType.IMPRESSION, from.getTime(), to.getTime()); float percent = 0.0f; if (maxViewCount > 0) { percent = (float)viewCount / (float)maxViewCount; } return percent; } else { return -1; } } else { return -1; } } catch (Exception e) { logger.error("", e); } return 0.0f; } class BannerDecorator { private BannerDefinition banner = null; private float percentage = -1.0f; public BannerDecorator (BannerDefinition banner) { this.banner = banner; } public float getPercentage() { return percentage; } public void setPercentage(float percentage) { this.percentage = percentage; } public BannerDefinition getBanner() { return banner; } public boolean hasPercentage () { return percentage != -1f; } } }