add StorageLocation model
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
j3d1 2023-11-29 23:38:01 +01:00
parent 0d394d531b
commit 93335b2776
8 changed files with 179 additions and 8 deletions

1
.gitignore vendored
View file

@ -129,4 +129,5 @@ dmypy.json
.pyre/
staticfiles/
userfiles/
testdata.py

View file

@ -7,8 +7,8 @@ from rest_framework.response import Response
from authentication.models import ToolshedUser, KnownIdentity
from authentication.signature_auth import SignatureAuthentication
from toolshed.models import InventoryItem
from toolshed.serializers import InventoryItemSerializer
from toolshed.models import InventoryItem, StorageLocation
from toolshed.serializers import InventoryItemSerializer, StorageLocationSerializer
router = routers.SimpleRouter()
@ -61,7 +61,19 @@ def search_inventory_items(request):
return Response({'error': 'No query provided.'}, status=400)
class StorageLocationViewSet(viewsets.ModelViewSet):
serializer_class = StorageLocationSerializer
authentication_classes = [SignatureAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
if type(self.request.user) == KnownIdentity and self.request.user.user.exists():
return StorageLocation.objects.filter(owner=self.request.user.user.get())
return StorageLocation.objects.none()
router.register(r'inventory_items', InventoryItemViewSet, basename='inventory_items')
router.register(r'storage_locations', StorageLocationViewSet, basename='storage_locations')
urlpatterns = router.urls + [
path('search/', search_inventory_items, name='search_inventory_items'),

View file

@ -0,0 +1,32 @@
# Generated by Django 4.2.2 on 2024-02-20 13:50
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('toolshed', '0003_inventoryitem_files_and_more'),
]
operations = [
migrations.CreateModel(
name='StorageLocation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True, null=True)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='storage_locations', to='toolshed.category')),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='storage_locations', to=settings.AUTH_USER_MODEL)),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='toolshed.storagelocation')),
],
),
migrations.AddField(
model_name='inventoryitem',
name='storage_location',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_items', to='toolshed.storagelocation'),
),
]

View file

@ -62,6 +62,8 @@ class InventoryItem(SoftDeleteModel):
tags = models.ManyToManyField(Tag, through='ItemTag', related_name='inventory_items')
properties = models.ManyToManyField(Property, through='ItemProperty')
files = models.ManyToManyField(File, related_name='connected_items')
storage_location = models.ForeignKey('StorageLocation', on_delete=models.CASCADE, null=True, blank=True,
related_name='inventory_items')
def clean(self):
if (self.name is None or self.name == "") and self.files.count() == 0:
@ -77,3 +79,16 @@ class ItemProperty(models.Model):
class ItemTag(models.Model):
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
inventory_item = models.ForeignKey(InventoryItem, on_delete=models.CASCADE)
class StorageLocation(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True,
related_name='storage_locations')
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
owner = models.ForeignKey(ToolshedUser, on_delete=models.CASCADE, related_name='storage_locations')
def __str__(self):
parent = str(self.parent) + "/" if self.parent else ""
return parent + self.name

View file

@ -3,7 +3,7 @@ from authentication.models import KnownIdentity, ToolshedUser, FriendRequestInco
from authentication.serializers import OwnerSerializer
from files.models import File
from files.serializers import FileSerializer
from toolshed.models import Category, Property, ItemProperty, InventoryItem, Tag
from toolshed.models import Category, Property, ItemProperty, InventoryItem, Tag, StorageLocation
class FriendSerializer(serializers.ModelSerializer):
@ -11,7 +11,7 @@ class FriendSerializer(serializers.ModelSerializer):
class Meta:
model = KnownIdentity
fields = ['username', 'public_key']
fields = ['id', 'username', 'public_key']
def get_username(self, obj):
return obj.username + '@' + obj.domain
@ -48,6 +48,23 @@ class CategorySerializer(serializers.ModelSerializer):
return Category.objects.get(name=data.split("/")[-1])
class StorageLocationSerializer(serializers.ModelSerializer):
owner = OwnerSerializer(read_only=True)
category = CategorySerializer(required=False, allow_null=True)
path = serializers.SerializerMethodField()
class Meta:
model = StorageLocation
fields = ['id', 'name', 'description', 'path', 'category', 'owner']
read_only_fields = ['path']
@staticmethod
def get_path(obj):
if obj.parent:
return StorageLocationSerializer.get_path(obj.parent) + "/" + obj.name
return obj.name
class ItemPropertySerializer(serializers.ModelSerializer):
property = PropertySerializer(read_only=True)
@ -74,7 +91,7 @@ class InventoryItemSerializer(serializers.ModelSerializer):
class Meta:
model = InventoryItem
fields = ['id', 'name', 'description', 'owner', 'category', 'availability_policy', 'owned_quantity', 'owner',
'tags', 'properties', 'files']
'tags', 'properties', 'files', 'storage_location']
def to_internal_value(self, data):
files = data.pop('files', [])

View file

@ -1,4 +1,4 @@
from toolshed.models import Category, Tag, Property, InventoryItem, ItemProperty
from toolshed.models import Category, Tag, Property, InventoryItem, ItemProperty, StorageLocation
class CategoryTestMixin:
@ -13,8 +13,10 @@ class CategoryTestMixin:
class TagTestMixin:
def prepare_tags(self):
self.f['tag1'] = Tag.objects.create(name='tag1', description='tag1 description', category=self.f['cat1'], origin='test')
self.f['tag2'] = Tag.objects.create(name='tag2', description='tag2 description', category=self.f['cat1'], origin='test')
self.f['tag1'] = Tag.objects.create(name='tag1', description='tag1 description', category=self.f['cat1'],
origin='test')
self.f['tag2'] = Tag.objects.create(name='tag2', description='tag2 description', category=self.f['cat1'],
origin='test')
self.f['tag3'] = Tag.objects.create(name='tag3', origin='test')
@ -41,3 +43,13 @@ class InventoryTestMixin(CategoryTestMixin, TagTestMixin, PropertyTestMixin):
self.f['item2'].tags.add(self.f['tag2'], through_defaults={})
ItemProperty.objects.create(inventory_item=self.f['item2'], property=self.f['prop1'], value='value1').save()
ItemProperty.objects.create(inventory_item=self.f['item2'], property=self.f['prop2'], value='value2').save()
class LocationTestMixin:
def prepare_locations(self):
self.f['loc1'] = StorageLocation.objects.create(name='loc1', owner=self.f['local_user1'])
self.f['loc2'] = StorageLocation.objects.create(name='loc2', owner=self.f['local_user1'],
category=self.f['cat1'])
self.f['loc3'] = StorageLocation.objects.create(name='loc3', owner=self.f['local_user1'], parent=self.f['loc1'])
self.f['loc4'] = StorageLocation.objects.create(name='loc4', owner=self.f['local_user1'], parent=self.f['loc1'],
category=self.f['cat1'])

View file

@ -210,6 +210,17 @@ class FriendRequestIncomingTestCase(UserTestMixin, ToolshedTestCase):
})
self.assertEqual(reply.status_code, 400)
def test_post_request_missing_key_none(self):
befriender = self.f['ext_user1']
befriendee = self.f['local_user1']
reply = client.post('/api/friendrequests/', befriender, {
'befriender': str(befriender),
'befriendee': str(befriendee),
'befriender_key': None,
'secret': 'secret2'
})
self.assertEqual(reply.status_code, 400)
def test_post_request_breaking_key(self):
befriender = self.f['ext_user1']
befriendee = self.f['local_user1']

View file

@ -0,0 +1,71 @@
from authentication.tests import SignatureAuthClient, UserTestMixin, ToolshedTestCase
from files.tests import FilesTestMixin
from toolshed.models import InventoryItem, Category
from toolshed.tests import InventoryTestMixin, LocationTestMixin
client = SignatureAuthClient()
class LocationApiTestCase(UserTestMixin, InventoryTestMixin, LocationTestMixin, ToolshedTestCase):
def setUp(self):
super().setUp()
self.prepare_users()
self.prepare_categories()
self.prepare_tags()
self.prepare_properties()
self.prepare_locations()
self.prepare_inventory()
def test_locations(self):
self.assertEqual("loc1", str(self.f['loc1']))
self.assertEqual("loc1", self.f['loc1'].name)
self.assertEqual("loc2", str(self.f['loc2']))
self.assertEqual("loc2", self.f['loc2'].name)
self.assertEqual("loc1/loc3", str(self.f['loc3']))
self.assertEqual("loc3", self.f['loc3'].name)
self.assertEqual(self.f['loc1'], self.f['loc3'].parent)
self.assertEqual("loc1/loc4", str(self.f['loc4']))
self.assertEqual("loc4", self.f['loc4'].name)
self.assertEqual(self.f['loc1'], self.f['loc4'].parent)
def test_get_inventory(self):
reply = client.get('/api/inventory_items/', self.f['local_user1'])
self.assertEqual(reply.status_code, 200)
self.assertEqual(len(reply.json()), 2)
self.assertEqual(reply.json()[0]['name'], 'test1')
self.assertEqual(reply.json()[0]['description'], 'test')
self.assertEqual(reply.json()[0]['owned_quantity'], 1)
self.assertEqual(reply.json()[0]['tags'], [])
self.assertEqual(reply.json()[0]['properties'], [])
self.assertEqual(reply.json()[0]['category'], 'cat1')
self.assertEqual(reply.json()[0]['availability_policy'], 'friends')
self.assertEqual(reply.json()[1]['name'], 'test2')
self.assertEqual(reply.json()[1]['description'], 'test2')
self.assertEqual(reply.json()[1]['owned_quantity'], 1)
self.assertEqual(reply.json()[1]['tags'], ['tag1', 'tag2'])
self.assertEqual(reply.json()[1]['properties'],
[{'name': 'prop1', 'value': 'value1'}, {'name': 'prop2', 'value': 'value2'}])
self.assertEqual(reply.json()[1]['category'], 'cat1')
self.assertEqual(reply.json()[1]['availability_policy'], 'friends')
def test_get_inventory_item(self):
reply = client.get('/api/storage_locations/', self.f['local_user1'])
self.assertEqual(reply.status_code, 200)
self.assertEqual(len(reply.json()), 4)
self.assertEqual(reply.json()[0]['name'], 'loc1')
self.assertEqual(reply.json()[0]['description'], None)
self.assertEqual(reply.json()[0]['category'], None)
self.assertEqual(reply.json()[0]['path'], 'loc1')
self.assertEqual(reply.json()[1]['name'], 'loc2')
self.assertEqual(reply.json()[1]['description'], None)
self.assertEqual(reply.json()[1]['category'], 'cat1')
self.assertEqual(reply.json()[1]['path'], 'loc2')
self.assertEqual(reply.json()[2]['name'], 'loc3')
self.assertEqual(reply.json()[2]['description'], None)
self.assertEqual(reply.json()[2]['category'], None)
self.assertEqual(reply.json()[2]['path'], 'loc1/loc3')
self.assertEqual(reply.json()[3]['name'], 'loc4')
self.assertEqual(reply.json()[3]['description'], None)
self.assertEqual(reply.json()[3]['category'], 'cat1')
self.assertEqual(reply.json()[3]['path'], 'loc1/loc4')