added CSV upload to enable fast creation of similar components
This commit is contained in:
110
shimatta_kenkyusho/parts/component_import.py
Normal file
110
shimatta_kenkyusho/parts/component_import.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import io
|
||||
import csv
|
||||
import requests
|
||||
from django.db import transaction
|
||||
from django.core.files.images import ImageFile
|
||||
from.models import ComponentParameter, ComponentType, Manufacturer, Component, \
|
||||
DistributorNum, Stock, Storage, Distributor, Package, ComponentParameterType
|
||||
|
||||
def _stock_component(component, storage_uuid, substorage_path, amount, lot, watermark):
|
||||
|
||||
if not amount or not any([storage_uuid, substorage_path]):
|
||||
return None
|
||||
|
||||
storage = Storage.from_path(substorage_path, storage_uuid)
|
||||
|
||||
stock = Stock.objects.create(component=component,
|
||||
storage=storage,
|
||||
amount=amount,
|
||||
lot=lot,
|
||||
watermark=watermark)
|
||||
return stock
|
||||
|
||||
def _set_additional_parameters(component, type, value):
|
||||
|
||||
if type.startswith('param:'):
|
||||
type = type[6:]
|
||||
param_type = ComponentParameterType.objects.get(parameter_name=type)
|
||||
|
||||
param = ComponentParameter.objects.create(component=component,
|
||||
parameter_type=param_type)
|
||||
if param_type == 'F':
|
||||
param.text_value = value
|
||||
else:
|
||||
param.value = float(value)
|
||||
param.save()
|
||||
|
||||
return param
|
||||
|
||||
elif type.startswith('distri:'):
|
||||
distri_name = type[7:]
|
||||
distri = Distributor.objects.get(name=distri_name)
|
||||
|
||||
distri_num = DistributorNum.objects.create(component=component,
|
||||
distributor=distri,
|
||||
distributor_part_number=value)
|
||||
return distri_num
|
||||
|
||||
def import_components_from_csv(csv_file):
|
||||
"""
|
||||
Imports components from a csv file containing the component model fields as
|
||||
well as storage information in the heading.
|
||||
|
||||
Parameters can be set by param:<parameter name>, distri numbers by
|
||||
distri:<distri name>.
|
||||
"""
|
||||
|
||||
with transaction.atomic():
|
||||
|
||||
# simulate a text-file for the csv lib
|
||||
with io.TextIOWrapper(csv_file, encoding='utf8') as csv_text_file:
|
||||
rows = csv.DictReader(csv_text_file, delimiter=";")
|
||||
|
||||
for data in rows:
|
||||
|
||||
image = None
|
||||
image_url = data.pop('image_url')
|
||||
if image_url:
|
||||
response = requests.get(image_url)
|
||||
image_content = response.content
|
||||
image_file = io.BytesIO(image_content)
|
||||
image = ImageFile(image_file, 'downloaded_file')
|
||||
|
||||
manufacturer = None
|
||||
manufacturer_name = data.pop('manufacturer')
|
||||
if manufacturer_name:
|
||||
manufacturer = Manufacturer.objects.get(name=manufacturer_name)
|
||||
|
||||
component_type = None
|
||||
component_type_name = data.pop('component_type')
|
||||
if component_type_name:
|
||||
component_type = ComponentType.objects.get(class_name=component_type_name)
|
||||
|
||||
distributor = None
|
||||
pref_distri = data.pop('pref_distri')
|
||||
if pref_distri:
|
||||
distributor = Distributor.objects.get(name=pref_distri)
|
||||
|
||||
package = None
|
||||
package_name = data.pop('package')
|
||||
if package_name:
|
||||
package = Package.objects.get(name=package_name)
|
||||
|
||||
comp = Component.objects.create(name=data.pop('name'),
|
||||
manufacturer=manufacturer,
|
||||
component_type=component_type,
|
||||
pref_distri=distributor,
|
||||
description=data.pop('description'),
|
||||
datasheet_link=data.pop('datasheet_link'),
|
||||
package=package,
|
||||
image=image)
|
||||
|
||||
_stock_component(comp,
|
||||
data.pop('storage_uuid'),
|
||||
data.pop('substorage_path'),
|
||||
data.pop('amount'),
|
||||
data.pop('lot'),
|
||||
data.pop('watermark'))
|
||||
|
||||
for key, value in data.items():
|
||||
_set_additional_parameters(comp, key, value)
|
@@ -213,7 +213,10 @@ class ComponentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = parts_models.Component
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class ImportComponentForm(forms.Form):
|
||||
csv_file = forms.FileField(label="CSV File")
|
||||
|
||||
class PackageForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = parts_models.Package
|
||||
|
@@ -122,6 +122,29 @@ class Storage(models.Model):
|
||||
sum = 0
|
||||
return sum
|
||||
|
||||
@classmethod
|
||||
def from_path(cls, path, root_storage=None):
|
||||
'''
|
||||
Get the storage object described by its complete path or the sub-path
|
||||
from the passed root_storage uuid
|
||||
'''
|
||||
parts = path.split('/')
|
||||
|
||||
# assemble filter query
|
||||
filter_dict = {}
|
||||
|
||||
layer = 0
|
||||
for part in parts[::-1]:
|
||||
filter_dict[f'{"parent_storage__" * layer}name'] = part
|
||||
layer += 1
|
||||
|
||||
if root_storage:
|
||||
filter_dict[f'{"parent_storage__" * layer}id'] = root_storage
|
||||
else:
|
||||
filter_dict[f'{"parent_storage__" * layer}isnull'] = True
|
||||
obj = cls.objects.get(**filter_dict)
|
||||
return obj
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.validate_unique()
|
||||
super(Storage, self).save(*args, **kwargs)
|
||||
|
@@ -7,14 +7,15 @@ from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
import django.forms as forms
|
||||
from django.views.generic import TemplateView, DetailView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
from .models import Storage, Stock, Component, Distributor, Manufacturer, Package, ComponentParameter, ComponentParameterType, DistributorNum
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from .models import Storage, Stock, Component, Distributor, Manufacturer, Package, ComponentParameter, DistributorNum
|
||||
from .qr_parser import QrCodeValidator
|
||||
from django.core.paginator import Paginator
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError
|
||||
from django.db.models import ProtectedError
|
||||
from .forms import *
|
||||
from .component_import import import_components_from_csv
|
||||
from django.db.models import Q
|
||||
from django.db.models.functions import Lower
|
||||
from django.forms import formset_factory
|
||||
@@ -205,6 +206,7 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
|
||||
context['components'] = comp_paginator.get_page(comp_page_num)
|
||||
context['comp_form'] = ComponentForm()
|
||||
context['import_comp_form'] = ImportComponentForm()
|
||||
context['search_string'] = search
|
||||
|
||||
if not parameter_formset:
|
||||
@@ -218,7 +220,6 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return self.get_context_data_int(advanced_search = None, parameter_formset=None, **kwargs)
|
||||
|
||||
|
||||
def handle_new_component_post(self, request, open=False, **kwargs):
|
||||
cform = ComponentForm(data=request.POST, files=request.FILES)
|
||||
@@ -233,7 +234,21 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
if open and new_component:
|
||||
return redirect(reverse('parts-components-detail', kwargs={'uuid':new_component.id}))
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
def handle_import_components_post(self, request, open=False, **kwargs):
|
||||
cform = ImportComponentForm(data=request.POST, files=request.FILES)
|
||||
context = self.get_context_data(**kwargs)
|
||||
if cform.is_valid():
|
||||
try:
|
||||
import_components_from_csv(cform.files['csv_file'].file)
|
||||
except Exception as ex:
|
||||
cform.add_error('csv_file', str(ex))
|
||||
context['import_comp_form'] = cform
|
||||
else:
|
||||
context['import_comp_form'] = cform
|
||||
|
||||
return self.render_to_response(context)
|
||||
|
||||
def handle_advanced_search_post(self, request, **kwargs):
|
||||
|
||||
form = AdvancedComponentSearchForm(auto_id='adv_search_%s', data=request.POST)
|
||||
@@ -254,6 +269,8 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
return self.handle_new_component_post(request, open=False, **kwargs)
|
||||
elif 'submit-edit-component-open' in request.POST:
|
||||
return self.handle_new_component_post(request, open=True, **kwargs)
|
||||
elif 'submit-import-components' in request.POST:
|
||||
return self.handle_import_components_post(request, open=True, **kwargs)
|
||||
elif 'submit-advanced-search' in request.POST:
|
||||
return self.handle_advanced_search_post(request, **kwargs)
|
||||
else:
|
||||
|
Reference in New Issue
Block a user