From 8786b5af88dcab2d891cc2a51dfaf1131c9a1c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:43:18 +0200 Subject: [PATCH] Initial villages feature --- bornhack/settings/base.py | 1 + bornhack/templates/base.html | 13 +----- bornhack/urls.py | 4 ++ shop/templates/shop_base.html | 16 ++++++- villages/__init__.py | 0 villages/admin.py | 8 ++++ villages/apps.py | 7 ++++ villages/migrations/0001_initial.py | 38 +++++++++++++++++ villages/migrations/__init__.py | 0 villages/models.py | 58 ++++++++++++++++++++++++++ villages/templates/village_detail.html | 16 +++++++ villages/templates/village_form.html | 16 +++++++ villages/templates/village_list.html | 38 +++++++++++++++++ villages/tests.py | 3 ++ villages/urls.py | 9 ++++ villages/views.py | 40 ++++++++++++++++++ 16 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 villages/__init__.py create mode 100644 villages/admin.py create mode 100644 villages/apps.py create mode 100644 villages/migrations/0001_initial.py create mode 100644 villages/migrations/__init__.py create mode 100644 villages/models.py create mode 100644 villages/templates/village_detail.html create mode 100644 villages/templates/village_form.html create mode 100644 villages/templates/village_list.html create mode 100644 villages/tests.py create mode 100644 villages/urls.py create mode 100644 villages/views.py diff --git a/bornhack/settings/base.py b/bornhack/settings/base.py index 4b7c2934..269257f9 100644 --- a/bornhack/settings/base.py +++ b/bornhack/settings/base.py @@ -29,6 +29,7 @@ INSTALLED_APPS = [ 'shop', 'news', 'utils', + 'villages', 'allauth', 'allauth.account', diff --git a/bornhack/templates/base.html b/bornhack/templates/base.html index 97fcefb1..cbc2ad57 100644 --- a/bornhack/templates/base.html +++ b/bornhack/templates/base.html @@ -42,19 +42,8 @@ {% if current_camp.shop_open %}
  • Shop
  • - - {% if current_order and current_order.get_number_of_items %} -
  • - - - - {{ current_order.get_number_of_items }} - - -
  • - {% endif %} - {% endif %} +
  • Villages
  • Speakers
  • Sponsors
  • Contact
  • diff --git a/bornhack/urls.py b/bornhack/urls.py index d291f52a..5cdd323b 100644 --- a/bornhack/urls.py +++ b/bornhack/urls.py @@ -74,6 +74,10 @@ urlpatterns = [ r'^news/', include('news.urls', namespace='news') ), + url( + r'^villages/', + include('villages.urls', namespace='villages') + ), url(r'^accounts/', include('allauth.urls')), url(r'^admin/', include(admin.site.urls)), ] diff --git a/shop/templates/shop_base.html b/shop/templates/shop_base.html index 71cf3a6e..9f213393 100644 --- a/shop/templates/shop_base.html +++ b/shop/templates/shop_base.html @@ -19,7 +19,21 @@ {% if has_tickets %}
  • Tickets
  • {% endif %} -
  • Orders
  • +
  • Orders
  • +
  • + + {% if current_order and current_order.get_number_of_items %} + + {% endif %} + + + + {{ current_order.get_number_of_items|default:0 }} + + {% if current_order and current_order.get_number_of_items %} + + {% endif %} +
  • {% endif %} diff --git a/villages/__init__.py b/villages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/villages/admin.py b/villages/admin.py new file mode 100644 index 00000000..c276e488 --- /dev/null +++ b/villages/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +from .models import Village + + +@admin.register(Village) +class VillageAdmin(admin.ModelAdmin): + pass diff --git a/villages/apps.py b/villages/apps.py new file mode 100644 index 00000000..fbf4978a --- /dev/null +++ b/villages/apps.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class VillagesConfig(AppConfig): + name = 'villages' diff --git a/villages/migrations/0001_initial.py b/villages/migrations/0001_initial.py new file mode 100644 index 00000000..47c87f10 --- /dev/null +++ b/villages/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-07-05 21:38 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('camps', '0005_auto_20160510_2011'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Village', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=255)), + ('slug', models.SlugField(blank=True, max_length=255)), + ('description', models.TextField()), + ('open', models.BooleanField(default=False, help_text='Is this village open for others to join?')), + ('camp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camps.Camp')), + ('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['name'], + }, + ), + ] diff --git a/villages/migrations/__init__.py b/villages/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/villages/models.py b/villages/models.py new file mode 100644 index 00000000..d57c9eb7 --- /dev/null +++ b/villages/models.py @@ -0,0 +1,58 @@ +from __future__ import unicode_literals + +from django.core.urlresolvers import reverse_lazy +from django.db import models +from django.utils.text import slugify + +from camps.models import Camp +from utils.models import CreatedUpdatedModel, UUIDModel + + +class Village(CreatedUpdatedModel, UUIDModel): + + class Meta: + ordering = ['name'] + + camp = models.ForeignKey('camps.Camp') + contact = models.ForeignKey('auth.User') + + name = models.CharField(max_length=255) + slug = models.SlugField(max_length=255, blank=True) + description = models.TextField() + + open = models.BooleanField( + default=False, + help_text='Is this village open for others to join?' + ) + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse_lazy('villages:detail', kwargs={'slug': self.slug}) + + def save(self, **kwargs): + if ( + not self.pk or + not self.slug or + Village.objects.filter(slug=self.slug).count() > 1 + ): + slug = slugify(self.name) + incrementer = 1 + + # We have to make sure that the slug won't clash with current slugs + while Village.objects.filter(slug=slug).exists(): + if incrementer == 1: + slug = '{}-1'.format(slug) + else: + slug = '{}-{}'.format( + '-'.join(slug.split('-')[:-1]), + incrementer + ) + incrementer += 1 + self.slug = slug + + if not hasattr(self, 'camp'): + self.camp = Camp.objects.current() + + super(Village, self).save(**kwargs) diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html new file mode 100644 index 00000000..3491060b --- /dev/null +++ b/villages/templates/village_detail.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% load commonmark %} + +{% block content %} + +

    {{ village.name }}

    + +{{ village.description|commonmark }} + +
    + +{% if user in village.users.all %} +Edit +{% endif %} + +{% endblock %} diff --git a/villages/templates/village_form.html b/villages/templates/village_form.html new file mode 100644 index 00000000..3b5f8534 --- /dev/null +++ b/villages/templates/village_form.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% load bootstrap3 %} + +{% block content %} + +
    + {% csrf_token %} + + {% bootstrap_form form %} + +
    + + +
    + +{% endblock %} \ No newline at end of file diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html new file mode 100644 index 00000000..c89cbeb0 --- /dev/null +++ b/villages/templates/village_list.html @@ -0,0 +1,38 @@ +{% extends 'base.html' %} + +{% block content %} + +

    + If this is your first hackercamp the term 'Village' might be confusing but it is fairly simple: a village is just a spot on the campsite where you and a bunch of your friends/likeminded people camp together. Apart from peoples individual tents which they sleep in, many villages bring a large common tent where you can hack and hang out during the day. +

    + +{% if user.is_authenticated %} +Create a village +{% endif %} + +
    + + + + + + + + + + {% for village in villages %} + + + + + {% endfor %} + +
    NamePrice
    + + {{ village.name }} + + + {{ village.description|truncatewords:15 }} +
    + +{% endblock %} \ No newline at end of file diff --git a/villages/tests.py b/villages/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/villages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/villages/urls.py b/villages/urls.py new file mode 100644 index 00000000..edc57aa5 --- /dev/null +++ b/villages/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import url +from views import * + +urlpatterns = [ + url(r'^$', VillageListView.as_view(), name='list'), + url(r'create/$', VillageCreateView.as_view(), name='create'), + url(r'(?P[-_\w+]+)/edit/$', VillageUpdateView.as_view(), name='update'), + url(r'(?P[-_\w+]+)/$', VillageDetailView.as_view(), name='detail'), +] diff --git a/villages/views.py b/villages/views.py new file mode 100644 index 00000000..5d4e589f --- /dev/null +++ b/villages/views.py @@ -0,0 +1,40 @@ +from django.core.urlresolvers import reverse_lazy +from django.http import HttpResponseRedirect +from django.views.generic import ListView, DetailView, CreateView, UpdateView +from .models import ( + Village, +) + + +class VillageListView(ListView): + model = Village + template_name = 'village_list.html' + context_object_name = 'villages' + + +class VillageDetailView(DetailView): + model = Village + template_name = 'village_detail.html' + context_object_name = 'village' + + +class VillageCreateView(CreateView): + model = Village + template_name = 'village_form.html' + fields = ['name', 'description', 'open'] + success_url = reverse_lazy('villages:list') + + def form_valid(self, form): + village = form.save(commit=False) + village.contact = self.request.user + village.save() + return HttpResponseRedirect(village.get_absolute_url()) + + +class VillageUpdateView(UpdateView): + model = Village + template_name = 'village_form.html' + fields = ['name', 'description', 'open'] + + def get_success_url(self): + return self.get_object().get_absolute_url()