Initial commit.
This commit is contained in:
commit
91ca821831
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.idea/
|
||||||
|
__pycache__/
|
||||||
|
db.sqlite3
|
18
README.md
Normal file
18
README.md
Normal file
|
@ -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
|
0
bornhack/__init__.py
Normal file
0
bornhack/__init__.py
Normal file
65
bornhack/settings.py
Normal file
65
bornhack/settings.py
Normal file
|
@ -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')
|
0
bornhack/settings/__init__.py
Normal file
0
bornhack/settings/__init__.py
Normal file
63
bornhack/settings/base.py
Normal file
63
bornhack/settings/base.py
Normal file
|
@ -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',
|
||||||
|
]
|
13
bornhack/settings/development.py
Normal file
13
bornhack/settings/development.py
Normal file
|
@ -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'),
|
||||||
|
}
|
||||||
|
}
|
15
bornhack/settings/production.py
Normal file
15
bornhack/settings/production.py
Normal file
|
@ -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(),
|
||||||
|
}
|
21
bornhack/urls.py
Normal file
21
bornhack/urls.py
Normal file
|
@ -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)),
|
||||||
|
]
|
22
bornhack/utils.py
Normal file
22
bornhack/utils.py
Normal file
|
@ -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,
|
||||||
|
)
|
16
bornhack/wsgi.py
Normal file
16
bornhack/wsgi.py
Normal file
|
@ -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()
|
0
camps/__init__.py
Normal file
0
camps/__init__.py
Normal file
29
camps/admin.py
Normal file
29
camps/admin.py
Normal file
|
@ -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
|
||||||
|
]
|
0
camps/migrations/__init__.py
Normal file
0
camps/migrations/__init__.py
Normal file
120
camps/models.py
Normal file
120
camps/models.py
Normal file
|
@ -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,
|
||||||
|
)
|
3
camps/tests.py
Normal file
3
camps/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
camps/views.py
Normal file
3
camps/views.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
13
manage.py
Executable file
13
manage.py
Executable file
|
@ -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)
|
0
profiles/__init__.py
Normal file
0
profiles/__init__.py
Normal file
5
profiles/admin.py
Normal file
5
profiles/admin.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from .models import Profile
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Profile)
|
0
profiles/migrations/__init__.py
Normal file
0
profiles/migrations/__init__.py
Normal file
32
profiles/models.py
Normal file
32
profiles/models.py
Normal file
|
@ -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)
|
3
profiles/tests.py
Normal file
3
profiles/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
profiles/views.py
Normal file
3
profiles/views.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
Loading…
Reference in a new issue