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 %} + +{% endif %} + + \ No newline at end of file diff --git a/expobanner/templates/grid.html b/expobanner/templates/grid.html new file mode 100644 index 00000000..9571fd2d --- /dev/null +++ b/expobanner/templates/grid.html @@ -0,0 +1,32 @@ +{% load banner %} + +
+ {% for banner in banners %} + {% banner_one banner.id %} + {% endfor %} +
+ + \ No newline at end of file diff --git a/expobanner/templates/group.html b/expobanner/templates/group.html new file mode 100644 index 00000000..36800ccc --- /dev/null +++ b/expobanner/templates/group.html @@ -0,0 +1,30 @@ +{% load banner %} + +
+ {% for banner in banners %} + {% banner_one banner.id %} + {% endfor %} + +
\ No newline at end of file diff --git a/expobanner/templates/slider.html b/expobanner/templates/slider.html new file mode 100644 index 00000000..fcd6bd12 --- /dev/null +++ b/expobanner/templates/slider.html @@ -0,0 +1,74 @@ +{% load thumbnail %} + +{% if banners %} + +
+ + + + + 〈   +   〉 + +
+ + + + + +{% endif %} \ No newline at end of file diff --git a/expobanner/templatetags/__init__.py b/expobanner/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/expobanner/templatetags/banner.py b/expobanner/templatetags/banner.py new file mode 100644 index 00000000..c1491a74 --- /dev/null +++ b/expobanner/templatetags/banner.py @@ -0,0 +1,74 @@ +from ..models import Banner +from ..models import BannerGroup +from ..models import URL + +from django import template + +# For render tag +from django.template import Context +from django.template import Template + +import re + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def banner_group(context, group, tpl='group.html'): + try: + page_url = context['request'].path_info + site = context['request'].site + group = BannerGroup.objects.get(slug=group) + good_urls = [] + for url in URL.objects.filter(public=True, sites__in=[site]): + if url.regex: + url_re = re.compile(url.url) + if url_re.findall(page_url): + good_urls.append(url) + elif page_url == url.url: + good_urls.append(url) + banners = Banner.objects.filter(public=True, group=group, urls__in=good_urls) + except: + banners = False + group = False + if(banners and group): + context['banners'] = banners + context['group'] = group + + t = template.loader.get_template(tpl) + return t.render(template.Context(context)) + + +@register.simple_tag(takes_context=True) +def banner_one(context, banner_id, tpl='banner.html'): + try: + page_url = context['request'].path_info + site = context['request'].site + good_urls = [] + for url in URL.objects.filter(public=True, sites__in=[site]): + if url.regex: + url_re = re.compile(url.url) + if url_re.findall(page_url): + good_urls.append(url) + elif page_url == url.url: + good_urls.append(url) + + banner = Banner.objects.get(id=banner_id, public=True, urls__in=good_urls) + except: + banner = False + + context['banner'] = banner + + t = template.loader.get_template(tpl) + return t.render(template.Context(context)) + + +# block render +@register.simple_tag(takes_context=True) +def render(context, content): + try: + tpl = Template(content) + content = Context(context) + return tpl.render(content) + except: + return 'Render Error' diff --git a/expobanner/tests.py b/expobanner/tests.py new file mode 100644 index 00000000..501deb77 --- /dev/null +++ b/expobanner/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/expobanner/urls.py b/expobanner/urls.py new file mode 100644 index 00000000..97f7f95e --- /dev/null +++ b/expobanner/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [ + url(r'^click/(?P\d{1,4})/(?P[-\w]+)/$', views.click, name='banner_click'), + url(r'^view/(?P\d+)/(?P[-\w]+)/$', views.view, name='banner_view'), +] diff --git a/expobanner/views.py b/expobanner/views.py new file mode 100644 index 00000000..8874f598 --- /dev/null +++ b/expobanner/views.py @@ -0,0 +1,14 @@ +from django.shortcuts import redirect, get_object_or_404 +from .models import Banner + + +def click(request, banner_id, key): + banner = get_object_or_404(Banner, pk=banner_id) + banner.log(request, 2, key) + return redirect(banner.url) + + +def view(request, banner_id, key): + banner = get_object_or_404(Banner, pk=banner_id) + banner.log(request, 1, key) + return redirect(banner.img.url) diff --git a/proj/settings.py b/proj/settings.py index e0e21c5d..65769f12 100644 --- a/proj/settings.py +++ b/proj/settings.py @@ -282,6 +282,7 @@ INSTALLED_APPS = ( 'core', 'country', 'directories', + 'expobanner', 'exposition', 'file', 'news', diff --git a/templates/client/blank.html b/templates/client/blank.html index 2755ecc6..d697bd86 100644 --- a/templates/client/blank.html +++ b/templates/client/blank.html @@ -108,6 +108,20 @@ This template include basic anf main styles and js files, ga('create', 'UA-3151423-1', 'auto'); ga('send', 'pageview'); + + diff --git a/templates/client/static_client/js/expo_b.js b/templates/client/static_client/js/expo_b.js new file mode 100644 index 00000000..4a4b1531 --- /dev/null +++ b/templates/client/static_client/js/expo_b.js @@ -0,0 +1,7 @@ +/** + * Created by kotzilla on 17/07/15. + */ +$(function () { + + console.log('banners'); +}); \ No newline at end of file