EEEVERYTHING!

This commit is contained in:
Sebastian Lohff 2017-01-19 18:42:11 +01:00
parent d20b8754ba
commit ee05f845cb
11 changed files with 332 additions and 111 deletions

View File

@ -1,8 +1,9 @@
from django.contrib import admin
from .models import Frequency, Band, Reference, QSO, User
from .models import Frequency, Band, Reference, QSO, User, Contest
admin.site.register(User)
admin.site.register(QSO)
admin.site.register(Band)
admin.site.register(Frequency)
admin.site.register(Reference)
admin.site.register(Contest)

View File

@ -3,25 +3,33 @@ from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractUser
from .validators import CallUsernameValidator
class Contest(models.Model):
name = models.CharField(max_length=20)
shortName = models.CharField(max_length=20, unique=True)
callQrg = models.ForeignKey("Frequency", null=True)
def __str__(self):
return self.name
class Reference(models.Model):
name = models.CharField(max_length=20, unique=True)
description = models.TextField()
def __str__(self):
return self.name
class User(AbstractUser):
ref = models.ForeignKey(Reference, null=True, blank=True)
class QSO(models.Model):
owner = models.ForeignKey(User)
time = models.DateTimeField()
call = models.CharField(max_length=20)
reportTX = models.CharField(max_length=7)
reportRX = models.CharField(max_length=7)
remarks = models.TextField()
def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
self._meta.get_field("username").validators = [CallUsernameValidator()]
class Band(models.Model):
name = models.CharField(max_length=10)
contest = models.ForeignKey(Contest)
def __str__(self):
return self.name
@ -33,6 +41,24 @@ class Frequency(models.Model):
qrg = models.DecimalField(max_digits=7, decimal_places=3)
band = models.ForeignKey(Band)
note = models.CharField(max_length=50)
def __str__(self):
return "Channel %s: %s MHz" % (self.channel, self.qrg)
class QSO(models.Model):
owner = models.ForeignKey(User)
time = models.DateTimeField(blank=True)
call = models.CharField(max_length=20)
band = models.ForeignKey(Band)
reportTX = models.CharField(max_length=7, default=59)
reportRX = models.CharField(max_length=7, default=59)
ownNo = models.IntegerField()
otherNo = models.IntegerField()
refStr = models.CharField(max_length=20)
ref = models.ForeignKey(Reference, null=True, blank=True)
remarks = models.CharField(max_length=50, blank=True)

View File

@ -16,9 +16,14 @@ Including another URLconf
from django.conf.urls import url
from contest.views import index, registerRefs
import contest.views as contest_views
urlpatterns = [
url('^$', index),
url('^regref/', registerRefs, name='registerRefs'),
url(r'^$', contest_views.contestIndex, name='index'),
url(r'^regref/$', contest_views.registerRefs, name='registerRefs'),
url(r'^regref/edit/(?P<uid>\d+)/$', contest_views.updateRef, name='updateRef'),
url(r'^overview/$', contest_views.overview, name='overview'),
url(r'^log/$', contest_views.log, name='log'),
url(r'^log/edit/(?P<qsoid>\d+)/$', contest_views.logEdit, name='logEdit'),
url(r'^log/delete/(?P<qsoid>\d+)/$', contest_views.logDelete, name='logDelete'),
]

View File

@ -1,14 +1,141 @@
from django.shortcuts import render
from django.contrib.admin.views.decorators import staff_member_required, login_required
from django.contrib.auth.decorators import login_required
from django.contrib.admin.views.decorators import staff_member_required
from django.db.models import Q
from django.contrib.auth.forms import AuthenticationForm
from django.http import HttpResponseRedirect
from django.contrib import messages
from django.urls import reverse
from .models import User
import datetime
from .models import User, Contest, Frequency, Reference, QSO
from .forms import UpdateRefForm, QSOForm
def index(request):
if request.user.is_authenticated():
return HttpResponseRedirect(reverse("contest:index"))
return render(request, "index.html", {"loginForm": AuthenticationForm()})
@login_required
def index(request):
return render(request, 'index.html', {})
def contestIndex(request):
#messages.debug(request, "Debug GLITCHHHHH")
#messages.info(request, "This info is very educational")
#messages.warning(request, "You got a warning")
#messages.error(request, "Error!!!")
#messages.success(request, "Great Success")
qsoform = QSOForm(request.user)
return render(request, 'contest/index.html', {"qsoform": qsoform})
@login_required
def log(request):
form = None
qsos = QSO.objects.filter(owner=request.user).order_by("-ownNo")
if request.method == 'POST':
form = QSOForm(user=request.user, data=request.POST)
if form.is_valid():
l = form.instance
if not l.time:
# set current time
l.time = datetime.datetime.now()
l.owner = request.user
l.save()
messages.success(request, "QSO saved!")
return HttpResponseRedirect(reverse("contest:log"))
else:
# FIXME: data initial my qso number
data = {
"ownNo": qsos[0].ownNo + 1 if len(qsos) > 0 else 1,
"reportRX": "59",
"reportTX": "59",
}
form = QSOForm(request.user, initial=data)
return render(request, 'contest/log.html', {'form': form, 'qsos': qsos})
@login_required
def logEdit(request, qsoid):
qso = QSO.objects.get(id=qsoid, owner=request.user)
form = None
if request.method == 'POST':
form = QSOForm(user=request.user, instance=qso, data=request.POST)
if form.is_valid():
form.instance.save()
messages.info(request, "QSO has been edited")
return HttpResponseRedirect(reverse("contest:log"))
else:
form = QSOForm(user=request.user, instance=qso)
return render(request, 'contest/logEdit.html', {'form': form, "qso": qso})
def logDelete(request, qsoid):
qso = QSO.objects.get(id=qsoid, owner=request.user)
if request.method == 'POST':
if "delete" in request.POST:
if request.POST["delete"] == "yes":
qso.delete()
messages.info(request, "QSO has been deleted")
return HttpResponseRedirect(reverse("contest:log"))
elif request.POST["delete"] == "no":
return HttpResponseRedirect(reverse("contest:log"))
return render(request, 'contest/logDelete.html', {"qso": qso})
@staff_member_required
def registerRefs(request):
allUser = User.objects.all()
refsMissingUser = User.objects.filter(ref=None).order_by("username")
refsNotMissingUser = User.objects.filter(~Q(ref=None)).order_by("username")
return render(request, 'register_refs.html', {'alluser': User.objects.all()})
qsos = QSO.objects.all().order_by("-time")
return render(request, 'contest/registerRefs.html', {'alluser': allUser, 'refsMissingUser': refsMissingUser, "refsNotMissinguser": refsNotMissingUser, "qsos": qsos})
@staff_member_required
def updateRef(request, uid):
user = User.objects.get(id=uid)
form = None
if request.method == 'POST':
form = UpdateRefForm(data=request.POST)
if form.is_valid():
ref = None
if form.cleaned_data["existingRef"]:
print("Got an existing Ref")
ref = form.cleaned_data["existingRef"]
else:
ref = Reference(name=form.cleaned_data["newRefName"])
ref.save()
messages.info(request, "New Ref '%s' created" % ref)
user.ref = ref
user.save()
messages.success(request, "%s ref set to %s" % (user, ref))
return HttpResponseRedirect(reverse("contest:registerRefs"))
else:
form = UpdateRefForm()
return render(request, 'contest/updateRef.html', {'user': user, 'form': form})
def overview(request):
# FIXME: Hardcoded for cqtu... everywhere
c = Contest.objects.get(id=1)
qrgs = Frequency.objects.filter(band__contest=c).order_by("channel")
return render(request, 'contest/overview.html', {'contest': c, 'qrgs': qrgs})
def register(request):
return render(request, 'registration/register.html', {})

View File

@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/1.10/ref/settings/
"""
import os
from django.contrib.messages import constants as messages
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -31,13 +33,17 @@ ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
# default
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
# local
'contest',
]
@ -73,6 +79,7 @@ WSGI_APPLICATION = 'cqtu.wsgi.application'
AUTH_USER_MODEL = 'contest.User'
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/'
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
@ -127,3 +134,7 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
MESSAGE_TAGS = {
messages.ERROR: 'danger',
}

View File

@ -17,14 +17,25 @@ from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.views.generic.edit import CreateView
#from django.contrib.auth.forms import UserCreationForm
from contest.forms import CustomUserCreationForm
from contest.views import index
urlpatterns = [
url('^$', index),
url('^$', index, name="index"),
url('^contest/', include('contest.urls', namespace='contest')),
url(r'^admin/', admin.site.urls),
url(r'^login/$', auth_views.login),
url(r'^logout/$', auth_views.logout),
url(r'^login/$', auth_views.login, name='login'),
url(r'^logout/$', auth_views.logout, {'next_page': '/'}, name='logout'),
#url(r'^register/$', register, name='register'),
url(r'^register/$', CreateView.as_view(
template_name='registration/register.html',
form_class=CustomUserCreationForm,
success_url='/',
), name='register'),
]

View File

@ -1,7 +1,37 @@
body {
min-height: 2000px;
min-height: 200px;
}
.navbar-static-top {
margin-bottom: 19px;
}
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 60px;
background-color: #f5f5f5;
}
.container {
width: auto;
padding: 0 15px;
}
.container .text-muted {
margin: 20px 0;
}
.asteriskField {
display: none;
}

View File

@ -14,6 +14,8 @@
<!-- Bootstrap core CSS -->
<link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">
<link href="{% static "css/uni-form.css" %}" rel="stylesheet">
<link href="{% static "css/style.uni-form.css" %}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{% static "style.css" %}" rel="stylesheet">
@ -32,25 +34,23 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">CQ TU 2017</a>
<a class="navbar-brand" href="{% url "index" %}">CQ TU 2017</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">Search</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
{% if user.is_authenticated %}
<li class="active"><a href="{% url "contest:index" %}">Home</a></li>
{% if user.ref %}
<li><a href="{% url "contest:log" %}">Log</a></li>
{% endif %}
{% if user.is_staff %}
<li><a href="{% url "contest:registerRefs" %}">Register Refs!</a></li>
{% endif %}
{% else %}
<li><a href="{% url "index" %}">Home</a></li>
<li><a href="{% url "register" %}">Register</a></li>
{% endif %}
<li><a href="{% url "contest:overview" %}">Contest Overview</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if user.is_staff %}
@ -68,10 +68,28 @@
<div class="container">
{% if messages %}
<div class="row">
<div class="col-12">
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }} {% endif %}alert-dismissible">{{ message }}</div>
{% endfor %}
</div>
</div>
{% endif %}
{% block content %}{% endblock %}
</div> <!-- /container -->
<footer class="footer">
<div class="container">
<p class="text-muted">CQ TU 2017, a <a href="http://dk0tu.de/">DK0TU</a> product</p>
</div>
</footer>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->

View File

@ -1,46 +1,40 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h2>Welcome!</h2>
<div class="row">
<div class="col-sm-6">
<p class="lead">Hello and welcome to the 2nd DK0TU CQ TU contest, the CQ TU 2107!</p>
<p>
What next? Here are some ideas:
</p>
<p>
<ul>
<li>If you want to review the contest rules, take a look at <a href="{% url "contest:overview" %}">the contest overview page</a></li>
<li>Need an account and already got a call? <a href="{% url "register" %}">Register here</a>!</li>
<li>Already have an account? Login on the right.</li>
</ul>
</p>
</div>
<div class="col-sm-6">
Login with callsign (uppercase) and password.
<form method="post" action="{% url "login" %}" class="uniForm">
{% csrf_token %}
{{ loginForm|crispy }}
<button type="submit" class="btn btn-primary">Login</button>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-12">
I have {{ call_count }} callsigns in my Database!
Und nun noch ein Wort von unserem Sponsor:
<blockquote>
Serverbasierte Loesung finde ich nach eingem Ueberlegen doof.
<footer>DL7BST</footer>
</blockquote>
</div>
</div>
{% if user.ref %}
{# user has a reference, we can start logging QSOs! #}
<div class="row">
<div class="col-sm-6">
Hey, nice! You are a Call at a certain level and you have a ref of {{ user.ref }}. Start doing the QSO foo! GO GO GO GO
</div>
<div class="col-sm-6">
<div class="panel panel-default">
<table class="table table-inverse">
<thead class="thead-inverse">
<tr>
<th>Date</th>
<th>Source</th>
<th>Version</th>
</tr>
</thead>
<tbody>
{% for log in call_import %}
<tr>
<td>{{ log.last_version_time }}</td>
<td>{{ log.datasource }}</td>
<td>{{ log.version }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% else %}
Hey you, you don't have a ref. You should have a ref! Register with contest people! on QRG FOOOO
{% endif %}
{% if user.is_staff %}
Hey, you are staff. Do you want to <a href="{% url "contest:registerRefs" %}">register people</a>?
{% endif %}
{% endblock %}

View File

@ -1,29 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h2>Welcome!</h2>
<div class="row">
<div class="col-sm-12">
Register Someone!!!!
Here be FORM for registering someone without a ref
</div>
</div>
<div class="row">
<div class="col-sm-12">
Here is a Table with all OMs/YLs in the contest!
<table>
<tr>
<th>Call</th>
<th>Ref</th>
</tr>
{% for u in alluser %}
<tr>
<td>{{ u.username }}</td>
<td>{{ u.ref|default:"unknown / unset" }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endblock %}

View File

@ -1,19 +1,46 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h2>Welcome!</h2>
<div class="row">
<div class="col-sm-12">
Please Login!
<form action="#" method="post">
{{ form.as_table }}
{% csrf_token %}
<div class="col-sm-4 col-sm-offset-4">
<h2 class="text-center">Please login</h2>
<p>
Don't have an account? <a href="{% url 'register' %}">Register here!</a>
</p>
<p>
Trouble logging in? Remember that your call is all upper-case.
</p>
<input type="submit" class="btn btn-default" value="Login">
<form action="{% url "login" %}" method="post" class="uniForm">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-default" value="Login">Login</button>
</form>
</div>
</div>
<!--
<div class="row">
<div class="col-sm-6">
<blockquote>
Serverbasierte Loesung finde ich nach eingem Ueberlegen doof.
<footer>DL7BST</footer>
</blockquote>
</div>
<div class="col-sm-6">
Please Login! Don't have an account? <a href="{% url 'register' %}">Register here!</a>
<form action="#" method="post" class="uniForm">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-default" value="Login">Login</button>
</form>
</div>
</div>
-->
</div>
{% endblock %}