commit 91ca82183173e49f56d9e983e6301bc9f4a97344 Author: Víðir Valberg Guðmundsson Date: Sat Oct 3 03:07:05 2015 +0200 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..30afe33a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +__pycache__/ +db.sqlite3 diff --git a/README.md b/README.md new file mode 100644 index 00000000..9940c8ad --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Bornhack + +Django project to power Bornhack. + +Features include: +- Create camp +- Control expenses for a camp +- Manage signups for attendees for a camp +- ... + +## Quickstart + + $ source path/to/venv/bin/activate + $ pip install -r requirements/development.txt + $ ./manage.py makemigrations + $ ./manage.py migrate + $ ./manage.py createsuperuser + $ ./manage.py runserver diff --git a/bornhack/__init__.py b/bornhack/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bornhack/settings.py b/bornhack/settings.py new file mode 100644 index 00000000..e1e07077 --- /dev/null +++ b/bornhack/settings.py @@ -0,0 +1,65 @@ +import os + +local_dir = lambda x: os.path.join(os.path.dirname(__file___), x) + +SECRET_KEY = '1&=htv#6aawk=3rxo$9e)s7g)lt2!x4=04om__sod!v6!uv&6=' +DEBUG = True +ALLOWED_HOSTS = ['*'] + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'bornhack.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [local_dir('templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'bornhack.wsgi.application' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' +USE_I18N = True +USE_L10N = True +USE_TZ = True + +STATIC_URL = '/static/' +STATIC_ROOT = local_dir('static') +MEDIA_URL = '/media/' +MEDIA_ROOT = local_dir('media') diff --git a/bornhack/settings/__init__.py b/bornhack/settings/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bornhack/settings/base.py b/bornhack/settings/base.py new file mode 100644 index 00000000..7b31886f --- /dev/null +++ b/bornhack/settings/base.py @@ -0,0 +1,63 @@ +import os + + +def local_dir(entry): + return os.path.join( + os.path.dirname(os.path.dirname(__file__)), + entry + ) + +WSGI_APPLICATION = 'bornhack.wsgi.application' +ROOT_URLCONF = 'bornhack.urls' + +INSTALLED_APPS = [ + 'flat', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + 'profiles', + 'camps', +] + +STATIC_URL = '/static/' +STATIC_ROOT = local_dir('static') +MEDIA_URL = '/media/' +MEDIA_ROOT = local_dir('media') + +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' +USE_I18N = True +USE_L10N = True +USE_TZ = True + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [local_dir('templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + + +MIDDLEWARE_CLASSES = [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +] diff --git a/bornhack/settings/development.py b/bornhack/settings/development.py new file mode 100644 index 00000000..429d850f --- /dev/null +++ b/bornhack/settings/development.py @@ -0,0 +1,13 @@ +from .base import * + +INSTALLED_APPS += ['debug_toolbar', ] + +SECRET_KEY = 'bornhack_development' +DEBUG = True + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': local_dir('db.sqlite3'), + } +} diff --git a/bornhack/settings/production.py b/bornhack/settings/production.py new file mode 100644 index 00000000..d4befb65 --- /dev/null +++ b/bornhack/settings/production.py @@ -0,0 +1,15 @@ +from .base import * +import environ + +env = environ.Env( + ENGINE='django.db.backends.postgres_psycopg2', +) +environ.Env.read_env() + +SECRET_KEY = env('SECRET_KEY') +DEBUG = False +ALLOWED_HOSTS = env('ALLOWED_HOSTS') + +DATABASES = { + 'default': env.db(), +} diff --git a/bornhack/urls.py b/bornhack/urls.py new file mode 100644 index 00000000..92900f89 --- /dev/null +++ b/bornhack/urls.py @@ -0,0 +1,21 @@ +"""bornhack URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.8/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Add an import: from blog import urls as blog_urls + 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) +""" +from django.conf.urls import include, url +from django.contrib import admin + +urlpatterns = [ + url(r'^admin/', include(admin.site.urls)), +] diff --git a/bornhack/utils.py b/bornhack/utils.py new file mode 100644 index 00000000..0919be72 --- /dev/null +++ b/bornhack/utils.py @@ -0,0 +1,22 @@ +import uuid + +from django.db import models + + +class CreatedUpdatedModel(models.Model): + class Meta: + abstract = True + + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now=True) + + +class UUIDModel(models.Model): + class Meta: + abstract = True + + uuid = models.UUIDField( + primary_key=True, + default=uuid.uuid4, + editable=False, + ) diff --git a/bornhack/wsgi.py b/bornhack/wsgi.py new file mode 100644 index 00000000..8a82caa6 --- /dev/null +++ b/bornhack/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for bornhack project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bornhack.settings") + +application = get_wsgi_application() diff --git a/camps/__init__.py b/camps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/camps/admin.py b/camps/admin.py new file mode 100644 index 00000000..ad85a412 --- /dev/null +++ b/camps/admin.py @@ -0,0 +1,29 @@ +from django.contrib import admin + +from .models import Camp, Expense, Signup + + +@admin.register(Expense) +class ExpenseAdmin(admin.ModelAdmin): + pass + + +class ExpenseInlineAdmin(admin.TabularInline): + model = Expense + + +@admin.register(Signup) +class SignupAdmin(admin.ModelAdmin): + pass + + +class SignupInlineAdmin(admin.TabularInline): + model = Signup + + +@admin.register(Camp) +class CampAdmin(admin.ModelAdmin): + inlines = [ + ExpenseInlineAdmin, + SignupInlineAdmin + ] diff --git a/camps/migrations/__init__.py b/camps/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/camps/models.py b/camps/models.py new file mode 100644 index 00000000..a85f27f1 --- /dev/null +++ b/camps/models.py @@ -0,0 +1,120 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from bornhack.utils import CreatedUpdatedModel, UUIDModel + + +class Camp(CreatedUpdatedModel, UUIDModel): + class Meta: + verbose_name = _('Camp') + verbose_name_plural = _('Camps') + + name = models.CharField( + verbose_name=_('Name'), + help_text=_('Name of the camp, ie. Bornhack.'), + max_length=255, + ) + + start = models.DateTimeField( + verbose_name=_('Start date'), + help_text=_('When the camp starts.'), + unique=True, + ) + + end = models.DateTimeField( + verbose_name=_('End date'), + help_text=_('When the camp ends.'), + unique=True, + ) + + def __str__(self): + return _('{} {}').format( + self.name, + self.start.year, + ) + + +class Expense(CreatedUpdatedModel, UUIDModel): + class Meta: + 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.'), + ) + + description = models.CharField( + 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.'), + 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, + ) + + covered_by = models.ForeignKey( + 'auth.User', + verbose_name=_('Covered by'), + help_text=_('Which user, if any, covered this expense.'), + null=True, + blank=True, + ) + + def __str__(self): + return _('{} {} for {}').format( + self.amount, + self.get_currency_display(), + self.camp, + ) + + +class Signup(CreatedUpdatedModel, UUIDModel): + class Meta: + verbose_name = _('Signup') + verbose_name_plural = _('Signups') + + camp = models.ForeignKey( + 'camps.Camp', + verbose_name=_('Camp'), + help_text=_('The camp that has been signed up for.'), + ) + + user = models.ForeignKey( + 'auth.User', + verbose_name=_('User'), + help_text=_('The user that has signed up.'), + ) + + cost = models.DecimalField( + verbose_name=_('Cost'), + help_text=_('What the user should/is willing to pay for this signup.'), + max_digits=7, + decimal_places=2, + default=1500.0 + ) + + paid = models.BooleanField( + verbose_name=_('Paid?'), + help_text=_('Whether the user has paid.'), + default=False, + ) diff --git a/camps/tests.py b/camps/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/camps/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/camps/views.py b/camps/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/camps/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/manage.py b/manage.py new file mode 100755 index 00000000..3362c3d2 --- /dev/null +++ b/manage.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", + "bornhack.settings.development" + ) + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/profiles/__init__.py b/profiles/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/profiles/admin.py b/profiles/admin.py new file mode 100644 index 00000000..5e7bf791 --- /dev/null +++ b/profiles/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import Profile + + +admin.site.register(Profile) diff --git a/profiles/migrations/__init__.py b/profiles/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/profiles/models.py b/profiles/models.py new file mode 100644 index 00000000..55fcf4a0 --- /dev/null +++ b/profiles/models.py @@ -0,0 +1,32 @@ +from django.contrib.auth.models import User +from django.db import models +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.utils.translation import ugettext_lazy as _ + +from bornhack.utils import CreatedUpdatedModel, UUIDModel + + +class Profile(CreatedUpdatedModel, UUIDModel): + class Meta: + verbose_name = _('Profile') + verbose_name_plural = _('Profiles') + + user = models.OneToOneField( + User, + verbose_name=_('User'), + help_text=_('The django user this profile belongs to.'), + ) + + @property + def email(self): + return self.user.email + + def __str__(self): + return self.user.username + + +@receiver(post_save, sender=User) +def create_profile(sender, created, instance, **kwargs): + if created: + Profile.objects.create(user=instance) diff --git a/profiles/tests.py b/profiles/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/profiles/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/profiles/views.py b/profiles/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/profiles/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here.