django-fixture-utils 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. django_fixture_utils-0.1.0/PKG-INFO +13 -0
  2. django_fixture_utils-0.1.0/api/__init__.py +0 -0
  3. django_fixture_utils-0.1.0/api/admin.py +64 -0
  4. django_fixture_utils-0.1.0/api/apps.py +9 -0
  5. django_fixture_utils-0.1.0/api/filters.py +13 -0
  6. django_fixture_utils-0.1.0/api/migrations/0001_initial.py +84 -0
  7. django_fixture_utils-0.1.0/api/migrations/0002_alter_laundryroom_room_id_and_more.py +30 -0
  8. django_fixture_utils-0.1.0/api/migrations/__init__.py +0 -0
  9. django_fixture_utils-0.1.0/api/models.py +139 -0
  10. django_fixture_utils-0.1.0/api/serializers.py +94 -0
  11. django_fixture_utils-0.1.0/api/services.py +85 -0
  12. django_fixture_utils-0.1.0/api/signals.py +19 -0
  13. django_fixture_utils-0.1.0/api/tests.py +154 -0
  14. django_fixture_utils-0.1.0/api/urls.py +20 -0
  15. django_fixture_utils-0.1.0/api/views.py +199 -0
  16. django_fixture_utils-0.1.0/config/__init__.py +0 -0
  17. django_fixture_utils-0.1.0/config/asgi.py +16 -0
  18. django_fixture_utils-0.1.0/config/exceptions.py +41 -0
  19. django_fixture_utils-0.1.0/config/logging_utils.py +8 -0
  20. django_fixture_utils-0.1.0/config/permissions.py +21 -0
  21. django_fixture_utils-0.1.0/config/response.py +8 -0
  22. django_fixture_utils-0.1.0/config/settings/__init__.py +0 -0
  23. django_fixture_utils-0.1.0/config/settings/base.py +134 -0
  24. django_fixture_utils-0.1.0/config/settings/dev.py +3 -0
  25. django_fixture_utils-0.1.0/config/settings/prod.py +3 -0
  26. django_fixture_utils-0.1.0/config/urls.py +33 -0
  27. django_fixture_utils-0.1.0/config/wsgi.py +16 -0
  28. django_fixture_utils-0.1.0/django_fixture_utils.egg-info/PKG-INFO +13 -0
  29. django_fixture_utils-0.1.0/django_fixture_utils.egg-info/SOURCES.txt +45 -0
  30. django_fixture_utils-0.1.0/django_fixture_utils.egg-info/dependency_links.txt +1 -0
  31. django_fixture_utils-0.1.0/django_fixture_utils.egg-info/requires.txt +7 -0
  32. django_fixture_utils-0.1.0/django_fixture_utils.egg-info/top_level.txt +4 -0
  33. django_fixture_utils-0.1.0/manage.py +22 -0
  34. django_fixture_utils-0.1.0/pyproject.toml +59 -0
  35. django_fixture_utils-0.1.0/setup.cfg +4 -0
  36. django_fixture_utils-0.1.0/users/__init__.py +0 -0
  37. django_fixture_utils-0.1.0/users/admin.py +6 -0
  38. django_fixture_utils-0.1.0/users/apps.py +5 -0
  39. django_fixture_utils-0.1.0/users/authentication.py +5 -0
  40. django_fixture_utils-0.1.0/users/migrations/0001_initial.py +39 -0
  41. django_fixture_utils-0.1.0/users/migrations/0002_user_created_at.py +20 -0
  42. django_fixture_utils-0.1.0/users/migrations/__init__.py +0 -0
  43. django_fixture_utils-0.1.0/users/models.py +38 -0
  44. django_fixture_utils-0.1.0/users/serializers.py +26 -0
  45. django_fixture_utils-0.1.0/users/tests.py +40 -0
  46. django_fixture_utils-0.1.0/users/urls.py +8 -0
  47. django_fixture_utils-0.1.0/users/views.py +57 -0
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-fixture-utils
3
+ Version: 0.1.0
4
+ Summary: Utilities for Django fixtures, tests and database setup
5
+ Author: Student
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: Django<6,>=5
8
+ Requires-Dist: djangorestframework<4,>=3.15
9
+ Requires-Dist: django-filter<26,>=24
10
+ Requires-Dist: psycopg2-binary<3,>=2.9
11
+ Requires-Dist: python-dotenv<2,>=1
12
+ Requires-Dist: pytest<9,>=8
13
+ Requires-Dist: pytest-django<5,>=4
File without changes
@@ -0,0 +1,64 @@
1
+ from django.contrib import admin
2
+ from .models import Location, LaundryRoom, WashingMachine, WashOrder
3
+ from .signals import sync_room
4
+
5
+
6
+ class MachineInline(admin.TabularInline):
7
+ model = WashingMachine
8
+ extra = 0
9
+ readonly_fields = ("machine_id", "status_changed_at")
10
+
11
+
12
+ @admin.register(LaundryRoom)
13
+ class LaundryRoomAdmin(admin.ModelAdmin):
14
+ list_display = ("name", "location", "number_free_machines", "created_at")
15
+ list_filter = ("location",)
16
+ search_fields = ("name",)
17
+ readonly_fields = ("number_free_machines",)
18
+ inlines = [MachineInline]
19
+
20
+ def save_formset(self, request, form, formset, change):
21
+ super().save_formset(request, form, formset, change)
22
+ for obj in formset.deleted_objects + formset.saved_objects:
23
+ sync_room(obj.room_id)
24
+
25
+
26
+ @admin.register(WashingMachine)
27
+ class WashingMachineAdmin(admin.ModelAdmin):
28
+ list_display = (
29
+ "machine_id",
30
+ "get_status_display",
31
+ "max_load_kg",
32
+ "room",
33
+ "building",
34
+ "status_changed_at",
35
+ )
36
+ list_filter = ("status", "room")
37
+ readonly_fields = ("status_changed_at",)
38
+ actions = ["to_service"]
39
+
40
+ @admin.action(description="В тех. обслуживание")
41
+ def to_service(self, request, queryset):
42
+ rooms = set(queryset.values_list("room_id", flat=True))
43
+ queryset.update(status="2")
44
+ for r_id in rooms:
45
+ sync_room(r_id)
46
+
47
+
48
+ @admin.register(WashOrder)
49
+ class WashOrderAdmin(admin.ModelAdmin):
50
+ list_display = (
51
+ "title",
52
+ "get_status_display",
53
+ "laundry_weight",
54
+ "user",
55
+ "machine",
56
+ "building",
57
+ "created_at",
58
+ )
59
+ list_filter = ("status", "building")
60
+ search_fields = ("title", "user__email")
61
+ date_hierarchy = "created_at"
62
+
63
+
64
+ admin.site.register(Location)
@@ -0,0 +1,9 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class ApiConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "api"
7
+
8
+ def ready(self):
9
+ import api.signals
@@ -0,0 +1,13 @@
1
+ import django_filters
2
+
3
+ from .models import WashingMachine
4
+
5
+
6
+ class WashingMachineFilter(django_filters.FilterSet):
7
+ status = django_filters.CharFilter(field_name="status")
8
+ room_id = django_filters.UUIDFilter(field_name="room_id")
9
+ min_load = django_filters.NumberFilter(field_name="max_load_kg", lookup_expr="gte")
10
+
11
+ class Meta:
12
+ model = WashingMachine
13
+ fields = ["status", "room_id", "min_load"]
@@ -0,0 +1,84 @@
1
+ # Generated by Django 6.0.6 on 2026-06-30 07:14
2
+
3
+ import django.core.validators
4
+ import django.db.models.deletion
5
+ import uuid
6
+ from django.conf import settings
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+
12
+ initial = True
13
+
14
+ dependencies = [
15
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16
+ ]
17
+
18
+ operations = [
19
+ migrations.CreateModel(
20
+ name='Location',
21
+ fields=[
22
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23
+ ('title', models.CharField(db_index=True, max_length=125, unique=True)),
24
+ ('floor_count', models.IntegerField(validators=[django.core.validators.MinValueValidator(0)])),
25
+ ('address', models.CharField(max_length=125)),
26
+ ('created_at', models.DateTimeField(auto_now_add=True)),
27
+ ],
28
+ ),
29
+ migrations.CreateModel(
30
+ name='LaundryRoom',
31
+ fields=[
32
+ ('room_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
33
+ ('name', models.CharField(max_length=125, unique=True)),
34
+ ('number_free_machines', models.IntegerField(db_index=True, default=0, validators=[django.core.validators.MinValueValidator(0)])),
35
+ ('created_at', models.DateTimeField(auto_now_add=True)),
36
+ ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.location')),
37
+ ],
38
+ ),
39
+ migrations.CreateModel(
40
+ name='WashingMachine',
41
+ fields=[
42
+ ('machine_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
43
+ ('max_load_kg', models.IntegerField(validators=[django.core.validators.MinValueValidator(0)])),
44
+ ('status', models.CharField(choices=[('0', 'Свободна'), ('1', 'Стирает'), ('2', 'Тех. обслуживание')], db_index=True, default='0', max_length=1)),
45
+ ('status_changed_at', models.DateTimeField(auto_now=True)),
46
+ ('created_at', models.DateTimeField(auto_now_add=True)),
47
+ ('building', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='machine_buildings', to='api.location')),
48
+ ('pickup_point', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='machine_pickup_points', to='api.location')),
49
+ ('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.laundryroom')),
50
+ ],
51
+ ),
52
+ migrations.CreateModel(
53
+ name='WashOrder',
54
+ fields=[
55
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
56
+ ('title', models.CharField(max_length=125, unique=True)),
57
+ ('laundry_weight', models.IntegerField(validators=[django.core.validators.MinValueValidator(0)])),
58
+ ('idempotency_key', models.CharField(blank=True, max_length=256, null=True, unique=True)),
59
+ ('status', models.CharField(choices=[('0', 'created'), ('1', 'assigned'), ('2', 'completed'), ('3', 'cancelled')], default='0', max_length=1)),
60
+ ('created_at', models.DateTimeField(auto_now_add=True)),
61
+ ('building', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_buildings', to='api.location')),
62
+ ('machine', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='order_machines', to='api.washingmachine')),
63
+ ('pickup_point', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_pickup_points', to='api.location')),
64
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
65
+ ],
66
+ ),
67
+ migrations.AddField(
68
+ model_name='washingmachine',
69
+ name='order',
70
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='machine_orders', to='api.washorder'),
71
+ ),
72
+ migrations.AddIndex(
73
+ model_name='laundryroom',
74
+ index=models.Index(fields=['location', 'name'], name='room_location_name_idx'),
75
+ ),
76
+ migrations.AddIndex(
77
+ model_name='washorder',
78
+ index=models.Index(fields=['status', 'created_at'], name='order_status_created_idx'),
79
+ ),
80
+ migrations.AddIndex(
81
+ model_name='washingmachine',
82
+ index=models.Index(fields=['room', 'status'], name='machine_room_status_idx'),
83
+ ),
84
+ ]
@@ -0,0 +1,30 @@
1
+ # Generated by Django 6.0.6 on 2026-06-30 17:04
2
+
3
+ import django.core.validators
4
+ import uuid
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('api', '0001_initial'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name='laundryroom',
17
+ name='room_id',
18
+ field=models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name='washingmachine',
22
+ name='max_load_kg',
23
+ field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]),
24
+ ),
25
+ migrations.AlterField(
26
+ model_name='washorder',
27
+ name='laundry_weight',
28
+ field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]),
29
+ ),
30
+ ]
File without changes
@@ -0,0 +1,139 @@
1
+ from django.db import models
2
+ from django.core.validators import MinValueValidator
3
+ from django.contrib.auth import get_user_model
4
+ from rest_framework.exceptions import ValidationError
5
+ from uuid import uuid4
6
+
7
+ User = get_user_model()
8
+
9
+
10
+ class Location(models.Model):
11
+ title = models.CharField(max_length=125, unique=True, db_index=True)
12
+ floor_count = models.IntegerField(validators=[MinValueValidator(0)])
13
+ address = models.CharField(max_length=125)
14
+ created_at = models.DateTimeField(auto_now_add=True)
15
+
16
+ def __str__(self):
17
+ return self.title
18
+
19
+
20
+ class LaundryRoom(models.Model):
21
+ room_id = models.UUIDField(default=uuid4, primary_key=True)
22
+ name = models.CharField(max_length=125, unique=True)
23
+ location = models.ForeignKey(Location, on_delete=models.CASCADE)
24
+ number_free_machines = models.IntegerField(
25
+ default=0, validators=[MinValueValidator(0)], db_index=True
26
+ )
27
+ created_at = models.DateTimeField(auto_now_add=True)
28
+
29
+ class Meta:
30
+ indexes = [
31
+ models.Index(fields=["location", "name"], name="room_location_name_idx")
32
+ ]
33
+
34
+ def __str__(self):
35
+ return self.name
36
+
37
+
38
+ class WashingMachine(models.Model):
39
+ class STATUS(models.TextChoices):
40
+ FREE = "0", "Свободна"
41
+ LAUNDRY = "1", "Стирает"
42
+ SERVICE = "2", "Тех. обслуживание"
43
+
44
+ machine_id = models.UUIDField(default=uuid4, primary_key=True, editable=False)
45
+ max_load_kg = models.IntegerField(validators=[MinValueValidator(1)])
46
+ status = models.CharField(
47
+ max_length=1, choices=STATUS.choices, default=STATUS.FREE, db_index=True
48
+ )
49
+ order = models.ForeignKey(
50
+ "WashOrder",
51
+ null=True,
52
+ blank=True,
53
+ on_delete=models.SET_NULL,
54
+ related_name="machine_orders",
55
+ )
56
+ room = models.ForeignKey(LaundryRoom, on_delete=models.CASCADE, db_index=True)
57
+ building = models.ForeignKey(
58
+ Location, on_delete=models.CASCADE, related_name="machine_buildings"
59
+ )
60
+ pickup_point = models.ForeignKey(
61
+ Location, on_delete=models.CASCADE, related_name="machine_pickup_points"
62
+ )
63
+ status_changed_at = models.DateTimeField(auto_now=True)
64
+ created_at = models.DateTimeField(auto_now_add=True)
65
+
66
+ class Meta:
67
+ indexes = [
68
+ models.Index(fields=["room", "status"], name="machine_room_status_idx")
69
+ ]
70
+
71
+ def _set_status(self, new_status, label):
72
+ if self.status == new_status:
73
+ raise ValidationError(f"Статус уже '{label}'")
74
+ self.status = new_status
75
+ self.save(update_fields=["status", "status_changed_at"])
76
+
77
+ def mark_washing(self):
78
+ self._set_status(self.STATUS.LAUNDRY, "Стирает")
79
+
80
+ def mark_free(self):
81
+ self._set_status(self.STATUS.FREE, "Свободна")
82
+
83
+ if self.order_id:
84
+ WashOrder.objects.filter(pk=self.order_id).update(
85
+ status=WashOrder.STATUS.COMPLETED
86
+ )
87
+ self.order = None
88
+ self.save(update_fields=["order"])
89
+
90
+ def mark_service(self):
91
+ self._set_status(self.STATUS.SERVICE, "Тех. обслуживание")
92
+
93
+ def can_take_laundry(self, weight: int) -> bool:
94
+ return self.max_load_kg >= weight
95
+
96
+ def __str__(self):
97
+ return str(self.machine_id)
98
+
99
+
100
+ class WashOrder(models.Model):
101
+ class STATUS(models.TextChoices):
102
+ CREATED = "0", "created"
103
+ ASSIGNED = "1", "assigned"
104
+ COMPLETED = "2", "completed"
105
+ CANCELLED = "3", "cancelled"
106
+
107
+ title = models.CharField(max_length=125, unique=True)
108
+ laundry_weight = models.IntegerField(validators=[MinValueValidator(1)])
109
+ user = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
110
+ machine = models.ForeignKey(
111
+ WashingMachine,
112
+ on_delete=models.SET_NULL,
113
+ null=True,
114
+ blank=True,
115
+ related_name="order_machines",
116
+ )
117
+ building = models.ForeignKey(
118
+ Location, on_delete=models.CASCADE, related_name="order_buildings"
119
+ )
120
+ pickup_point = models.ForeignKey(
121
+ Location, on_delete=models.CASCADE, related_name="order_pickup_points"
122
+ )
123
+ idempotency_key = models.CharField(
124
+ max_length=256, unique=True, null=True, blank=True
125
+ )
126
+ status = models.CharField(
127
+ max_length=1, choices=STATUS.choices, default=STATUS.CREATED
128
+ )
129
+ created_at = models.DateTimeField(auto_now_add=True)
130
+
131
+ class Meta:
132
+ indexes = [
133
+ models.Index(
134
+ fields=["status", "created_at"], name="order_status_created_idx"
135
+ )
136
+ ]
137
+
138
+ def __str__(self):
139
+ return self.title
@@ -0,0 +1,94 @@
1
+ from rest_framework import serializers
2
+ from .models import Location, LaundryRoom, WashingMachine, WashOrder
3
+
4
+
5
+ def validate_locations(building, pickup_point):
6
+ if building and pickup_point and building.pk == pickup_point.pk:
7
+ raise serializers.ValidationError("building and pickup_point must differ")
8
+
9
+ def validate_id(building_id, pickup_point_id):
10
+ if building_id == pickup_point_id:
11
+ raise serializers.ValidationError("building and pickup_point must differ")
12
+ return True
13
+
14
+
15
+ class LocationSerializer(serializers.ModelSerializer):
16
+ class Meta:
17
+ model = Location
18
+ fields = "__all__"
19
+ read_only_fields = ["id", "created_at"]
20
+
21
+
22
+ class LaundryRoomSerializer(serializers.ModelSerializer):
23
+ class Meta:
24
+ model = LaundryRoom
25
+ fields = "__all__"
26
+ read_only_fields = ["room_id", "number_free_machines", "created_at"]
27
+
28
+
29
+ class WashingMachineSerializer(serializers.ModelSerializer):
30
+ status_label = serializers.CharField(source="get_status_display", read_only=True)
31
+
32
+ class Meta:
33
+ model = WashingMachine
34
+ fields = "__all__"
35
+ read_only_fields = ["machine_id", "status", "status_changed_at", "created_at"]
36
+
37
+ def validate(self, attrs):
38
+ building = attrs.get("building") or getattr(self.instance, "building", None)
39
+ pickup = attrs.get("pickup_point") or getattr(
40
+ self.instance, "pickup_point", None
41
+ )
42
+ validate_locations(building, pickup)
43
+ return attrs
44
+
45
+
46
+ class WashOrderSerializer(serializers.ModelSerializer):
47
+ building_id = serializers.PrimaryKeyRelatedField(
48
+ queryset=Location.objects.all(), source="building", write_only=True
49
+ )
50
+ pickup_point_id = serializers.PrimaryKeyRelatedField(
51
+ queryset=Location.objects.all(), source="pickup_point", write_only=True
52
+ )
53
+
54
+ class Meta:
55
+ model = WashOrder
56
+ fields = "__all__"
57
+ read_only_fields = [
58
+ "id",
59
+ "user",
60
+ "machine",
61
+ "building",
62
+ "pickup_point",
63
+ "idempotency_key",
64
+ "status",
65
+ "created_at",
66
+ ]
67
+
68
+ def validate(self, attrs):
69
+ validate_locations(attrs.get("building"), attrs.get("pickup_point"))
70
+ weight = attrs.get("laundry_weight")
71
+ if weight is not None and weight <= 0:
72
+ raise serializers.ValidationError(
73
+ {"laundry_weight": ["Must be greater than 0"]}
74
+ )
75
+ return attrs
76
+
77
+
78
+ class AllocateSerializer(serializers.Serializer):
79
+ laundry_weight = serializers.IntegerField(min_value=1)
80
+ building_id = serializers.IntegerField()
81
+ pickup_point_id = serializers.IntegerField()
82
+
83
+ def validate(self, attrs):
84
+ validate_id(attrs["building_id"], attrs["pickup_point_id"])
85
+ return attrs
86
+
87
+
88
+ class MachineWashingSerializer(serializers.Serializer):
89
+ building_id = serializers.IntegerField()
90
+ pickup_point_id = serializers.IntegerField()
91
+
92
+ def validate(self, attrs):
93
+ validate_id(attrs["building_id"], attrs["pickup_point_id"])
94
+ return attrs
@@ -0,0 +1,85 @@
1
+ from django.db import transaction
2
+ from django.shortcuts import get_object_or_404
3
+ from rest_framework.exceptions import ValidationError
4
+
5
+ from config.exceptions import NoFreeMachines
6
+ from config.logging_utils import log_event
7
+ from .models import Location, WashingMachine, WashOrder
8
+
9
+
10
+ @transaction.atomic
11
+ def allocate_machine(laundry_weight, building_id, pickup_point_id):
12
+ if laundry_weight <= 0:
13
+ raise ValidationError({"laundry_weight": ["Must be greater than 0"]})
14
+
15
+ if building_id == pickup_point_id:
16
+ raise ValidationError(
17
+ {"building_id": ["building and pickup_point must differ"]}
18
+ )
19
+
20
+ building = get_object_or_404(Location, pk=building_id)
21
+ pickup_point = get_object_or_404(Location, pk=pickup_point_id)
22
+
23
+ machine = (
24
+ WashingMachine.objects.select_for_update()
25
+ .select_related("room")
26
+ .filter(
27
+ status=WashingMachine.STATUS.FREE,
28
+ max_load_kg__gte=laundry_weight,
29
+ )
30
+ .order_by("-room__number_free_machines", "status_changed_at")
31
+ .first()
32
+ )
33
+
34
+ if not machine:
35
+ log_event("allocate_conflict", laundry_weight=laundry_weight)
36
+ raise NoFreeMachines()
37
+
38
+ order = (
39
+ WashOrder.objects.select_for_update()
40
+ .filter(
41
+ status=WashOrder.STATUS.CREATED,
42
+ machine__isnull=True,
43
+ laundry_weight=laundry_weight,
44
+ building_id=building_id,
45
+ pickup_point_id=pickup_point_id,
46
+ )
47
+ .order_by("created_at")
48
+ .first()
49
+ )
50
+
51
+ machine.status = WashingMachine.STATUS.LAUNDRY
52
+ machine.building = building
53
+ machine.pickup_point = pickup_point
54
+
55
+ if order:
56
+ machine.order = order
57
+ order.machine = machine
58
+ order.status = WashOrder.STATUS.ASSIGNED
59
+ order.save(update_fields=["machine", "status"])
60
+ machine.save(
61
+ update_fields=[
62
+ "status",
63
+ "status_changed_at",
64
+ "building",
65
+ "pickup_point",
66
+ "order",
67
+ ]
68
+ )
69
+ else:
70
+ machine.save(
71
+ update_fields=[
72
+ "status",
73
+ "status_changed_at",
74
+ "building",
75
+ "pickup_point",
76
+ ]
77
+ )
78
+
79
+ log_event(
80
+ "allocate",
81
+ machine_id=str(machine.machine_id),
82
+ order_id=order.id if order else None,
83
+ laundry_weight=laundry_weight,
84
+ )
85
+ return machine, order
@@ -0,0 +1,19 @@
1
+ from django.db.models.signals import post_save, post_delete
2
+ from django.dispatch import receiver
3
+ from .models import WashingMachine, LaundryRoom
4
+
5
+
6
+ def sync_room(room_id):
7
+ if room_id:
8
+ cnt = WashingMachine.objects.filter(room_id=room_id, status="0").count()
9
+ LaundryRoom.objects.filter(pk=room_id).update(number_free_machines=cnt)
10
+
11
+
12
+ @receiver(post_save, sender=WashingMachine)
13
+ def machine_save(sender, instance, created, **kwargs):
14
+ sync_room(instance.room_id)
15
+
16
+
17
+ @receiver(post_delete, sender=WashingMachine)
18
+ def machine_del(sender, instance, **kwargs):
19
+ sync_room(instance.room_id)