From f8a513ec727b11273a13d29c4d3dca678653c334 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Sun, 25 Dec 2016 15:52:55 +0100 Subject: [PATCH] this is the beginning of multicamp support, much work still to be done --- bornhack/settings.py | 82 +++--- bornhack/urls.py | 56 ++++ camps/admin.py | 27 +- camps/context_processors.py | 10 +- camps/managers.py | 10 - camps/migrations/0007_auto_20161212_1803.py | 93 ++++++ camps/migrations/0008_delete_day.py | 19 ++ camps/migrations/0009_auto_20161220_1645.py | 39 +++ camps/migrations/0010_auto_20161220_1714.py | 31 ++ camps/mixins.py | 13 + camps/models.py | 142 ++++------ camps/templates/camp_detail.html | 5 + camps/views.py | 9 +- info/__init__.py | 0 info/admin.py | 3 + info/apps.py | 7 + info/migrations/0001_initial.py | 57 ++++ info/migrations/__init__.py | 0 info/models.py | 71 +++++ info/templates/info.html | 85 ++++++ {camps => info}/tests.py | 0 info/views.py | 17 ++ news/admin.py | 14 +- news/managers.py | 11 - .../migrations/0006_remove_newsitem_public.py | 19 ++ news/migrations/0007_auto_20161220_1136.py | 20 ++ news/migrations/0008_newsitem_archived.py | 20 ++ news/models.py | 44 ++- news/views.py | 29 +- profiles/models.py | 1 + program/admin.py | 9 - program/migrations/0010_auto_20161212_1809.py | 52 ++++ program/models.py | 18 +- program/views.py | 19 +- requirements.txt | 35 ++- shop/admin.py | 1 - shop/context_processors.py | 1 + shop/migrations/0033_auto_20161212_1756.py | 23 ++ shop/models.py | 14 +- shop/views.py | 2 - .../css/bootstrap-theme.css | 0 .../css/bootstrap-theme.css.map | 0 .../css/bootstrap-theme.min.css | 0 .../css/bootstrap.css | 0 .../css/bootstrap.css.map | 0 .../css/bootstrap.min.css | 0 .../css/bornhack.css | 0 .../static_src => static_src}/css/leaflet.css | 0 .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../fonts/glyphicons-halflings-regular.woff2 | Bin .../img/logo-new-paths.svg | 0 .../img/logo-new.png | Bin .../img/logo-new.svg | 0 .../static_src => static_src}/img/logo.png | Bin .../static_src => static_src}/img/logo.svg | 0 .../sponsors/CSIS_PRI_LOGO_TURQUOISE_RGB.jpg | Bin .../img/sponsors/DKUUGlogo.jpeg | Bin .../sponsors/KristiansKaffe_Logo_Hires1.0.jpg | Bin .../img/sponsors/PROSA-logo.png | Bin .../img/sponsors/PS_Logo_CMYK_DK.png | Bin .../img/sponsors/ShopGun_LogoType_Green.png | Bin .../img/sponsors/bitbureauet.png | Bin .../img/sponsors/bornfiber-logoweb.png | Bin .../img/sponsors/brklogo.png | Bin .../img/sponsors/danskebank.png | Bin .../img/sponsors/fortconsultlogo.png | Bin .../img/sponsors/prosa-u35-1.png | Bin .../img/sponsors/samdatalogo.jpg | Bin .../img/sponsors/septima-logo.png | Bin .../img/sponsors/tyktech_logo.png | Bin .../img/sponsors/zencurity_logo.png | Bin .../img/sponsors/zybersafe_logo.png | Bin .../static_src => static_src}/js/bootstrap.js | 0 .../js/bootstrap.min.js | 0 .../js/images/layers-2x.png | Bin .../js/images/layers.png | Bin .../js/images/marker-icon-2x.png | Bin .../js/images/marker-icon.png | Bin .../js/images/marker-shadow.png | Bin .../js/jquery.min.js | 0 .../js/leaflet-src.js | 0 .../static_src => static_src}/js/leaflet.js | 0 .../pdf/bornhack_2016_letterhead.odt | Bin .../pdf/bornhack_2016_letterhead.pdf | Bin .../pdf/bornhack_2016_test_letterhead.odt | Bin .../pdf/bornhack_2016_test_letterhead.pdf | Bin .../bornhack_webshop_cancellation_form.pdf | Bin {bornhack/templates => templates}/404.html | 0 {bornhack/templates => templates}/500.html | 0 {bornhack/templates => templates}/base.html | 25 +- {bornhack/templates => templates}/coc.html | 0 .../templates => templates}/contact.html | 0 .../templates => templates}/frontpage.html | 0 .../includes/contact.html | 0 {bornhack/templates => templates}/info.html | 0 .../legal/general_terms_and_conditions.html | 0 .../legal/privacy_policy.html | 0 .../templates => templates}/speakers.html | 0 .../templates => templates}/sponsors.html | 0 utils/management/__init__.py | 0 utils/management/commands/__init__.py | 0 .../management/commands/bootstrap-devsite.py | 266 ++++++++++++++++++ utils/models.py | 27 +- utils/templatetags/commonmark.py | 2 + .../migrations/0006_remove_village_camp.py | 19 ++ villages/models.py | 6 +- 109 files changed, 1167 insertions(+), 286 deletions(-) delete mode 100644 camps/managers.py create mode 100644 camps/migrations/0007_auto_20161212_1803.py create mode 100644 camps/migrations/0008_delete_day.py create mode 100644 camps/migrations/0009_auto_20161220_1645.py create mode 100644 camps/migrations/0010_auto_20161220_1714.py create mode 100644 camps/mixins.py create mode 100644 camps/templates/camp_detail.html create mode 100644 info/__init__.py create mode 100644 info/admin.py create mode 100644 info/apps.py create mode 100644 info/migrations/0001_initial.py create mode 100644 info/migrations/__init__.py create mode 100644 info/models.py create mode 100644 info/templates/info.html rename {camps => info}/tests.py (100%) create mode 100644 info/views.py delete mode 100644 news/managers.py create mode 100644 news/migrations/0006_remove_newsitem_public.py create mode 100644 news/migrations/0007_auto_20161220_1136.py create mode 100644 news/migrations/0008_newsitem_archived.py create mode 100644 program/migrations/0010_auto_20161212_1809.py create mode 100644 shop/migrations/0033_auto_20161212_1756.py rename {bornhack/static_src => static_src}/css/bootstrap-theme.css (100%) rename {bornhack/static_src => static_src}/css/bootstrap-theme.css.map (100%) rename {bornhack/static_src => static_src}/css/bootstrap-theme.min.css (100%) rename {bornhack/static_src => static_src}/css/bootstrap.css (100%) rename {bornhack/static_src => static_src}/css/bootstrap.css.map (100%) rename {bornhack/static_src => static_src}/css/bootstrap.min.css (100%) rename {bornhack/static_src => static_src}/css/bornhack.css (100%) rename {bornhack/static_src => static_src}/css/leaflet.css (100%) rename {bornhack/static_src => static_src}/fonts/glyphicons-halflings-regular.eot (100%) rename {bornhack/static_src => static_src}/fonts/glyphicons-halflings-regular.svg (100%) rename {bornhack/static_src => static_src}/fonts/glyphicons-halflings-regular.ttf (100%) rename {bornhack/static_src => static_src}/fonts/glyphicons-halflings-regular.woff (100%) rename {bornhack/static_src => static_src}/fonts/glyphicons-halflings-regular.woff2 (100%) rename {bornhack/static_src => static_src}/img/logo-new-paths.svg (100%) rename {bornhack/static_src => static_src}/img/logo-new.png (100%) rename {bornhack/static_src => static_src}/img/logo-new.svg (100%) rename {bornhack/static_src => static_src}/img/logo.png (100%) rename {bornhack/static_src => static_src}/img/logo.svg (100%) rename {bornhack/static_src => static_src}/img/sponsors/CSIS_PRI_LOGO_TURQUOISE_RGB.jpg (100%) rename {bornhack/static_src => static_src}/img/sponsors/DKUUGlogo.jpeg (100%) rename {bornhack/static_src => static_src}/img/sponsors/KristiansKaffe_Logo_Hires1.0.jpg (100%) rename {bornhack/static_src => static_src}/img/sponsors/PROSA-logo.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/PS_Logo_CMYK_DK.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/ShopGun_LogoType_Green.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/bitbureauet.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/bornfiber-logoweb.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/brklogo.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/danskebank.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/fortconsultlogo.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/prosa-u35-1.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/samdatalogo.jpg (100%) rename {bornhack/static_src => static_src}/img/sponsors/septima-logo.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/tyktech_logo.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/zencurity_logo.png (100%) rename {bornhack/static_src => static_src}/img/sponsors/zybersafe_logo.png (100%) rename {bornhack/static_src => static_src}/js/bootstrap.js (100%) rename {bornhack/static_src => static_src}/js/bootstrap.min.js (100%) rename {bornhack/static_src => static_src}/js/images/layers-2x.png (100%) rename {bornhack/static_src => static_src}/js/images/layers.png (100%) rename {bornhack/static_src => static_src}/js/images/marker-icon-2x.png (100%) rename {bornhack/static_src => static_src}/js/images/marker-icon.png (100%) rename {bornhack/static_src => static_src}/js/images/marker-shadow.png (100%) rename {bornhack/static_src => static_src}/js/jquery.min.js (100%) rename {bornhack/static_src => static_src}/js/leaflet-src.js (100%) rename {bornhack/static_src => static_src}/js/leaflet.js (100%) rename {bornhack/static_src => static_src}/pdf/bornhack_2016_letterhead.odt (100%) rename {bornhack/static_src => static_src}/pdf/bornhack_2016_letterhead.pdf (100%) rename {bornhack/static_src => static_src}/pdf/bornhack_2016_test_letterhead.odt (100%) rename {bornhack/static_src => static_src}/pdf/bornhack_2016_test_letterhead.pdf (100%) rename {bornhack/static_src => static_src}/pdf/bornhack_webshop_cancellation_form.pdf (100%) rename {bornhack/templates => templates}/404.html (100%) rename {bornhack/templates => templates}/500.html (100%) rename {bornhack/templates => templates}/base.html (67%) rename {bornhack/templates => templates}/coc.html (100%) rename {bornhack/templates => templates}/contact.html (100%) rename {bornhack/templates => templates}/frontpage.html (100%) rename {bornhack/templates => templates}/includes/contact.html (100%) rename {bornhack/templates => templates}/info.html (100%) rename {bornhack/templates => templates}/legal/general_terms_and_conditions.html (100%) rename {bornhack/templates => templates}/legal/privacy_policy.html (100%) rename {bornhack/templates => templates}/speakers.html (100%) rename {bornhack/templates => templates}/sponsors.html (100%) create mode 100644 utils/management/__init__.py create mode 100644 utils/management/commands/__init__.py create mode 100644 utils/management/commands/bootstrap-devsite.py create mode 100644 villages/migrations/0006_remove_village_camp.py diff --git a/bornhack/settings.py b/bornhack/settings.py index 9759ce78..c9108e34 100644 --- a/bornhack/settings.py +++ b/bornhack/settings.py @@ -42,48 +42,13 @@ INSTALLED_APPS = [ 'utils', 'villages', 'program', + 'info', 'allauth', 'allauth.account', 'bootstrap3', ] -DEBUG = env('DEBUG') -if DEBUG: - EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' - INSTALLED_APPS += ['debug_toolbar', ] -else: - EMAIL_HOST = env('EMAIL_HOST') - EMAIL_PORT = env('EMAIL_PORT') - EMAIL_HOST_USER = env('EMAIL_HOST_USER') - EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD') - EMAIL_USE_TLS = env('EMAIL_USE_TLS') - DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL') - SERVER_EMAIL = env('DEFAULT_FROM_EMAIL') - ARCHIVE_EMAIL = env('ARCHIVE_EMAIL') - - LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'mail_admins': { - 'level': 'ERROR', - 'class': 'django.utils.log.AdminEmailHandler', - }, - 'console': { - 'level':'DEBUG', - 'class':'logging.StreamHandler', - }, - }, - 'loggers': { - 'django.request': { - 'handlers': ['mail_admins'], - 'level': 'ERROR', - 'propagate': True, - }, - } - } - STATIC_URL = '/static/' STATIC_ROOT = local_dir('static') STATICFILES_DIRS = [local_dir('static_src')] @@ -105,9 +70,9 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', - 'camps.context_processors.current_camp', 'shop.context_processors.current_order', 'shop.context_processors.user_has_tickets', + 'camps.context_processors.camps', ], }, }, @@ -128,8 +93,8 @@ MIDDLEWARE_CLASSES = [ LOGIN_REDIRECT_URL = 'profiles:detail' AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', # Login to admin with username - 'allauth.account.auth_backends.AuthenticationBackend', + 'django.contrib.auth.backends.ModelBackend', # Handles login to admin with username + 'allauth.account.auth_backends.AuthenticationBackend', # Handles regular logins ) ACCOUNT_AUTHENTICATION_METHOD = 'email' @@ -163,3 +128,42 @@ BANKACCOUNT_REG = env('BANKACCOUNT_REG') BANKACCOUNT_ACCOUNT = env('BANKACCOUNT_ACCOUNT') TICKET_CATEGORY_ID = env('TICKET_CATEGORY_ID') + +DEBUG = env('DEBUG') +if DEBUG: + EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + INSTALLED_APPS += ['debug_toolbar', ] + MIDDLEWARE_CLASSES += ['debug_toolbar.middleware.DebugToolbarMiddleware', ] + +else: + EMAIL_HOST = env('EMAIL_HOST') + EMAIL_PORT = env('EMAIL_PORT') + EMAIL_HOST_USER = env('EMAIL_HOST_USER') + EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD') + EMAIL_USE_TLS = env('EMAIL_USE_TLS') + DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL') + SERVER_EMAIL = env('DEFAULT_FROM_EMAIL') + ARCHIVE_EMAIL = env('ARCHIVE_EMAIL') + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + }, + 'console': { + 'level':'DEBUG', + 'class':'logging.StreamHandler', + }, + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} + diff --git a/bornhack/urls.py b/bornhack/urls.py index be35409a..03759c06 100644 --- a/bornhack/urls.py +++ b/bornhack/urls.py @@ -10,6 +10,9 @@ from django.conf.urls import include, url from django.contrib import admin from django.views.generic import TemplateView, RedirectView from django.core.urlresolvers import reverse_lazy +from camps.views import * +from info.views import CampInfoView + urlpatterns = [ url( @@ -84,4 +87,57 @@ urlpatterns = [ ), url(r'^accounts/', include('allauth.urls')), url(r'^admin/', include(admin.site.urls)), + + # camp specific urls below here + + url(r'(?P[-_\w+]+)/', include([ + url( + r'^$', + CampDetailView.as_view(), + name='camp_detail' + ), + url( + r'^info/$', + CampInfoView.as_view(), + name='info' + ), + url( + r'^schedule/$', + CampScheduleView.as_view(), + name='schedule' + ), + url( + r'^sponsors/$', + CampSponsorView.as_view(), + name='camp_sponsors' + ), + url(r'^villages/$', include([ + url( + r'^$', + VillageListView.as_view(), + name='village_list' + ), + url( + r'create/$', + VillageCreateView.as_view(), + name='village_create' + ), + url( + r'(?P[-_\w+]+)/delete/$', + VillageDeleteView.as_view(), + name='village_delete' + ), + url( + r'(?P[-_\w+]+)/edit/$', + VillageUpdateView.as_view(), + name='village_update' + ), + url( + r'(?P[-_\w+]+)/$', + VillageDetailView.as_view(), + name='village_detail' + ), + ])), + ])), ] + diff --git a/camps/admin.py b/camps/admin.py index 54fda1ab..f777d688 100644 --- a/camps/admin.py +++ b/camps/admin.py @@ -1,29 +1,8 @@ from django.contrib import admin - -from .models import Camp, Day, Expense +from . import models -@admin.register(Expense) -class ExpenseAdmin(admin.ModelAdmin): +@admin.register(models.Camp) +class CampModelAdmin(admin.ModelAdmin): pass - -class ExpenseInlineAdmin(admin.TabularInline): - model = Expense - - -@admin.register(Day) -class DayAdmin(admin.ModelAdmin): - pass - - -class DayInlineAdmin(admin.TabularInline): - model = Day - - -@admin.register(Camp) -class CampAdmin(admin.ModelAdmin): - inlines = [ - DayInlineAdmin, - ExpenseInlineAdmin, - ] diff --git a/camps/context_processors.py b/camps/context_processors.py index 01cdfacf..faf0aed7 100644 --- a/camps/context_processors.py +++ b/camps/context_processors.py @@ -1,5 +1,11 @@ +from django.conf import settings from .models import Camp +from django.utils import timezone -def current_camp(request): - return {'current_camp': Camp.objects.current()} +def camps(request): + return { + 'upcoming_camps': Camp.objects.filter(camp_start__gt=timezone.now()), + 'previous_camps': Camp.objects.filter(camp_start__lt=timezone.now()), + } + diff --git a/camps/managers.py b/camps/managers.py deleted file mode 100644 index 908f1fe4..00000000 --- a/camps/managers.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.utils import timezone -from django.db.models import QuerySet - - -class CampQuerySet(QuerySet): - def current(self): - now = timezone.now() - if self.filter(start__year=now.year).exists(): - return self.get(start__year=now.year) - return None diff --git a/camps/migrations/0007_auto_20161212_1803.py b/camps/migrations/0007_auto_20161212_1803.py new file mode 100644 index 00000000..6aa6c96e --- /dev/null +++ b/camps/migrations/0007_auto_20161212_1803.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-12 18:03 +from __future__ import unicode_literals + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('camps', '0006_auto_20160804_1705'), + ] + + operations = [ + migrations.RemoveField( + model_name='day', + name='camp', + ), + migrations.RemoveField( + model_name='camp', + name='shop_open', + ), + migrations.RemoveField( + model_name='expense', + name='amount', + ), + migrations.RemoveField( + model_name='expense', + name='camp', + ), + migrations.RemoveField( + model_name='expense', + name='covered_by', + ), + migrations.RemoveField( + model_name='expense', + name='currency', + ), + migrations.AddField( + model_name='camp', + name='slug', + field=models.SlugField(default='', help_text=b'The url slug to use for this camp', verbose_name=b'Url Slug'), + preserve_default=False, + ), + migrations.AddField( + model_name='expense', + name='dkk_amount', + field=models.DecimalField(decimal_places=2, default=0, help_text=b'The DKK amount of the expense.', max_digits=7, verbose_name=b'DKK Amount'), + preserve_default=False, + ), + migrations.AddField( + model_name='expense', + name='payment_time', + field=models.DateTimeField(default=datetime.datetime(2016, 12, 12, 18, 3, 10, 378604, tzinfo=utc), help_text=b'The date and time this expense was paid.', verbose_name=b'Expense date/time'), + preserve_default=False, + ), + migrations.AddField( + model_name='expense', + name='receipt', + field=models.ImageField(default='', help_text=b'Upload a scan or image of the receipt', upload_to=b'', verbose_name=b'Image of receipt'), + preserve_default=False, + ), + migrations.AddField( + model_name='expense', + name='refund_paid', + field=models.BooleanField(default=False, help_text=b'Has this expense been refunded to the user?', verbose_name=b'Refund paid?'), + ), + migrations.AddField( + model_name='expense', + name='refund_user', + field=models.ForeignKey(blank=True, help_text=b'Which user, if any, covered this expense and should be refunded.', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name=b'Refund user'), + ), + migrations.AlterField( + model_name='camp', + name='end', + field=models.DateTimeField(help_text=b'When the camp ends.', verbose_name=b'End date'), + ), + migrations.AlterField( + model_name='camp', + name='name', + field=models.CharField(help_text=b'Name of the camp, ie. Bornhack 2016.', max_length=255, verbose_name=b'Name'), + ), + migrations.AlterField( + model_name='camp', + name='start', + field=models.DateTimeField(help_text=b'When the camp starts.', verbose_name=b'Start date'), + ), + ] diff --git a/camps/migrations/0008_delete_day.py b/camps/migrations/0008_delete_day.py new file mode 100644 index 00000000..58ad3137 --- /dev/null +++ b/camps/migrations/0008_delete_day.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-12 18:09 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('program', '0010_auto_20161212_1809'), + ('camps', '0007_auto_20161212_1803'), + ] + + operations = [ + migrations.DeleteModel( + name='Day', + ), + ] diff --git a/camps/migrations/0009_auto_20161220_1645.py b/camps/migrations/0009_auto_20161220_1645.py new file mode 100644 index 00000000..c6214432 --- /dev/null +++ b/camps/migrations/0009_auto_20161220_1645.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-20 16:45 +from __future__ import unicode_literals + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0008_delete_day'), + ] + + operations = [ + migrations.RenameField( + model_name='camp', + old_name='end', + new_name='camp_end', + ), + migrations.RenameField( + model_name='camp', + old_name='start', + new_name='camp_start', + ), + migrations.AddField( + model_name='camp', + name='buildup_start', + field=models.DateTimeField(default=datetime.datetime(2016, 12, 20, 16, 45, 39, 609630, tzinfo=utc), help_text=b'When the camp buildup starts.', verbose_name=b'Buildup Start date'), + preserve_default=False, + ), + migrations.AddField( + model_name='camp', + name='teardown_end', + field=models.DateTimeField(default=datetime.datetime(2016, 12, 20, 16, 45, 44, 532143, tzinfo=utc), help_text=b'When the camp teardown ends.', verbose_name=b'Start date'), + preserve_default=False, + ), + ] diff --git a/camps/migrations/0010_auto_20161220_1714.py b/camps/migrations/0010_auto_20161220_1714.py new file mode 100644 index 00000000..770763dd --- /dev/null +++ b/camps/migrations/0010_auto_20161220_1714.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-20 17:14 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0009_auto_20161220_1645'), + ] + + operations = [ + migrations.RemoveField( + model_name='camp', + name='name', + ), + migrations.AddField( + model_name='camp', + name='tagline', + field=models.CharField(default='', help_text=b'Tagline of the camp, ie. "Initial Commit"', max_length=255, verbose_name=b'Tagline'), + preserve_default=False, + ), + migrations.AddField( + model_name='camp', + name='title', + field=models.CharField(default='', help_text=b'Title of the camp, ie. Bornhack 2016.', max_length=255, verbose_name=b'Title'), + preserve_default=False, + ), + ] diff --git a/camps/mixins.py b/camps/mixins.py new file mode 100644 index 00000000..cee4d1ad --- /dev/null +++ b/camps/mixins.py @@ -0,0 +1,13 @@ +from django.views.generic.detail import SingleObjectMixin +from camps.models import Camp +from django.shortcuts import get_object_or_404 + + +class CampViewMixin(Object): + def dispatch(self, request, *args, **kwargs): + self.camp = get_object_or_404(Camp, slug=self.kwargs.camp_slug) + return super(CampViewMixin, self).dispatch(request, *args, **kwargs) + + def get_queryset(self): + return self.objects.filter(camp=self.camp) + diff --git a/camps/models.py b/camps/models.py index bcbeb731..8118f660 100644 --- a/camps/models.py +++ b/camps/models.py @@ -1,135 +1,93 @@ import datetime from django.db import models -from django.utils.translation import ugettext_lazy as _ - from utils.models import UUIDModel, CreatedUpdatedModel -from .managers import CampQuerySet - class Camp(CreatedUpdatedModel, UUIDModel): class Meta: - verbose_name = _('Camp') - verbose_name_plural = _('Camps') + verbose_name = 'Camp' + verbose_name_plural = 'Camps' - name = models.CharField( - verbose_name=_('Name'), - help_text=_('Name of the camp, ie. Bornhack.'), + title = models.CharField( + verbose_name='Title', + help_text='Title of the camp, ie. Bornhack 2016.', max_length=255, ) - start = models.DateTimeField( - verbose_name=_('Start date'), - help_text=_('When the camp starts.'), - unique=True, + tagline = models.CharField( + verbose_name='Tagline', + help_text='Tagline of the camp, ie. "Initial Commit"', + max_length=255, ) - end = models.DateTimeField( - verbose_name=_('End date'), - help_text=_('When the camp ends.'), - unique=True, + slug = models.SlugField( + verbose_name='Url Slug', + help_text='The url slug to use for this camp' ) - shop_open = models.BooleanField( - verbose_name=_('Shop open?'), - help_text=_('Whether the shop is open or not.'), - default=False, + buildup_start = models.DateTimeField( + verbose_name='Buildup Start date', + help_text='When the camp buildup starts.', ) - objects = CampQuerySet.as_manager() - - def __str__(self): - return _('{} {}').format( - self.name, - self.start.year, - ) - - def create_days(self): - delta = self.end - self.start - for day_offset in range(0, delta.days + 1): - day, created = self.days.get_or_create( - date=self.start + datetime.timedelta(days=day_offset) - ) - - def save(self, **kwargs): - super(Camp, self).save(**kwargs) - self.create_days() - - -class Day(CreatedUpdatedModel, UUIDModel): - class Meta: - verbose_name = _('Day') - verbose_name_plural = _('Days') - ordering = ['date'] - - camp = models.ForeignKey( - 'camps.Camp', - verbose_name=_('Camp'), - help_text=_('Which camp does this day belong to.'), - related_name='days', + camp_start = models.DateTimeField( + verbose_name='Start date', + help_text='When the camp starts.', ) - date = models.DateField( - verbose_name=_('Date'), - help_text=_('What date?') + camp_end = models.DateTimeField( + verbose_name='End date', + help_text='When the camp ends.', ) - def __str__(self): - return '{} ({})'.format( - self.date.strftime('%A'), - self.date - ) + teardown_end = models.DateTimeField( + verbose_name='Start date', + help_text='When the camp teardown ends.', + ) + + def __unicode__(self): + return "%s - %s" % (self.title, self.tagline) class Expense(CreatedUpdatedModel, UUIDModel): class Meta: - verbose_name = _('Expense') - verbose_name_plural = _('Expenses') + verbose_name = 'Expense' + verbose_name_plural = 'Expenses' - camp = models.ForeignKey( - 'camps.Camp', - verbose_name=_('Camp'), - help_text=_('The camp to which this expense relates to.'), + payment_time = models.DateTimeField( + verbose_name='Expense date/time', + help_text='The date and time this expense was paid.', ) description = models.CharField( - verbose_name=_('Description'), - help_text=_('What this expense covers.'), + verbose_name='Description', + help_text='What this expense covers.', max_length=255, ) - amount = models.DecimalField( - verbose_name=_('Amount'), - help_text=_('The amount of the expense.'), + dkk_amount = models.DecimalField( + verbose_name='DKK Amount', + help_text='The DKK amount of the expense.', max_digits=7, decimal_places=2, ) - CURRENCIES = [ - ('btc', 'BTC'), - ('dkk', 'DKK'), - ('eur', 'EUR'), - ('sek', 'SEK'), - ] - - currency = models.CharField( - verbose_name=_('Currency'), - help_text=_('What currency the amount is in.'), - choices=CURRENCIES, - max_length=3, + receipt = models.ImageField( + verbose_name='Image of receipt', + help_text='Upload a scan or image of the receipt', ) - covered_by = models.ForeignKey( + refund_user = models.ForeignKey( 'auth.User', - verbose_name=_('Covered by'), - help_text=_('Which user, if any, covered this expense.'), + verbose_name='Refund user', + help_text='Which user, if any, covered this expense and should be refunded.', null=True, blank=True, ) - def __str__(self): - return _('{} {} for {}').format( - self.amount, - self.get_currency_display(), - self.camp, - ) + refund_paid = models.BooleanField( + default=False, + verbose_name='Refund paid?', + help_text='Has this expense been refunded to the user?', + ) + diff --git a/camps/templates/camp_detail.html b/camps/templates/camp_detail.html new file mode 100644 index 00000000..92cdd69a --- /dev/null +++ b/camps/templates/camp_detail.html @@ -0,0 +1,5 @@ +{% extends 'base.html' %} +{% block content %} +{{ camp.title }} +{% endblock content %} + diff --git a/camps/views.py b/camps/views.py index 7c378d64..f791138c 100644 --- a/camps/views.py +++ b/camps/views.py @@ -1,3 +1,10 @@ -from . import models +from django.views.generic import ListView, DetailView +from django.utils import timezone +from .models import * +class CampDetailView(DetailView): + model = Camp + template_name = 'camp_detail.html' + slug_url_kwarg = 'camp_slug' + diff --git a/info/__init__.py b/info/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/info/admin.py b/info/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/info/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/info/apps.py b/info/apps.py new file mode 100644 index 00000000..f4738cf0 --- /dev/null +++ b/info/apps.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class InfoConfig(AppConfig): + name = 'info' diff --git a/info/migrations/0001_initial.py b/info/migrations/0001_initial.py new file mode 100644 index 00000000..f554d348 --- /dev/null +++ b/info/migrations/0001_initial.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-24 22:11 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('camps', '0010_auto_20161220_1714'), + ] + + operations = [ + migrations.CreateModel( + name='InfoCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('headline', models.CharField(help_text='The headline of this info category', max_length=100)), + ('anchor', models.SlugField(help_text='The HTML anchor to use for this info category.')), + ('weight', models.PositiveIntegerField(help_text='Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically.')), + ('camp', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='infocategories', to='camps.Camp')), + ], + options={ + 'ordering': ['-weight', 'headline'], + }, + ), + migrations.CreateModel( + name='InfoItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('headline', models.CharField(help_text='Headline of this info item.', max_length=100)), + ('anchor', models.SlugField(help_text='The HTML anchor to use for this info item.')), + ('body', models.TextField(help_text='Body of this info item. Markdown is supported.')), + ('weight', models.PositiveIntegerField(help_text='Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically.')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='infoitems', to='info.InfoCategory')), + ], + options={ + 'ordering': ['-weight', 'headline'], + }, + ), + migrations.AlterUniqueTogether( + name='infoitem', + unique_together=set([('headline', 'category'), ('anchor', 'category')]), + ), + migrations.AlterUniqueTogether( + name='infocategory', + unique_together=set([('headline', 'camp'), ('anchor', 'camp')]), + ), + ] diff --git a/info/migrations/__init__.py b/info/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/info/models.py b/info/models.py new file mode 100644 index 00000000..303b083c --- /dev/null +++ b/info/models.py @@ -0,0 +1,71 @@ +from __future__ import unicode_literals +from django.contrib import messages +from django.db import models +from utils.models import CreatedUpdatedModel +from django.core.exceptions import ValidationError + + +class InfoCategory(CreatedUpdatedModel): + class Meta: + ordering = ['-weight', 'headline'] + unique_together = (('anchor', 'camp'), ('headline', 'camp')) + + camp = models.ForeignKey( + 'camps.Camp', + related_name = 'infocategories', + on_delete = models.PROTECT + ) + + headline = models.CharField( + max_length = 100, + help_text = "The headline of this info category" + ) + + anchor = models.SlugField( + help_text = "The HTML anchor to use for this info category." + ) + + weight = models.PositiveIntegerField( + help_text = 'Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically.' + ) + + def clean(self): + if InfoItem.objects.filter(camp=self.camp, anchor=self.anchor).exists(): + # this anchor is already in use on an item, so it cannot be used (must be unique on the page) + raise ValidationError({'anchor': 'Anchor is already in use on an info item for this camp'}) + + +class InfoItem(CreatedUpdatedModel): + class Meta: + ordering = ['-weight', 'headline'] + unique_together = (('anchor', 'category'), ('headline', 'category')) + + category = models.ForeignKey( + 'info.InfoCategory', + related_name = 'infoitems', + on_delete = models.PROTECT + ) + + headline = models.CharField( + max_length = 100, + help_text = "Headline of this info item." + ) + + anchor = models.SlugField( + help_text = "The HTML anchor to use for this info item." + ) + + body = models.TextField( + help_text = 'Body of this info item. Markdown is supported.' + ) + + weight = models.PositiveIntegerField( + help_text = 'Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically.' + ) + + def clean(self): + if InfoCategory.objects.filter(camp=self.camp, anchor=self.anchor).exists(): + # this anchor is already in use on a category, so it cannot be used here (they must be unique on the entire page) + raise ValidationError({'anchor': 'Anchor is already in use on an info category for this camp'}) + + diff --git a/info/templates/info.html b/info/templates/info.html new file mode 100644 index 00000000..1c2d1b2d --- /dev/null +++ b/info/templates/info.html @@ -0,0 +1,85 @@ +{% extends 'base.html' %} +{% load static from staticfiles %} + +{% block title %} +Info | {{ block.super }} +{% endblock %} + +{% block extra_head %} + +{% endblock %} + +{% block content %} + + +
+
+

Table of Contents

+

+ {% for category in categories %} + {{ category.headline }} + {% endfor %} +

+
+
+ + {% for category in categories %} + +
+
+

When is BornHack happening?

+
+ {% for item in category.infoitems %} +
+
+ +

+ {{ item.title }} + + + +

+
+
+

{{ item.body }}

+
+
+ {% endfor %} +
+
+
+ {% endfor %} + + + +{% endblock %} diff --git a/camps/tests.py b/info/tests.py similarity index 100% rename from camps/tests.py rename to info/tests.py diff --git a/info/views.py b/info/views.py new file mode 100644 index 00000000..3a00a0f7 --- /dev/null +++ b/info/views.py @@ -0,0 +1,17 @@ +from django.shortcuts import render +from django.views.generic import ListView, DetailView +from django.utils import timezone +from .models import * + + +class CampInfoView(ListView): + model = InfoCategory + template_name = 'info.html' + context_object_name = 'categories' + + def get_queryset(self, **kwargs): + return InfoCategory.objects.filter( + camp__slug=self.kwargs['camp_slug'] + ) + + diff --git a/news/admin.py b/news/admin.py index 587c9ff9..ea5a22a9 100644 --- a/news/admin.py +++ b/news/admin.py @@ -5,5 +5,15 @@ from . import models @admin.register(models.NewsItem) class NewsItemModelAdmin(admin.ModelAdmin): - list_display = ['title', 'public', 'published_at'] - list_filter = ['public'] + list_display = ['title', 'published_at', 'archived'] + actions = ['archive_news_items', 'unarchive_news_items'] + + def archive_news_items(self, request, queryset): + queryset.filter(archived=False).update(archived=True) + archive_news_items.description = 'Mark newsitem(s) as archived' + + def unarchive_news_items(self, request, queryset): + queryset.filter(archived=True).update(archived=False) + unarchive_news_items.description = 'Mark newsitem(s) as not archived' + + diff --git a/news/managers.py b/news/managers.py deleted file mode 100644 index c25424f5..00000000 --- a/news/managers.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.db.models import QuerySet -from django.utils import timezone - - -class NewsItemQuerySet(QuerySet): - - def public(self): - return self.filter( - public=True, - published_at__lt=timezone.now() - ) diff --git a/news/migrations/0006_remove_newsitem_public.py b/news/migrations/0006_remove_newsitem_public.py new file mode 100644 index 00000000..6577b1bc --- /dev/null +++ b/news/migrations/0006_remove_newsitem_public.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-20 11:27 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0005_auto_20160618_1902'), + ] + + operations = [ + migrations.RemoveField( + model_name='newsitem', + name='public', + ), + ] diff --git a/news/migrations/0007_auto_20161220_1136.py b/news/migrations/0007_auto_20161220_1136.py new file mode 100644 index 00000000..7d03a585 --- /dev/null +++ b/news/migrations/0007_auto_20161220_1136.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-20 11:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0006_remove_newsitem_public'), + ] + + operations = [ + migrations.AlterField( + model_name='newsitem', + name='published_at', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/news/migrations/0008_newsitem_archived.py b/news/migrations/0008_newsitem_archived.py new file mode 100644 index 00000000..ec2cf548 --- /dev/null +++ b/news/migrations/0008_newsitem_archived.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-20 13:03 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0007_auto_20161220_1136'), + ] + + operations = [ + migrations.AddField( + model_name='newsitem', + name='archived', + field=models.BooleanField(default=False), + ), + ] diff --git a/news/models.py b/news/models.py index 4b08b223..7e6995c7 100644 --- a/news/models.py +++ b/news/models.py @@ -5,7 +5,6 @@ from django.utils import encoding from django.utils.text import slugify from utils.models import CreatedUpdatedModel -from news.managers import NewsItemQuerySet @encoding.python_2_unicode_compatible @@ -15,36 +14,33 @@ class NewsItem(CreatedUpdatedModel): title = models.CharField(max_length=100) content = models.TextField() - public = models.BooleanField(default=False) - published_at = models.DateTimeField() + published_at = models.DateTimeField(null=True, blank=True) slug = models.SlugField(max_length=255, blank=True) + archived = models.BooleanField(default=False) def __str__(self): return self.title def save(self, **kwargs): - if ( - not self.pk or - not self.slug or - NewsItem.objects.filter(slug=self.slug).count() > 1 - ): - published_at_string = self.published_at.strftime('%Y-%m-%d') - base_slug = slugify(self.title) - slug = '{}-{}'.format(published_at_string, base_slug) - incrementer = 1 + if self.published_at: + # if this is a new newsitem, or it doesn't have a slug, or the slug is in use on another item, create a new slug + if (not self.pk or not self.slug or NewsItem.objects.filter(slug=self.slug).count() > 1): + published_at_string = self.published_at.strftime('%Y-%m-%d') + base_slug = slugify(self.title) + slug = '{}-{}'.format(published_at_string, base_slug) + incrementer = 1 - # We have to make sure that the slug won't clash with current slugs - while NewsItem.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 + # We have to make sure that the slug won't clash with current slugs + while NewsItem.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 super(NewsItem, self).save(**kwargs) - objects = NewsItemQuerySet.as_manager() diff --git a/news/views.py b/news/views.py index 45891c12..9b8a47b0 100644 --- a/news/views.py +++ b/news/views.py @@ -1,33 +1,22 @@ from django.views.generic import ListView, DetailView from django.utils import timezone -from . import models - +from .models import * class NewsIndex(ListView): - model = models.NewsItem + model = NewsItem template_name = 'news_index.html' context_object_name = 'news_items' - def get_queryset(self): - return self.model.objects.public() + def get_queryset(self, **kwargs): + return NewsItem.objects.filter( + published_at__isnull=False, + published_at__lt=timezone.now(), + archived=False + ) class NewsDetail(DetailView): - model = models.NewsItem + model = NewsItem template_name = 'news_detail.html' context_object_name = 'news_item' - def get_context_data(self, **kwargs): - context = super(NewsDetail, self).get_context_data(**kwargs) - news_item = self.get_object() - timed = news_item.published_at > timezone.now() - - if news_item.public and timed: - context['not_public'] = True - context['timed'] = True - elif not news_item.public: - context['not_public'] = True - context['timed'] = False - - return context - diff --git a/profiles/models.py b/profiles/models.py index c072b585..2e85ca33 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -30,3 +30,4 @@ class Profile(CreatedUpdatedModel, UUIDModel): def create_profile(sender, created, instance, **kwargs): if created: Profile.objects.create(user=instance) + diff --git a/program/admin.py b/program/admin.py index 5256637a..1e2d908c 100644 --- a/program/admin.py +++ b/program/admin.py @@ -22,17 +22,8 @@ class EventAdmin(admin.ModelAdmin): list_display = [ 'title', 'event_type', - 'get_days', - 'start', - 'end', ] - def get_days(self, obj): - return ', '.join([ - str(day.date.strftime('%a')) - for day in obj.days.all() - ]) - inlines = [ SpeakerInline ] diff --git a/program/migrations/0010_auto_20161212_1809.py b/program/migrations/0010_auto_20161212_1809.py new file mode 100644 index 00000000..e85ea059 --- /dev/null +++ b/program/migrations/0010_auto_20161212_1809.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-12 18:09 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0007_auto_20161212_1803'), + ('program', '0009_auto_20160827_0752'), + ] + + operations = [ + migrations.CreateModel( + name='EventInstance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('start', models.DateTimeField()), + ('end', models.DateTimeField()), + ], + options={ + 'ordering': ['start'], + }, + ), + migrations.RemoveField( + model_name='event', + name='days', + ), + migrations.RemoveField( + model_name='event', + name='end', + ), + migrations.RemoveField( + model_name='event', + name='start', + ), + migrations.AddField( + model_name='event', + name='camp', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='camps.Camp'), + ), + migrations.AddField( + model_name='eventinstance', + name='event', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='program.Event'), + ), + ] diff --git a/program/models.py b/program/models.py index fadf7442..73b6ee14 100644 --- a/program/models.py +++ b/program/models.py @@ -23,9 +23,7 @@ class Event(CreatedUpdatedModel): slug = models.SlugField(blank=True, max_length=255) abstract = models.TextField() event_type = models.ForeignKey(EventType) - days = models.ManyToManyField('camps.Day', blank=True) - start = models.TimeField(null=True, blank=True) - end = models.TimeField(null=True, blank=True) + camp = models.ForeignKey('camps.Camp', null=True) class Meta: ordering = ['title'] @@ -39,6 +37,20 @@ class Event(CreatedUpdatedModel): super(Event, self).save(**kwargs) +class EventInstance(CreatedUpdatedModel): + """ An instance of an event """ + event = models.ForeignKey('program.event', related_name='instances') + start = models.DateTimeField() + end = models.DateTimeField() + + class Meta: + ordering = ['start'] + + def __unicode__(self): + return '%s (%s to %s)' % (self.event, self.start, self.end) + + + class Speaker(CreatedUpdatedModel): """ Person anchoring an event. """ name = models.CharField(max_length=150) diff --git a/program/views.py b/program/views.py index 01d71dc7..79e13227 100644 --- a/program/views.py +++ b/program/views.py @@ -2,24 +2,27 @@ from collections import OrderedDict import datetime from django.views.generic import ListView, TemplateView, DetailView +from camp.mixins import CampViewMixin -from camps.models import Day from . import models -class SpeakerDetailView(DetailView): +class SpeakerDetailView(CampViewMixin, DetailView): model = models.Speaker template_name = 'speaker_detail.html' -class SpeakerListView(ListView): + +class SpeakerListView(CampViewMixin, ListView): model = models.Speaker template_name = 'speaker_list.html' -class EventListView(ListView): + +class EventListView(CampViewMixin, ListView): model = models.Event template_name = 'event_list.html' -class ProgramOverviewView(ListView): + +class ProgramOverviewView(CampViewMixin, ListView): model = models.Event template_name = 'program_overview.html' @@ -54,7 +57,7 @@ class ProgramOverviewView(ListView): return context -class ProgramDayView(TemplateView): +class ProgramDayView(CampViewMixin, TemplateView): template_name = 'program_day.html' def get_context_data(self, **kwargs): @@ -71,7 +74,7 @@ class ProgramDayView(TemplateView): return context -class EventDetailView(DetailView): +class EventDetailView(CampViewMixin, DetailView): model = models.Event template_name = 'program_event_detail.html' @@ -80,3 +83,5 @@ class EventDetailView(DetailView): # TODO: date__year is hardcoded here - need fix for 2017 :P context['days'] = Day.objects.filter(date__year=2016) return context + + diff --git a/requirements.txt b/requirements.txt index 677e072c..546e3348 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,27 @@ -django>=1.10 -django-allauth>=0.29.0 -django-bootstrap3>=7.0.1 -django-environ>=0.4.1 -psycopg2>=2.6.2 -PyPDF2>=1.26 -django-wkhtmltopdf>=3.1.0 -Pillow==3.2.0 -qrcode==5.3 CommonMark==0.7.2 +Django==1.10.4 +Pillow==3.2.0 +PyPDF2==1.26.0 +Unidecode==0.04.19 +argparse==1.2.1 +bleach==1.5.0 +django-allauth==0.29.0 django-bleach==0.3.0 -Unidecode==0.4.19 -django-debug-toolbar>=1.6 +django-bootstrap3==7.1.0 +django-debug-toolbar==1.6 +django-environ==0.4.1 +django-wkhtmltopdf==3.1.0 +future==0.16.0 +html5lib==0.9999999 +oauthlib==2.0.1 +psycopg2==2.6.2 +python-openid==2.2.5 +qrcode==5.3 +requests==2.12.3 +requests-oauthlib==0.7.0 +six==1.10.0 +sqlparse==0.2.2 +webencodings==0.5 +wsgiref==0.1.2 +pytz==2016.10 diff --git a/shop/admin.py b/shop/admin.py index bb1a73a2..4675c053 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -58,7 +58,6 @@ class OrderAdmin(admin.ModelAdmin): ] list_filter = [ - 'camp', 'payment_method', 'open', 'paid', diff --git a/shop/context_processors.py b/shop/context_processors.py index 9e1e27dc..0f55fba9 100644 --- a/shop/context_processors.py +++ b/shop/context_processors.py @@ -20,3 +20,4 @@ def user_has_tickets(request): ).exists(): has_tickets = True return {'has_tickets': has_tickets} + diff --git a/shop/migrations/0033_auto_20161212_1756.py b/shop/migrations/0033_auto_20161212_1756.py new file mode 100644 index 00000000..527c5c88 --- /dev/null +++ b/shop/migrations/0033_auto_20161212_1756.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-12 17:56 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0032_order_customer_comment'), + ] + + operations = [ + migrations.RemoveField( + model_name='customorder', + name='camp', + ), + migrations.RemoveField( + model_name='order', + name='camp', + ), + ] diff --git a/shop/models.py b/shop/models.py index 56f56bd9..a22c7c4f 100644 --- a/shop/models.py +++ b/shop/models.py @@ -17,12 +17,6 @@ from unidecode import unidecode class CustomOrder(CreatedUpdatedModel): - camp = models.ForeignKey( - 'camps.Camp', - verbose_name=_('Camp'), - help_text=_('The camp this custom order is for.'), - ) - text = models.TextField( help_text=_('The invoice text') ) @@ -78,12 +72,6 @@ class Order(CreatedUpdatedModel): default=True, ) - camp = models.ForeignKey( - 'camps.Camp', - verbose_name=_('Camp'), - help_text=_('The camp this shop order is for.'), - ) - CREDIT_CARD = 'credit_card' BLOCKCHAIN = 'blockchain' BANK_TRANSFER = 'bank_transfer' @@ -168,7 +156,7 @@ class Order(CreatedUpdatedModel): @property def description(self): - return "BornHack %s order #%s" % (self.camp.start.year, self.pk) + return "Order #%s" % self.pk def get_absolute_url(self): return str(reverse_lazy('shop:order_detail', kwargs={'pk': self.pk})) diff --git a/shop/views.py b/shop/views.py index 89daf2b4..1c6583fe 100644 --- a/shop/views.py +++ b/shop/views.py @@ -20,7 +20,6 @@ from django.views.decorators.csrf import csrf_exempt from django.utils.dateparse import parse_datetime from django.utils import timezone -from camps.models import Camp from shop.models import ( Order, Product, @@ -234,7 +233,6 @@ class ProductDetailView(FormView, DetailView): # no open order - open a new one order = Order.objects.create( user=self.request.user, - camp=Camp.objects.current() ) # get product from kwargs diff --git a/bornhack/static_src/css/bootstrap-theme.css b/static_src/css/bootstrap-theme.css similarity index 100% rename from bornhack/static_src/css/bootstrap-theme.css rename to static_src/css/bootstrap-theme.css diff --git a/bornhack/static_src/css/bootstrap-theme.css.map b/static_src/css/bootstrap-theme.css.map similarity index 100% rename from bornhack/static_src/css/bootstrap-theme.css.map rename to static_src/css/bootstrap-theme.css.map diff --git a/bornhack/static_src/css/bootstrap-theme.min.css b/static_src/css/bootstrap-theme.min.css similarity index 100% rename from bornhack/static_src/css/bootstrap-theme.min.css rename to static_src/css/bootstrap-theme.min.css diff --git a/bornhack/static_src/css/bootstrap.css b/static_src/css/bootstrap.css similarity index 100% rename from bornhack/static_src/css/bootstrap.css rename to static_src/css/bootstrap.css diff --git a/bornhack/static_src/css/bootstrap.css.map b/static_src/css/bootstrap.css.map similarity index 100% rename from bornhack/static_src/css/bootstrap.css.map rename to static_src/css/bootstrap.css.map diff --git a/bornhack/static_src/css/bootstrap.min.css b/static_src/css/bootstrap.min.css similarity index 100% rename from bornhack/static_src/css/bootstrap.min.css rename to static_src/css/bootstrap.min.css diff --git a/bornhack/static_src/css/bornhack.css b/static_src/css/bornhack.css similarity index 100% rename from bornhack/static_src/css/bornhack.css rename to static_src/css/bornhack.css diff --git a/bornhack/static_src/css/leaflet.css b/static_src/css/leaflet.css similarity index 100% rename from bornhack/static_src/css/leaflet.css rename to static_src/css/leaflet.css diff --git a/bornhack/static_src/fonts/glyphicons-halflings-regular.eot b/static_src/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from bornhack/static_src/fonts/glyphicons-halflings-regular.eot rename to static_src/fonts/glyphicons-halflings-regular.eot diff --git a/bornhack/static_src/fonts/glyphicons-halflings-regular.svg b/static_src/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from bornhack/static_src/fonts/glyphicons-halflings-regular.svg rename to static_src/fonts/glyphicons-halflings-regular.svg diff --git a/bornhack/static_src/fonts/glyphicons-halflings-regular.ttf b/static_src/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from bornhack/static_src/fonts/glyphicons-halflings-regular.ttf rename to static_src/fonts/glyphicons-halflings-regular.ttf diff --git a/bornhack/static_src/fonts/glyphicons-halflings-regular.woff b/static_src/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from bornhack/static_src/fonts/glyphicons-halflings-regular.woff rename to static_src/fonts/glyphicons-halflings-regular.woff diff --git a/bornhack/static_src/fonts/glyphicons-halflings-regular.woff2 b/static_src/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from bornhack/static_src/fonts/glyphicons-halflings-regular.woff2 rename to static_src/fonts/glyphicons-halflings-regular.woff2 diff --git a/bornhack/static_src/img/logo-new-paths.svg b/static_src/img/logo-new-paths.svg similarity index 100% rename from bornhack/static_src/img/logo-new-paths.svg rename to static_src/img/logo-new-paths.svg diff --git a/bornhack/static_src/img/logo-new.png b/static_src/img/logo-new.png similarity index 100% rename from bornhack/static_src/img/logo-new.png rename to static_src/img/logo-new.png diff --git a/bornhack/static_src/img/logo-new.svg b/static_src/img/logo-new.svg similarity index 100% rename from bornhack/static_src/img/logo-new.svg rename to static_src/img/logo-new.svg diff --git a/bornhack/static_src/img/logo.png b/static_src/img/logo.png similarity index 100% rename from bornhack/static_src/img/logo.png rename to static_src/img/logo.png diff --git a/bornhack/static_src/img/logo.svg b/static_src/img/logo.svg similarity index 100% rename from bornhack/static_src/img/logo.svg rename to static_src/img/logo.svg diff --git a/bornhack/static_src/img/sponsors/CSIS_PRI_LOGO_TURQUOISE_RGB.jpg b/static_src/img/sponsors/CSIS_PRI_LOGO_TURQUOISE_RGB.jpg similarity index 100% rename from bornhack/static_src/img/sponsors/CSIS_PRI_LOGO_TURQUOISE_RGB.jpg rename to static_src/img/sponsors/CSIS_PRI_LOGO_TURQUOISE_RGB.jpg diff --git a/bornhack/static_src/img/sponsors/DKUUGlogo.jpeg b/static_src/img/sponsors/DKUUGlogo.jpeg similarity index 100% rename from bornhack/static_src/img/sponsors/DKUUGlogo.jpeg rename to static_src/img/sponsors/DKUUGlogo.jpeg diff --git a/bornhack/static_src/img/sponsors/KristiansKaffe_Logo_Hires1.0.jpg b/static_src/img/sponsors/KristiansKaffe_Logo_Hires1.0.jpg similarity index 100% rename from bornhack/static_src/img/sponsors/KristiansKaffe_Logo_Hires1.0.jpg rename to static_src/img/sponsors/KristiansKaffe_Logo_Hires1.0.jpg diff --git a/bornhack/static_src/img/sponsors/PROSA-logo.png b/static_src/img/sponsors/PROSA-logo.png similarity index 100% rename from bornhack/static_src/img/sponsors/PROSA-logo.png rename to static_src/img/sponsors/PROSA-logo.png diff --git a/bornhack/static_src/img/sponsors/PS_Logo_CMYK_DK.png b/static_src/img/sponsors/PS_Logo_CMYK_DK.png similarity index 100% rename from bornhack/static_src/img/sponsors/PS_Logo_CMYK_DK.png rename to static_src/img/sponsors/PS_Logo_CMYK_DK.png diff --git a/bornhack/static_src/img/sponsors/ShopGun_LogoType_Green.png b/static_src/img/sponsors/ShopGun_LogoType_Green.png similarity index 100% rename from bornhack/static_src/img/sponsors/ShopGun_LogoType_Green.png rename to static_src/img/sponsors/ShopGun_LogoType_Green.png diff --git a/bornhack/static_src/img/sponsors/bitbureauet.png b/static_src/img/sponsors/bitbureauet.png similarity index 100% rename from bornhack/static_src/img/sponsors/bitbureauet.png rename to static_src/img/sponsors/bitbureauet.png diff --git a/bornhack/static_src/img/sponsors/bornfiber-logoweb.png b/static_src/img/sponsors/bornfiber-logoweb.png similarity index 100% rename from bornhack/static_src/img/sponsors/bornfiber-logoweb.png rename to static_src/img/sponsors/bornfiber-logoweb.png diff --git a/bornhack/static_src/img/sponsors/brklogo.png b/static_src/img/sponsors/brklogo.png similarity index 100% rename from bornhack/static_src/img/sponsors/brklogo.png rename to static_src/img/sponsors/brklogo.png diff --git a/bornhack/static_src/img/sponsors/danskebank.png b/static_src/img/sponsors/danskebank.png similarity index 100% rename from bornhack/static_src/img/sponsors/danskebank.png rename to static_src/img/sponsors/danskebank.png diff --git a/bornhack/static_src/img/sponsors/fortconsultlogo.png b/static_src/img/sponsors/fortconsultlogo.png similarity index 100% rename from bornhack/static_src/img/sponsors/fortconsultlogo.png rename to static_src/img/sponsors/fortconsultlogo.png diff --git a/bornhack/static_src/img/sponsors/prosa-u35-1.png b/static_src/img/sponsors/prosa-u35-1.png similarity index 100% rename from bornhack/static_src/img/sponsors/prosa-u35-1.png rename to static_src/img/sponsors/prosa-u35-1.png diff --git a/bornhack/static_src/img/sponsors/samdatalogo.jpg b/static_src/img/sponsors/samdatalogo.jpg similarity index 100% rename from bornhack/static_src/img/sponsors/samdatalogo.jpg rename to static_src/img/sponsors/samdatalogo.jpg diff --git a/bornhack/static_src/img/sponsors/septima-logo.png b/static_src/img/sponsors/septima-logo.png similarity index 100% rename from bornhack/static_src/img/sponsors/septima-logo.png rename to static_src/img/sponsors/septima-logo.png diff --git a/bornhack/static_src/img/sponsors/tyktech_logo.png b/static_src/img/sponsors/tyktech_logo.png similarity index 100% rename from bornhack/static_src/img/sponsors/tyktech_logo.png rename to static_src/img/sponsors/tyktech_logo.png diff --git a/bornhack/static_src/img/sponsors/zencurity_logo.png b/static_src/img/sponsors/zencurity_logo.png similarity index 100% rename from bornhack/static_src/img/sponsors/zencurity_logo.png rename to static_src/img/sponsors/zencurity_logo.png diff --git a/bornhack/static_src/img/sponsors/zybersafe_logo.png b/static_src/img/sponsors/zybersafe_logo.png similarity index 100% rename from bornhack/static_src/img/sponsors/zybersafe_logo.png rename to static_src/img/sponsors/zybersafe_logo.png diff --git a/bornhack/static_src/js/bootstrap.js b/static_src/js/bootstrap.js similarity index 100% rename from bornhack/static_src/js/bootstrap.js rename to static_src/js/bootstrap.js diff --git a/bornhack/static_src/js/bootstrap.min.js b/static_src/js/bootstrap.min.js similarity index 100% rename from bornhack/static_src/js/bootstrap.min.js rename to static_src/js/bootstrap.min.js diff --git a/bornhack/static_src/js/images/layers-2x.png b/static_src/js/images/layers-2x.png similarity index 100% rename from bornhack/static_src/js/images/layers-2x.png rename to static_src/js/images/layers-2x.png diff --git a/bornhack/static_src/js/images/layers.png b/static_src/js/images/layers.png similarity index 100% rename from bornhack/static_src/js/images/layers.png rename to static_src/js/images/layers.png diff --git a/bornhack/static_src/js/images/marker-icon-2x.png b/static_src/js/images/marker-icon-2x.png similarity index 100% rename from bornhack/static_src/js/images/marker-icon-2x.png rename to static_src/js/images/marker-icon-2x.png diff --git a/bornhack/static_src/js/images/marker-icon.png b/static_src/js/images/marker-icon.png similarity index 100% rename from bornhack/static_src/js/images/marker-icon.png rename to static_src/js/images/marker-icon.png diff --git a/bornhack/static_src/js/images/marker-shadow.png b/static_src/js/images/marker-shadow.png similarity index 100% rename from bornhack/static_src/js/images/marker-shadow.png rename to static_src/js/images/marker-shadow.png diff --git a/bornhack/static_src/js/jquery.min.js b/static_src/js/jquery.min.js similarity index 100% rename from bornhack/static_src/js/jquery.min.js rename to static_src/js/jquery.min.js diff --git a/bornhack/static_src/js/leaflet-src.js b/static_src/js/leaflet-src.js similarity index 100% rename from bornhack/static_src/js/leaflet-src.js rename to static_src/js/leaflet-src.js diff --git a/bornhack/static_src/js/leaflet.js b/static_src/js/leaflet.js similarity index 100% rename from bornhack/static_src/js/leaflet.js rename to static_src/js/leaflet.js diff --git a/bornhack/static_src/pdf/bornhack_2016_letterhead.odt b/static_src/pdf/bornhack_2016_letterhead.odt similarity index 100% rename from bornhack/static_src/pdf/bornhack_2016_letterhead.odt rename to static_src/pdf/bornhack_2016_letterhead.odt diff --git a/bornhack/static_src/pdf/bornhack_2016_letterhead.pdf b/static_src/pdf/bornhack_2016_letterhead.pdf similarity index 100% rename from bornhack/static_src/pdf/bornhack_2016_letterhead.pdf rename to static_src/pdf/bornhack_2016_letterhead.pdf diff --git a/bornhack/static_src/pdf/bornhack_2016_test_letterhead.odt b/static_src/pdf/bornhack_2016_test_letterhead.odt similarity index 100% rename from bornhack/static_src/pdf/bornhack_2016_test_letterhead.odt rename to static_src/pdf/bornhack_2016_test_letterhead.odt diff --git a/bornhack/static_src/pdf/bornhack_2016_test_letterhead.pdf b/static_src/pdf/bornhack_2016_test_letterhead.pdf similarity index 100% rename from bornhack/static_src/pdf/bornhack_2016_test_letterhead.pdf rename to static_src/pdf/bornhack_2016_test_letterhead.pdf diff --git a/bornhack/static_src/pdf/bornhack_webshop_cancellation_form.pdf b/static_src/pdf/bornhack_webshop_cancellation_form.pdf similarity index 100% rename from bornhack/static_src/pdf/bornhack_webshop_cancellation_form.pdf rename to static_src/pdf/bornhack_webshop_cancellation_form.pdf diff --git a/bornhack/templates/404.html b/templates/404.html similarity index 100% rename from bornhack/templates/404.html rename to templates/404.html diff --git a/bornhack/templates/500.html b/templates/500.html similarity index 100% rename from bornhack/templates/500.html rename to templates/500.html diff --git a/bornhack/templates/base.html b/templates/base.html similarity index 67% rename from bornhack/templates/base.html rename to templates/base.html index 675664fd..7f2547c2 100644 --- a/bornhack/templates/base.html +++ b/templates/base.html @@ -38,14 +38,26 @@ -
diff --git a/bornhack/templates/coc.html b/templates/coc.html similarity index 100% rename from bornhack/templates/coc.html rename to templates/coc.html diff --git a/bornhack/templates/contact.html b/templates/contact.html similarity index 100% rename from bornhack/templates/contact.html rename to templates/contact.html diff --git a/bornhack/templates/frontpage.html b/templates/frontpage.html similarity index 100% rename from bornhack/templates/frontpage.html rename to templates/frontpage.html diff --git a/bornhack/templates/includes/contact.html b/templates/includes/contact.html similarity index 100% rename from bornhack/templates/includes/contact.html rename to templates/includes/contact.html diff --git a/bornhack/templates/info.html b/templates/info.html similarity index 100% rename from bornhack/templates/info.html rename to templates/info.html diff --git a/bornhack/templates/legal/general_terms_and_conditions.html b/templates/legal/general_terms_and_conditions.html similarity index 100% rename from bornhack/templates/legal/general_terms_and_conditions.html rename to templates/legal/general_terms_and_conditions.html diff --git a/bornhack/templates/legal/privacy_policy.html b/templates/legal/privacy_policy.html similarity index 100% rename from bornhack/templates/legal/privacy_policy.html rename to templates/legal/privacy_policy.html diff --git a/bornhack/templates/speakers.html b/templates/speakers.html similarity index 100% rename from bornhack/templates/speakers.html rename to templates/speakers.html diff --git a/bornhack/templates/sponsors.html b/templates/sponsors.html similarity index 100% rename from bornhack/templates/sponsors.html rename to templates/sponsors.html diff --git a/utils/management/__init__.py b/utils/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/utils/management/commands/__init__.py b/utils/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/utils/management/commands/bootstrap-devsite.py b/utils/management/commands/bootstrap-devsite.py new file mode 100644 index 00000000..f759d577 --- /dev/null +++ b/utils/management/commands/bootstrap-devsite.py @@ -0,0 +1,266 @@ +from django.core.management.base import BaseCommand +from django.conf import settings +from django.utils import timezone +from camps.models import Camp +from news.models import NewsItem +from program.models import EventType, Event, EventInstance, Speaker +import datetime +from django.contrib.auth.models import User + + +class Command(BaseCommand): + args = 'none' + help = 'Create mock data for development instances' + + def output(self, message): + self.stdout.write('%s: %s' % (timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message)) + + def handle(self, *args, **options): + self.output('Creating camps...') + camp1 = Camp.objects.create( + name='BornHack 2016', + slug='bornhack-2016', + start=timezone.datetime(2016, 8, 27, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 9, 4, 12, 0, tzinfo=timezone.utc) + ) + camp2 = Camp.objects.create( + name='BornHack 2015', + slug='bornhack-2015', + start=timezone.datetime(2015, 8, 27, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2015, 9, 4, 12, 0, tzinfo=timezone.utc) + ) + + self.output('Creating news...') + news1 = NewsItem.objects.create( + title='welcome to bornhack 2016', + content='news body here with html support', + published_at=timezone.datetime(2016, 8, 27, 12, 0, tzinfo=timezone.utc) + ) + news2 = NewsItem.objects.create( + title='bornhack 2016 is over', + content='news body here', + published_at=timezone.datetime(2016, 9, 4, 12, 0, tzinfo=timezone.utc) + ) + news3 = NewsItem.objects.create( + title='unpublished news item', + content='unpublished news body here', + ) + news4 = NewsItem.objects.create( + title='welcome to bornhack 2015', + content='news body here', + published_at=timezone.datetime(2015, 8, 22, 12, 0, tzinfo=timezone.utc), + archived=True + ) + news5 = NewsItem.objects.create( + title='bornhack 2015 is over', + content='news body here', + published_at=timezone.datetime(2015, 8, 27, 12, 0, tzinfo=timezone.utc), + archived=True + ) + + self.output("creating users...") + user1 = User.objects.create_user( + username='user1', + email='user1@example.com', + password='user1', + ) + user2 = User.objects.create_user( + username='user2', + email='user2@example.com', + password='user2', + ) + user3 = User.objects.create_user( + username='user3', + email='user3@example.com', + password='user3', + ) + user4 = User.objects.create_user( + username='user4', + email='user4@example.com', + password='user4', + ) + admin = User.objects.create_superuser( + username='admin', + email='admin@example.com', + password='admin', + ) + + self.output("creating event types...") + et1 = EventType.objects.create( + name='Workshops', + slug='workshops', + color='#ff9900', + light_text=False + ) + + et2 = EventType.objects.create( + name='Talks', + slug='talks', + color='#2D9595', + light_text=True + ) + + et3 = EventType.objects.create( + name='Keynotes', + slug='keynotes', + color='#FF3453', + light_text=True + ) + + et4 = EventType.objects.create( + name='Facilities', + slug='facilities', + color='#cccccc', + light_text=False + ) + + + self.output("creating events...") + ev1 = Event.objects.create( + title='Developing the BornHack website', + abstract='abstract here, bla bla bla', + event_type=et2, + camp=camp1 + ) + ev2 = Event.objects.create( + title='State of the world', + abstract='abstract here, bla bla bla', + event_type=et3, + camp=camp1 + ) + ev3 = Event.objects.create( + title='Welcome to bornhack!', + abstract='abstract here, bla bla bla', + event_type=et2, + camp=camp1 + ) + ev4 = Event.objects.create( + title='bar is open', + abstract='the bar is open, yay', + event_type=et4, + camp=camp1 + ) + + ev5 = Event.objects.create( + title='Network something', + abstract='abstract here, bla bla bla', + event_type=et2, + camp=camp2 + ) + ev6 = Event.objects.create( + title='State of outer space', + abstract='abstract here, bla bla bla', + event_type=et3, + camp=camp2 + ) + ev7 = Event.objects.create( + title='Welcome to bornhack!', + abstract='abstract here, bla bla bla', + event_type=et2, + camp=camp2 + ) + ev8 = Event.objects.create( + title='bar is open', + abstract='the bar is open, yay', + event_type=et4, + camp=camp2 + ) + + + self.output("creating speakers...") + sp1 = Speaker.objects.create( + name='Henrik Kramse', + biography='Henrik is an internet samurai working in internet and security around the world.', + slug='henrik-kramshj' + ) + sp1.events.add(ev5, ev6) + sp2 = Speaker.objects.create( + name='Thomas Tykling', + biography='random danish hacker', + slug='thomas-tykling' + ) + sp2.events.add(ev7, ev3, ev1) + sp3 = Speaker.objects.create( + name='Alex Ahf', + biography='functional alcoholic', + slug='alex-ahf' + ) + sp3.events.add(ev4, ev8, ev2) + + self.output("creating eventinstances...") + ei1 = EventInstance.objects.create( + event=ev3, + start=timezone.datetime(2016, 8, 27, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 27, 13, 0, tzinfo=timezone.utc), + ) + ei2 = EventInstance.objects.create( + event=ev1, + start=timezone.datetime(2016, 8, 28, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 28, 13, 0, tzinfo=timezone.utc), + ) + ei3 = EventInstance.objects.create( + event=ev2, + start=timezone.datetime(2016, 8, 29, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 29, 13, 0, tzinfo=timezone.utc), + ) + ei4 = EventInstance.objects.create( + event=ev4, + start=timezone.datetime(2016, 8, 27, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 28, 5, 0, tzinfo=timezone.utc), + ) + ei5 = EventInstance.objects.create( + event=ev4, + start=timezone.datetime(2016, 8, 28, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 29, 5, 0, tzinfo=timezone.utc), + ) + ei6 = EventInstance.objects.create( + event=ev4, + start=timezone.datetime(2016, 8, 29, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 30, 5, 0, tzinfo=timezone.utc), + ) + ei7 = EventInstance.objects.create( + event=ev4, + start=timezone.datetime(2016, 8, 30, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 31, 5, 0, tzinfo=timezone.utc), + ) + + ei8 = EventInstance.objects.create( + event=ev7, + start=timezone.datetime(2016, 8, 27, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 27, 13, 0, tzinfo=timezone.utc), + ) + ei9 = EventInstance.objects.create( + event=ev5, + start=timezone.datetime(2016, 8, 28, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 28, 13, 0, tzinfo=timezone.utc), + ) + ei10 = EventInstance.objects.create( + event=ev6, + start=timezone.datetime(2016, 8, 29, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 29, 13, 0, tzinfo=timezone.utc), + ) + ei11 = EventInstance.objects.create( + event=ev8, + start=timezone.datetime(2016, 8, 27, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 28, 5, 0, tzinfo=timezone.utc), + ) + ei12 = EventInstance.objects.create( + event=ev8, + start=timezone.datetime(2016, 8, 28, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 29, 5, 0, tzinfo=timezone.utc), + ) + ei13 = EventInstance.objects.create( + event=ev8, + start=timezone.datetime(2016, 8, 29, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 30, 5, 0, tzinfo=timezone.utc), + ) + ei14 = EventInstance.objects.create( + event=ev8, + start=timezone.datetime(2016, 8, 30, 12, 0, tzinfo=timezone.utc), + end=timezone.datetime(2016, 8, 31, 5, 0, tzinfo=timezone.utc), + ) + + + self.output("created users user1/user1 user2/user2 user3/user3 user4/user4 and admin user admin/admin") + self.output("done!") + diff --git a/utils/models.py b/utils/models.py index 8dc5292c..d94a9162 100644 --- a/utils/models.py +++ b/utils/models.py @@ -3,7 +3,26 @@ import uuid from django.db import models -class UUIDModel(models.Model): +class CleanedModel(models.Model): + class Meta: + abstract = True + + def save(self, **kwargs): + try: + # call this models full_clean() method before saving, + # which in turn calls .clean_fields(), .clean() and .validate_unique() + self.full_clean() + except ValidationError as e: + message = "Got ValidationError while saving: %s" % e + if hasattr(self, 'request'): + messages.error(self.request, message) + print(message) + # dont save, just return + return + super(CreatedUpdatedModel, self).save(**kwargs) + + +class UUIDModel(CleanedModel): class Meta: abstract = True @@ -14,9 +33,11 @@ class UUIDModel(models.Model): ) -class CreatedUpdatedModel(models.Model): +class CreatedUpdatedModel(CleanedModel): class Meta: abstract = True created = models.DateTimeField(auto_now_add=True) - updated = models.DateTimeField(auto_now=True) \ No newline at end of file + updated = models.DateTimeField(auto_now=True) + + diff --git a/utils/templatetags/commonmark.py b/utils/templatetags/commonmark.py index d14fba2b..f2410bf4 100644 --- a/utils/templatetags/commonmark.py +++ b/utils/templatetags/commonmark.py @@ -10,6 +10,7 @@ register = template.Library() @register.filter @stringfilter def commonmark(value): + """Returns HTML given some CommonMark Markdown. Does not clean HTML, not for use with untrusted input.""" parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(value) @@ -18,6 +19,7 @@ def commonmark(value): @register.filter @stringfilter def unsafecommonmark(value): + """Returns HTML given some CommonMark Markdown. Cleans HTML from input using bleach, suitable for use with untrusted input.""" parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(bleach.clean(value)) diff --git a/villages/migrations/0006_remove_village_camp.py b/villages/migrations/0006_remove_village_camp.py new file mode 100644 index 00000000..b6c5c25f --- /dev/null +++ b/villages/migrations/0006_remove_village_camp.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-12 17:56 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('villages', '0005_auto_20160712_2036'), + ] + + operations = [ + migrations.RemoveField( + model_name='village', + name='camp', + ), + ] diff --git a/villages/models.py b/villages/models.py index 5104b915..da7104b0 100644 --- a/villages/models.py +++ b/villages/models.py @@ -4,7 +4,6 @@ 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 from .managers import VillageQuerySet @@ -15,7 +14,6 @@ class Village(CreatedUpdatedModel, UUIDModel): class Meta: ordering = ['name'] - camp = models.ForeignKey('camps.Camp') contact = models.ForeignKey('auth.User') name = models.CharField(max_length=255) @@ -62,11 +60,9 @@ class Village(CreatedUpdatedModel, UUIDModel): incrementer += 1 self.slug = slug - if not hasattr(self, 'camp'): - self.camp = Camp.objects.current() - super(Village, self).save(**kwargs) def delete(self, using=None, keep_parents=False): self.deleted = True self.save() +