Merge pull request #38 from SchrodingersGat/master

Added /track/ api
This commit is contained in:
Oliver 2017-04-14 16:15:10 +10:00 committed by GitHub
commit 146ad5e9ae
12 changed files with 184 additions and 74 deletions

View File

@ -17,13 +17,13 @@ def Inventree404(self):
urlpatterns = [
url(r'^stock/', include('stock.urls')),
url(r'^part/', include('part.urls')),
url(r'^supplier/', include('supplier.urls')),
url(r'^track/', include('track.urls')),
url(r'^project/', include('project.urls')),
url(r'^admin/', admin.site.urls),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^stock/?', include('stock.urls')),
url(r'^part/?', include('part.urls')),
url(r'^supplier/?', include('supplier.urls')),
url(r'^track/?', include('track.urls')),
url(r'^project/?', include('project.urls')),
url(r'^admin/?', admin.site.urls),
url(r'^auth/?', include('rest_framework.urls', namespace='rest_framework')),
# Any other URL
url(r'', Inventree404)

View File

@ -149,11 +149,9 @@ class PartParameterManager(models.Manager):
part_id = kwargs['part']
template_id = kwargs['template']
try:
params = self.filter(part=part_id, template=template_id)
params = self.filter(part=part_id, template=template_id)
if len(params) > 0:
return params[0]
except:
pass
return super(PartParameterManager, self).create(*args, **kwargs)

View File

@ -32,16 +32,11 @@ class PartSerializer(serializers.ModelSerializer):
'stock')
class PartCategoryBriefSerializer(serializers.ModelSerializer):
class PartCategorySerializer(serializers.ModelSerializer):
class Meta:
model = PartCategory
fields = ('pk',
'name',
'description')
children = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class PartCategoryDetailSerializer(serializers.ModelSerializer):
parts = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = PartCategory
@ -49,7 +44,9 @@ class PartCategoryDetailSerializer(serializers.ModelSerializer):
'name',
'description',
'parent',
'path')
'path',
'children',
'parts')
class PartTemplateSerializer(serializers.ModelSerializer):

View File

@ -5,7 +5,7 @@ from rest_framework import generics, permissions
from InvenTree.models import FilterChildren
from .models import PartCategory, Part, PartParameter, PartParameterTemplate
from .serializers import PartSerializer
from .serializers import PartCategoryDetailSerializer
from .serializers import PartCategorySerializer
from .serializers import PartParameterSerializer
from .serializers import PartTemplateSerializer
@ -32,13 +32,6 @@ class PartParamList(generics.ListCreateAPIView):
serializer_class = PartParameterSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def create(self, request, *args, **kwargs):
# Ensure part link is set correctly
part_id = self.request.query_params.get('part', None)
if part_id:
request.data['part'] = part_id
return super(PartParamList, self).create(request, *args, **kwargs)
class PartParamDetail(generics.RetrieveUpdateDestroyAPIView):
""" Detail view of a single PartParameter
@ -83,7 +76,7 @@ class PartCategoryDetail(generics.RetrieveUpdateDestroyAPIView):
""" Return information on a single PartCategory
"""
queryset = PartCategory.objects.all()
serializer_class = PartCategoryDetailSerializer
serializer_class = PartCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
@ -102,7 +95,7 @@ class PartCategoryList(generics.ListCreateAPIView):
return categories
queryset = PartCategory.objects.filter(parent=None)
serializer_class = PartCategoryDetailSerializer
serializer_class = PartCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

View File

@ -29,17 +29,11 @@ class Project(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500, blank=True)
category = models.ForeignKey(ProjectCategory, on_delete=models.CASCADE)
category = models.ForeignKey(ProjectCategory, on_delete=models.CASCADE, related_name='projects')
def __str__(self):
return self.name
@property
def projectParts(self):
""" Return a list of all project parts associated with this project
"""
return self.projectpart_set.all()
class ProjectPartManager(models.Manager):
""" Manager for handling ProjectParts
@ -56,11 +50,9 @@ class ProjectPartManager(models.Manager):
project_id = kwargs['project']
part_id = kwargs['part']
try:
project_parts = self.filter(project=project_id, part=part_id)
project_parts = self.filter(project=project_id, part=part_id)
if len(project_parts) > 0:
return project_parts[0]
except:
pass
return super(ProjectPartManager, self).create(*args, **kwargs)

View File

@ -26,14 +26,11 @@ class ProjectSerializer(serializers.ModelSerializer):
'category')
class ProjectCategoryBriefSerializer(serializers.ModelSerializer):
class ProjectCategorySerializer(serializers.ModelSerializer):
class Meta:
model = ProjectCategory
fields = ('pk', 'name', 'description')
children = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class ProjectCategoryDetailSerializer(serializers.ModelSerializer):
projects = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = ProjectCategory
@ -41,4 +38,6 @@ class ProjectCategoryDetailSerializer(serializers.ModelSerializer):
'name',
'description',
'parent',
'path')
'path',
'children',
'projects')

View File

@ -3,7 +3,7 @@ from rest_framework import generics, permissions
from InvenTree.models import FilterChildren
from .models import ProjectCategory, Project, ProjectPart
from .serializers import ProjectSerializer
from .serializers import ProjectCategoryDetailSerializer
from .serializers import ProjectCategorySerializer
from .serializers import ProjectPartSerializer
@ -40,7 +40,7 @@ class ProjectCategoryDetail(generics.RetrieveUpdateAPIView):
"""
queryset = ProjectCategory.objects.all()
serializer_class = ProjectCategoryDetailSerializer
serializer_class = ProjectCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
@ -57,7 +57,7 @@ class ProjectCategoryList(generics.ListCreateAPIView):
return categories
serializer_class = ProjectCategoryDetailSerializer
serializer_class = ProjectCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
@ -82,13 +82,6 @@ class ProjectPartsList(generics.ListCreateAPIView):
return parts
def create(self, request, *args, **kwargs):
# Ensure project link is set correctly
prj_id = self.request.query_params.get('project', None)
if prj_id:
request.data['project'] = prj_id
return super(ProjectPartsList, self).create(request, *args, **kwargs)
class ProjectPartDetail(generics.RetrieveUpdateDestroyAPIView):
""" Detail for a single project part

View File

@ -45,13 +45,6 @@ class StockList(generics.ListCreateAPIView):
return items
def create(self, request, *args, **kwargs):
# If the PART parameter is passed in the URL, use that
part_id = self.request.query_params.get('part', None)
if part_id:
request.data['part'] = part_id
return super(StockList, self).create(request, *args, **kwargs)
class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
""" Return information on a specific stock location

View File

@ -1,4 +1,5 @@
from __future__ import unicode_literals
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
from django.db import models
from django.contrib.auth.models import User
@ -7,12 +8,37 @@ from supplier.models import Customer
from part.models import Part, PartRevision
class UniquePartManager(models.Manager):
""" Ensures UniqueParts are correctly handled
"""
def create(self, *args, **kwargs):
part_id = kwargs['part']
sn = kwargs.get('serial', None)
if not sn:
raise ValidationError(_("Serial number must be supplied"))
if not isinstance(sn, int):
raise ValidationError(_("Serial number must be integer"))
# Does a part already exists with this serial number?
parts = self.filter(part=part_id, serial=sn)
if len(parts) > 0:
raise ValidationError(_("Matching part and serial number found!"))
return super(UniquePartManager, self).create(*args, **kwargs)
class UniquePart(models.Model):
""" A unique instance of a Part object.
Used for tracking parts based on serial numbers,
and tracking all events in the life of a part
"""
objects = UniquePartManager()
part = models.ForeignKey(Part, on_delete=models.CASCADE)
revision = models.ForeignKey(PartRevision,
@ -50,6 +76,17 @@ class UniquePart(models.Model):
def __str__(self):
return self.part.name
def save(self, *args, **kwargs):
# Disallow saving a serial number that already exists
matches = UniquePart.objects.filter(serial=self.serial, part=self.part)
matches = matches.filter(~models.Q(id=self.id))
if len(matches) > 0:
raise ValidationError(_("Matching serial number already exists"))
super(UniquePart, self).save(*args, **kwargs)
class PartTrackingInfo(models.Model):
""" Single data-point in the life of a UniquePart
@ -57,7 +94,6 @@ class PartTrackingInfo(models.Model):
a new PartTrackingInfo object should be created.
"""
part = models.ForeignKey(UniquePart, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True,
editable=False)
part = models.ForeignKey(UniquePart, on_delete=models.CASCADE, related_name='tracking_info')
date = models.DateField(auto_now_add=True, editable=False)
notes = models.CharField(max_length=500)

View File

@ -0,0 +1,27 @@
from rest_framework import serializers
from .models import UniquePart, PartTrackingInfo
class UniquePartSerializer(serializers.ModelSerializer):
tracking_info = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = UniquePart
fields = ['pk',
'part',
'revision',
'creation_date',
'serial',
'createdBy',
'customer',
'status',
'tracking_info']
class PartTrackingInfoSerializer(serializers.ModelSerializer):
class Meta:
model = PartTrackingInfo
fields = '__all__'

View File

@ -1,7 +1,19 @@
from django.conf.urls import url
from django.conf.urls import url, include
from . import views
urlpatterns = [
url(r'^$', views.index, name='index')
infopatterns = [
url(r'^(?P<pk>[0-9]+)/?$', views.PartTrackingDetail.as_view()),
url(r'^\?*[^/]*/?$', views.PartTrackingList.as_view())
]
urlpatterns = [
url(r'info/?', include(infopatterns)),
# Detail for a single unique part
url(r'^(?P<pk>[0-9]+)$', views.UniquePartDetail.as_view()),
# List all unique parts, with optional filters
url(r'^\?*[^/]*/?$', views.UniquePartList.as_view()),
]

View File

@ -1,5 +1,75 @@
from django.http import HttpResponse
import django_filters
from rest_framework import generics, permissions
from .models import UniquePart, PartTrackingInfo
from .serializers import UniquePartSerializer, PartTrackingInfoSerializer
def index(request):
return HttpResponse("This is the Tracking page")
class UniquePartDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = UniquePart.objects.all()
serializer_class = UniquePartSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class UniquePartFilter(django_filters.rest_framework.FilterSet):
# Filter based on serial number
min_sn = django_filters.NumberFilter(name='serial', lookup_expr='gte')
max_sn = django_filters.NumberFilter(name='serial', lookup_expr='lte')
class Meta:
model = UniquePart
fields = ['serial', ]
class UniquePartList(generics.ListCreateAPIView):
serializer_class = UniquePartSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filter_class = UniquePartFilter
def get_queryset(self):
parts = UniquePart.objects.all()
query = self.request.query_params
# Filter by associated part
part_id = query.get('part', None)
if part_id:
parts = parts.filter(part=part_id)
# Filter by serial number
sn = query.get('sn', None)
if sn:
parts = parts.filter(serial=sn)
# Filter by customer
customer = query.get('customer', None)
if customer:
parts = parts.filter(customer=customer)
return parts
class PartTrackingDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = PartTrackingInfo.objects.all()
serializer_class = PartTrackingInfoSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class PartTrackingList(generics.ListCreateAPIView):
serializer_class = PartTrackingInfoSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
tracking = PartTrackingInfo.objects.all()
query = self.request.query_params
part_id = query.get('part', None)
if part_id:
tracking = tracking.filter(part=part_id)
return tracking