Compare commits
31 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
78e522b1fa | 8 years ago |
|
|
16608ac882 | 8 years ago |
|
|
1b00dd3df7 | 8 years ago |
|
|
aece04aa6c | 8 years ago |
|
|
a0f1e499af | 8 years ago |
|
|
a690891eaf | 8 years ago |
|
|
9a9cbea429 | 8 years ago |
|
|
ed9bd43fb0 | 8 years ago |
|
|
b283f5f758 | 8 years ago |
|
|
4e188bbfbf | 8 years ago |
|
|
29a55180e3 | 8 years ago |
|
|
c95c522754 | 8 years ago |
|
|
4ad070c9bc | 8 years ago |
|
|
a8957c9d10 | 8 years ago |
|
|
5a56c404a4 | 8 years ago |
|
|
1603c2bd50 | 8 years ago |
|
|
4aa8633aae | 8 years ago |
|
|
0d8a608da0 | 8 years ago |
|
|
d5e72786fe | 8 years ago |
|
|
7d372f1ad3 | 8 years ago |
|
|
99c2a596c0 | 8 years ago |
|
|
f40786a535 | 8 years ago |
|
|
58d082e8d5 | 8 years ago |
|
|
2ada05ea76 | 8 years ago |
|
|
ab8a623b74 | 8 years ago |
|
|
b6a6fc47ea | 8 years ago |
|
|
0b37076dfd | 8 years ago |
|
|
47019cf459 | 8 years ago |
|
|
3f352e2cc8 | 8 years ago |
|
|
a976fe8405 | 8 years ago |
|
|
ec5e86ce3f | 8 years ago |
47 changed files with 1160 additions and 81 deletions
@ -0,0 +1,7 @@ |
|||||||
|
from __future__ import absolute_import, unicode_literals |
||||||
|
|
||||||
|
# This will make sure the app is always imported when |
||||||
|
# Django starts so that shared_task will use this app. |
||||||
|
from .celery import app as celery_app |
||||||
|
|
||||||
|
__all__ = ['celery_app'] |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
|
||||||
|
|
||||||
|
def dev(request): |
||||||
|
try: |
||||||
|
PROD_MODE = settings.PROD_MODE |
||||||
|
except AttributeError: |
||||||
|
PROD_MODE = True |
||||||
|
|
||||||
|
context = { |
||||||
|
'PROD_MODE': PROD_MODE |
||||||
|
} |
||||||
|
context['request'] = request |
||||||
|
return context |
||||||
@ -1,7 +1,11 @@ |
|||||||
from easy_thumbnails.files import get_thumbnailer |
from easy_thumbnails.files import get_thumbnailer |
||||||
|
from easy_thumbnails.exceptions import InvalidImageFormatError |
||||||
|
|
||||||
|
|
||||||
def thumbnail(img, size='420x420'): |
def thumbnail(img, size='420x420'): |
||||||
weight, height = size.split('x') |
weight, height = size.split('x') |
||||||
options = {'size': (int(weight), int(height)), 'background': '#fff'} |
options = {'size': (int(weight), int(height)), 'background': '#fff'} |
||||||
return get_thumbnailer(img).get_thumbnail(options) |
try: |
||||||
|
return get_thumbnailer(img).get_thumbnail(options) |
||||||
|
except (ValueError, InvalidImageFormatError): |
||||||
|
return {'url': ''} |
||||||
|
|||||||
@ -0,0 +1,29 @@ |
|||||||
|
{% extends 'base.jinja' %} |
||||||
|
|
||||||
|
{% block meta_description %}{% endblock %} |
||||||
|
|
||||||
|
{% block title %} |
||||||
|
Поймай свою цену |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block stylesheet %}{% endblock stylesheet %} |
||||||
|
|
||||||
|
|
||||||
|
{% block jss %} |
||||||
|
<script src="/static/js/dtys.js"></script> |
||||||
|
{% endblock jss %} |
||||||
|
|
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<div class="index-goods"> |
||||||
|
<div class="row"> |
||||||
|
<h1>Поймай свою цену</h1> |
||||||
|
|
||||||
|
<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,50 @@ |
|||||||
|
<div class="col-md-4 col-xs-4 col-sm-4 col-lg-4"> |
||||||
|
<div class="thumbnail dtys__product {% if object.is_stopped %}stopped{% endif %}" id="dtys_product_{{ object.id }}"> |
||||||
|
<div class="wait-block"> |
||||||
|
<span class="wait-icon glyphicon glyphicon-refresh glyphicon-refresh-animate"></span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="sold-block alert-danger"> |
||||||
|
<div class="sold-block__message">Продано!</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
{% set product = object.product.product %} |
||||||
|
{% set im = product.main_image()|thumbnail("420x420") %} |
||||||
|
|
||||||
|
<a> |
||||||
|
<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"> |
||||||
|
<span> |
||||||
|
{{ product.title }} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="price counter"> |
||||||
|
{% 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,10 @@ |
|||||||
|
DTYS |
||||||
|
|
||||||
|
Для возврата незаказанного товара нужно чтобы работал celery |
||||||
|
Его нужно запустить со следующими параметрами: |
||||||
|
|
||||||
|
celery -A dtys.tasks worker --loglevel=info |
||||||
|
|
||||||
|
|
||||||
|
Выбранный (остановленный) товар возвращается на страницу после некоторого |
||||||
|
количества секунд, указанного в settings.RESUME_DTYS_DELAY_SECONDS |
||||||
@ -0,0 +1 @@ |
|||||||
|
default_app_config = 'dtys.apps.DtysConfig' |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
from django.contrib import admin |
||||||
|
|
||||||
|
from .models import DTYSModel |
||||||
|
|
||||||
|
|
||||||
|
@admin.register(DTYSModel) |
||||||
|
class DTYSModelAdmin(admin.ModelAdmin): |
||||||
|
raw_id_fields = ('product',) |
||||||
|
list_display = ( |
||||||
|
'id', |
||||||
|
'product', |
||||||
|
'is_stopped', |
||||||
|
'is_public' |
||||||
|
) |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
from django.apps import AppConfig |
||||||
|
|
||||||
|
|
||||||
|
class DtysConfig(AppConfig): |
||||||
|
"""Drop `Til You Shop""" |
||||||
|
|
||||||
|
name = 'dtys' |
||||||
|
verbose_name = "Поймай свою цену" |
||||||
@ -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,65 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-11-03 21:58 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('dtys', '0001_initial'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='is_ordered', |
||||||
|
field=models.BooleanField(default=False, verbose_name='Заказан'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='end_date', |
||||||
|
field=models.DateField(verbose_name='Дата окончания'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='end_time', |
||||||
|
field=models.TimeField(verbose_name='Время окончания'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='is_public', |
||||||
|
field=models.BooleanField(default=False, verbose_name='Опубликован'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='is_stopped', |
||||||
|
field=models.BooleanField(default=False, verbose_name='Остановлен'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='price_end', |
||||||
|
field=models.IntegerField(verbose_name='Конечная цена (минимальная)'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='price_start', |
||||||
|
field=models.IntegerField(verbose_name='Начальная цена'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='price_stopped', |
||||||
|
field=models.IntegerField(default=0, verbose_name='Прайс заказа'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='start_date', |
||||||
|
field=models.DateField(verbose_name='Дата начала'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='dtysmodel', |
||||||
|
name='start_time', |
||||||
|
field=models.TimeField(verbose_name='Время начала'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,95 @@ |
|||||||
|
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): |
||||||
|
"""Drop `Til You Shop Model""" |
||||||
|
|
||||||
|
product = models.ForeignKey(ProductVariation) |
||||||
|
|
||||||
|
price_start = models.IntegerField(verbose_name="Начальная цена") |
||||||
|
price_end = models.IntegerField(verbose_name="Конечная цена (минимальная)") |
||||||
|
price_stopped = models.IntegerField(verbose_name="Прайс заказа", default=0) |
||||||
|
|
||||||
|
start_date = models.DateField(verbose_name='Дата начала') |
||||||
|
start_time = models.TimeField(verbose_name='Время начала') |
||||||
|
|
||||||
|
end_date = models.DateField(verbose_name='Дата окончания') |
||||||
|
end_time = models.TimeField(verbose_name='Время окончания') |
||||||
|
|
||||||
|
is_stopped = models.BooleanField(verbose_name="Остановлен", default=False) |
||||||
|
is_public = models.BooleanField(verbose_name="Опубликован", default=False) |
||||||
|
is_ordered = models.BooleanField(verbose_name="Заказан", default=False) |
||||||
|
|
||||||
|
class Meta: |
||||||
|
verbose_name = "Счётчик цены" |
||||||
|
verbose_name_plural = "Счётчики цен" |
||||||
|
|
||||||
|
@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_dec_by_sec(self): |
||||||
|
return "%.1f" % (self.get_price_diff() / self.get_date_delta()) |
||||||
|
|
||||||
|
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 self.price_stopped == 0: |
||||||
|
self.price_stopped = max(self.price_end, self.get_current_price()) |
||||||
|
|
||||||
|
self.save() |
||||||
|
|
||||||
|
def save(self, *args, **kwargs): |
||||||
|
if not self.id: |
||||||
|
pass |
||||||
|
|
||||||
|
ProductVariation.objects.filter(id=self.product.id)\ |
||||||
|
.update(has_discount=False) |
||||||
|
|
||||||
|
super(DTYSModel, self).save(*args, **kwargs) |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
celery=4.1.0 |
||||||
|
redis |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
from celery import task |
||||||
|
|
||||||
|
# app = Celery('tasks', broker='redis://localhost:6379/0') |
||||||
|
|
||||||
|
|
||||||
|
@task |
||||||
|
def return_dtys(dtys_id): |
||||||
|
from .models import DTYSModel |
||||||
|
|
||||||
|
DTYSModel.objects.filter(id=dtys_id, is_ordered=False)\ |
||||||
|
.update(is_stopped=False, price_stopped=0) |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
from django.test import TestCase |
||||||
|
|
||||||
|
# Create your tests here. |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
from django.conf.urls import patterns, url |
||||||
|
|
||||||
|
from .views import DTYSListView, DTYSInfoView, DTYSBuyView, dtys_modified |
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns( |
||||||
|
'', |
||||||
|
url(r'^$', DTYSListView.as_view()), |
||||||
|
url(r'^updates$', dtys_modified), |
||||||
|
url(r'^info/$', DTYSInfoView.as_view()), |
||||||
|
url(r'^buy/(?P<pk>\d+)/$', DTYSBuyView.as_view()) |
||||||
|
) |
||||||
@ -0,0 +1,165 @@ |
|||||||
|
import os |
||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
from django.http import ( |
||||||
|
Http404, |
||||||
|
HttpResponse, |
||||||
|
HttpResponseRedirect, |
||||||
|
JsonResponse, |
||||||
|
) |
||||||
|
from django.core.cache import cache |
||||||
|
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__) |
||||||
|
|
||||||
|
|
||||||
|
DTYS_UPDATE_FILE = os.path.join(settings.MEDIA_ROOT, "dtys_update") |
||||||
|
|
||||||
|
|
||||||
|
def update_dtys_etag(): |
||||||
|
fhandle = open(DTYS_UPDATE_FILE, 'a') |
||||||
|
|
||||||
|
try: |
||||||
|
os.utime(DTYS_UPDATE_FILE, None) |
||||||
|
except FileNotFoundError: |
||||||
|
open(DTYS_UPDATE_FILE, 'a').close() |
||||||
|
finally: |
||||||
|
fhandle.close() |
||||||
|
|
||||||
|
|
||||||
|
def latest_entry(request): |
||||||
|
DTYSModel.objects.filter( |
||||||
|
end_date__lte=timezone.now().date(), |
||||||
|
end_time__lte=timezone.now().time() |
||||||
|
).update(is_stopped=True) |
||||||
|
|
||||||
|
d = DTYSModel.objects.filter( |
||||||
|
is_public=True, |
||||||
|
is_stopped=False |
||||||
|
) |
||||||
|
|
||||||
|
if d.count() != cache.get("dtysETAGCount"): |
||||||
|
cache.set("dtysETAGCount", d.count()) |
||||||
|
update_dtys_etag() |
||||||
|
|
||||||
|
if not os.path.exists(DTYS_UPDATE_FILE): |
||||||
|
update_dtys_etag() |
||||||
|
|
||||||
|
modified = str(os.path.getmtime(DTYS_UPDATE_FILE)) |
||||||
|
|
||||||
|
return modified |
||||||
|
|
||||||
|
|
||||||
|
@etag(latest_entry) |
||||||
|
def dtys_modified(request): |
||||||
|
return HttpResponse('') |
||||||
|
|
||||||
|
|
||||||
|
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() |
||||||
|
) |
||||||
|
if qs.count() == 0: |
||||||
|
raise Http404() |
||||||
|
|
||||||
|
return qs |
||||||
|
|
||||||
|
|
||||||
|
class DTYSBuyView(DetailView): |
||||||
|
model = DTYSModel |
||||||
|
|
||||||
|
def get_queryset(self, *args, **kwargs): |
||||||
|
qs = super(DTYSBuyView, 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() |
||||||
|
) |
||||||
|
|
||||||
|
if qs.count() == 0: |
||||||
|
raise Http404() |
||||||
|
|
||||||
|
return qs |
||||||
|
|
||||||
|
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 |
||||||
|
from .tasks import return_dtys |
||||||
|
|
||||||
|
update_dtys_etag() |
||||||
|
|
||||||
|
# stop DTYS for this product |
||||||
|
obj.stop() |
||||||
|
|
||||||
|
ProductVariation.objects.filter(id=obj.product.id)\ |
||||||
|
.update(price=obj.price_stopped) |
||||||
|
obj.product.price = int(obj.price_stopped) |
||||||
|
obj.product.save() |
||||||
|
|
||||||
|
# Add to cart |
||||||
|
cart = Cart(self.request) |
||||||
|
|
||||||
|
cart.add_item(CartItem(obj.product, 1)) |
||||||
|
|
||||||
|
return_dtys.apply_async( |
||||||
|
(obj.id, ), countdown=settings.RESUME_DTYS_DELAY_SECONDS) |
||||||
|
|
||||||
|
return HttpResponseRedirect('/store/cart/') |
||||||
|
|
||||||
|
return HttpResponseRedirect('/dtys/') |
||||||
|
|
||||||
|
|
||||||
|
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, |
||||||
|
'stopped_price': i.price_stopped |
||||||
|
} |
||||||
|
|
||||||
|
if not i.is_stopped: |
||||||
|
dtys_item['current_price'] = i.current_price_format() |
||||||
|
dtys_item['price_dec_in_sec'] = i.get_dec_by_sec() |
||||||
|
|
||||||
|
data['dtys_list'].append(dtys_item) |
||||||
|
|
||||||
|
return JsonResponse(data) |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
celery -A batiskaf worker --loglevel=info >> celery.log & |
||||||
@ -0,0 +1,162 @@ |
|||||||
|
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 countdownPrice(el, beginValue, decInSecond) { |
||||||
|
var counter = el.find(".counter"); |
||||||
|
|
||||||
|
counter.text(beginValue); |
||||||
|
|
||||||
|
if (el.attr("data-tid") ) { |
||||||
|
var tid = el.attr("data-tid") |
||||||
|
clearInterval(tid); |
||||||
|
} |
||||||
|
|
||||||
|
var delay = 1000; |
||||||
|
var div = 2.0; |
||||||
|
|
||||||
|
var timerId = setInterval(function(){ |
||||||
|
var val = parseFloat(counter.text()); |
||||||
|
var new_val = (val - (decInSecond / div)).toFixed(); |
||||||
|
counter.text(new_val); |
||||||
|
}, delay / div); |
||||||
|
|
||||||
|
el.attr("data-tid", timerId); |
||||||
|
} |
||||||
|
|
||||||
|
function countdownPriceStop(el) { |
||||||
|
clearInterval(el.attr("data-tid")); |
||||||
|
} |
||||||
|
|
||||||
|
function get_price() { |
||||||
|
$.ajax({ |
||||||
|
url: "/dtys/info/", |
||||||
|
method: "GET", |
||||||
|
success: function(data, textStatus, jqXHR) { |
||||||
|
console.log(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(); |
||||||
|
|
||||||
|
product.find('.price').text(i.stopped_price); |
||||||
|
|
||||||
|
countdownPriceStop(product); |
||||||
|
} else { |
||||||
|
product.removeClass("stopped"); |
||||||
|
product.find(".price").text(i.current_price); |
||||||
|
fake_add_to_cart_btn.hide(); |
||||||
|
add_to_cart_btn.show(); |
||||||
|
|
||||||
|
countdownPrice(product, i.current_price, i.price_dec_in_sec); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}, |
||||||
|
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){ |
||||||
|
$(".dtys__product").each(function(){ |
||||||
|
$(this).removeClass("wait"); |
||||||
|
}); |
||||||
|
|
||||||
|
if(me.ETag != x.getResponseHeader('ETag')){ |
||||||
|
me.params.onModified(data,x,me.lastModified); |
||||||
|
me.lastModified = x.getResponseHeader('Last-Modified'); |
||||||
|
} |
||||||
|
me.ETag = x.getResponseHeader('ETag'); |
||||||
|
}, |
||||||
|
error: function(jqXHR, textStatus, errorThrown) { |
||||||
|
$(".dtys__product").each(function(){ |
||||||
|
$(this).addClass("wait"); |
||||||
|
countdownPriceStop($(this)); |
||||||
|
}); |
||||||
|
}, |
||||||
|
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(); |
||||||
|
this.doUpdate(); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
$(function(){ |
||||||
|
var upd = new Updater(); |
||||||
|
|
||||||
|
get_price(); |
||||||
|
|
||||||
|
upd.init({ |
||||||
|
url: "/dtys/updates", |
||||||
|
onModified: function(data, x, modified) { |
||||||
|
get_price(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
upd.start(); |
||||||
|
}); |
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,19 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0044_auto_20151223_1612'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='orderdata', |
||||||
|
name='deliv_type', |
||||||
|
field=models.CharField(max_length=2, blank=True, verbose_name='Способ Алемтат', choices=[('E', 'Экспресс-отправления по РК'), ('T', 'Не срочные отправления по РК')], null=True, default=''), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0045_auto_20160107_1403'), |
||||||
|
('store', '0045_auto_20151223_1646'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0046_merge'), |
||||||
|
('store', '0046_auto_20160202_1121'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0047_merge'), |
||||||
|
('store', '0047_auto_20160229_1409'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0048_auto_20160302_0839'), |
||||||
|
('store', '0048_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2016-03-07 20:44 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0049_merge'), |
||||||
|
('store', '0049_auto_20160307_2043'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2016-03-12 16:33 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0050_merge'), |
||||||
|
('store', '0050_auto_20160312_1628'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2016-03-14 11:41 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0051_cartmodel'), |
||||||
|
('store', '0051_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2016-04-04 10:33 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0053_productvariation_delivery_date'), |
||||||
|
('store', '0052_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2016-07-15 19:20 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0054_merge'), |
||||||
|
('store', '0054_auto_20160715_1811'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-03-07 15:49 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0057_auto_20170306_1933'), |
||||||
|
('store', '0055_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-05-07 02:57 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0059_auto_20170506_1116'), |
||||||
|
('store', '0058_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-06-03 02:27 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0060_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='orderdata', |
||||||
|
name='city', |
||||||
|
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Город Алемтат'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='orderdata', |
||||||
|
name='deliv_type', |
||||||
|
field=models.CharField(blank=True, default='', max_length=2, null=True, verbose_name='Способ Алемтат'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-06-03 02:28 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0061_auto_20170603_0220'), |
||||||
|
('store', '0061_auto_20170603_0227'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-11-07 16:56 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0061_auto_20170603_0220'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='productvariation', |
||||||
|
name='has_discount', |
||||||
|
field=models.BooleanField(default=True, help_text='Если не указано, то скидка при расчёте цены не учитывается', verbose_name='Со скидкой'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.9.3 on 2017-11-07 18:10 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0062_productvariation_has_discount'), |
||||||
|
('store', '0062_merge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
] |
||||||
Loading…
Reference in new issue