Merge branch 'master' of github.com:pretix/pretix

This commit is contained in:
Raphael Michel
2015-07-06 15:01:09 +02:00
28 changed files with 630 additions and 341 deletions
+13
View File
@@ -0,0 +1,13 @@
include LICENSE
include README.rst
recursive-include base/static *
recursive-include base/templates *
recursive-include control/static *
recursive-include control/templates *
recursive-include presale/static *
recursive-include presale/templates *
recursive-include plugins/banktransfer/templates *
recursive-include plugins/paypal/templates *
recursive-include plugins/stripe/templates *
recursive-include plugins/stripe/static *
+4 -27
View File
@@ -1408,22 +1408,8 @@ class Quota(Versionable):
:raises Quota.LockTimeoutException: if the quota is locked every time we try
to obtain the lock
"""
retries = 5
for i in range(retries):
dt = now()
updated = Quota.objects.current.filter(
Q(identity=self.identity)
& Q(Q(locked__lt=dt - timedelta(seconds=120)) | Q(locked__isnull=True))
& Q(version_end_date__isnull=True)
).update(
locked=dt
)
if updated:
self.locked_here = dt
self.locked = dt
return True
time.sleep(2 ** i / 100)
raise Quota.LockTimeoutException()
from .services import locking
return locking.lock_quota(self)
def release(self, force=False):
"""
@@ -1431,17 +1417,8 @@ class Quota(Versionable):
the lock will only be released if it was issued in _this_ python
representation of the database object.
"""
if not self.locked_here and not force:
return False
updated = Quota.objects.current.filter(
identity=self.identity,
version_end_date__isnull=True
).update(
locked=None
)
self.locked_here = None
self.locked = None
return updated
from .services import locking
return locking.release_quota(self, force)
class Order(Versionable):
+111
View File
@@ -0,0 +1,111 @@
from datetime import timedelta
import logging
import time
from django.db.models import Q
from django.utils.timezone import now
from pretix import settings
from pretix.base.models import Quota
logger = logging.getLogger('pretix.base.locking')
def lock_quota(quota):
"""
Issue a lock on this quota so nobody can take tickets from this quota until
you release the lock. Will retry 5 times on failure.
:raises Quota.LockTimeoutException: if the quota is locked every time we try
to obtain the lock
"""
if settings.HAS_REDIS:
return lock_quota_redis(quota)
else:
return lock_quota_db(quota)
def lock_quota_db(quota):
retries = 5
for i in range(retries):
dt = now()
updated = Quota.objects.current.filter(
Q(identity=quota.identity)
& Q(Q(locked__lt=dt - timedelta(seconds=120)) | Q(locked__isnull=True))
& Q(version_end_date__isnull=True)
).update(
locked=dt
)
if updated:
quota.locked_here = dt
quota.locked = dt
return True
time.sleep(2 ** i / 100)
raise Quota.LockTimeoutException()
def release_quota(quota, force=False):
"""
Release a lock placed by :py:meth:`lock()`. If the parameter force is not set to ``True``,
the lock will only be released if it was issued in _this_ python
representation of the database object.
"""
if not quota.locked_here and not force:
return False
if settings.HAS_REDIS:
return release_quota_redis(quota)
else:
return release_quota_db(quota)
def release_quota_db(quota):
updated = Quota.objects.current.filter(
identity=quota.identity,
version_end_date__isnull=True
).update(
locked=None
)
quota.locked_here = None
quota.locked = None
return updated
def redis_lock_from_quota(quota):
from django_redis import get_redis_connection
from redis.lock import Lock
if not hasattr(quota, '_redis_lock'):
rc = get_redis_connection("redis")
quota._redis_lock = Lock(redis=rc, name='pretix_quota_%s' % quota.identity, timeout=120)
return quota._redis_lock
def lock_quota_redis(quota):
from redis.exceptions import RedisError
lock = redis_lock_from_quota(quota)
retries = 5
for i in range(retries):
dt = now()
try:
if lock.acquire(False):
quota.locked_here = dt
quota.locked = dt
return True
except RedisError:
logger.exception('Error locking a quota')
raise Quota.LockTimeoutException()
time.sleep(2 ** i / 100)
raise Quota.LockTimeoutException()
def release_quota_redis(quota):
from redis import RedisError
lock = redis_lock_from_quota(quota)
try:
lock.release()
except RedisError:
logger.exception('Error releasing a quota lock')
raise Quota.LockTimeoutException()
quota.locked_here = None
quota.locked = None
return True
@@ -18,6 +18,11 @@ nav.navbar {
margin: 0;
}
.navbar-header .navbar-events {
color: white;
padding-top: 6px;
padding-bottom: 6px;
}
.nav-pills {
margin-bottom: 10px;
}
@@ -66,4 +71,9 @@ nav.navbar {
transform: rotateX(180deg);
-ms-transform: rotateX(180deg); /* IE 9 */
-webkit-transform: rotateX(180deg); /* Safari and Chrome */
}
}
@media (max-width: @screen-sm-max) {
.navbar-nav {
margin-left: 0;
}
}
@@ -8,6 +8,7 @@
{% compress css %}
<link rel="stylesheet" type="text/less" href="{% static "pretixcontrol/less/auth.less" %}" />
{% endcompress %}
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="container">
@@ -18,20 +18,26 @@
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/main.js" %}"></script>
{% endcompress %}
{{ html_head|safe }}
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="#wrapper">
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-nav-collapse">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<button type="button" class="navbar-toggle navbar-events"
data-toggle="collapse" data-target=".navbar-events-collapse">
<i class="fa fa-calendar"></i><span class="caret"></span>
</button>
<a class="navbar-brand" href="{% url "control:index" %}">{{ settings.PRETIX_INSTANCE_NAME }}</a>
</div>
<ul class="nav navbar-nav navbar-top-links navbar-left">
<ul class="nav navbar-nav navbar-top-links navbar-left hidden-xs">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-calendar"></i>
{{ request.event }} <span class="caret"></span></a>
@@ -61,7 +67,19 @@
</li>
</ul>
<div class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-collapse">
<div class="sidebar-nav navbar-events-collapse navbar-collapse hidden-md hidden-lg">
<ul class="nav">
<li><a href="{% url "control:events" %}">{% trans "Event overview" %}</a></li>
{% for e in request.user.events_cache %}
<li>
<a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">
{{ e.name }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="sidebar-nav navbar-nav-collapse navbar-collapse">
<ul class="nav" id="side-menu">
{% block nav %}
<li>
@@ -7,46 +7,48 @@
<fieldset>
<legend>{% trans "Permissions" %}</legend>
{{ formset.management_form }}
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>{% trans "User" %}</th>
<th>{% trans "Change settings" %}</th>
<th>{% trans "Change products" %}</th>
<th>{% trans "View orders" %}</th>
<th>{% trans "Change orders" %}</th>
<th>{% trans "Change permissions" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<div class="table-responsive">
<table class="table table-striped table-condensed">
<thead>
<tr>
<td>{{ form.id }}{{ form.instance.user }}</td>
<td>{{ form.can_change_settings }}</td>
<td>{{ form.can_change_items }}</td>
<td>{{ form.can_view_orders }}</td>
<td>{{ form.can_change_orders }}</td>
<td>{{ form.can_change_permissions }}</td>
<td>{{ form.DELETE }}</td>
<th>{% trans "User" %}</th>
<th>{% trans "Change settings" %}</th>
<th>{% trans "Change products" %}</th>
<th>{% trans "View orders" %}</th>
<th>{% trans "Change orders" %}</th>
<th>{% trans "Change permissions" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
{% endfor %}
<tr>
<td>
<div class="row-fluid">
<div class="col-sm-12">
{% bootstrap_field add_form.user layout='inline' %}
</thead>
<tbody>
{% for form in formset %}
<tr>
<td>{{ form.id }}{{ form.instance.user }}</td>
<td>{{ form.can_change_settings }}</td>
<td>{{ form.can_change_items }}</td>
<td>{{ form.can_view_orders }}</td>
<td>{{ form.can_change_orders }}</td>
<td>{{ form.can_change_permissions }}</td>
<td>{{ form.DELETE }}</td>
</tr>
{% endfor %}
<tr>
<td>
<div class="row-fluid">
<div class="col-sm-12">
{% bootstrap_field add_form.user layout='inline' %}
</div>
</div>
</div>
</td>
<td>{{ add_form.can_change_settings }}</td>
<td>{{ add_form.can_change_items }}</td>
<td>{{ add_form.can_view_orders }}</td>
<td>{{ add_form.can_change_orders }}</td>
<td>{{ add_form.can_change_permissions }}</td>
</tr>
</tbody>
</table>
</td>
<td>{{ add_form.can_change_settings }}</td>
<td>{{ add_form.can_change_items }}</td>
<td>{{ add_form.can_view_orders }}</td>
<td>{{ add_form.can_change_orders }}</td>
<td>{{ add_form.can_change_permissions }}</td>
</tr>
</tbody>
</table>
</div>
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
@@ -4,25 +4,27 @@
{% block inside %}
<form action="" method="post">
{% csrf_token %}
<table class="table">
<thead>
<tr>
<th>{{ properties.0 }}</th>
<th>{% trans "Active" %}</th>
<th>{% trans "Price" %}</th>
</tr>
</thead>
<tbody>
{% for form in forms %}
{% bootstrap_form_errors form type='all' layout='inline' %}
<tr>
<td>{{ form.values.0 }}</td>
<td>{% bootstrap_field form.active layout='inline' %}</td>
<td>{% bootstrap_field form.default_price layout='inline' %} {{ form.default_price.errors }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ properties.0 }}</th>
<th>{% trans "Active" %}</th>
<th>{% trans "Price" %}</th>
</tr>
</thead>
<tbody>
{% for form in forms %}
{% bootstrap_form_errors form type='all' layout='inline' %}
<tr>
<td>{{ form.values.0 }}</td>
<td>{% bootstrap_field form.active layout='inline' %}</td>
<td>{% bootstrap_field form.default_price layout='inline' %} {{ form.default_price.errors }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
@@ -8,36 +8,38 @@
{% if major.row %}
<h3>{{ major.row }}</h3>
{% endif %}
<table class="table variation-matrix">
<thead>
<tr>
<th></th>
{% for val in properties.1.values.all %}
<th>{{ val.value }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for sub in major.forms %}
<div class="table-responsive">
<table class="table variation-matrix">
<thead>
<tr>
<td>{{ sub.row.value }}</td>
{% for form in sub.forms %}
<td>
<div class="row">
<div class="col-sm-5">
{% bootstrap_field form.active layout='inline' %}
</div>
<div class="col-sm-7">
{% bootstrap_field form.default_price layout='inline' %}
</div>
</div>
{{ form.default_price.errors }}
</td>
<th></th>
{% for val in properties.1.values.all %}
<th>{{ val.value }}</th>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for sub in major.forms %}
<tr>
<td>{{ sub.row.value }}</td>
{% for form in sub.forms %}
<td>
<div class="row">
<div class="col-sm-5">
{% bootstrap_field form.active layout='inline' %}
</div>
<div class="col-sm-7">
{% bootstrap_field form.default_price layout='inline' %}
</div>
</div>
{{ form.default_price.errors }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
@@ -6,26 +6,28 @@
<p>
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new category" %}</a>
</p>
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Product categories" %}</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for c in categories %}
<tr>
<td><strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}">{{ c.name }}</a></strong></td>
<td>
<a href="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
<a href="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
</td>
<td class="text-right"><a href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Product categories" %}</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for c in categories %}
<tr>
<td><strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}">{{ c.name }}</a></strong></td>
<td>
<a href="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
<a href="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
</td>
<td class="text-right"><a href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
@@ -14,30 +14,32 @@
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new product" %}</a>
</p>
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>{% trans "Product name" %}</th>
<th>{% trans "Category" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% regroup items by category as cat_list %}
{% for c in cat_list %}
{% for i in c.list %}
<div class="table-responsive">
<table class="table table-condensed table-hover">
<thead>
<tr>
<td><strong><a href="
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}">{{ i.name }}</a></strong></td>
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
<td>
<a href="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
<a href="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
</td>
<th>{% trans "Product name" %}</th>
<th>{% trans "Category" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% regroup items by category as cat_list %}
{% for c in cat_list %}
{% for i in c.list %}
<tr>
<td><strong><a href="
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}">{{ i.name }}</a></strong></td>
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
<td>
<a href="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
<a href="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
</td>
</tr>
{% endfor %}
{% endfor %}
{% endfor %}
</tbody>
</table>
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
@@ -6,23 +6,25 @@
<p>
<a href="{% url "control:event.items.properties.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new property" %}</a>
</p>
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Product properties" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for p in properties %}
<tr>
<td><strong><a href="
{% url "control:event.items.properties.edit" organizer=request.event.organizer.slug event=request.event.slug property=p.identity %}">{{ p.name }}</a></strong></td>
<td class="text-right"><a href="
{% url "control:event.items.properties.delete" organizer=request.event.organizer.slug event=request.event.slug property=p.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Product properties" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for p in properties %}
<tr>
<td><strong><a href="
{% url "control:event.items.properties.edit" organizer=request.event.organizer.slug event=request.event.slug property=p.identity %}">{{ p.name }}</a></strong></td>
<td class="text-right"><a href="
{% url "control:event.items.properties.delete" organizer=request.event.organizer.slug event=request.event.slug property=p.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
@@ -6,25 +6,27 @@
<p>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new question" %}</a>
</p>
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Question" %}</th>
<th>{% trans "Type" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for q in questions %}
<tr>
<td><strong><a href="
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.identity %}">{{ q.question }}</a></strong></td>
<td>{{ q.get_type_display }}</td>
<td class="text-right"><a href="
{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Question" %}</th>
<th>{% trans "Type" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for q in questions %}
<tr>
<td><strong><a href="
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.identity %}">{{ q.question }}</a></strong></td>
<td>{{ q.get_type_display }}</td>
<td class="text-right"><a href="
{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
@@ -6,35 +6,37 @@
<p>
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new quota" %}</a>
</p>
<table class="table table-hover table-quotas">
<thead>
<tr>
<th>{% trans "Quota name" %}</th>
<th>{% trans "Products" %}</th>
<th>{% trans "Total capacity" %}</th>
<th>{% trans "Capacity left" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for q in quotas %}
<tr>
<td><strong><a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.identity %}">{{ q.name }}</a></strong></td>
<td>
<ul>
{% for item in q.items.all %}
<li><a href="
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.identity %}"
>{{ item.name }}</a></li>
{% endfor %}
</ul>
</td>
<td>{{ q.size }}</td>
<td></td>
<td class="text-right"><a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-hover table-quotas">
<thead>
<tr>
<th>{% trans "Quota name" %}</th>
<th>{% trans "Products" %}</th>
<th>{% trans "Total capacity" %}</th>
<th>{% trans "Capacity left" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for q in quotas %}
<tr>
<td><strong><a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.identity %}">{{ q.name }}</a></strong></td>
<td>
<ul>
{% for item in q.items.all %}
<li><a href="
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.identity %}"
>{{ item.name }}</a></li>
{% endfor %}
</ul>
</td>
<td>{{ q.size }}</td>
<td></td>
<td class="text-right"><a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
@@ -26,29 +26,31 @@
<button class="btn btn-primary" type="submit">{% trans "Filter" %}</button>
</form>
</p>
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>{% trans "Order code" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Order total" %}</th>
<th>{% trans "Order date" %}</th>
<th>{% trans "Status" %}</th>
</tr>
</thead>
<tbody>
{% for o in orders %}
<tr>
<td><strong><a
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=o.code%}"
>{{ o.code }}</a></strong></td>
<td>{{ o.user.get_short_name }}</td>
<td>{{ o.total|floatformat:2 }} {{ request.event.currency }}</td>
<td>{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}</td>
<td>{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>{% trans "Order code" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Order total" %}</th>
<th>{% trans "Order date" %}</th>
<th>{% trans "Status" %}</th>
</tr>
</thead>
<tbody>
{% for o in orders %}
<tr>
<td><strong><a
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=o.code%}"
>{{ o.code }}</a></strong></td>
<td>{{ o.user.get_short_name }}</td>
<td>{{ o.total|floatformat:2 }} {{ request.event.currency }}</td>
<td>{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}</td>
<td>{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
@@ -3,62 +3,64 @@
{% block title %}{% trans "Order overview" %}{% endblock %}
{% block content %}
<h1>{% trans "Order overview" %}</h1>
<table class="table table-condensed table-hover table-product-overview">
<thead>
<tr>
<th>{% trans "Product" %}</th>
<th>{% trans "Total orders" %}</th>
<th>{% trans "Payment pending" %}</th>
<th>{% trans "Cancelled" %}</th>
<th>{% trans "Refunded" %}</th>
<th>{% trans "Paid" %}</th>
</tr>
</thead>
<tbody>
{% for tup in items_by_category %}
{% if tup.0 %}
<tr class="category">
<th>{{ tup.0.name }}</th>
<th>{{ tup.0.num_total }}</th>
<th>{{ tup.0.num_pending }}</th>
<th>{{ tup.0.num_cancelled }}</th>
<th>{{ tup.0.num_refunded }}</th>
<th>{{ tup.0.num_paid }}</th>
</tr>
{% endif %}
{% for item in tup.1 %}
<tr class="item {% if tup.0 %}categorized{% endif %}">
<td>{{ item.name }}</td>
<td>{{ item.num_total }}</td>
<td>{{ item.num_pending }}</td>
<td>{{ item.num_cancelled }}</td>
<td>{{ item.num_refunded }}</td>
<td>{{ item.num_paid }}</td>
</tr>
{% if item.has_variations %}
{% for var in item.all_variations %}
<tr class="variation {% if tup.0 %}categorized{% endif %}">
<td>{{ var }}</td>
<td>{{ var.num_total }}</td>
<td>{{ var.num_pending }}</td>
<td>{{ var.num_cancelled }}</td>
<td>{{ var.num_refunded }}</td>
<td>{{ var.num_paid }}</td>
</tr>
{% endfor %}
<div class="table-responsive">
<table class="table table-condensed table-hover table-product-overview">
<thead>
<tr>
<th>{% trans "Product" %}</th>
<th>{% trans "Total orders" %}</th>
<th>{% trans "Payment pending" %}</th>
<th>{% trans "Cancelled" %}</th>
<th>{% trans "Refunded" %}</th>
<th>{% trans "Paid" %}</th>
</tr>
</thead>
<tbody>
{% for tup in items_by_category %}
{% if tup.0 %}
<tr class="category">
<th>{{ tup.0.name }}</th>
<th>{{ tup.0.num_total }}</th>
<th>{{ tup.0.num_pending }}</th>
<th>{{ tup.0.num_cancelled }}</th>
<th>{{ tup.0.num_refunded }}</th>
<th>{{ tup.0.num_paid }}</th>
</tr>
{% endif %}
{% for item in tup.1 %}
<tr class="item {% if tup.0 %}categorized{% endif %}">
<td>{{ item.name }}</td>
<td>{{ item.num_total }}</td>
<td>{{ item.num_pending }}</td>
<td>{{ item.num_cancelled }}</td>
<td>{{ item.num_refunded }}</td>
<td>{{ item.num_paid }}</td>
</tr>
{% if item.has_variations %}
{% for var in item.all_variations %}
<tr class="variation {% if tup.0 %}categorized{% endif %}">
<td>{{ var }}</td>
<td>{{ var.num_total }}</td>
<td>{{ var.num_pending }}</td>
<td>{{ var.num_cancelled }}</td>
<td>{{ var.num_refunded }}</td>
<td>{{ var.num_paid }}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
</tbody>
<tfoot>
<tr class="total">
<th>{% trans "Total" %}</th>
<th>{{ total.num_total }}</th>
<th>{{ total.num_pending }}</th>
<th>{{ total.num_cancelled }}</th>
<th>{{ total.num_refunded }}</th>
<th>{{ total.num_paid }}</th>
</tr>
</tfoot>
</table>
</tbody>
<tfoot>
<tr class="total">
<th>{% trans "Total" %}</th>
<th>{{ total.num_total }}</th>
<th>{{ total.num_pending }}</th>
<th>{{ total.num_cancelled }}</th>
<th>{{ total.num_refunded }}</th>
<th>{{ total.num_paid }}</th>
</tr>
</tfoot>
</table>
</div>
{% endblock %}
@@ -53,7 +53,6 @@ class BankTransfer(BasePaymentProvider):
def order_control_render(self, request, order) -> str:
if order.payment_info:
payment_info = json.loads(order.payment_info)
payment_info['amount'] /= 100
else:
payment_info = None
template = get_template('pretixplugins/banktransfer/control.html')
@@ -66,3 +66,11 @@
.checkout-button-row {
padding: 15px 0;
}
@media (max-width: @screen-sm-max) {
.checkout-button-row div {
margin-bottom: 10px;
}
.page-header h1 small {
display: block;
}
}
@@ -41,4 +41,4 @@ footer {
a:hover .panel-primary > .panel-heading {
background-color: darken(@btn-primary-bg, 10%);
border-color: darken(@btn-primary-border, 12%);
}
}
@@ -15,6 +15,7 @@
<script type="text/javascript" src="{% static "pretixpresale/js/ui/main.js" %}"></script>
{% endcompress %}
{{ html_head|safe }}
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="container event">
@@ -8,6 +8,7 @@
Your order: {{ code }}
{% endblocktrans %}
{% include "pretixpresale/event/fragment_order_status.html" with order=order class="pull-right" %}
<div class="clearfix"></div>
</h2>
{% if order.status == "n" %}
<div class="panel panel-danger">
@@ -4,34 +4,37 @@
{% block content %}
<h2>{% trans "Your orders" %}</h2>
<table class="table">
<thead>
<th>{% trans "Order code" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Total" %}</th>
<th>{% trans "Status" %}</th>
<th></th>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td>{{ order.code }}</td>
<td>{{ order.datetime|date:"SHORT_DATE_FORMAT" }}</td>
<td>{{ event.currency }} {{ order.total|floatformat:2 }}</td>
<td>{% include "pretixpresale/event/fragment_order_status.html" with order=order %}</td>
<td><a href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug order=order.code %}">
{% trans "View details" %}
</a></td>
</tr>
{% empty %}
<tr>
<td colspan="5">
<em>{% trans "You did not yet place any orders." %}</em>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="table-responsive">
<table class="table">
<thead>
<th>{% trans "Order code" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Total" %}</th>
<th>{% trans "Status" %}</th>
<th></th>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td><a href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug order=order.code %}">
<strong>{{ order.code }}</strong></td>
<td>{{ order.datetime|date:"SHORT_DATE_FORMAT" }}</td>
<td>{{ event.currency }} {{ order.total|floatformat:2 }}</td>
<td>{% include "pretixpresale/event/fragment_order_status.html" with order=order %}</td>
<td><a href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug order=order.code %}">
{% trans "View details" %}
</a></td>
</tr>
{% empty %}
<tr>
<td colspan="5">
<em>{% trans "You did not yet place any orders." %}</em>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<a href="{% url "presale:event.index" event=request.event.slug organizer=request.event.organizer.slug %}"
class="btn btn-primary btn-lg">
<span class="fa fa-plus"></span>
+27 -6
View File
@@ -38,7 +38,7 @@ DEBUG = TEMPLATE_DEBUG = config.getboolean('django', 'debug', fallback=False)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.' + config.get('database', 'backend', fallback='sqlite3'),
'NAME': config.get('database', 'name', fallback=os.path.join(BASE_DIR, 'db.sqlite3')),
'NAME': config.get('database', 'name', fallback=os.path.join(DATA_DIR, 'db.sqlite3')),
'USER': config.get('database', 'user', fallback=''),
'PASSWORD': config.get('database', 'password', fallback=''),
'HOST': config.get('database', 'host', fallback=''),
@@ -76,13 +76,34 @@ SESSION_COOKIE_SECURE = SESSION_COOKIE_HTTPONLY = config.getboolean(
LANGUAGE_COOKIE_DOMAIN = SESSION_COOKIE_DOMAIN = CSRF_COOKIE_DOMAIN = config.get(
'pretix', 'cookiedomain', fallback=None)
if config.has_option('memcached', 'location'):
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': config.get('memcached', 'location'),
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
HAS_MEMCACHED = config.has_option('memcached', 'location')
if HAS_MEMCACHED:
CACHES['default'] = {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': config.get('memcached', 'location'),
}
HAS_REDIS = config.has_option('redis', 'location')
if HAS_REDIS:
CACHES['redis'] = {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": config.get('redis', 'location'),
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
if not HAS_MEMCACHED:
CACHES['default'] = CACHES['redis']
if config.getboolean('redis', 'sessions', fallback=False):
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "redis"
# Internal settings
+2
View File
@@ -0,0 +1,2 @@
django-redis>=4.1,<4.2
redis>=2.10,<2.11
+54
View File
@@ -0,0 +1,54 @@
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
# To use a consistent encoding
from codecs import open
from os import path
here = path.abspath(path.dirname(__file__))
# Get the long description from the relevant file
with open(path.join(here, '../README.md'), encoding='utf-8') as f:
long_description = f.read()
setup(
name='pretix',
version='0.0.0',
description='Reinventing ticket presales',
long_description=long_description,
url='http://pretix.eu',
author='Raphael Michel',
author_email='mail@raphaelmichel.de',
license='Apache License 2.0',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Intended Audience :: Other Audience',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Framework :: Django :: 1.8'
],
# What does your project relate to?
keywords='tickets web shop ecommerce',
packages=['pretix'],
install_requires=[
'Django>=1.8,<1.9', 'python-dateutil>=2.4,<2.5',
'pytz', 'django-bootstrap3>=6.1,<6.2', 'django-formset-js',
'cleanerversion>=1.5,<1.7', 'django-compressor>=1.6,<2.0',
'reportlab>=3.1.44,<3.2', 'PyPDF2', 'BeautifulSoup4', 'html5lib',
'slimit', 'lxml', 'static3==0.6.1', 'dj-static', 'chardet'
],
extras_require={
'dev': ['django-debug-toolbar>=1.3.0,<2.0'],
'test': ['pep8==1.5.7', 'pyflakes', 'pep8-naming', 'flake8', 'coverage',
'selenium', 'pytest', 'pytest-django'],
'memcached': ['pylibmc'],
'mysql': ['mysqlclient'],
'paypal': ['paypalrestsdk>=1.9,<1.10,<2.0'],
'postgres': ['psycopg2'],
'redis': ['django-redis>=4.1,<4.2', 'django-redis>=4.1,<4.2'],
'stripe': ['stripe>=1.22,<1.23']
},
include_package_data=True,
)