add dtys app

remotes/origin/stepan-k
Stepan Krapivin 8 years ago
parent 3f352e2cc8
commit 47019cf459
  1. 1
      batiskaf/settings.py
  2. 43
      batiskaf/templates/jinja2/dtys/index.jinja
  3. 37
      batiskaf/templates/jinja2/includes/dtys_product_thumb.jinja
  4. 0
      dtys/__init__.py
  5. 15
      dtys/admin.py
  6. 6
      dtys/apps.py
  7. 34
      dtys/migrations/0001_initial.py
  8. 0
      dtys/migrations/__init__.py
  9. 76
      dtys/models.py
  10. 3
      dtys/tests.py
  11. 11
      dtys/urls.py
  12. 114
      dtys/views.py
  13. 124
      static/js/dtys.js

@ -77,6 +77,7 @@ INSTALLED_APPS = (
'store',
'news',
'promo',
'dtys',
)
SITE_ID = 1

@ -0,0 +1,43 @@
{% extends 'base.jinja' %}
{% block meta_description %}{% endblock %}
{% block title %}
DTYS index
{% endblock %}
{% block stylesheet %}
<style>
.dtys__product {
margin-bottom: 20pt;
border: 1px dashed lightgray;
padding: 4pt;
}
.dtys .stopped {
border: 2px solid red;
}
</style>
{% endblock stylesheet %}
{% block jss %}
<script src="/static/js/dtys.js"></script>
{% endblock jss %}
{% block content %}
<div class="index-goods">
<div class="row">
<h1>D`TYS</h1>
<input type="hidden" value="{{ csrf_token }}">
<div class="dtys">
{% for object in object_list %}
{% include "includes/dtys_product_thumb.jinja" with context %}
{% endfor %}
</div>
</div>
</div>
{% endblock content %}

@ -0,0 +1,37 @@
<div class="col-md-4 col-xs-4 col-sm-4 col-lg-4">
<div class="thumbnail {% if object.is_stopped %}stopped{% endif %}" id="dtys_product_{{ object.id }}">
{% set product = object.product.product %}
{% set im = product.main_image()|thumbnail("420x420") %}
<a href="{{ product.get_absolute_url() }}">
<img src="/static/{{ im.url|watermark('medium-photo') }}"
class="img-responsive" alt="Купить {{ product.title }}"
title="Купить {{ product.title }}" width="210" height="210"></a>
<div class="caption">
<div class="title">
<a href="{{ product.get_absolute_url() }}">
{{ product.title }} ({{ object.product.product.id }})
</a>
</div>
<div class="price">
{% if not object.is_stopped %}
{{ object.current_price_format() }}
{% else %}
{{ object.price_stopped }}
{% endif %}
</div>
<div class="in-cart">
<a class="btn btn-warning btn-block add-to-cart"
href="/dtys/buy/{{ object.id }}/"
{% if object.is_stopped %}style="display: none"{% endif %}>
<span><i class="glyphicon glyphicon-shopping-cart"
aria-hidden="true"></i> Добавить в корзину</span>
</a>
<span class="btn btn-warning btn-block fake-add-to-cart" {% if not object.is_stopped %}style="display: none"{% endif %}>
<span><i class="glyphicon glyphicon-shopping-cart"
aria-hidden="true"></i> Добавить в корзину</span>
</span>
</div>
</div>
</div>
</div>

@ -0,0 +1,15 @@
from django.contrib import admin
from .models import DTYSModel
@admin.register(DTYSModel)
class DTYSModelAdmin(admin.ModelAdmin):
list_display = (
'id',
'product',
'is_stopped',
'is_public'
)
prepopulated_fields = {"price_stopped": ("price_end",)}

@ -0,0 +1,6 @@
from django.apps import AppConfig
class DtysConfig(AppConfig):
"Drop `Til You Shop"
name = 'dtys'

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.3 on 2017-10-26 22:45
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('store', '0061_auto_20170603_0220'),
]
operations = [
migrations.CreateModel(
name='DTYSModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('price_start', models.IntegerField()),
('price_end', models.IntegerField()),
('price_stopped', models.IntegerField(blank=True, null=True)),
('start_date', models.DateField()),
('start_time', models.TimeField()),
('end_date', models.DateField()),
('end_time', models.TimeField()),
('is_stopped', models.BooleanField(default=False)),
('is_public', models.BooleanField(default=False)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='store.ProductVariation')),
],
),
]

@ -0,0 +1,76 @@
from datetime import datetime
from django.db import models
from store.models import ProductVariation
def get_change(current, previous):
if current == previous:
return 100.0
try:
return (abs(current - previous) / previous) * 100.0
except ZeroDivisionError:
return 0
class DTYSModel(models.Model):
product = models.ForeignKey(ProductVariation)
price_start = models.IntegerField()
price_end = models.IntegerField()
price_stopped = models.IntegerField(default=0)
start_date = models.DateField()
start_time = models.TimeField()
end_date = models.DateField()
end_time = models.TimeField()
is_stopped = models.BooleanField(default=False)
is_public = models.BooleanField(default=False)
@property
def end_datetime(self):
return datetime.combine(self.end_date, self.end_time)
@property
def start_datetime(self):
return datetime.combine(self.start_date, self.start_time)
def get_price_diff(self):
return self.price_start - self.price_end
def _time_percent(self):
time_percent = get_change(
self.get_delta_now(),
self.get_date_delta()
)
return time_percent
def get_delta_now(self):
"""in seconds"""
from django.utils import timezone as tz
_now = tz.now()
return (self.end_datetime - _now).total_seconds()
def get_date_delta(self):
""" in seconds """
return (self.end_datetime - self.start_datetime).total_seconds()
def get_current_price(self):
dec_from_price = (self.get_price_diff() * (self._time_percent() / 100))
price_current = self.price_start - dec_from_price
return price_current
def current_price_format(self):
return "%.1f" % self.get_current_price()
def stop(self):
"""Stop this DTYS"""
self.is_stopped = True
if not self.price_stopped:
self.price_stopped = max(self.price_end, self.get_current_price())
self.save()

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,11 @@
from django.conf.urls import url
from .views import DTYSListView, DTYSInfoView, DTYSBuyView, dtys_modified
urlpatterns = [
url(r'^$', DTYSListView.as_view()),
url(u'^etg/$', dtys_modified),
url(u'^info/$', DTYSInfoView.as_view()),
url(u'^buy/(?P<pk>\d)/$', DTYSBuyView.as_view())
]

@ -0,0 +1,114 @@
from django.core.urlresolvers import reverse_lazy, reverse
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
from django.utils import timezone
from django.views.decorators.http import etag
from django.views.generic import View, ListView, DetailView
from .models import DTYSModel
import logging
log = logging.getLogger(__name__)
def get_change(current, previous):
if current == previous:
return 100.0
try:
return (abs(current - previous) / previous) * 100.0
except ZeroDivisionError:
return 0
class DTYSListView(ListView):
model = DTYSModel
template_name = "dtys/index.jinja"
def get_queryset(self, *args, **kwargs):
qs = super(DTYSListView, self).get_queryset(*args, **kwargs)
qs = qs.filter(
is_public=True,
start_date__lte=timezone.now().date(),
end_date__gte=timezone.now().date(),
start_time__lte=timezone.now().time(),
end_time__gte=timezone.now().time()
)
return qs
class DTYSBuyView(DetailView):
model = DTYSModel
def get(self, request, *args, **kwargs):
obj = self.get_object()
log.debug(obj.id)
if obj.is_public and not obj.is_stopped:
from store.cart import Cart, CartItem
from store.models import ProductVariation
# stop DTYS for this product
obj.stop()
# Add to cart
cart = Cart(self.request)
try:
item_variant = ProductVariation.objects.get(pk=obj.product.pk)
except ProductVariation.DoesNotExist:
return HttpResponseRedirect('/')
cart.add_item(CartItem(item_variant, 1))
return HttpResponseRedirect('/store/cart/')
return HttpResponseRedirect('/dtys/')
def latest_entry(request):
d = DTYSModel.objects.filter(
is_public=True,
is_stopped=False
)
if d.count() > 0:
pass
return "myETAG" + str(timezone.now().second)
@etag(latest_entry)
def dtys_modified(request):
return HttpResponse('')
class DTYSInfoView(View):
"""Ajax info"""
def get(self, request):
data = {
"dtys_list": []
}
dtys_list = DTYSModel.objects.filter(
is_public=True,
start_date__lte=timezone.now(),
end_date__gte=timezone.now(),
start_time__lte=timezone.now().time()
)
for i in dtys_list:
if i.end_time <= timezone.now().time():
i.stop()
dtys_item = {
'id': i.id,
'stopped': i.is_stopped
}
if not i.is_stopped:
dtys_item['current_price'] = i.current_price_format()
data['dtys_list'].append(dtys_item)
return JsonResponse(data)

@ -0,0 +1,124 @@
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function get_price() {
$.ajax({
url: "/dtys/info/",
method: "GET",
// beforeSend: function(xhr) {
// // var csrftoken = getCookie("csrftoken");
// // if (csrftoken) {
// // xhr.setRequestHeader("X-CSRFToken", csrftoken);
// // }
// },
success: function(data, textStatus, jqXHR) {
// console.log("DTYS data", data);
if (data.dtys_list.length > 0) {
var active = data.dtys_list;
active.forEach(function(i, idx, list) {
var product = $("#dtys_product_" + i.id);
var add_to_cart_btn = product.find(".add-to-cart");
var fake_add_to_cart_btn = product.find(".fake-add-to-cart");
if (i.stopped) {
product.addClass("stopped");
fake_add_to_cart_btn.show();
add_to_cart_btn.hide();
} else {
product.removeClass("stopped");
product.find(".price").text(i.current_price);
fake_add_to_cart_btn.hide();
add_to_cart_btn.show();
}
});
}
},
error: function(jqXHR, textStatus, errorThrow) {
console.log("что-то пошло не так, сервер не отвечает");
}
});
};
var Updater = function(){
this.params = {
period: 3000,
url: '',
onModified: function(data,x,modified){},
bgPause: false
};
this.interval = null;
this.ETag = '';
this.lastModified = '';
this.init = function(params){
var me = this;
this.params = $.extend(this.params, params);
if(this.params.bgPause){
$(window).blur(function(){ me.pause() });
$(window).focus(function(){ me.resume() });
}
};
this.start = function(){
var me = this;
this.interval = setInterval(function(){ me.doUpdate() }, this.params.period);
};
this.doUpdate = function(){
var me = this;
$.ajax(this.params.url, {
success: function(data,status,x){
if(me.ETag != x.getResponseHeader('ETag')){
me.params.onModified(data,x,me.lastModified);
me.lastModified = x.getResponseHeader('Last-Modified');
}
me.ETag = x.getResponseHeader('ETag');
},
beforeSend: function(x){
if(me.ETag != '') { x.setRequestHeader('If-None-Match', me.ETag); }
},
cache: false
});
};
this.pause = function(){
clearInterval(this.interval);
this.interval = null;
};
this.resume = function(){
if(this.interval != null) return;
this.start();
};
};
$(function(){
var upd = new Updater();
upd.init({
url: "/dtys/etg/",
onModified: function(data, x, modified) {
get_price();
console.log(data, x, modified);
}
});
upd.start();
});
Loading…
Cancel
Save