diff --git a/core/inventory/admin.py b/core/inventory/admin.py index 2457b5b..24cd14a 100644 --- a/core/inventory/admin.py +++ b/core/inventory/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from inventory.models import Item, Container, Event +from inventory.models import Item, Container, ItemPlacement, Comment, Event class ItemAdmin(admin.ModelAdmin): @@ -22,3 +22,17 @@ class EventAdmin(admin.ModelAdmin): admin.site.register(Event, EventAdmin) + + +class ItemPlacementAdmin(admin.ModelAdmin): + pass + + +admin.site.register(ItemPlacement, ItemPlacementAdmin) + + +class CommentAdmin(admin.ModelAdmin): + pass + + +admin.site.register(Comment, CommentAdmin) diff --git a/core/inventory/api_v2.py b/core/inventory/api_v2.py index 39052f8..e21bf22 100644 --- a/core/inventory/api_v2.py +++ b/core/inventory/api_v2.py @@ -27,6 +27,17 @@ class ContainerViewSet(viewsets.ModelViewSet): queryset = Container.objects.all() +class ItemViewSet(viewsets.ModelViewSet): + serializer_class = ItemSerializer + + def get_queryset(self): + queryset = Item.objects.all() + serializer = self.get_serializer_class() + if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'prefetch_related_fields'): + queryset = queryset.prefetch_related(*serializer.Meta.prefetch_related_fields) + return queryset + + def filter_items(items, query): query_tokens = query.split(' ') for item in items: @@ -54,6 +65,7 @@ def search_items(request, event_slug, query): @api_view(['GET', 'POST']) @permission_classes([IsAuthenticated]) def item(request, event_slug): + vs = ItemViewSet() try: event = None if event_slug != 'none': @@ -61,7 +73,7 @@ def item(request, event_slug): if request.method == 'GET': if not request.user.has_event_perm(event, 'view_item'): return Response(status=403) - return Response(ItemSerializer(Item.objects.filter(event=event), many=True).data) + return Response(ItemSerializer(vs.get_queryset().filter(event=event), many=True).data) elif request.method == 'POST': if not request.user.has_event_perm(event, 'add_item'): return Response(status=403) @@ -106,7 +118,7 @@ def add_comment(request, event_slug, id): def item_by_id(request, event_slug, id): try: event = Event.objects.get(slug=event_slug) - item = Item.objects.get(event=event, id=id) + item = ItemViewSet().get_queryset().get(event=event, id=id) if request.method == 'GET': if not request.user.has_event_perm(event, 'view_item'): return Response(status=403) diff --git a/core/inventory/migrations/0007_rename_container_item_container_old_itemplacement_and_more.py b/core/inventory/migrations/0007_rename_container_item_container_old_itemplacement_and_more.py new file mode 100644 index 0000000..918f636 --- /dev/null +++ b/core/inventory/migrations/0007_rename_container_item_container_old_itemplacement_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 4.2.7 on 2024-11-23 15:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('inventory', '0006_alter_event_table'), + ] + + def set_initial_container(apps, schema_editor): + Item = apps.get_model('inventory', 'Item') + for item in Item.objects.all(): + item.container_history.get_or_create(container=item.container_old) + item.save() + + operations = [ + migrations.RenameField( + model_name='item', + old_name='container', + new_name='container_old', + ), + migrations.CreateModel( + name='ItemPlacement', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('container', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_history', + to='inventory.container')), + ('item', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='container_history', + to='inventory.item')), + ], + ), + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('comment', models.TextField()), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', + to='inventory.item')), + ], + ), + migrations.RunPython(set_initial_container), + migrations.RemoveField( + model_name='item', + name='container_old', + ), + ] diff --git a/core/inventory/serializers.py b/core/inventory/serializers.py index c349e19..90a75aa 100644 --- a/core/inventory/serializers.py +++ b/core/inventory/serializers.py @@ -60,6 +60,7 @@ class ItemSerializer(BasicItemSerializer): fields = ['cid', 'box', 'id', 'description', 'file', 'dataImage', 'returned', 'event', 'related_issues', 'timeline'] read_only_fields = ['id'] + prefetch_related_fields = ['comments', 'issue_relation_changes', 'container_history'] def to_internal_value(self, data): container = None diff --git a/core/tickets/api_v2.py b/core/tickets/api_v2.py index 9e601ba..4b01db0 100644 --- a/core/tickets/api_v2.py +++ b/core/tickets/api_v2.py @@ -21,8 +21,13 @@ from tickets.shared_serializers import RelationSerializer class IssueViewSet(viewsets.ModelViewSet): serializer_class = IssueSerializer - queryset = IssueThread.objects.all().prefetch_related('state_changes', 'comments', 'emails', 'emails__attachments', - 'assignments', 'item_relation_changes', 'shipping_vouchers') + + def get_queryset(self): + queryset = IssueThread.objects.all() + serializer = self.get_serializer_class() + if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'prefetch_related_fields'): + queryset = queryset.prefetch_related(*serializer.Meta.prefetch_related_fields) + return queryset class RelationViewSet(viewsets.ModelViewSet): diff --git a/core/tickets/serializers.py b/core/tickets/serializers.py index 4faa69c..b382b38 100644 --- a/core/tickets/serializers.py +++ b/core/tickets/serializers.py @@ -47,6 +47,8 @@ class IssueSerializer(BasicIssueSerializer): model = IssueThread fields = ('id', 'timeline', 'name', 'state', 'assigned_to', 'last_activity', 'uuid', 'related_items', 'event') read_only_fields = ('id', 'timeline', 'last_activity', 'uuid', 'related_items') + prefetch_related_fields = ['state_changes', 'comments', 'emails', 'emails__attachments', 'assignments', + 'item_relation_changes', 'shipping_vouchers'] def to_internal_value(self, data): ret = super().to_internal_value(data) @@ -63,12 +65,14 @@ class IssueSerializer(BasicIssueSerializer): @staticmethod def get_last_activity(self): try: - last_state_change = max([t.timestamp for t in self.state_changes.all()]) if self.state_changes.exists() else None + last_state_change = max( + [t.timestamp for t in self.state_changes.all()]) if self.state_changes.exists() else None last_comment = max([t.timestamp for t in self.comments.all()]) if self.comments.exists() else None last_mail = max([t.timestamp for t in self.emails.all()]) if self.emails.exists() else None last_assignment = max([t.timestamp for t in self.assignments.all()]) if self.assignments.exists() else None - last_relation = max([t.timestamp for t in self.item_relation_changes.all()]) if self.item_relation_changes.exists() else None + last_relation = max([t.timestamp for t in + self.item_relation_changes.all()]) if self.item_relation_changes.exists() else None args = [x for x in [last_state_change, last_comment, last_mail, last_assignment, last_relation] if x is not None] return max(args) diff --git a/deploy/ansible/playbooks/templates/nginx.conf.j2 b/deploy/ansible/playbooks/templates/nginx.conf.j2 index 3533f37..1bde29a 100644 --- a/deploy/ansible/playbooks/templates/nginx.conf.j2 +++ b/deploy/ansible/playbooks/templates/nginx.conf.j2 @@ -75,6 +75,14 @@ server { allow 127.0.0.1; allow ::1; deny all; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_redirect off; + proxy_buffering off; + proxy_pass http://c3lf-sys3; } listen 443 ssl http2; # managed by Certbot