forked from hswaw/spejstore
Compare commits
1 commit
master
...
storage-un
Author | SHA1 | Date | |
---|---|---|---|
|
76e0d405e0 |
10 changed files with 200 additions and 13 deletions
|
@ -17,6 +17,14 @@ docker-compose up
|
||||||
docker-compose build
|
docker-compose build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
Before test run containers. See "Build & run".
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker-compose exec web python manage.py test --noinput
|
||||||
|
```
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
- https://askubuntu.com/q/615394/413683
|
- https://askubuntu.com/q/615394/413683
|
||||||
|
|
|
@ -14,3 +14,5 @@ pyldap==2.4.28
|
||||||
requests==2.16.5
|
requests==2.16.5
|
||||||
urllib3==1.21.1
|
urllib3==1.21.1
|
||||||
django_markdown2==0.3.0
|
django_markdown2==0.3.0
|
||||||
|
factory-boy==2.11.1
|
||||||
|
Faker==0.8.16
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
|
||||||
from django_select2.forms import ModelSelect2Widget, Select2MultipleWidget
|
from django_select2.forms import Select2MultipleWidget
|
||||||
|
|
||||||
from .models import Item, ItemImage, Category, Label
|
from .models import Item, ItemImage, Category, Label
|
||||||
from .widgets import ItemSelectWidget, PropsSelectWidget
|
from .widgets import ItemSelectWidget, PropsSelectWidget
|
||||||
|
@ -36,7 +37,8 @@ class ItemAdmin(admin.ModelAdmin):
|
||||||
inlines = [ItemImageInline, LabelInline]
|
inlines = [ItemImageInline, LabelInline]
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
|
|
||||||
def _name(self, obj):
|
@classmethod
|
||||||
|
def _name(cls, obj):
|
||||||
return '-' * obj.get_level() + '> ' + obj.name
|
return '-' * obj.get_level() + '> ' + obj.name
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
|
@ -72,9 +74,5 @@ class ItemAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
admin.site.register(Item, ItemAdmin)
|
admin.site.register(Item, ItemAdmin)
|
||||||
admin.site.register(Category)
|
admin.site.register(Category)
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.unregister(Group)
|
admin.site.unregister(Group)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from rest_framework import viewsets, generics, filters
|
from rest_framework import viewsets, filters
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.decorators import detail_route
|
from rest_framework.decorators import detail_route
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
|
@ -46,7 +46,6 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||||
filter_backends = (SmartSearchFilterBackend, filters.OrderingFilter)
|
filter_backends = (SmartSearchFilterBackend, filters.OrderingFilter)
|
||||||
ordering_fields = '__all__'
|
ordering_fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Item.get_roots()
|
return Item.get_roots()
|
||||||
|
|
||||||
|
|
65
storage/factories.py
Normal file
65
storage/factories.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
from factory import LazyAttribute, SubFactory
|
||||||
|
from factory.django import DjangoModelFactory
|
||||||
|
from faker import Faker
|
||||||
|
from random import randint
|
||||||
|
from tree.fields import Path
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from .models import Category, Item, ItemImage, Label, STATES
|
||||||
|
|
||||||
|
fake = Faker('pl_PL')
|
||||||
|
|
||||||
|
|
||||||
|
class UserFactory(DjangoModelFactory):
|
||||||
|
"""
|
||||||
|
Creates and returns instance of User model
|
||||||
|
"""
|
||||||
|
username = LazyAttribute(lambda n: '{}{}'.format(fake.user_name(), randint(0, 100000)))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryFactory(DjangoModelFactory):
|
||||||
|
"""
|
||||||
|
Creates and returns instance of Category
|
||||||
|
"""
|
||||||
|
name = 'elektronika'
|
||||||
|
icon_id = 'elka'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Category
|
||||||
|
|
||||||
|
|
||||||
|
class ItemFactory(DjangoModelFactory):
|
||||||
|
"""
|
||||||
|
Creates and returns instance of Item
|
||||||
|
"""
|
||||||
|
path = Path(field='field1', value='path1')
|
||||||
|
name = 'śrubokręt'
|
||||||
|
description = 'czerwony śrubokręt krzyżakowy'
|
||||||
|
state = STATES[0][0]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Item
|
||||||
|
|
||||||
|
|
||||||
|
class ItemImageFactory(DjangoModelFactory):
|
||||||
|
"""
|
||||||
|
Creates and returns instance of ItemImage and creates related Item
|
||||||
|
"""
|
||||||
|
item = SubFactory(ItemFactory)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ItemImage
|
||||||
|
|
||||||
|
|
||||||
|
class LabelFactory(DjangoModelFactory):
|
||||||
|
"""
|
||||||
|
Creates and returns instance of Label and creates related Item
|
||||||
|
"""
|
||||||
|
item = SubFactory(ItemFactory)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Label
|
|
@ -1,8 +1,9 @@
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from storage.models import Item
|
from storage.models import Item
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Imports book library from specified wiki page dump'
|
help = 'Imports book library from specified wiki page dump'
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ class ItemSerializer(HStoreSerializer):
|
||||||
model = Item
|
model = Item
|
||||||
fields = ('uuid', 'name', 'description', 'props', 'state', 'parent')
|
fields = ('uuid', 'name', 'description', 'props', 'state', 'parent')
|
||||||
|
|
||||||
|
|
||||||
class LabelSerializer(serializers.ModelSerializer):
|
class LabelSerializer(serializers.ModelSerializer):
|
||||||
item = ItemSerializer(required=False)
|
item = ItemSerializer(required=False)
|
||||||
item_id = serializers.PrimaryKeyRelatedField(queryset=Item.objects, source='item')
|
item_id = serializers.PrimaryKeyRelatedField(queryset=Item.objects, source='item')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Label
|
model = Label
|
||||||
fields = ('id', 'item', 'item_id', 'style')
|
fields = ('id', 'item', 'item_id', 'style')
|
||||||
|
|
111
storage/tests.py
111
storage/tests.py
|
@ -1,3 +1,112 @@
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.shortcuts import reverse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
from .factories import CategoryFactory, ItemFactory, ItemImageFactory, LabelFactory, UserFactory
|
||||||
|
from .models import Category, Item, ItemImage, Label
|
||||||
|
|
||||||
|
|
||||||
|
class ModelsTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
Test factories and models
|
||||||
|
"""
|
||||||
|
def test_user_factory(self):
|
||||||
|
"""
|
||||||
|
Test user creation
|
||||||
|
"""
|
||||||
|
u1 = UserFactory()
|
||||||
|
u2 = UserFactory(email='kowalski@example.com')
|
||||||
|
|
||||||
|
self.assertIsInstance(u1, User)
|
||||||
|
self.assertIsInstance(u2, User)
|
||||||
|
|
||||||
|
def test_category_factory(self):
|
||||||
|
"""
|
||||||
|
test category creation
|
||||||
|
"""
|
||||||
|
c = CategoryFactory()
|
||||||
|
|
||||||
|
self.assertIsInstance(c, Category)
|
||||||
|
|
||||||
|
def test_category_model(self):
|
||||||
|
"""
|
||||||
|
create category instance
|
||||||
|
"""
|
||||||
|
c = CategoryFactory(
|
||||||
|
name='Elektronika',
|
||||||
|
icon_id='elektronika-icon-id',
|
||||||
|
)
|
||||||
|
self.assertIsInstance(c, Category)
|
||||||
|
|
||||||
|
# TODO: create tests for Item and ItemImage and Label models
|
||||||
|
# TODO: create tests for parent items
|
||||||
|
|
||||||
|
def test_item_factory(self):
|
||||||
|
"""
|
||||||
|
test item creation
|
||||||
|
"""
|
||||||
|
i = ItemFactory()
|
||||||
|
|
||||||
|
self.assertIsInstance(i, Item)
|
||||||
|
|
||||||
|
def test_item_image_factory(self):
|
||||||
|
"""
|
||||||
|
test item creation
|
||||||
|
"""
|
||||||
|
ii = ItemImageFactory()
|
||||||
|
|
||||||
|
self.assertIsInstance(ii, ItemImage)
|
||||||
|
|
||||||
|
def test_get_or_create_label(self):
|
||||||
|
"""
|
||||||
|
test get_or_create_label method
|
||||||
|
"""
|
||||||
|
i = ItemFactory()
|
||||||
|
|
||||||
|
obj = i.get_or_create_label()
|
||||||
|
|
||||||
|
self.assertIsInstance(obj, Label)
|
||||||
|
|
||||||
|
def test_label_factory(self):
|
||||||
|
"""
|
||||||
|
test label creation
|
||||||
|
"""
|
||||||
|
label = LabelFactory()
|
||||||
|
|
||||||
|
self.assertIsInstance(label, Label)
|
||||||
|
|
||||||
|
|
||||||
|
class ViewsTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Create some models
|
||||||
|
"""
|
||||||
|
c = CategoryFactory(
|
||||||
|
name='Elektronika',
|
||||||
|
icon_id='elektronika-icon-id',
|
||||||
|
)
|
||||||
|
i = ItemFactory(name='butelka')
|
||||||
|
i.categories.add(c)
|
||||||
|
|
||||||
|
i = ItemFactory(name='nakrętka')
|
||||||
|
i.categories.add(c)
|
||||||
|
|
||||||
|
def test_search_view(self):
|
||||||
|
"""
|
||||||
|
test search with query
|
||||||
|
"""
|
||||||
|
url = reverse('item-search')
|
||||||
|
self.assertEqual('/search', url)
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertTemplateUsed(response, 'results.html')
|
||||||
|
|
||||||
|
# Additional, try to check some content
|
||||||
|
self.assertEqual(2, len(response.context[0]['results']))
|
||||||
|
for item in response.context[0]['results']:
|
||||||
|
self.assertIsInstance(item, Item)
|
||||||
|
|
||||||
|
# TODO: create tests for other views
|
||||||
|
# TODO: test searching items
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import url
|
||||||
from storage.views import (
|
from storage.views import (
|
||||||
index, search, item_display, label_lookup, ItemSelectView, PropSelectView
|
index, search, item_display, label_lookup, ItemSelectView, PropSelectView
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', index),
|
url(r'^$', index),
|
||||||
url(r'^search$', search),
|
url(r'^search$', search, name='item-search'),
|
||||||
url(r'^item/(?P<pk>.*)$', item_display, name='item-display'),
|
url(r'^item/(?P<pk>.*)$', item_display, name='item-display'),
|
||||||
url(r'^autocomplete.json$', ItemSelectView.as_view(), name='item-complete'),
|
url(r'^autocomplete.json$', ItemSelectView.as_view(), name='item-complete'),
|
||||||
url(r'^autocomplete_prop.json$', PropSelectView.as_view(), name='prop-complete'),
|
url(r'^autocomplete_prop.json$', PropSelectView.as_view(), name='prop-complete'),
|
||||||
|
|
|
@ -31,7 +31,10 @@ class PropsSelectWidget(DictionaryFieldWidget):
|
||||||
attrs = {}
|
attrs = {}
|
||||||
# it's called "original" because it will be replaced by a copy
|
# it's called "original" because it will be replaced by a copy
|
||||||
attrs['class'] = 'hstore-original-textarea'
|
attrs['class'] = 'hstore-original-textarea'
|
||||||
w = HeavySelect2Widget(data_view='prop-complete', attrs={'data-tags': 'true', 'class': 'hs-key'})
|
w = HeavySelect2Widget(
|
||||||
|
data_view='prop-complete',
|
||||||
|
attrs={'data-tags': 'true', 'class': 'hs-key'},
|
||||||
|
)
|
||||||
|
|
||||||
# get default HTML from AdminTextareaWidget
|
# get default HTML from AdminTextareaWidget
|
||||||
html = AdminTextareaWidget.render(self, name, value, attrs)
|
html = AdminTextareaWidget.render(self, name, value, attrs)
|
||||||
|
|
Loading…
Reference in a new issue