Skip to content

Commit

Permalink
Merge pull request #144 from volunteer-planner/release/2.0.0
Browse files Browse the repository at this point in the history
Release 2.0.0
  • Loading branch information
pitpalme committed Oct 4, 2015
2 parents 887f4d6 + 8d3d124 commit 78f376f
Show file tree
Hide file tree
Showing 546 changed files with 22,241 additions and 85,635 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ manlocal.py
db.sqlite3
.DS_Store
*.mo
/htmlcov
/.coverage
*.log
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: python
python: "2.7"
before_install:
- sudo apt-get update && sudo apt-get --reinstall install -qq language-pack-en language-pack-de
install: "pip install -r requirements/dev.txt"
env: DJANGO_SETTINGS_MODULE=volunteer_planner.settings.local
script: py.test --create-db tests/
notifications:
email: false
9 changes: 9 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com

[volunteer-planner.django]
file_filter = locale/<lang>/LC_MESSAGES/django.po
source_file = locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO

50 changes: 31 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# volunteer_planner
# volunteer-planner.org

A platform to schedule shifts of volunteers.

**TODO**: Add general project description and goals, ie. link to wiki.

## Project Setup

### 0. Prerequisites (Ubuntu 14.04 example)
Expand Down Expand Up @@ -109,13 +111,18 @@ You might consider to use this example `postactivate` script
git fetch --all
git status

#### 2.3.1 Setup your local environment (optional)
*Note:* You'll need to re-active your virtual environment after each change to it's `postactivate` hook to take effect. Just run `workon vp` again, to make sure your current venv session has executed the `postactivate` hook.

#### 2.3.1 ... settings module for using MySQL

When you prefer to use MySQL locally, you'll probably need to use the settings module `volunteer_planner.settings.local_mysql` instead of `volunteer_planner.settings.local`.

#### 2.3.2 Setup your local environment (optional)

Also, if you need to use non-default settings values, setting (exporting) the
environment variables in your virtualenvs' `postactivate` hook is a good place
if you're not using an IDE to configure your environment variables.


### 3. Initialize the database with Django

Activate your env and change dir to your local forks' git repository (if not done yet).
Expand Down Expand Up @@ -167,32 +174,37 @@ A good read on TDD is the free o'Reilly eBook ["Test-Driven Development with Pyt

To run the tests, run the following command (with your virtual env activated, see 3.)

$ py.test [/path/to/volunteer_planner.git/]
$ py.test -v [/path/to/volunteer_planner.git/]

### Translations
If you want to generate a coverage report as well, run

Can create/update the translations file with
$ py.test --cov=. --cov-report html --cov-report term-missing --no-cov-on-fail -v

```
./manage.py makemessages --no-obsolete --no-wrap
```

The options are intended to make the output more git-friendly.
This generates a nice HTML coverage page, to poke around which can be found at `/path/to/volunteer_planner.git/htmlcov/index.html`.

Compile the messages file with
*Note*: The directory `htmlcov` is git-ignored.

### Translations
We use transiflex for managing translations.
You first need to make sure that the transiflex client is installed.
```
./manage.py compilemessages
pip install transifex-client
```
For further installation infos check http://docs.transifex.com/client/setup/

The workflow is like

1. you code you stuff
2. "./manage.py makemessages --no-obsolete --no-wrap" The options are intended to make the output more git-friendly.
3. "tx push -s django"
3. do translations on transiflex
4. "tx pull"
5. "./manage.py compilemessages"
6. test if it looks good
7. commit push with git

Your local installation should be translated then.
The .mo file created by compilemessages is gitignored,
you'll need to (re-)generate it locally every time the .po file changes.


### CSS / Less

We use less for precompiling css. The less file you will find in
`scheduler/static/bootstrap/less/project.less` To make this work you can just
initialize the folder with "npm install -g" and then let grunt watch for
changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,16 @@ def add_arguments(self, parser):

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
RegistrationProfile.objects.delete_expired_users(dry_run)
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()
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ class Migration(migrations.Migration):

operations = [
migrations.CreateModel(
name='RegistrationProfile',
name='UserAccount',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('activation_key', models.CharField(max_length=40, verbose_name='activation key')),
('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, unique=True)),
('user', models.OneToOneField(related_name='account', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'registration profile',
'verbose_name_plural': 'registration profiles',
'verbose_name': 'user account',
'verbose_name_plural': 'user accounts',
},
bases=(models.Model,),
),
]
22 changes: 21 additions & 1 deletion accounts/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# coding: utf-8

from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
from registration.signals import user_activated
from django.dispatch import receiver


class UserAccount(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
related_name='account')

class Meta:
verbose_name = _('user account')
verbose_name_plural = _('user accounts')

@receiver(user_activated)
def registration_completed(sender, user, request, **kwargs):
account, created = UserAccount.objects.get_or_create(user=user)
print account, created


# Create your models here.
2 changes: 1 addition & 1 deletion accounts/templates/user_account_edit.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends 'home.html' %}
{% extends 'base_non_logged_in.html' %}
{% load i18n %}

{% block title %}{{ user.username }}{% endblock %}
Expand Down
2 changes: 1 addition & 1 deletion accounts/templates/user_detail.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends 'home.html' %}
{% extends 'base_non_logged_in.html' %}
{% load i18n %}

{% block title %}{{ user.username }}{% endblock %}
Expand Down
4 changes: 2 additions & 2 deletions blueprint/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib import admin
from django import forms

from django.utils.translation import ugettext_lazy as _
from .models import BluePrintCreator, NeedBluePrint


Expand All @@ -12,7 +12,7 @@ class Meta:
def clean(self):
try:
if BluePrintCreator.objects.get(location=self.data['location']):
raise forms.ValidationError("Ort hat bereits eine Vorlage!")
raise forms.ValidationError(_("There is already a blueprint for this location!"))
except Exception:
return self.cleaned_data

Expand Down
24 changes: 12 additions & 12 deletions blueprint/models.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# coding: utf-8

from django.db import models

from django.utils.translation import ugettext_lazy as _

class BluePrintCreator(models.Model):
class Meta:
verbose_name = "Vorlage"
verbose_name_plural = "Vorlagen"
verbose_name = _("Blueprint")
verbose_name_plural = _("Blueprints")

title = models.CharField(verbose_name="Name der Vorlage", max_length=255)
location = models.ForeignKey('scheduler.Location', verbose_name="Ort")
needs = models.ManyToManyField('NeedBluePrint', verbose_name="Schichten")
title = models.CharField(verbose_name=_("blueprint title"), max_length=255)
location = models.ForeignKey('scheduler.Location', verbose_name=_("location"))
needs = models.ManyToManyField('NeedBluePrint', verbose_name=_("shifts"))

def __unicode__(self):
return u'{}'.format(self.title)


class NeedBluePrint(models.Model):
class Meta:
verbose_name = "Schicht Vorlage"
verbose_name_plural = "Schicht Vorlagen"
verbose_name = _("Blueprint Item")
verbose_name_plural = _("Blueprint Items")

topic = models.ForeignKey('scheduler.Topics', verbose_name="Hilfetyp")
from_time = models.CharField(verbose_name='Uhrzeit von', max_length=5)
to_time = models.CharField(verbose_name='Uhrzeit bis', max_length=5)
slots = models.IntegerField(verbose_name="Anz. benoetigter Freiwillige")
topic = models.ForeignKey('scheduler.Topics', verbose_name=_("topic"))
from_time = models.CharField(verbose_name=_('from hh:mm'), max_length=5)
to_time = models.CharField(verbose_name=_('until hh:mm'), max_length=5)
slots = models.IntegerField(verbose_name=_("number of volunteers needed"))

def get_location(self):
return self.blueprintcreator_set.all().get().location
Expand Down
13 changes: 7 additions & 6 deletions blueprint/templates/blueprint_executor.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{% load i18n %}
<!DOCTYPE html>
<html>
<head lang="de">
<head>
<meta charset="UTF-8">
<title>{% block title %}Home{% endblock %}</title>
<title>{% block title %}{% trans "Home" %}{% endblock %}</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
Expand Down Expand Up @@ -70,23 +71,23 @@
<div class="container">
<div class="col-md-12">
<div class="jumbotron">
<h2 class="text-center">Blueprint auf folgenden Tag anwenden:</h2>
<h2 class="text-center">{% trans "Apply template for the following day:" %}</h2>

<p class="text-center">
<select id="tokenize" multiple="multiple" class="tokenize-sample">
{% for location in locations %}
<option value="{{ location.id }}">{{ location.name }}</option>
{% endfor %}
</select></p>
<p class="text-center">Date: <input type="text" id="datepicker"></p>
<p class="text-center">{% trans "Date" %}: <input type="text" id="datepicker"></p>

<p>
<button class="btn btn-primary btn-lg" href="#" role="button">anwenden</button>
<button class="btn btn-primary btn-lg" href="#" role="button">{% trans "apply" %}</button>
</p>
</div>
</div>
</div>


</body>
</html>
</html>
8 changes: 0 additions & 8 deletions common/templatetags/string_filters.py

This file was deleted.

46 changes: 46 additions & 0 deletions common/templatetags/volunteer_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# coding: utf-8

from datetime import timedelta

from django import template
from django.contrib.auth.models import User
from django.db.models import Count
from django.utils import timezone

from scheduler.models import Need, Location

register = template.Library()


@register.assignment_tag
def get_facility_count():
return Location.objects.filter().count()


@register.assignment_tag
def get_volunteer_number():
return User.objects.filter(is_active=True).count()


@register.assignment_tag
def get_volunteer_hours():
"""
Returns the number of total volunteer hours worked.
"""
finished_needs = Need.objects.filter(
starting_time__lte=timezone.now()).annotate(
slots_done=Count('helpers'))
delta = timedelta()
for need in finished_needs:
delta += need.slots_done * (need.ending_time - need.starting_time)
hours = int(delta.total_seconds() / 3600)
return hours


@register.assignment_tag
def get_volunteer_stats():
return {
'volunteer_count': get_volunteer_number(),
'facility_count': get_facility_count(),
'volunteer_hours': get_volunteer_hours(),
}
51 changes: 51 additions & 0 deletions common/templatetags/vpfilters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# coding: utf-8

from numbers import Number

from django import template

register = template.Library()


@register.filter
def subtract(lhs, rhs):
rhs = int(rhs) if not isinstance(rhs, Number) else rhs
lhs = int(lhs) if not isinstance(lhs, Number) else lhs
return rhs - lhs


@register.filter
def divide(lhs, rhs):
lhs = float(lhs) if not isinstance(lhs, Number) else lhs
rhs = float(rhs) if not isinstance(rhs, Number) else rhs
return lhs / rhs if rhs else None


@register.filter
def contains(enumeratable, obj):
return obj in enumeratable


@register.filter
def split(value, separator=' '):
return value.split(separator)


@register.filter
def eq(lhs, rhs):
return lhs == rhs


@register.filter
def neq(lhs, rhs):
return lhs != rhs


@register.filter
def yes(lhs, rhs, default=""):
return rhs if lhs else default


@register.filter
def no(lhs, rhs, default=""):
return rhs if not lhs else default
Loading

0 comments on commit 78f376f

Please sign in to comment.