Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Room #11

Merged
merged 4 commits into from
Feb 4, 2019
Merged
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
30 changes: 26 additions & 4 deletions paikkala/admin.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
from django.contrib import admin

from paikkala.models import Program, Row, Ticket, Zone
from paikkala.models import Program, Row, Ticket, Zone, Room
from paikkala.models.blocks import PerProgramBlock


class OptimizedRowQueryMixin:

def get_field_queryset(self, db, db_field, request):
queryset = super().get_field_queryset(db, db_field, request)
if db_field.name in ('row', 'rows'):
queryset = (queryset or Row.objects.all()).select_related('zone', 'zone__room')
return queryset


class RowInline(admin.TabularInline):
model = Row
readonly_fields = ('capacity',)


class ZoneAdmin(admin.ModelAdmin):
inlines = [RowInline]
list_display = ('name', 'capacity')
list_select_related = ('room',)
list_display = ('name', 'room', 'capacity')
list_filter = ('room',)


class PerProgramBlockInline(admin.TabularInline):
class RoomAdmin(admin.ModelAdmin):
search_fields = ('name',)
list_display = ('name',)


class PerProgramBlockInline(OptimizedRowQueryMixin, admin.TabularInline):
model = PerProgramBlock


class ProgramAdmin(admin.ModelAdmin):
class ProgramAdmin(OptimizedRowQueryMixin, admin.ModelAdmin):
list_display = (
'name',
'event_name',
'room',
'reservation_start',
'reservation_end',
'reserved_tickets',
Expand All @@ -35,6 +52,10 @@ class ProgramAdmin(admin.ModelAdmin):
)
list_filter = (
'event_name',
'room',
)
list_select_related = (
'room',
)
search_fields = (
'name',
Expand All @@ -58,3 +79,4 @@ class TicketAdmin(admin.ModelAdmin):
admin.site.register(Program, ProgramAdmin)
admin.site.register(Ticket, TicketAdmin)
admin.site.register(Zone, ZoneAdmin)
admin.site.register(Room, RoomAdmin)
2 changes: 1 addition & 1 deletion paikkala/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def label_from_instance(self, obj):
if obj in self.reservation_statuses:
info = self.reservation_statuses[obj]
return force_text(self.label_format).format(
zone=obj,
zone=obj.name,
capacity=info.total_capacity,
reserved=info.total_reserved,
remaining=info.total_remaining,
Expand Down
1 change: 1 addition & 0 deletions paikkala/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def mangle_zone_field(self):
if isinstance(zone_field, ReservationZoneChoiceField):
# This additional magic is required because widgets don't have access to their
# parent fields. That would be all too easy.
# ReservationZoneSelect.create_option will process the `z` object here to something sane.
zone_field.choices = [(z.id, z) for z in zone_field.queryset]
zone_field.populate_reservation_statuses(program=self.instance)
zone_field.widget = ReservationZoneSelect(
Expand Down
25 changes: 25 additions & 0 deletions paikkala/migrations/0007_excluded_numbers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.1.5 on 2019-02-04 15:34

import django.core.validators
from django.db import migrations, models
import re


class Migration(migrations.Migration):

dependencies = [
('paikkala', '0006_perprogramblock'),
]

operations = [
migrations.AlterField(
model_name='perprogramblock',
name='excluded_numbers',
field=models.CharField(blank=True, help_text='seat numbers to block from this row in this program', max_length=128, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:,\\d+)*\\Z'), code='invalid', message='Enter only digits separated by commas.')]),
),
migrations.AlterField(
model_name='row',
name='excluded_numbers',
field=models.CharField(blank=True, help_text='seat numbers to consider not part of the row; comma-separated integers', max_length=128, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:,\\d+)*\\Z'), code='invalid', message='Enter only digits separated by commas.')]),
),
]
31 changes: 31 additions & 0 deletions paikkala/migrations/0008_room.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 2.1.5 on 2019-02-04 15:36

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


class Migration(migrations.Migration):

dependencies = [
('paikkala', '0007_excluded_numbers'),
]

operations = [
migrations.CreateModel(
name='Room',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
],
),
migrations.AddField(
model_name='zone',
name='room',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='paikkala.Room'),
),
migrations.AddField(
model_name='program',
name='room',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='paikkala.Room'),
),
]
22 changes: 22 additions & 0 deletions paikkala/migrations/0009_default_room.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 2.1.5 on 2019-02-04 15:36

from django.db import migrations


def assign_default_room(apps, schema_editor):
Room = apps.get_model('paikkala', 'Room')
Zone = apps.get_model('paikkala', 'Zone')
Program = apps.get_model('paikkala', 'Program')
default_room = (Room.objects.first() or Room.objects.create(name='Room'))
Zone.objects.filter(room__isnull=True).update(room=default_room)
Program.objects.filter(room__isnull=True).update(room=default_room)


class Migration(migrations.Migration):
dependencies = [
('paikkala', '0008_room'),
]

operations = [
migrations.RunPython(assign_default_room, migrations.RunPython.noop),
]
24 changes: 24 additions & 0 deletions paikkala/migrations/0010_nonblank_room.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 2.1.5 on 2019-02-04 15:54

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


class Migration(migrations.Migration):

dependencies = [
('paikkala', '0009_default_room'),
]

operations = [
migrations.AlterField(
model_name='program',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='paikkala.Room'),
),
migrations.AlterField(
model_name='zone',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='paikkala.Room'),
),
]
4 changes: 3 additions & 1 deletion paikkala/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from .blocks import PerProgramBlock
from .programs import Program
from .zones import Zone
from .rooms import Room
from .rows import Row
from .tickets import Ticket
from .zones import Zone
1 change: 1 addition & 0 deletions paikkala/models/programs.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Program(models.Model):
null=True,
help_text='the time after which tickets for this program are considered out-of-date, e.g. for hiding from UI',
)
room = models.ForeignKey('paikkala.Room', on_delete=models.PROTECT)
rows = models.ManyToManyField('paikkala.Row')
max_tickets = models.IntegerField()
require_user = models.BooleanField(default=False)
Expand Down
8 changes: 8 additions & 0 deletions paikkala/models/rooms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.db import models


class Room(models.Model):
name = models.CharField(max_length=64)

def __str__(self):
return self.name
6 changes: 5 additions & 1 deletion paikkala/models/rows.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ def save(self, **kwargs):
self.zone.cache_total_capacity(save=True)

def __str__(self):
return '{zone} – {name}'.format(zone=self.zone.name, name=self.name)
return '{room} – {zone} – {name}'.format(
room=self.zone.room.name,
zone=self.zone.name,
name=self.name,
)

def get_numbers(self, additional_excluded_set=set()):
excluded_set = self.get_excluded_set() | additional_excluded_set
Expand Down
2 changes: 1 addition & 1 deletion paikkala/models/tickets.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ def save(self, **kwargs):
def __str__(self):
return '{program} – {zone} – {number}'.format(
program=self.program.name,
zone=self.zone,
zone=self.zone.name,
number=self.number,
)
3 changes: 2 additions & 1 deletion paikkala/models/zones.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ def total_capacity(self):


class Zone(models.Model):
room = models.ForeignKey('paikkala.Room', on_delete=models.PROTECT)
name = models.CharField(max_length=100)
capacity = models.IntegerField(editable=False, default=0)

def __str__(self):
return '{name}'.format(name=self.name)
return '{room} / {name}'.format(room=self.room.name, name=self.name)

def cache_total_capacity(self, save=False):
self.capacity = self.rows.aggregate(capacity=Sum('capacity')).get('capacity', 0)
Expand Down
7 changes: 5 additions & 2 deletions paikkala/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils.crypto import get_random_string
from django.utils.timezone import now

from paikkala.models import Program, Row, Zone
from paikkala.models import Program, Row, Zone, Room
from paikkala.utils.importer import import_zones, read_csv_file

sibeliustalo_rows = list(read_csv_file(os.path.join(os.path.dirname(__file__), 'sibeliustalo.txt')))
Expand All @@ -19,7 +19,9 @@ def sibeliustalo_zones():

@pytest.fixture
def jussi_program(sibeliustalo_zones):
room = sibeliustalo_zones[0].room
program = Program.objects.create(
room=room,
name='Jussi laskeutuu katosta enkelikuoron saattelemana',
reservation_start=now() - timedelta(hours=1),
reservation_end=now() + timedelta(hours=1),
Expand All @@ -32,11 +34,12 @@ def jussi_program(sibeliustalo_zones):

@pytest.fixture
def lattia_program():
zone = Zone.objects.create(name='lattia')
zone = Zone.objects.create(name='lattia', room=Room.objects.first())
row = Row.objects.create(zone=zone, start_number=1, end_number=10, excluded_numbers='3,4,5')
assert row.capacity == 7
t = now()
program = Program.objects.create(
room=zone.room,
name='program',
max_tickets=100,
reservation_start=t,
Expand Down
14 changes: 9 additions & 5 deletions paikkala/utils/importer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from paikkala.models import Zone
from paikkala.models import Zone, Room


def read_csv(infp, separator=','):
Expand All @@ -17,15 +17,19 @@ def read_csv_file(filename, separator=','):


def import_zones(csv_list):
zones = {}
rooms_zones = {}
for r_dict in csv_list:
zone = zones.get(r_dict['zone'])
room_name = r_dict.get('room', 'Room')
zone_name = r_dict['zone']
rz_key = (room_name, zone_name)
zone = rooms_zones.get(rz_key)
if not zone:
zone = zones[r_dict['zone']] = Zone.objects.create(name=r_dict['zone'])
room, _ = Room.objects.get_or_create(name=room_name)
zone = rooms_zones[rz_key] = Zone.objects.create(room=room, name=zone_name)
row = zone.rows.create(
start_number=int(r_dict['start']),
end_number=int(r_dict['end']),
name=int(r_dict['row']),
)
assert row.capacity > 0
return list(zones.values())
return list(rooms_zones.values())
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ envlist =
{py34,py36}-{django111,django20}

[testenv]
commands = py.test -ra -vvv --cov=paikkala --cov-report=term-missing --nomig
commands = py.test -ra -vvv --cov=paikkala --cov-report=term-missing
extras = dev
deps =
django111: Django~=1.11
Expand Down