diff --git a/.gitignore b/.gitignore index 27eecc6..9b4a7d5 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,11 @@ ENV/ # Rope project settings .ropeproject jdav_web/db.sqlite3 +*.swp + +# test images for file upload +*.jpeg +*.png *.py.swp # django database migrations diff --git a/jdav_web/jdav_web/settings.py b/jdav_web/jdav_web/settings.py index 3c29819..f67581e 100644 --- a/jdav_web/jdav_web/settings.py +++ b/jdav_web/jdav_web/settings.py @@ -27,10 +27,16 @@ DEBUG = True ALLOWED_HOSTS = [] +# Define media paths e.g. for image storage +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') +MEDIA_URL = '/media/' # Application definition INSTALLED_APPS = [ + 'startpage.apps.StartpageConfig', + 'material.apps.MaterialConfig', + 'members.apps.MembersConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -42,6 +48,7 @@ INSTALLED_APPS = [ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -75,8 +82,11 @@ WSGI_APPLICATION = 'jdav_web.wsgi.application' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'jdav_db', + 'USER': 'jdav_user', + 'PASSWORD': 'jdav00jdav', + 'HOST': 'localhost' } } @@ -118,3 +128,8 @@ USE_TZ = True # https://docs.djangoproject.com/en/1.10/howto/static-files/ STATIC_URL = '/static/' + + +# Locale files (translations) + +LOCALE_PATHS = (os.path.join(BASE_DIR, 'locale'),) diff --git a/jdav_web/jdav_web/urls.py b/jdav_web/jdav_web/urls.py index 3a2b7ff..9ebd7a6 100644 --- a/jdav_web/jdav_web/urls.py +++ b/jdav_web/jdav_web/urls.py @@ -13,9 +13,18 @@ Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin +from django.conf.urls.static import static +from django.conf.urls.i18n import i18n_patterns +from django.conf import settings -urlpatterns = [ +urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + +urlpatterns += i18n_patterns( url(r'^admin/', admin.site.urls), -] + url(r'^$', include('startpage.urls')), +) + +# TODO: django serving from MEDIA_URL should be disabled in production stage +# see http://stackoverflow.com/questions/5871730/need-a-minimal-django-file-upload-example diff --git a/jdav_web/material/__init__.py b/jdav_web/material/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jdav_web/material/admin.py b/jdav_web/material/admin.py new file mode 100644 index 0000000..782b960 --- /dev/null +++ b/jdav_web/material/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin + +from .models import MaterialPart, Ownership + + +# Register your models here. +class OwnershipInline(admin.StackedInline): + """ + This shows the ownership selection directly in the MaterialPart edit + view + """ + model = Ownership + extra = 0 + + +class MaterialAdmin(admin.ModelAdmin): + """Edit view of a MaterialPart""" + + list_display = ('name', 'buy_date', 'lifetime', 'not_too_old', 'photo') + inlines = [OwnershipInline] + +admin.site.register(MaterialPart, MaterialAdmin) diff --git a/jdav_web/material/apps.py b/jdav_web/material/apps.py new file mode 100644 index 0000000..047a5ab --- /dev/null +++ b/jdav_web/material/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class MaterialConfig(AppConfig): + name = 'material' + verbose_name = _('material') diff --git a/jdav_web/material/locale/de/LC_MESSAGES/django.po b/jdav_web/material/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..fd7587b --- /dev/null +++ b/jdav_web/material/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,67 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-10-21 23:38+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: apps.py:7 +msgid "material" +msgstr "Material" + +#: models.py:17 +msgid "name" +msgstr "Name" + +#: models.py:18 +msgid "purchase date" +msgstr "Kaufdatum" + +#: models.py:19 +msgid "lifetime (years)" +msgstr "Lebenszeit (Jahre)" + +#: models.py:21 +msgid "photo" +msgstr "Bild" + +#: models.py:35 +msgid "Not too old?" +msgstr "Nicht zu alt?" + +#: models.py:38 +msgid "material part" +msgstr "Materialteil" + +#: models.py:39 +msgid "material parts" +msgstr "Materialteile" + +#: models.py:45 +msgid "owner" +msgstr "Besitzer" + +#: models.py:46 +msgid "count" +msgstr "Anzahl" + +#: models.py:53 +msgid "ownership" +msgstr "Besitztum" + +#: models.py:54 +msgid "ownerships" +msgstr "Besitztümer" diff --git a/jdav_web/material/migrations/0001_initial.py b/jdav_web/material/migrations/0001_initial.py new file mode 100644 index 0000000..7f71953 --- /dev/null +++ b/jdav_web/material/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-18 19:07 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('members', '0004_auto_20161018_1744'), + ] + + operations = [ + migrations.CreateModel( + name='MaterialPart', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('buy_date', models.DateField(verbose_name='purchase date')), + ], + ), + migrations.CreateModel( + name='Ownership', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('count', models.IntegerField(default=1)), + ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='material.MaterialPart')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Member')), + ], + ), + ] diff --git a/jdav_web/material/migrations/__init__.py b/jdav_web/material/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jdav_web/material/models.py b/jdav_web/material/models.py new file mode 100644 index 0000000..131eec9 --- /dev/null +++ b/jdav_web/material/models.py @@ -0,0 +1,66 @@ +from datetime import datetime + +from django.db import models +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ + +# maximum time in years of a material part until being replaced +MAX_TIME_MATERIAL = 5 + + +# Create your models here. +class MaterialPart(models.Model): + """ + Represents one part of material, which is owned (and stored) by different + members of the association (Ownership) + """ + name = models.CharField(_('name'), max_length=30) + buy_date = models.DateField(_('purchase date'), editable=True) + lifetime = models.DecimalField(_('lifetime (years)'), decimal_places=0, + max_digits=3) + photo = models.ImageField(_('photo'), upload_to='images', blank=True) + + def __str__(self): + """String representation""" + return self.name + + def not_too_old(self): + """Returns wether the part should be replaced cause of age""" + buy_time = timezone.make_aware(datetime.combine(self.buy_date, + datetime.min.time())) + return yearsago(self.lifetime) < buy_time + + not_too_old.admin_order_field = 'buy_date' + not_too_old.boolean = True + not_too_old.short_description = _('Not too old?') + + class Meta: + verbose_name = _('material part') + verbose_name_plural = _('material parts') + + +class Ownership(models.Model): + """Represents the connection between a MaterialPart and a Member""" + material = models.ForeignKey(MaterialPart, on_delete=models.CASCADE) + owner = models.ForeignKey('members.Member', verbose_name=_('owner')) + count = models.IntegerField(_('count'), default=1) + + def __str__(self): + """String representation""" + return str(self.owner) + + class Meta: + verbose_name = _('ownership') + verbose_name_plural = _('ownerships') + + +def yearsago(years, from_date=None): + """Function to return the date with a delta of years in the past""" + if from_date is None: + from_date = timezone.now() + try: + return from_date.replace(year=from_date.year - years) + except ValueError: + # 29.02 -> use 28.02 + assert from_date.month == 2 and from_date.day == 29 + return from_date.replace(month=2, day=28, year=from_date.year - years) diff --git a/jdav_web/material/tests.py b/jdav_web/material/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/jdav_web/material/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/jdav_web/material/views.py b/jdav_web/material/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/jdav_web/material/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/jdav_web/members/__init__.py b/jdav_web/members/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py new file mode 100644 index 0000000..078ea1f --- /dev/null +++ b/jdav_web/members/admin.py @@ -0,0 +1,19 @@ +from django.contrib import admin + +from .models import Member, Group + + +# Register your models here. +class MemberAdmin(admin.ModelAdmin): + fields = ['prename', 'lastname', 'email', 'birth_date', 'group'] + list_display = ('name', 'birth_date') + list_filter = ('group',) + + +class GroupAdmin(admin.ModelAdmin): + fields = ['name', 'min_age'] + list_display = ('name', 'min_age') + + +admin.site.register(Member, MemberAdmin) +admin.site.register(Group, GroupAdmin) diff --git a/jdav_web/members/apps.py b/jdav_web/members/apps.py new file mode 100644 index 0000000..bc8b0c5 --- /dev/null +++ b/jdav_web/members/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class MembersConfig(AppConfig): + name = 'members' + verbose_name = _('members') diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..b845483 --- /dev/null +++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,55 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-10-21 23:37+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: apps.py:7 models.py:45 +msgid "members" +msgstr "Teilnehmer" + +#: models.py:10 +msgid "name" +msgstr "Name" + +#: models.py:12 +msgid "minimum age (years)" +msgstr "Mindestalter (Jahre)" + +#: models.py:19 +msgid "group" +msgstr "Gruppe" + +#: models.py:20 +msgid "groups" +msgstr "Gruppen" + +#: models.py:28 +msgid "prename" +msgstr "Vorname" + +#: models.py:29 +msgid "last name" +msgstr "Nachname" + +#: models.py:31 +msgid "birth date" +msgstr "Geburtsdatum" + +#: models.py:44 +msgid "member" +msgstr "Teilnehmer" diff --git a/jdav_web/members/migrations/0001_initial.py b/jdav_web/members/migrations/0001_initial.py new file mode 100644 index 0000000..3c010b2 --- /dev/null +++ b/jdav_web/members/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-18 17:36 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Group', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20)), + ('min_age', models.IntegerField(default=5)), + ], + ), + migrations.CreateModel( + name='Member', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('prename', models.CharField(max_length=20)), + ('lastname', models.CharField(max_length=20)), + ('birth_date', models.DateField(verbose_name='birth date')), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Group')), + ], + ), + ] diff --git a/jdav_web/members/migrations/0002_member_email.py b/jdav_web/members/migrations/0002_member_email.py new file mode 100644 index 0000000..a95ffbc --- /dev/null +++ b/jdav_web/members/migrations/0002_member_email.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-18 17:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='member', + name='email', + field=models.CharField(default='', max_length=100), + ), + ] diff --git a/jdav_web/members/migrations/0003_auto_20161018_1742.py b/jdav_web/members/migrations/0003_auto_20161018_1742.py new file mode 100644 index 0000000..f95b120 --- /dev/null +++ b/jdav_web/members/migrations/0003_auto_20161018_1742.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-18 17:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0002_member_email'), + ] + + operations = [ + migrations.AlterField( + model_name='member', + name='email', + field=models.EmailField(default='', max_length=100), + ), + ] diff --git a/jdav_web/members/migrations/0004_auto_20161018_1744.py b/jdav_web/members/migrations/0004_auto_20161018_1744.py new file mode 100644 index 0000000..e479a16 --- /dev/null +++ b/jdav_web/members/migrations/0004_auto_20161018_1744.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-18 17:44 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0003_auto_20161018_1742'), + ] + + operations = [ + migrations.RemoveField( + model_name='member', + name='group', + ), + migrations.AddField( + model_name='member', + name='group', + field=models.ManyToManyField(to='members.Group'), + ), + ] diff --git a/jdav_web/members/migrations/__init__.py b/jdav_web/members/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py new file mode 100644 index 0000000..1dc3516 --- /dev/null +++ b/jdav_web/members/models.py @@ -0,0 +1,45 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Group(models.Model): + """ + Represents one group of the association + e.g: J1, J2, Jugendleiter, etc. + """ + name = models.CharField(max_length=20, verbose_name=_('name')) # e.g: J1 + min_age = models.IntegerField(default=5, + verbose_name=_('minimum age (years)')) + + def __str__(self): + """String representation""" + return self.name + + class Meta: + verbose_name = _('group') + verbose_name_plural = _('groups') + + +class Member(models.Model): + """ + Represents a member of the association + Might be a member of different groups: e.g. J1, J2, Jugendleiter, etc. + """ + prename = models.CharField(max_length=20, verbose_name=_('prename')) + lastname = models.CharField(max_length=20, verbose_name=_('last name')) + email = models.EmailField(max_length=100, default="") + birth_date = models.DateField(_('birth date')) # to determine the age + group = models.ManyToManyField(Group) + + def __str__(self): + """String representation""" + return self.name + + @property + def name(self): + """Returning whole name (prename + lastname)""" + return "{0} {1}".format(self.prename, self.lastname) + + class Meta: + verbose_name = _('member') + verbose_name_plural = _('members') diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/jdav_web/members/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/jdav_web/members/views.py b/jdav_web/members/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/jdav_web/members/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/jdav_web/startpage/__init__.py b/jdav_web/startpage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jdav_web/startpage/admin.py b/jdav_web/startpage/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/jdav_web/startpage/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/jdav_web/startpage/apps.py b/jdav_web/startpage/apps.py new file mode 100644 index 0000000..836dc89 --- /dev/null +++ b/jdav_web/startpage/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class StartpageConfig(AppConfig): + name = 'startpage' diff --git a/jdav_web/startpage/locale/de/LC_MESSAGES/django.po b/jdav_web/startpage/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..0973fdc --- /dev/null +++ b/jdav_web/startpage/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,23 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-10-22 00:04+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: templates/startpage/index.html:2 +msgid "Awesome JDAV website being able to do a lot!" +msgstr "Tolle JDAV Webseite die ganz viel kann!" diff --git a/jdav_web/startpage/migrations/__init__.py b/jdav_web/startpage/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jdav_web/startpage/models.py b/jdav_web/startpage/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/jdav_web/startpage/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/jdav_web/startpage/templates/startpage/index.html b/jdav_web/startpage/templates/startpage/index.html new file mode 100644 index 0000000..390893c --- /dev/null +++ b/jdav_web/startpage/templates/startpage/index.html @@ -0,0 +1,2 @@ +{% load i18n %} +{% trans "Awesome JDAV website being able to do a lot!" %} diff --git a/jdav_web/startpage/tests.py b/jdav_web/startpage/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/jdav_web/startpage/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/jdav_web/startpage/urls.py b/jdav_web/startpage/urls.py new file mode 100644 index 0000000..9cb3403 --- /dev/null +++ b/jdav_web/startpage/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [ + url(r'^$', views.index, name='index') +] diff --git a/jdav_web/startpage/views.py b/jdav_web/startpage/views.py new file mode 100644 index 0000000..fa24047 --- /dev/null +++ b/jdav_web/startpage/views.py @@ -0,0 +1,6 @@ +from django.shortcuts import render + + +# Create your views here. +def index(request): + return render(request, 'startpage/index.html') diff --git a/requirements.txt b/requirements.txt index 50c8909..6c4722e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ Django==1.10.2 +mysqlclient==1.3.9 +PyMySQL==0.7.9