From ee4f75c2fb67f54ad1a08c0a7d8d76d91935b7c6 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 22 Nov 2019 13:25:08 +0100 Subject: [PATCH] Introduce Seat.sorting_rank (#1499) * Introduce Seat.sorting_rank * Fix comments * Comments, for real --- .../base/migrations/0141_seat_sorting_rank.py | 18 ++++++++++ src/pretix/base/models/seating.py | 34 ++++++++++++++++--- src/pretix/base/services/seating.py | 2 ++ 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 src/pretix/base/migrations/0141_seat_sorting_rank.py diff --git a/src/pretix/base/migrations/0141_seat_sorting_rank.py b/src/pretix/base/migrations/0141_seat_sorting_rank.py new file mode 100644 index 000000000..07763057e --- /dev/null +++ b/src/pretix/base/migrations/0141_seat_sorting_rank.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-11-22 11:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0140_voucher_seat'), + ] + + operations = [ + migrations.AddField( + model_name='seat', + name='sorting_rank', + field=models.BigIntegerField(default=0), + ), + ] diff --git a/src/pretix/base/models/seating.py b/src/pretix/base/models/seating.py index 3ac6fd33b..c3a359204 100644 --- a/src/pretix/base/models/seating.py +++ b/src/pretix/base/models/seating.py @@ -40,7 +40,7 @@ class SeatingPlan(LoggedModel): layout = models.TextField(validators=[SeatingPlanLayoutValidator()]) Category = namedtuple('Categrory', 'name') - RawSeat = namedtuple('Seat', 'name guid number row category zone') + RawSeat = namedtuple('Seat', 'name guid number row category zone sorting_rank') def __str__(self): return self.name @@ -60,16 +60,36 @@ class SeatingPlan(LoggedModel): ] def iter_all_seats(self): - for z in self.layout_data['zones']: - for r in z['rows']: - for s in r['seats']: + # This returns all seats in a plan and assignes each of them a rank. The rank is used for sorting lists of + # seats later. The rank does not say anything about the *quality* of a seat, and is only meant as a heuristic + # to make it easier for humas to process lists of seats. The current algorithm assumes that there are less + # than 10'000 zones, less than 10'000 rows in every zone and less than 10'000 seats in every row. + # Respectively, no row/seat numbers may be numeric with a value of 10'000 or more. The resulting ranks + # *will* have gaps. We chose this way over just sorting the seats and continuously enumerating them as an + # optimization, because this way we do not need to update the rank of very seat if we change a plan a little. + for zi, z in enumerate(self.layout_data['zones']): + for ri, r in enumerate(z['rows']): + try: + row_rank = int(r['row_number']) + except ValueError: + row_rank = ri + for si, s in enumerate(r['seats']): + try: + seat_rank = int(s['seat_number']) + except ValueError: + seat_rank = si + rank = ( + 10000 * 10000 * zi + 10000 * row_rank + seat_rank + ) + yield self.RawSeat( number=s['seat_number'], guid=s['seat_guid'], name='{} {}'.format(r['row_number'], s['seat_number']), # TODO: Zone? Variable scheme? row=r['row_number'], zone=z['name'], - category=s['category'] + category=s['category'], + sorting_rank=rank ) @@ -98,6 +118,10 @@ class Seat(models.Model): seat_guid = models.CharField(max_length=190, db_index=True) product = models.ForeignKey('Item', null=True, blank=True, related_name='seats', on_delete=models.CASCADE) blocked = models.BooleanField(default=False) + sorting_rank = models.BigIntegerField(default=0) + + class Meta: + ordering = ['sorting_rank', 'seat_guid'] def __str__(self): parts = [] diff --git a/src/pretix/base/services/seating.py b/src/pretix/base/services/seating.py index 739ba7d03..54371c52f 100644 --- a/src/pretix/base/services/seating.py +++ b/src/pretix/base/services/seating.py @@ -51,6 +51,7 @@ def generate_seats(event, subevent, plan, mapping): update(seat, 'row_name', ss.row), update(seat, 'seat_number', ss.number), update(seat, 'zone_name', ss.zone), + update(seat, 'sorting_rank', ss.sorting_rank), ]) if updated: seat.save() @@ -63,6 +64,7 @@ def generate_seats(event, subevent, plan, mapping): row_name=ss.row, seat_number=ss.number, zone_name=ss.zone, + sorting_rank=ss.sorting_rank, product=p, ))