Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,7 @@ db.sqlite3
hsf/dev_settings.py

.*_cache/


# Editor save files
*~
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ in the console. Once it's running, you should now be able to access your develop
[http://localhost:8000](http://localhost:8000). Any changes you make to your development
copy should be automatically reloaded.

If you need to run it on another port, edit the `docker-compose.yml` and change the first port, keep the second the same as this is the port docker is using internally, example:

- "8080:8000"

If you `Ctrl+c` the process, it'll stop the containers. If you run
`docker-compose down`, it'll destroy the containers *along with your development database*.

Expand Down Expand Up @@ -58,6 +62,10 @@ u.is_staff = True
u.save()
```

## Import some spaces

The repository has a static file: static/data.json containing details on some spaces (is unlikely to be uptodate!), to import it as a starting set, visit [http://localhost:8000/import_spaces](http://localhost:8000/import_spaces) after you have made your user an admin.

## Managing Dependencies

This app uses [Pipenv](https://pipenv.readthedocs.io) to manage dependencies. If you want
Expand Down
3 changes: 2 additions & 1 deletion main/admin.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.contrib import admin

from .models import User, Space, SupporterMembership, GocardlessMandate, GocardlessPayment
from .models import User, Space, SupporterMembership, SpaceMembership, GocardlessMandate, GocardlessPayment

admin.site.register(User)
admin.site.register(Space)
admin.site.register(SupporterMembership)
admin.site.register(SpaceMembership)
admin.site.register(GocardlessMandate)
admin.site.register(GocardlessPayment)
28 changes: 27 additions & 1 deletion main/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.forms import ModelForm
from django.contrib.auth.forms import UserCreationForm
from .models import User, SupporterMembership
from .models import User, SupporterMembership, SpaceMembership
from django.utils import timezone
from django.core.mail import EmailMessage
from django.template.loader import get_template
Expand Down Expand Up @@ -104,6 +104,9 @@ def send_confirmation_email(self):
# TODO: oh dear - how should we handle this gracefully?!?
print("Error sending email" + str(e))

user.space_status = 'Emailed'
user.save()


class SupporterMembershipForm(ModelForm):
class Meta:
Expand All @@ -128,6 +131,29 @@ def clean_statement(self):
return data


class SpaceMembershipForm(ModelForm):
class Meta:
model = SpaceMembership
fields = ('fee', 'statement')
widgets = {
'fee': forms.NumberInput(attrs={'step': 0.25, 'min': 20.0})
}

# ensure fee is not less than £20.00
def clean_fee(self):
data = self.cleaned_data['fee']
if data < 20:
raise forms.ValidationError("Minimum £20.00")
return data

# ensure statement is not empty
def clean_statement(self):
data = self.cleaned_data['statement']
if data == "":
raise forms.ValidationError("Please write at least a few words :)")
return data


class NewSpaceForm(forms.Form):
name = forms.CharField(required=True)
email = forms.EmailField(required=True)
Expand Down
50 changes: 50 additions & 0 deletions main/migrations/0041_auto_20190701_1658.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 2.0.13 on 2019-07-01 16:58

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('main', '0040_char_to_text'),
]

operations = [
migrations.CreateModel(
name='SpaceMembership',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.TextField(choices=[('Pending', 'Pending'), ('Approved', 'Approved'), ('Rejected', 'Rejected')], default='Pending')),
('approval_request_count', models.IntegerField(default=0)),
('fee', models.DecimalField(decimal_places=2, default=20.0, max_digits=8)),
('statement', models.TextField(blank=True)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('started_at', models.DateField(null=True)),
('expired_at', models.DateField(null=True)),
('redirect_flow_id', models.TextField(blank=True)),
('session_token', models.TextField(default='')),
],
options={
'db_table': 'spacemembership',
'ordering': ['created_at'],
},
),
migrations.AlterField(
model_name='user',
name='space_status',
field=models.CharField(choices=[('Blank', 'Blank'), ('Pending', 'Pending'), ('Emailed', 'Emailed'), ('Approved', 'Approved'), ('Rejected', 'Rejected')], default='Blank', max_length=8),
),
migrations.AddField(
model_name='spacemembership',
name='applied_by',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='spacemembership',
name='space',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Space'),
),
]
19 changes: 19 additions & 0 deletions main/migrations/0042_gocardlessmandate_space_membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 2.0.13 on 2019-07-02 12:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('main', '0041_auto_20190701_1658'),
]

operations = [
migrations.AddField(
model_name='gocardlessmandate',
name='space_membership',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='main.SpaceMembership'),
),
]
2 changes: 2 additions & 0 deletions main/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from .user import User, SpaceUserManager
from .space import Space, SpaceManager
from .supporter_membership import SupporterMembership, SupporterMembershipManager
from .space_membership import SpaceMembership, SpaceMembershipManager
from .gocardless_mandate import GocardlessMandate, GocardlessMandateManager
from .gocardless_payment import GocardlessPayment, GocardlessPaymentManager

__all__ = [
'User', 'SpaceUserManager',
'Space', 'SpaceManager',
'SupporterMembership', 'SupporterMembershipManager',
'SpaceMembership', 'SpaceMembershipManager',
'GocardlessMandate', 'GocardlessMandateManager',
'GocardlessPayment', 'GocardlessPaymentManager'
]
23 changes: 20 additions & 3 deletions main/models/gocardless_mandate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,20 @@ def get_mandate_for_supporter_membership(self, supporter_membership):
def get_membership_status_for_supporter_membership(self, supporter_membership):
return self.get_mandate_for_supporter_membership(supporter_membership).status

# get_or_create that populates fields from json
# get all mandate records for space membership
def get_mandates_for_space_membership(self, space_membership):
return super(GocardlessMandateManager, self).get_queryset().filter(
space_membership=space_membership)

# get latest mandate for space_membership
def get_mandate_for_space_membership(self, space_membership):
return self.get_mandates_for_space_membership(space_membership).latest('created_at')

# get latest mandate status for space_membership or throw DoesNotExist
def get_membership_status_for_space_membership(self, space_membership):
return self.get_mandate_for_space_membership(space_membership).status

# get_or_create that populates fields from json
# e.g. as received from gocardless webhook or mandate creation api
# payload should be a dict, e.g. parsed from webhook json
def get_or_create_from_payload(self, payload):
Expand Down Expand Up @@ -99,7 +112,7 @@ class GocardlessMandate(models.Model):
# which Supporter membership is this mandate associated with (or null)
supporter_membership = models.ForeignKey('SupporterMembership', models.CASCADE, null=True)
# which Space Membership is this mandate associated with (or null)
# TODO: ^^
space_membership = models.ForeignKey('SpaceMembership', models.CASCADE, null=True)

# override default manager
objects = GocardlessMandateManager()
Expand Down Expand Up @@ -131,7 +144,9 @@ def save(self, force_insert=False, force_update=False):
def is_supporter_mandate(self):
return self.supporter_membership is not None

# TODO: is_space_mandate
# is_space_mandate
def is_space_mandate(self):
return self.space_membership is not None

# is_active - is this mandate active
def is_active(self):
Expand Down Expand Up @@ -206,3 +221,5 @@ def handle_payment_updated(self, payment):
if payment.status == 'paid_out':
if self.supporter_membership is not None:
self.supporter_membership.handle_payment_received(payment)
if self.space_membership is not None:
self.space_membership.handle_payment_received(payment)
21 changes: 21 additions & 0 deletions main/models/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from django.utils import timezone
import logging

from .space_membership import SpaceMembership

# get instance of a logger
logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -96,3 +98,22 @@ def as_geojson_feature(self):
"logo": self.logo_image_url
}
}

# get membership type, returns: None, Member
def member_type(self):
if self.membership_status() != 'None':
return 'Member'
else:
return 'None'

# get membership status, will return a APPROVAL_STATUS_CHOICES value
def membership_status(self):
return SpaceMembership.objects.get_membership_status(self)

# get latest membership record for this user
def current_membership(self):
return SpaceMembership.objects.get_membership(self)

# get all membership records for this user
def memberships(self):
return SpaceMembership.objects.get_memberships(self)
Loading