diff --git a/expobanner/__init__.py b/expobanner/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/expobanner/managers.py b/expobanner/managers.py new file mode 100644 index 00000000..78887ffa --- /dev/null +++ b/expobanner/managers.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -* +from random import choice, shuffle +from django.db import models + + +class BiasedManager(models.Manager): + def by_time(self, **kwargs): + all = super(BiasedManager, self).get_query_set().filter(**kwargs) + result = [] + for i in all: + for j in range(i.often): + result.append(i) + return result + + def one(self, **kwargs): + return choice(self.by_time(**kwargs)) + + def by_often(self, **kwargs): + result = self.by_time(**kwargs) + shuffle(result) + return result diff --git a/expobanner/models.py b/expobanner/models.py new file mode 100644 index 00000000..1984fcc3 --- /dev/null +++ b/expobanner/models.py @@ -0,0 +1,170 @@ +import hashlib +from datetime import datetime + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings +from django.contrib.sites.models import Site + +from .managers import BiasedManager + + +class URL(models.Model): + title = models.CharField(verbose_name=_('Title'), max_length=256) + url = models.CharField(verbose_name=_('URL or URL RegEx'), max_length=2048) + regex = models.BooleanField(verbose_name=_('RegEx'), default=False) + sites = models.ManyToManyField(Site, related_name='site_urls', verbose_name=_('Sites'), null=True, blank=True) + + public = models.BooleanField(verbose_name=_('Public'), default=True) + created_at = models.DateTimeField(verbose_name=_('Created At'), auto_now_add=True) + updated_at = models.DateTimeField(verbose_name=_('Updated At'), auto_now=True) + + def __unicode__(self): + return self.title + + class Meta: + ordering = ['-created_at'] + verbose_name = _('URL') + verbose_name_plural = _('URLs') + + +class BannerGroup (models.Model): + name = models.CharField(verbose_name=_('Name'), max_length=255) + slug = models.SlugField(verbose_name=_('Slug'), unique=True) + width = models.PositiveSmallIntegerField(verbose_name=_('Width'), default=0) + height = models.PositiveSmallIntegerField(verbose_name=_('Height'), default=0) + speed = models.PositiveSmallIntegerField(verbose_name=_('Speed'), default=2000) + + public = models.BooleanField(verbose_name=_('Public'), default=True) + created_at = models.DateTimeField(verbose_name=_('Created At'), auto_now_add=True) + updated_at = models.DateTimeField(verbose_name=_('Updated At'), auto_now=True) + + def size(self): + return '%sx%s' % (self.width, self.height) + + def __unicode__(self): + return '%s - [%s x %s]' % (self.name, self.width, self.height) + + class Meta: + ordering = ['name'] + verbose_name = _('Banner Group') + verbose_name_plural = _('Banner Groups') + + +class Banner(models.Model): + objects = BiasedManager() + + title = models.CharField(verbose_name=_('Title'), max_length=255, blank=True) + alt = models.CharField(verbose_name=_('Alt'), max_length=255) + + text = models.TextField(verbose_name=_('Text'), blank=True, null=True) + img = models.FileField(verbose_name=_('Image'), upload_to='expo_upload', blank=True, null=True) + url = models.CharField(verbose_name=_('URL'), max_length=1024) + + sort = models.PositiveSmallIntegerField(verbose_name=_('Sort'), default=500) + + group = models.ForeignKey(BannerGroup, related_name='banners', verbose_name=_('Group')) + often = models.PositiveSmallIntegerField( + verbose_name=_('Often'), + help_text=_('A ten will display 10 times more often that a one.'), + choices=[[i, i] for i in range(11)] + ) + urls = models.ManyToManyField(URL, related_name='url_banners', verbose_name=_('URLs'), null=True, blank=True) + + html = models.BooleanField(verbose_name=_('Is HTML?'), default=False) + flash = models.BooleanField(verbose_name=_('Is Flash?'), default=False) + + + public = models.BooleanField(verbose_name=_('Public'), default=True) + created_at = models.DateTimeField(verbose_name=_('Created At'), auto_now_add=True) + updated_at = models.DateTimeField(verbose_name=_('Updated At'), auto_now=True) + + def key(slef): + if hasattr(settings, 'SECRET_KEY'): + key = str(datetime.now()) + settings.SECRET_KEY + else: + key = str(datetime.now()) + return hashlib.md5(key).hexdigest() + + def log(self, request, type, key): + log = { + 'type': type, + 'key': key, + 'banner': self, + 'group': self.group, + 'ip': request.META.get('REMOTE_ADDR'), + 'user_agent': request.META.get('HTTP_USER_AGENT'), + 'page': request.META.get('HTTP_REFERER'), + } + + if request.user.is_authenticated(): + log['user'] = request.user + return Log.objects.create(**log) + + @models.permalink + def image(self): + return ('banner_view', (), {'banner_id': self.pk, 'key': self.key()}) + + def impressions(self): + return Log.objects.filter(banner=self.pk, type=0).count() + + def views(self): + return Log.objects.filter(banner=self.pk, type=1).count() + + def clicks(self): + return Log.objects.filter(banner=self.pk, type=2).count() + + def __unicode__(self): + return self.title or self.alt + + def get_absolute_url(self): + if self.url == '#': + return self.url + else: + @models.permalink + def get_absolute_url(self): + return ('banner_click', (), {'banner_id': self.pk, 'key': self.key()}) + return get_absolute_url(self) + + class Meta: + ordering = ['sort'] + verbose_name = _('Banner') + verbose_name_plural = _('Banners') + + +class Log(models.Model): + banner = models.ForeignKey(Banner, related_name='banner_logs') + group = models.ForeignKey(BannerGroup, related_name='group_logs', verbose_name=_('Group'), blank=True) + urls = models.ManyToManyField(URL, related_name='url_logs', verbose_name=_('URLs'), blank=True) + + user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='users', verbose_name=_('User')) + datetime = models.DateTimeField(verbose_name=_('Clicked At'), auto_now_add=True) + ip = models.IPAddressField(verbose_name=_('IP'), null=True, blank=True) + user_agent = models.CharField(verbose_name=_('User Agent'), max_length=1024, null=True, blank=True) + page = models.URLField(verbose_name=_('Page'), null=True, blank=True) + key = models.CharField(verbose_name=_('User Agent'), max_length=32, null=True, blank=True) + TYPE_CHOICES = ( + (0, 'impressions'), + (1, 'view'), + (2, 'click') + ) + + type = models.PositiveSmallIntegerField(verbose_name=_('Type'), max_length=1, default=0, choices=TYPE_CHOICES) + + def __unicode__(self): + return '%s - (%s)' % (self.banner, self.datetime) + + +class LogStat(models.Model): + banner = models.ForeignKey(Banner, related_name='banner_stat', verbose_name=_('Banner'), blank=True) + group = models.ForeignKey(BannerGroup, related_name='group_stat', verbose_name=_('Group'), blank=True) + urls = models.ManyToManyField(URL, related_name='url_bloks', verbose_name=_('URLs'), null=True, blank=True) + + date = models.DateField(verbose_name=_('Data')) + view = models.PositiveIntegerField(verbose_name=_('Views')) + click = models.PositiveIntegerField(verbose_name=_('Clicks')) + unique_click = models.PositiveIntegerField(verbose_name=_('Unique Views'), blank=True, null=True) + unique_view = models.PositiveIntegerField(verbose_name=_('Unique Clicks')) + + def __unicode__(self): + return '%s - (%s)' % (self.banner, self.date) diff --git a/expobanner/static/banners/css/slider.css b/expobanner/static/banners/css/slider.css new file mode 100644 index 00000000..d6d86672 --- /dev/null +++ b/expobanner/static/banners/css/slider.css @@ -0,0 +1,159 @@ +/* + ************************** + * =COMMON + ************************** +*/ +.b-slider { + position: relative; + display: block; + overflow: hidden; + margin: 0; + padding: 0; + width: 725px; + height: 360px; +} + +.b-slider-list { + position: absolute; + display: block; + overflow: hidden; + margin: 0; + padding: 0; + width: 725px; + height: 360px; + list-style: none; +} + +.b-slider-item { + position: absolute; + z-index: 50; + float: left; + overflow: hidden; + margin: 0; + padding: 0; + width: 725px; + height: 360px; +} + +.m-slider-current { + z-index: 100; +} + +.b-slider-item__img, +.b-slider-item__title, +.b-slider-item__text { + position: absolute; +} + +.b-slider-item__img { + top: 0; + left: 0; + text-decoration: none; +} + +.b-slider-item__link { + +} + +.b-slider-item__title { + bottom: 10px; + left: 10px; + width: 500px; + color: #333; + text-transform: uppercase; + text-shadow: 0 0 3px #fff, 0 0 2px #fff, 0 0 1px #fff; + letter-spacing: -5px; + font-size: 65px; + font-family: Calibri; + line-height: 0.8em; +} + +.b-slider-item__text { + position: absolute; + top: 150px; + left: 300px; + display: inline-block; + display: none; + margin: -10px; + padding: 10px; + max-width: 300px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.5); + box-shadow: 0 0 5px #fff; + color: #555; + line-height: 1.5em; +} + + +/* + ************************** + * =NAV + ************************** +*/ +.b-slider-nav { + position: absolute; + width: 100%; + top: 10px; + height: 30px; + left: 0; + z-index: 500; + display: block; + margin: 0; + padding: 0; + list-style-type: none; + text-align: center; +} + +.b-slider-nav-button { + display: inline-block; + margin: 5px; + width: 16px; + height: 16px; + border-radius: 8px; + background: #ddd; + color: transparent; + text-align: center; + font-weight: bold; + font-size: 8px; + line-height: 16px; + cursor: pointer; +} + +.b-slider-nav-button:hover { + background: #eee; +} + +.m-slider-nav-current_button { + background: #fff; +} + +.b-slider-nav-prev, +.b-slider-nav-next { + position: absolute; + top: 0; + z-index: 500; + padding-right: 20px; + padding-left: 20px; + color: #000; + text-shadow: 0 0 3px #fff, 0 0 2px #fff, 0 0 1px #fff; + font-size: 50px; + font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; + line-height: 360px; + opacity: 0.5; + cursor: pointer; +} + +.b-slider-nav-prev:hover, +.b-slider-nav-next:hover { + opacity: 0.8; +} + +.b-slider-nav-prev { + left: 0; + padding-left: 0; +} + +.b-slider-nav-next { + right: 0; + padding-right: 0; +} diff --git a/expobanner/static/banners/js/log.js b/expobanner/static/banners/js/log.js new file mode 100644 index 00000000..eed8e2a1 --- /dev/null +++ b/expobanner/static/banners/js/log.js @@ -0,0 +1,10 @@ + +$(document).ready(function() { + $('.b-banner-click').on('click', function(){ + $.get($(this).data('url')); + }); + + $(".b-banner").on('load', function() { + $.get($(this).data('view')); + }); +}); diff --git a/expobanner/static/banners/js/slider.js b/expobanner/static/banners/js/slider.js new file mode 100644 index 00000000..34769495 --- /dev/null +++ b/expobanner/static/banners/js/slider.js @@ -0,0 +1,71 @@ +$.fn.bannersSlider = function(options) { + $this = this; + var settings = $.extend( { + 'auto_play': false, + 'effect': 'fade', + 'speed' : 3000 + }, options); + + $this.current = 1; + $this.old = 1; + $this.len = $this.find('.b-slider-list li').length; + + $this.find('.b-slider-item').hide(); + $this.find('.m-slider-item-1').show(); + + $this.fadeTo = function(new_num) { + old_num = $this.old; + if (new_num != old_num) { + $this.find('.m-slider-item-' + new_num).hide(); + $this.find('.m-slider-item-' + new_num).fadeIn(1000); + $this.find('.m-slider-current').fadeOut(1000); + $this.find('.m-slider-current').removeClass('m-slider-current'); + $this.find('.m-slider-item-' + new_num).addClass('m-slider-current'); + + $this.find('.b-slider-nav-button').removeClass('m-slider-nav-current_button'); + + $this.find('.b-slider-nav-button[data-slide=' + new_num + ']').addClass('m-slider-nav-current_button'); + } + }; + + $this.prev = function() { + prev = ($this.current != 1) ? $this.current - 1 : $this.len; + $this.old = $this.current; + $this.current = prev; + + $this.fadeTo(prev); + }; + + $this.next = function() { + next = ($this.current != $this.len) ? $this.current + 1 : 1; + $this.old = $this.current; + $this.current = next; + + $this.fadeTo(next); + }; + + $this.children('.b-slider-nav-prev').on('click', function(){ + $this.prev(); + }); + + $this.children('.b-slider-nav-next').on('click', function(){ + $this.next(); + }); + + $this.find('.b-slider-nav-button').on('click', function(){ + to_slide = $(this).data('slide'); + + $this.old = $this.current; + $this.current = to_slide; + + $this.fadeTo(to_slide); + }); + + if (settings.auto_play) { + setInterval(function() { + $this.next(); + }, settings.speed); + } + + return $this; +}; diff --git a/expobanner/templates/banner.html b/expobanner/templates/banner.html new file mode 100644 index 00000000..85f460e1 --- /dev/null +++ b/expobanner/templates/banner.html @@ -0,0 +1,55 @@ +{% if banner.html %} + {% load banner %} +
+{% elif banner.flash %} + + + +{% else %} + +