Skip to content

Commit

Permalink
Merge pull request #268 from coders4help/develop
Browse files Browse the repository at this point in the history
Release 3.0.0 Merge
  • Loading branch information
christophmeissner committed Nov 9, 2015
2 parents c7fedf7 + 66ed9af commit c327c41
Show file tree
Hide file tree
Showing 122 changed files with 66,998 additions and 1,687 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ db.sqlite3
/htmlcov
/.coverage
*.log
.cache
38 changes: 32 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
# volunteer-planner.org

A platform to schedule shifts of volunteers.
Volunteer Planner is a platform to schedule shifts of volunteers. Volunteers register at the platform and choose shifts.
The admin of the website can easily add new organizations, places and shifts. The software has a location based
hierarchy (country / region / area / city) and has a hierarchy of organizations (organizations, facilities, tasks and
workplaces) - it can be used for a variety of purposes.

**TODO**: Add general project description and goals, ie. link to wiki.
## Status
The project is currently running at https://volunteer-planner.org/.

## Project Setup
## Work in progress
There are some feature requests to be implemented in the future.
The software currently needs a centralized administration of the shifts, but it is one of the main goals of the current
development to empower organizations to schedule shifts for their facilities on their own.

If you are interested to join the development team, just make pull requests or come to a meeting in Berlin/Germany:
http://www.meetup.com/de/coders4help/

## System context
**User**: The volunteers and administrators just need a (modern) web browser to use the volunteer-planner application.

**Developer**: Developers need a python development environment (see project setup) and specific versions of external
libraries (see /requirements directory, t). Development can be done with a sqlite databases, there is no need to run
and configure postgres or mysql.

**Server**: For production use you need a Python ready web server, for example uWSGI as web server for the Python WSGI
with nginx as proxy server visible to the end user (volunteers and administrators). You also need a MySQL or PostgreSQL
database.


## Project setup for development

### 0. Prerequisites (Ubuntu 14.04 example)

Expand Down Expand Up @@ -61,7 +85,8 @@ If you have questions concerning our workflow please read the

#### 2.1. Create a virtual env

Create an virtualenv (using [virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/)):
Create an virtualenv (follow the installation guide at [virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/)
to install virtualenvwrapper):

$ mkvirtualenv vp

Expand Down Expand Up @@ -159,14 +184,15 @@ the superuser you created earlier (in case you don't see an error page here).

### Create Dummy Data

run management command " python manage.py create_dummy_data 5 --flush True " with activated virtualenv to get 5 days of dummy data and delete tables in advance.
Run management command " python manage.py create_dummy_data 5 --flush True " with activated virtualenv to get 5 days of
dummy data and delete tables in advance.

The number (5 in the above example) creates 5 days dummy data count from today.
If you just use "python manage.py create_dummy_data 5" without --flush it is NOT deleting data before putting new data in.

### Running Tests

Feature PR should be accompanied by appropriate test. We have unit and integration tests that
Feature pull requests should be accompanied by appropriate tests. We have unit and integration tests that
are run with `py.test`, and functional/behave tests that are run with `selenium`.

To run unit tests, run the following command (with your virtual env activated, see 3.)
Expand Down
39 changes: 38 additions & 1 deletion accounts/admin.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
# coding: utf-8

from django.contrib import admin
from django.utils.translation import ugettext_lazy as _

from .models import UserAccount


@admin.register(UserAccount)
class UserAccountAdmin(admin.ModelAdmin):
list_display = (u'id', 'user')

def get_user_first_name(self, obj):
return obj.user.first_name

get_user_first_name.short_description = _(u'first name')
get_user_first_name.admin_order_field = 'user__first_name'


def get_user_last_name(self, obj):
return obj.user.last_name

get_user_last_name.short_description = _(u'first name')
get_user_last_name.admin_order_field = 'user__last_name'

def get_user_email(self, obj):
return obj.user.email

get_user_email.short_description = _(u'email')
get_user_email.admin_order_field = 'user__email'

list_display = (
'user',
'get_user_email',
'get_user_first_name',
'get_user_last_name'
)
raw_id_fields = ('user',)
search_fields = (
'user__username',
'user__email',
'user__last_name',
'user__first_name'
)

list_filter = (
'user__is_active',
'user__is_staff',
)
39 changes: 17 additions & 22 deletions accounts/management/commands/clean_expired.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
# coding=utf-8
from django.conf import settings
from django.core.management.base import BaseCommand

from datetime import date, timedelta

from registration.models import RegistrationProfile


class Command(BaseCommand):
help = 'Cleanup expired registrations'

OPT_SIMULATE = 'dry-run'
def handle(self, *args, **options):
profiles = RegistrationProfile.objects \
.exclude(activation_key=RegistrationProfile.ACTIVATED) \
.prefetch_related('user', 'user__account') \
.exclude(user__is_active=True) \
.filter(user__date_joined__lt=(date.today() - timedelta(settings.ACCOUNT_ACTIVATION_DAYS)))

def add_arguments(self, parser):
parser.add_argument(''.join(['--', self.OPT_SIMULATE]),
action='store_true',
dest=self.OPT_SIMULATE,
default=False,
help='Only print registrations that would be deleted')
if settings.DEBUG:
self.stderr.write(u'SQL: {}'.format(profiles.query))

def handle(self, *args, **options):
self.stdout.write('Deleting expired user registrations')
dry_run = True if self.OPT_SIMULATE in options and options[
self.OPT_SIMULATE] else False
if dry_run:
user_count, reg_profile_count = 0, 0
for profile in RegistrationProfile.objects.select_related(
'user').exclude(user__is_active=True):
if profile.activation_key_expired():
user_count += 1
reg_profile_count += 1
print "Would delete {} User and {} RegistrationProfile objects".format(
user_count, reg_profile_count)
else:
RegistrationProfile.objects.delete_expired_users()
for profile in profiles:
if hasattr(profile, 'user'):
if hasattr(profile.user, 'account'):
profile.user.account.delete()
profile.user.delete()
profile.delete()
4 changes: 4 additions & 0 deletions common/migrations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# coding: utf-8

def skip(*_, **__):
pass
14 changes: 14 additions & 0 deletions common/templatetags/site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# coding: utf-8

# coding: utf-8

from django import template

from django.contrib.sites.shortcuts import get_current_site

register = template.Library()


@register.assignment_tag
def request_site(request):
return get_current_site(request_site)
5 changes: 5 additions & 0 deletions common/templatetags/vpfilters.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ def yes(lhs, rhs, default=""):
@register.filter
def no(lhs, rhs, default=""):
return rhs if not lhs else default


@register.filter
def get(obj, key):
return obj[key]
Empty file added content/__init__.py
Empty file.
67 changes: 67 additions & 0 deletions content/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# coding: utf-8
from django.conf import settings

from django.contrib import admin
from django.contrib.flatpages.admin import FlatPageAdmin
from django.contrib.flatpages.models import FlatPage
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _

from . import models, forms


class FlatPageStyleInline(admin.StackedInline):
model = models.FlatPageExtraStyle
verbose_name = _('additional CSS')
verbose_name_plural = _('additional CSS')
template = 'flatpages/additional_flat_page_style_inline.html'


class FlatPageTranslationInline(admin.StackedInline):
model = models.FlatPageTranslation
form = forms.FlatPageTranslationFormWithHTMLEditor
verbose_name = _('translation')
verbose_name_plural = _('translations')
extra = 0


class FlatPageTranslationAdmin(FlatPageAdmin):
inlines = [
FlatPageStyleInline,
FlatPageTranslationInline,
]

form = forms.FlatPageFormWithHTMLEditor
list_filter = ('sites', 'registration_required',)
list_display = (
'title',
'url',
'registration_required',
'get_translations',
)
search_fields = ('url', 'title')

def get_translations(self, obj):
translations = list(obj.translations.all())
trans_list = None
if translations:
translations = sorted(
t.get_language_display() for t in translations)

trans_list = u'<ul>{}</ul>'.format(
u'\n'.join(u'<li>{}</li>'.format(t) for t in translations)
)
return format_html(
u'{}/{}:<br />{}'.format(
len(translations),
len(settings.LANGUAGES),
trans_list or _('No translation available')
)
)

get_translations.allow_tags = True
get_translations.short_description = _('translations')


admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageTranslationAdmin)
18 changes: 18 additions & 0 deletions content/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# coding: utf-8
from ckeditor.widgets import CKEditorWidget
from django.contrib.flatpages.forms import FlatpageForm
from django import forms

from content.models import FlatPageTranslation


class FlatPageTranslationFormWithHTMLEditor(forms.ModelForm):
content = forms.CharField(widget=CKEditorWidget())

class Meta:
fields = '__all__'
model = FlatPageTranslation


class FlatPageFormWithHTMLEditor(FlatpageForm):
content = forms.CharField(widget=CKEditorWidget())
44 changes: 44 additions & 0 deletions content/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('flatpages', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='FlatPageExtraStyle',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('css', models.TextField(verbose_name='additional CSS', blank=True)),
('flatpage', models.OneToOneField(related_name='extra_style', verbose_name='additional style', to='flatpages.FlatPage')),
],
options={
'verbose_name': 'additional flat page style',
'verbose_name_plural': 'additional flat page styles',
},
),
migrations.CreateModel(
name='FlatPageTranslation',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('language', models.CharField(max_length=20, verbose_name='language', choices=[(b'de', 'German'), (b'en', 'English'), (b'hu', 'Hungarian'), (b'sv', 'Swedish')])),
('title', models.CharField(max_length=200, verbose_name='title', blank=True)),
('content', models.TextField(verbose_name='content', blank=True)),
('flatpage', models.ForeignKey(related_name='translations', verbose_name='flat page', to='flatpages.FlatPage')),
],
options={
'verbose_name': 'flat page translation',
'verbose_name_plural': 'flat page translations',
},
),
migrations.AlterUniqueTogether(
name='flatpagetranslation',
unique_together=set([('flatpage', 'language')]),
),
]
19 changes: 19 additions & 0 deletions content/migrations/0002_add_greek_language.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('content', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='flatpagetranslation',
name='language',
field=models.CharField(max_length=20, verbose_name='language', choices=[(b'en', 'English'), (b'de', 'German'), (b'el', 'Greek'), (b'hu', 'Hungarian'), (b'sv', 'Swedish')]),
),
]
Empty file added content/migrations/__init__.py
Empty file.
Loading

0 comments on commit c327c41

Please sign in to comment.