Merge branch 'release/1.0.0'
This commit is contained in:
commit
abb314cde9
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ build/
|
||||
*/*/__pycache__/
|
||||
caromserver/local_settings.py
|
||||
/staticfiles/
|
||||
.venv/
|
||||
update.sh
|
||||
|
18
Pipfile
Normal file
18
Pipfile
Normal file
@ -0,0 +1,18 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
django = "==2.1.5"
|
||||
django-crispy-forms = "==1.7.2"
|
||||
django-debug-toolbar = "==1.11"
|
||||
django-extensions = "==2.1.5"
|
||||
django-tables2 = "==2.0.4"
|
||||
djangorestframework = "==3.9.1"
|
||||
requests = "==2.21.0"
|
||||
|
||||
[requires]
|
||||
python_version = "3.5"
|
126
Pipfile.lock
generated
Normal file
126
Pipfile.lock
generated
Normal file
@ -0,0 +1,126 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "de1e65bd4b2342db22fea58996979f48314fcad56bd7b50f4c939035771ef85c"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.5"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
|
||||
"sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
|
||||
],
|
||||
"version": "==2018.11.29"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:a32c22af23634e1d11425574dce756098e015a165be02e4690179889b207c7a8",
|
||||
"sha256:d6393918da830530a9516bbbcbf7f1214c3d733738779f06b0f649f49cc698c3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.1.5"
|
||||
},
|
||||
"django-crispy-forms": {
|
||||
"hashes": [
|
||||
"sha256:5952bab971110d0b86c278132dae0aa095beee8f723e625c3d3fa28888f1675f",
|
||||
"sha256:705ededc554ad8736157c666681165fe22ead2dec0d5446d65fc9dd976a5a876"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.7.2"
|
||||
},
|
||||
"django-debug-toolbar": {
|
||||
"hashes": [
|
||||
"sha256:89d75b60c65db363fb24688d977e5fbf0e73386c67acf562d278402a10fc3736",
|
||||
"sha256:c2b0134119a624f4ac9398b44f8e28a01c7686ac350a12a74793f3dd57a9eea0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.11"
|
||||
},
|
||||
"django-extensions": {
|
||||
"hashes": [
|
||||
"sha256:6fcedb2ea660c9dbf9ac59441721ffdd4ab5b753fbd6159c3e28f391a65bab46",
|
||||
"sha256:a607459e5fa8c579a672131b63366fa52fab80adb2a862d362f5fb48cd2d2cac"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.1.5"
|
||||
},
|
||||
"django-tables2": {
|
||||
"hashes": [
|
||||
"sha256:a893fca1afe2e95b9739c6428cc6c9735a219f65707e24274df3920f61358525",
|
||||
"sha256:b5f7b4c76160ee927005e52ebea633c86d4529cf84757c0acd5d0434d31798a1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.4"
|
||||
},
|
||||
"djangorestframework": {
|
||||
"hashes": [
|
||||
"sha256:79c6efbb2514bc50cf25906d7c0a5cfead714c7af667ff4bd110312cd380ae66",
|
||||
"sha256:a4138613b67e3a223be6c97f53b13d759c5b90d2b433bad670b8ebf95402075f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.9.1"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
],
|
||||
"version": "==2.8"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
|
||||
"sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
|
||||
],
|
||||
"version": "==2018.9"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
|
||||
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.21.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
},
|
||||
"sqlparse": {
|
||||
"hashes": [
|
||||
"sha256:ce028444cfab83be538752a2ffdb56bc417b7784ff35bb9a3062413717807dec",
|
||||
"sha256:d9cf190f51cbb26da0412247dfe4fb5f4098edb73db84e02f9fc21fdca31fed4"
|
||||
],
|
||||
"version": "==0.2.4"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
|
||||
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
|
||||
],
|
||||
"version": "==1.24.1"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
@ -35,6 +35,12 @@ class ClientAdmin(admin.ModelAdmin):
|
||||
fields = ['location', 'uuid', 'report_user', 'last_seen']
|
||||
|
||||
|
||||
@admin.register(ClientData)
|
||||
class ClientDataAdmin(admin.ModelAdmin):
|
||||
list_display = ('uuid', 'last_seen')
|
||||
fields = ['location', 'last_seen']
|
||||
|
||||
|
||||
@admin.register(LocationData)
|
||||
class LocationDataAdmin(admin.ModelAdmin):
|
||||
def get_urls(self):
|
||||
@ -46,6 +52,8 @@ class LocationDataAdmin(admin.ModelAdmin):
|
||||
return my_urls + urls
|
||||
|
||||
def process_locationdata(self, request):
|
||||
from .tasks import process_location_data
|
||||
process_location_data(sender=None)
|
||||
messages.success(request, 'Items processed.')
|
||||
return redirect('admin:billard_locationdata_changelist')
|
||||
|
||||
|
10
billard/forms.py
Normal file
10
billard/forms.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class UserInformationUpdateForm(forms.ModelForm):
|
||||
email = forms.EmailField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('first_name', 'last_name', 'email',)
|
24
billard/migrations/0027_clientdata.py
Normal file
24
billard/migrations/0027_clientdata.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Generated by Django 2.0.2 on 2018-02-19 10:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('billard', '0026_client_last_seen'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ClientData',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uuid', models.UUIDField(verbose_name='Identifier')),
|
||||
('last_seen', models.DateTimeField(verbose_name='Letzter Update')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Client Data logs',
|
||||
'verbose_name_plural': 'Client Data logs',
|
||||
},
|
||||
),
|
||||
]
|
@ -4,8 +4,6 @@ import uuid
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -102,7 +100,13 @@ class Accounting(models.Model):
|
||||
verbose_name_plural = "Buchhaltungseinträge"
|
||||
|
||||
|
||||
@receiver(post_save, sender=LocationData)
|
||||
def test(sender, **kwargs):
|
||||
from .tasks import process_location_data
|
||||
process_location_data()
|
||||
class ClientData(models.Model):
|
||||
uuid = models.UUIDField(verbose_name="Identifier")
|
||||
last_seen = models.DateTimeField(verbose_name="Letzter Update")
|
||||
|
||||
def __str__(self):
|
||||
return '{}, {}'.format(self.uuid, self.last_seen)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Client Data logs"
|
||||
verbose_name_plural = "Client Data logs"
|
||||
|
@ -1,6 +1,6 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from billard.models import LocationData, Client
|
||||
from billard.models import LocationData, ClientData
|
||||
|
||||
|
||||
class LocationDataSerializer(serializers.HyperlinkedModelSerializer):
|
||||
@ -11,5 +11,5 @@ class LocationDataSerializer(serializers.HyperlinkedModelSerializer):
|
||||
|
||||
class ClientUpdateLastSeenSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Client
|
||||
model = ClientData
|
||||
fields = ('uuid', 'last_seen')
|
||||
|
22
billard/tables.py
Normal file
22
billard/tables.py
Normal file
@ -0,0 +1,22 @@
|
||||
import django_tables2 as tables
|
||||
|
||||
from .models import Location
|
||||
|
||||
|
||||
class LocationTable(tables.Table):
|
||||
code = tables.TemplateColumn(template_name='billard/tc_location_detail.html')
|
||||
|
||||
class Meta:
|
||||
model = Location
|
||||
fields = ('code', 'name', 'street', 'plz', 'city')
|
||||
orderable = False
|
||||
|
||||
|
||||
class LocationAccountingTable(tables.Table):
|
||||
code = tables.TemplateColumn(template_name='billard/tc_location_detail.html')
|
||||
accounting = tables.TemplateColumn(template_name='billard/tc_accounting_detail.html')
|
||||
|
||||
class Meta:
|
||||
model = Location
|
||||
fields = ('code', 'name', 'street', 'plz', 'city', 'accounting')
|
||||
orderable = False
|
@ -2,13 +2,28 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
import billard.utils as utils
|
||||
from billard.models import LocationData, Client, Accounting
|
||||
from billard.models import LocationData, Client, Accounting, ClientData
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_location_data():
|
||||
@receiver(post_save, sender=ClientData)
|
||||
def process_client_data(sender, **kwargs):
|
||||
data = ClientData.objects.all().order_by('last_seen')
|
||||
for cd in data:
|
||||
client = Client.objects.get(uuid=cd.uuid)
|
||||
client.last_seen = cd.last_seen
|
||||
client.save()
|
||||
cd.delete()
|
||||
|
||||
|
||||
@receiver(post_save, sender=LocationData)
|
||||
def process_location_data(sender, **kwargs):
|
||||
log.info('Starte die Verarbeitung der Location-Data-Objecte')
|
||||
data = LocationData.objects.filter(processed=False).order_by('tst')
|
||||
for ld in data:
|
||||
try:
|
||||
@ -64,3 +79,10 @@ def process_location_data():
|
||||
log.error(ld.error_msg)
|
||||
except:
|
||||
log.exception('', exc_info=True)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Accounting)
|
||||
def process_accounting_data(sender, **kwargs):
|
||||
log.info('Starte die Verarbeitung der Accounting-Data-Objecte')
|
||||
data = Accounting.objects.filter(prize=0.0, reporter_uuid__isnull=True).exclude(time_to__isnull=True)
|
||||
data.delete()
|
||||
|
@ -2,7 +2,7 @@
|
||||
{% load i18n admin_urls static admin_list %}
|
||||
{% block object-tools-items %}
|
||||
<li>
|
||||
<a href="{% url 'admin:process_locationdata' %}">
|
||||
<a href="{% url 'admin:process_locationdata' %}" title="Verarbeiten der LocationDate Elemente">
|
||||
LD Verarbeiten
|
||||
</a>
|
||||
</li>
|
||||
|
@ -15,30 +15,33 @@
|
||||
|
||||
{{ pk }}
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Start-Datum:</th>
|
||||
<th>Stop-Datum:</th>
|
||||
<th>Preis Normal:</th>
|
||||
<th>Preis Happy Hour:</th>
|
||||
<th>Preis gesamt:</th>
|
||||
</tr>
|
||||
{% for acc in accounting %}
|
||||
<tr>
|
||||
<td>{{ acc.time_from }}</td>
|
||||
<td>{{ acc.time_to }}</td>
|
||||
<td>{{ acc.prize_normal }}</td>
|
||||
<td>{{ acc.prize_hh }}</td>
|
||||
<td>{{ acc.prize }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<form action="confirm/" method="post" id="accounting">
|
||||
{% csrf_token %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Start-Datum:</th>
|
||||
<th>Stop-Datum:</th>
|
||||
<th>Preis Normal:</th>
|
||||
<th>Preis Happy Hour:</th>
|
||||
<th>Preis gesamt:</th>
|
||||
</tr>
|
||||
{% for acc in accounting %}
|
||||
<tr>
|
||||
<td><input type="checkbox" name="list_acc_id" id="option{{ acc.id }}"
|
||||
value={{ acc.id }} checked="checked"/></td>
|
||||
<td>{{ acc.time_from }}</td>
|
||||
<td>{{ acc.time_to }}</td>
|
||||
<td>{{ acc.prize_normal }}</td>
|
||||
<td>{{ acc.prize_hh }}</td>
|
||||
<td>{{ acc.prize }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<input type="hidden" name="location-selector" value="{{ location_id }}">
|
||||
<input type="hidden" name="accountings" value="{{ acc_ids }}">
|
||||
<button type="submit" class="btn btn-default">Abrechnen</button>
|
||||
<button type="submit" class="btn btn-danger">Abrechnen</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,49 +0,0 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load display_client %}
|
||||
|
||||
{% block title %}Location Data{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if not locations|length_is:"1" %}
|
||||
<form action="." method="post" id="location-form">
|
||||
{% csrf_token %}
|
||||
<div id="location-selector" class="alert">
|
||||
<select class="form-control" form="location-form" name="location-selector" id="location-select">
|
||||
{% for loc in locations %}
|
||||
<option value="{{ loc.id }}"{% if loc.id == location_id %} selected{% endif %}>{{ loc.code }}
|
||||
- {{ loc.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="desk_data">
|
||||
{% include 'billard/index_ajax.html' %}
|
||||
</div>
|
||||
<div id="modal-wrapper">
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
var interval;
|
||||
$(document).ready(function () {
|
||||
$.ajaxSetup({cache: false});
|
||||
interval = window.setInterval(refresh_page, 1000);
|
||||
});
|
||||
|
||||
function refresh_page() {
|
||||
$('#desk_data').load('#');
|
||||
$('#modal-wrapper').load('{% url 'accountmodal' %}', function () {
|
||||
if ($('#accountsmodal').length) {
|
||||
window.clearInterval(interval);
|
||||
$('#accountsmodal').modal('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,10 +0,0 @@
|
||||
{% load display_client %}
|
||||
{% if clients %}
|
||||
{% for cli in clients %}
|
||||
{% for i in range %} {{ cli|display_client:i }} {% endfor %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-12 alert alert-danger">Keine Tische angelegt!</div>
|
||||
</div>
|
||||
{% endif %}
|
@ -1,4 +1,4 @@
|
||||
{% load display_client %}
|
||||
{% load display_client display_daily_sale %}
|
||||
{% if location.clients.all %}
|
||||
{% for cli in location.clients.all %}
|
||||
{% for i in "12345678" %}
|
||||
@ -10,3 +10,8 @@
|
||||
<div class="alert alert-danger">Keine Tische angelegt!</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col col-12">
|
||||
<div class="alert alert-warning">
|
||||
{{ location|display_daily_sale }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}Standortliste{% endblock %}
|
||||
|
||||
@ -7,37 +8,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if location_list %}
|
||||
<h2>Bitte Standort auswählen:</h2>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Name</th>
|
||||
<th>Strasse</th>
|
||||
<th>Plz</th>
|
||||
<th>Ort</th>
|
||||
{% if perms.billard.change_accounting %}
|
||||
<th>Accounting</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% for loc in location_list %}
|
||||
<tr>
|
||||
<td><a href="{% url 'billard:location_detail' loc.id %}"
|
||||
class="btn btn-outline-primary btn-sm">{{ loc.code|default_if_none:"" }}</a></td>
|
||||
<td>{{ loc.name|default_if_none:"" }}</td>
|
||||
<td>{{ loc.street|default_if_none:"" }}</td>
|
||||
<td>{{ loc.plz|default_if_none:"" }}</td>
|
||||
<td>{{ loc.city|default_if_none:"" }}</td>
|
||||
{% if perms.billard.change_accounting %}
|
||||
<td><a href="{% url 'billard:accounting_detail' loc.id %}"
|
||||
class="btn btn-outline-danger btn-sm">Abrechnen</a></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>Keine Standorte Zugeordnet.</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>Bitte Standort auswählen:</h2>
|
||||
{% render_table table %}
|
||||
{% endblock %}
|
||||
|
@ -1,17 +0,0 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
{% block title %}Location Data{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'billard/locationdata_detail_ajax.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
setInterval(function () {
|
||||
$("#content").load("#")
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,51 +0,0 @@
|
||||
<h1>Locationdata: {{ locationdata.id }}</h1>
|
||||
|
||||
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
|
||||
|
||||
<form action="{% url 'index' %}" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="location_id" class="col-sm-2 control-label">Location Id</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="location_id" type="text" name="location_id" value="{{ locationdata.location_id }}"
|
||||
placeholder="Locaton" class="form-control" disabled="disabled"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="table_no" class="col-sm-2 control-label">Table Number</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="table_no" type="number" name="table_no" value="{{ locationdata.table_no }}"
|
||||
placeholder="Table" class="form-control" min="1" max="8" disabled="disabled"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tst" class="col-sm-2 control-label">Timestamp</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="tst" type="datetime" name="tst" value="{{ locationdata.tst }}"
|
||||
placeholder="Table" class="form-control" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="on_off" class="col-sm-2 control-label">On / Off</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="on_off" type="checkbox" name="on_off" value="{{ locationdata.on_off }}"
|
||||
placeholder="Table" class="form-control" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="processed" class="col-sm-2 control-label">Processed</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="processed" type="checkbox" name="processed" value="{{ locationdata.processed }}"
|
||||
placeholder="Table" class="form-control" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="error_msg" class="col-sm-2 control-label">Error Message</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="error_msg" type="text" name="error_msg" value="{{ locationdata.error_msg }}"
|
||||
placeholder="Table" class="form-control" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">Abschicken</button>
|
||||
<a class="btn btn-default" href=".." role="button">Zurück</a>
|
||||
</form>
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
{% block title %}Location Data{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'billard/locationdata_list_ajax.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
setInterval(function () {
|
||||
$("#content").load("#")
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,42 +0,0 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
{% block title %}Location Data{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="location-selector" class="alert">
|
||||
<select class="form-control">
|
||||
<option value="1">Casino 1</option>
|
||||
<option value="2">Casino 2</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{% if object_list %}
|
||||
<h1>Location Data</h1>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Location</th>
|
||||
<th>Table</th>
|
||||
<th>Timestamp</th>
|
||||
<th>On_Off</th>
|
||||
<th>Proc</th>
|
||||
<th>Error</th>
|
||||
</tr>
|
||||
{% for location_data in object_list %}
|
||||
<tr>
|
||||
<td><a href="{% url 'detail' location_data.id %}">{{ location_data.id }}</a></td>
|
||||
<td>{{ location_data.location_id }}</td>
|
||||
<td>{{ location_data.table_no }}</td>
|
||||
<td>{{ location_data.tst }}</td>
|
||||
<td>{{ location_data.on_off }}</td>
|
||||
<td>{{ location_data.processed }}</td>
|
||||
<td>{{ location_data.error_msg }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No data available.</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
2
billard/templates/billard/tc_accounting_detail.html
Normal file
2
billard/templates/billard/tc_accounting_detail.html
Normal file
@ -0,0 +1,2 @@
|
||||
{% load static from staticfiles %}
|
||||
<a href="{% url 'billard:accounting_detail' record.id %}" class="btn btn-outline-danger btn-sm">Abrechnen</a>
|
3
billard/templates/billard/tc_location_detail.html
Normal file
3
billard/templates/billard/tc_location_detail.html
Normal file
@ -0,0 +1,3 @@
|
||||
{% load static from staticfiles %}
|
||||
<a href="{% url 'billard:location_detail' record.id %}"
|
||||
class="btn btn-outline-primary btn-sm">{{ record.code|default_if_none:"" }}</a>
|
@ -17,28 +17,10 @@ def display_client(client, desk_no):
|
||||
loc = desk.client.location
|
||||
if not desk.enabled:
|
||||
return ''
|
||||
alert = 'alert-success'
|
||||
acc = desk.accounting_set.all()[:3][::-1]
|
||||
if acc is not None and len(acc) > 0:
|
||||
a = acc[-1]
|
||||
if a.time_to is None:
|
||||
alert = 'alert-info'
|
||||
prize, u1, u2 = utils.get_prize_for(
|
||||
start=a.time_from,
|
||||
end=datetime.now(),
|
||||
pph=desk.prize,
|
||||
hh_start=loc.happy_hour_start,
|
||||
hh_end=loc.happy_hour_end,
|
||||
pphh=desk.prize_hh,
|
||||
)
|
||||
prize = '{0:.2f}'.format(prize)
|
||||
if prize != a.prize:
|
||||
a.prize = prize
|
||||
before5min = datetime.now() - timedelta(minutes=5)
|
||||
if client.last_seen is not None and client.last_seen < before5min:
|
||||
alert = 'alert-danger'
|
||||
_calc_prize(desk, acc)
|
||||
html = '<div class="col col-12 col-lg-6">\n'
|
||||
html += ' <div class="table-info alert {}">\n'.format(alert)
|
||||
html += ' <div class="table-info alert {}">\n'.format(_get_alert_name(desk))
|
||||
html += ' <h4 style="text-align: center">({}) {}</h4>\n'.format(desk_no, desk.name)
|
||||
if loc.happy_hour_start is not None and desk.prize_hh is not None:
|
||||
html += ' <h6 style="text-align: center">Preis: {:.2f} € / Stunde | {} - {}: {:.2f} / € Stunde</h6>\n' \
|
||||
@ -67,3 +49,33 @@ def display_client(client, desk_no):
|
||||
html += '</div>\n'
|
||||
html = format_html(html)
|
||||
return html
|
||||
|
||||
|
||||
def _get_alert_name(desk):
|
||||
alert = 'alert-success'
|
||||
acc = desk.accounting_set.all()[:3][::-1]
|
||||
if acc is not None and len(acc) > 0:
|
||||
a = acc[-1]
|
||||
if a.time_to is None:
|
||||
alert = 'alert-info'
|
||||
before5min = datetime.now() - timedelta(minutes=5)
|
||||
if desk.client.last_seen is not None and desk.client.last_seen < before5min:
|
||||
alert = 'alert-danger'
|
||||
return alert
|
||||
|
||||
|
||||
def _calc_prize(desk, acc):
|
||||
if acc is not None and len(acc) > 0:
|
||||
a = acc[-1]
|
||||
if a.time_to is None:
|
||||
prize, u1, u2 = utils.get_prize_for(
|
||||
start=a.time_from,
|
||||
end=datetime.now(),
|
||||
pph=desk.prize,
|
||||
hh_start=desk.client.location.happy_hour_start,
|
||||
hh_end=desk.client.location.happy_hour_end,
|
||||
pphh=desk.prize_hh,
|
||||
)
|
||||
prize = '{0:.2f}'.format(prize)
|
||||
if prize != a.prize:
|
||||
a.prize = prize
|
||||
|
19
billard/templatetags/display_daily_sale.py
Normal file
19
billard/templatetags/display_daily_sale.py
Normal file
@ -0,0 +1,19 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django import template
|
||||
from django.db.models import Sum
|
||||
|
||||
from billard.models import Accounting
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def display_daily_sale(location):
|
||||
start_date = datetime.now().replace(hour=5, minute=0, second=0, microsecond=0)
|
||||
end_date = start_date + timedelta(days=1)
|
||||
prize__sum = Accounting.objects.filter(desk__client__location=location,
|
||||
time_to__range=(start_date, end_date)).aggregate(Sum('prize'))
|
||||
if prize__sum['prize__sum'] is None:
|
||||
prize__sum['prize__sum'] = 0
|
||||
return "Tagesumsatz: {0:.2f} EUR".format(prize__sum['prize__sum'])
|
@ -25,6 +25,6 @@ urlpatterns = [
|
||||
path('<int:loc_pk>/account_modal/<pks>/confirm/', views.account_modal_confirm_view, name='account_modal_confirm'),
|
||||
# ex. /billard/api/v1/ (rest api)
|
||||
path('api/v1/', include(router.urls)),
|
||||
# ex. /billard/process_location_data/
|
||||
path('process_location_data/', views.process_location_data, name='process_location_data'),
|
||||
# ex. /billard/myaccount/
|
||||
path('my_account', views.UserUpdateView.as_view(), name='my_account'),
|
||||
]
|
||||
|
@ -1,23 +1,25 @@
|
||||
import ast
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db.models import Sum
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render, redirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import generic
|
||||
from django.views.generic import UpdateView
|
||||
from rest_framework import viewsets
|
||||
|
||||
from billard.models import LocationData, Location, Client, Accounting
|
||||
from billard.serializers import LocationDataSerializer, ClientUpdateLastSeenSerializer
|
||||
from billard.tasks import process_location_data
|
||||
from .forms import UserInformationUpdateForm
|
||||
from .tables import LocationTable, LocationAccountingTable
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocationIndexView(generic.ListView):
|
||||
class LocationIndexView(LoginRequiredMixin, generic.ListView):
|
||||
template_name = 'billard/location_index.html'
|
||||
context_object_name = 'location_list'
|
||||
|
||||
@ -25,8 +27,17 @@ class LocationIndexView(generic.ListView):
|
||||
"""Return the last five published questions."""
|
||||
return Location.objects.filter(users__id=self.request.user.id).order_by('code')
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context = super().get_context_data(object_list=object_list, **kwargs)
|
||||
table = LocationTable(self.get_queryset())
|
||||
user = self.request.user
|
||||
if user.has_perm('billard.change_accounting'):
|
||||
table = LocationAccountingTable(self.get_queryset())
|
||||
context['table'] = table
|
||||
return context
|
||||
|
||||
class LocationDetailView(generic.DetailView):
|
||||
|
||||
class LocationDetailView(LoginRequiredMixin, generic.DetailView):
|
||||
model = Location
|
||||
template_name = 'billard/location_detail.html'
|
||||
|
||||
@ -67,7 +78,7 @@ class AccountingView(generic.ListView):
|
||||
def accounting_confirm(request, pk):
|
||||
if request.method == 'POST':
|
||||
if 'accountings' in request.POST:
|
||||
acc_ids = ast.literal_eval(request.POST['accountings'])
|
||||
acc_ids = request.POST.getlist('list_acc_id')
|
||||
if len(acc_ids) > 0:
|
||||
Accounting.objects.filter(id__in=acc_ids).update(
|
||||
billed=True,
|
||||
@ -113,6 +124,11 @@ class ClientUpdateLastSeenViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = ClientUpdateLastSeenSerializer
|
||||
|
||||
|
||||
def process_location_data(request):
|
||||
process_location_data()
|
||||
return HttpResponse('DONE')
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class UserUpdateView(UpdateView):
|
||||
form_class = UserInformationUpdateForm
|
||||
template_name = 'registration/my_account.html'
|
||||
success_url = reverse_lazy('billard:my_account')
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
@ -35,10 +35,13 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# third party apps
|
||||
'crispy_forms',
|
||||
'debug_toolbar',
|
||||
'django_tables2',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
'debug_toolbar',
|
||||
'crispy_forms',
|
||||
# carom apps
|
||||
'billard',
|
||||
]
|
||||
|
||||
@ -160,10 +163,12 @@ EMAIL_PORT = 25
|
||||
URL_LOCATION_PROCESSOR = 'http://127.0.0.1:8000/billard/process_locationdata'
|
||||
|
||||
PRODUCT_INFO = 'CAROM-DEV'
|
||||
PRODUCT_VERSION = 'v 0.5.1'
|
||||
PRODUCT_VERSION = 'v 1.0.0'
|
||||
|
||||
INTERNAL_IPS = ['127.0.0.1']
|
||||
|
||||
DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap4.html'
|
||||
|
||||
try:
|
||||
from local_settings import *
|
||||
except ImportError:
|
||||
|
@ -24,8 +24,6 @@ urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||
path('billard/', include('billard.urls')),
|
||||
path('login/', auth_views.login, name='login'),
|
||||
path('logout/', auth_views.logout, name='logout'),
|
||||
path('', include('django.contrib.auth.urls')),
|
||||
path('', RedirectView.as_view(url='billard/', permanent=False), name='index')
|
||||
]
|
||||
|
@ -1,6 +0,0 @@
|
||||
Django<2.1
|
||||
django-crispy-forms==1.7.0
|
||||
django-extensions>=1.7.0
|
||||
djangorestframework>=3.6.0
|
||||
requests>=2.18.0
|
||||
django-debug-toolbar<2.0.0
|
2050
static/css/bootstrap-grid.css
vendored
2050
static/css/bootstrap-grid.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
static/css/bootstrap-grid.min.css
vendored
7
static/css/bootstrap-grid.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
330
static/css/bootstrap-reboot.css
vendored
330
static/css/bootstrap-reboot.css
vendored
@ -1,330 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button,
|
||||
html [type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
8
static/css/bootstrap-reboot.min.css
vendored
8
static/css/bootstrap-reboot.min.css
vendored
@ -1,8 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
File diff suppressed because one or more lines are too long
2524
static/css/bootstrap.css
vendored
2524
static/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
4
static/css/bootstrap.min.css
vendored
4
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6328
static/js/bootstrap.bundle.js
vendored
6328
static/js/bootstrap.bundle.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
static/js/bootstrap.bundle.min.js
vendored
7
static/js/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2207
static/js/bootstrap.js
vendored
2207
static/js/bootstrap.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
4
static/js/bootstrap.min.js
vendored
4
static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8269
static/js/jquery-3.3.1.slim.js
Normal file
8269
static/js/jquery-3.3.1.slim.js
Normal file
File diff suppressed because it is too large
Load Diff
2
static/js/jquery-3.3.1.slim.min.js
vendored
Normal file
2
static/js/jquery-3.3.1.slim.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/js/jquery-3.3.1.slim.min.map
Normal file
1
static/js/jquery-3.3.1.slim.min.map
Normal file
File diff suppressed because one or more lines are too long
@ -36,11 +36,6 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'billard:location_index' %}">Standorte</a>
|
||||
</li>
|
||||
{% if user.is_superuser %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin">Administration</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% if user.is_authenticated %}
|
||||
<ul class="navbar-nav ml-auto">
|
||||
@ -50,8 +45,12 @@
|
||||
{{ user.username }}
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="userMenu">
|
||||
<!-- <a class="dropdown-item" href="#">My account</a> -->
|
||||
<a class="disabled dropdown-item" href="#">{% settings_value "PRODUCT_VERSION" %}</a>
|
||||
<a class="dropdown-item" href="{% url 'billard:my_account' %}">My account</a>
|
||||
{% if user.is_superuser %}
|
||||
<a class="dropdown-item" href="/admin">Administration</a>
|
||||
{% endif %}
|
||||
<a class="disabled dropdown-item"
|
||||
href="#">{% settings_value "PRODUCT_VERSION" %}</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
|
||||
</div>
|
||||
|
@ -1,23 +1,28 @@
|
||||
{% extends '_base_accounts.html' %}
|
||||
|
||||
{% extends '_base.html' %}
|
||||
{% load static from staticfiles %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}login{% endblock %}
|
||||
{% load form_tags %}
|
||||
{% block title %}Anmelden{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="row justify-content-center" style="margin-top: 80px;">
|
||||
<div class="col-lg-4 col-md-6 col-sm-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Log in</h3>
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
<div class="card">
|
||||
<div class="card-header text-center">
|
||||
<h3>{% settings_value "PRODUCT_INFO" %}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Login</h3>
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary btn-block">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
21
templates/registration/my_account.html
Normal file
21
templates/registration/my_account.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}My account{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
<li class="breadcrumb-item active">My account</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-8 col-sm-10">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-success">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user