From 377985be10cb959fa975e9cddc5161d346f3d2c6 Mon Sep 17 00:00:00 2001 From: Kyle McFarland Date: Sun, 21 Oct 2018 19:26:32 -0600 Subject: Add view suppliers view and suppliers API endpoint This adds a simple view (mostly cribbed from the component view but without the database stats) to view details for a given supplier, currently lists the components they supply and name/email address pairs for representatives however there's room to add fields for other representative contact and company information as well. On the API side this adds 3 endpoints: * api/suppliers: returns a list of all suppliers and their representatives * api/suppliers/: returns a supplier object containing the representatives for the given supplier * api/suppliers//components: returns a supplier object containing a list of components supplied by the given supplier This also contains minor documentation and base template changes, fixing the navigation menu to use class="active" on the currently selected page (previously didn't work due to page_name capitalization) --- coding_assignment_project/templates/base.html | 7 ++- procurement/api.py | 16 ++++++- procurement/forms.py | 20 ++++++--- procurement/serializers.py | 11 +++++ .../templates/procurement/documentation.html | 7 ++- .../procurement/includes/supplier_details.html | 51 ++++++++++++++++++++++ .../templates/procurement/view_suppliers.html | 35 +++++++++++++++ procurement/urls.py | 8 +++- procurement/views.py | 22 +++++++++- 9 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 procurement/templates/procurement/includes/supplier_details.html create mode 100644 procurement/templates/procurement/view_suppliers.html diff --git a/coding_assignment_project/templates/base.html b/coding_assignment_project/templates/base.html index 231c385..5645842 100644 --- a/coding_assignment_project/templates/base.html +++ b/coding_assignment_project/templates/base.html @@ -56,10 +56,13 @@
  • - Documentation + Documentation
  • - Source Components + Source Components +
  • +
  • + View Suppliers
  • diff --git a/procurement/api.py b/procurement/api.py index 0419ce8..d1830c2 100644 --- a/procurement/api.py +++ b/procurement/api.py @@ -1,7 +1,7 @@ from rest_framework.generics import ListAPIView, RetrieveAPIView -from procurement.models import Component -from procurement.serializers import ComponentSerializer +from procurement.models import Component, Supplier +from procurement.serializers import ComponentSerializer, SupplierSerializer, SupplierComponentsSerializer class ComponentAPIList(ListAPIView): @@ -12,3 +12,15 @@ class ComponentAPIList(ListAPIView): class ComponentAPIRetrieve(RetrieveAPIView): queryset = Component.objects.all() serializer_class = ComponentSerializer + +class SupplierAPIList(ListAPIView): + queryset = Supplier.objects.filter(is_authorized=True) + serializer_class = SupplierSerializer + +class SupplierAPIRetrieve(RetrieveAPIView): + queryset = Supplier.objects.filter(is_authorized=True) + serializer_class = SupplierSerializer + +class SupplierAPIComponents(RetrieveAPIView): + queryset = Supplier.objects.filter(is_authorized=True) + serializer_class = SupplierComponentsSerializer diff --git a/procurement/forms.py b/procurement/forms.py index 4e8b0aa..0aeca9b 100644 --- a/procurement/forms.py +++ b/procurement/forms.py @@ -1,14 +1,24 @@ from django import forms -from procurement.models import Component +from procurement.models import Component, Supplier +class FormControlBase(forms.Form): + _formcontrol_fields = [] + def __init__(self, *args, **kwargs): + super(FormControlBase, self).__init__(*args, **kwargs) + for fieldname in self._formcontrol_fields: + self.fields[fieldname].widget.attrs.update({"class": "form-control"}) -class ComponentSearchForm(forms.Form): +class ComponentSearchForm(FormControlBase): + _formcontrol_fields = ["component"] component = forms.ModelChoiceField( queryset=Component.objects.all(), required=False ) - def __init__(self, *args, **kwargs): - super(ComponentSearchForm, self).__init__(*args, **kwargs) - self.fields['component'].widget.attrs.update({"class": "form-control"}) +class SupplierSearchForm(FormControlBase): + _formcontrol_fields = ["supplier"] + supplier = forms.ModelChoiceField( + queryset=Supplier.objects.filter(is_authorized=True), + required=False + ) diff --git a/procurement/serializers.py b/procurement/serializers.py index 0466402..b3e9280 100644 --- a/procurement/serializers.py +++ b/procurement/serializers.py @@ -13,6 +13,17 @@ class SupplierSerializer(serializers.ModelSerializer): model = Supplier exclude = ('created', 'updated') +class SupplierComponentSerializer(serializers.ModelSerializer): + text = serializers.CharField(source="__str__", read_only=True) + class Meta: + model = Component + exclude = ('created', 'updated') + +class SupplierComponentsSerializer(serializers.ModelSerializer): + components = SupplierComponentSerializer(many=True, read_only=True) + class Meta: + model = Supplier + exclude = ('created', 'updated') class ComponentSerializer(serializers.ModelSerializer): text = serializers.CharField(source='__str__', read_only=True) diff --git a/procurement/templates/procurement/documentation.html b/procurement/templates/procurement/documentation.html index 4775890..f482a1d 100644 --- a/procurement/templates/procurement/documentation.html +++ b/procurement/templates/procurement/documentation.html @@ -55,7 +55,9 @@

    Components and their approved Suppliers can be located under source components. From this page you may search for a - component, and then click the Find Suppliers button to display its Suppliers + component, and then click the Find Suppliers button to display its Suppliers. + You can also view details for any authorized Supplier in the system directly under + view suppliers.

    API

    @@ -63,6 +65,7 @@ A simple REST api is integrated into the system, using the Django Rest Framework. It provides a means of querying all components or, by providing an id in the url, a specific component. + You may also use the API to query all authorized suppliers, a specific supplier by providing an id, or the components a specific supplier provides.

    @@ -73,4 +76,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/procurement/templates/procurement/includes/supplier_details.html b/procurement/templates/procurement/includes/supplier_details.html new file mode 100644 index 0000000..0e46c67 --- /dev/null +++ b/procurement/templates/procurement/includes/supplier_details.html @@ -0,0 +1,51 @@ + +
    +
    + Supplier information for {{ supplier.name }} +
    + +
    +
    + + + + + + + + + {% for representative in supplier.representatives.all %} + + + + + {% empty %} + + + + {% endfor %} + +
    RepresentativeEmail
    {{ representative.name }}{{ representative.email }}
    {{ supplier_name }} has no representatives
    +
    + +
    + +
    + + +
    +
    + Components Supplied by {{ supplier.name }} +
    +
    +
    +
    + {% csrf_token %} + {{ component_form }} +
    +
    + +
    +
    +
    +
    diff --git a/procurement/templates/procurement/view_suppliers.html b/procurement/templates/procurement/view_suppliers.html new file mode 100644 index 0000000..918ce62 --- /dev/null +++ b/procurement/templates/procurement/view_suppliers.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% load static %} + +{% block additional_css %} + + +{% endblock %} + +{% block main_content_area %} +
    +
    +
    +
    +
    + {% csrf_token %} + {{ form }} +
    +
    + +
    +
    +
    + +
    + + {% if supplier != None %} + {% include 'procurement/includes/supplier_details.html' %} + {% endif %} +
    +{% endblock %} + +{% block additional_js %} + + +{% endblock %} diff --git a/procurement/urls.py b/procurement/urls.py index d629635..be721ae 100644 --- a/procurement/urls.py +++ b/procurement/urls.py @@ -1,12 +1,16 @@ from django.urls import path -from procurement.views import ComponentSearchView, DocumentationView -from procurement.api import ComponentAPIList, ComponentAPIRetrieve +from procurement.views import ComponentSearchView, SupplierSearchView, DocumentationView +from procurement.api import ComponentAPIList, ComponentAPIRetrieve, SupplierAPIList, SupplierAPIRetrieve, SupplierAPIComponents urlpatterns = [ path('', ComponentSearchView.as_view(), name='component-search'), + path('suppliers', SupplierSearchView.as_view(), name='supplier-search'), path('documentation', DocumentationView.as_view(), name='documentation'), # API Urls path('api/components/', ComponentAPIList.as_view(), name='api-component-list'), path('api/components//', ComponentAPIRetrieve.as_view(), name='api-component-retrieve'), + path('api/suppliers/', SupplierAPIList.as_view(), name='api-supplier-list'), + path('api/suppliers//', SupplierAPIRetrieve.as_view(), name='api-supplier-retrieve'), + path('api/suppliers//components', SupplierAPIComponents.as_view(), name='api-supplier-components'), ] diff --git a/procurement/views.py b/procurement/views.py index 1516455..c1e66c9 100644 --- a/procurement/views.py +++ b/procurement/views.py @@ -1,6 +1,6 @@ from django.views.generic import FormView, TemplateView -from procurement.forms import ComponentSearchForm +from procurement.forms import ComponentSearchForm, SupplierSearchForm from procurement.models import Supplier, Component, Representative @@ -49,6 +49,26 @@ class ComponentSearchView(FormView): return super(ComponentSearchView, self).get(self.request) +class SupplierSearchView(FormView): + template_name = "procurement/view_suppliers.html" + form_class = SupplierSearchForm + + supplier = None + component_form = None + + def form_valid(self, form): + self.supplier = form.cleaned_data['supplier'] + if self.supplier: + self.component_form = ComponentSearchForm() + self.component_form.fields["component"].queryset = self.supplier.components + return super(SupplierSearchView, self).get(self.request) + + def get_context_data(self): + context = super().get_context_data() + context["supplier"] = self.supplier + context["component_form"] = self.component_form + context["page_name"] = "View Supplier Details" + return context class DocumentationView(TemplateView): template_name = 'procurement/documentation.html' -- cgit v1.1