Skip to content

Commit

Permalink
Merge pull request #172 from T0jan/template-handling
Browse files Browse the repository at this point in the history
Template handling
  • Loading branch information
eeintech authored Oct 14, 2023
2 parents 11bc59c + f9c6ede commit d5c1461
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 83 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test_deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
git clone https://github.com/inventree/InvenTree/
mkdir InvenTree/static
cp tests/files/inventree_default_db.sqlite3 InvenTree/
cd InvenTree/ && git switch 0.11.x && invoke install && invoke migrate && cd -
cd InvenTree/ && git switch stable && invoke install && invoke migrate && cd -
- name: Ki-nTree setup
run: |
invoke install
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@ Ki-nTree currently supports APIs for the following electronics suppliers: Digi-K
5. It will take some time to complete the part creation in InvenTree and/or KiCad, once it finishes you'll be notified of the result
6. Finally, if the part was created or found in InvenTree, your browser will automatically open a new tab with the part information

#### Kicad Templates

The automatic part generation in KiCad is controlled via templates:

* Template examples are shipped together with Ki-nTree, these can be adjusted to your likings or you also can create completely new ones.
* Each template has its own library file where the file name defines the templates name.
* The templates can use the parameters and attributes of the InvenTree part on a wildcard base. So you can add for example `Resistance @ Tolerance` into a field and the resulting part will then have the resitance and the tolerance value inside this text field.


Enjoy!

*For any problem/bug you find, please [report an issue](https://github.com/sparkmicro/Ki-nTree/issues).*
Expand Down Expand Up @@ -272,4 +281,4 @@ The Ki-nTree source code is licensed under the [GPL3.0 license](https://github.c
* https://github.com/mvnmgrx/kiutils
* https://github.com/peeter123/digikey-api

The [KiCad templates](https://github.com/sparkmicro/Ki-nTree/tree/main/kintree/kicad/templates) are licensed under the [Creative Commons CC0 1.0 license](https://github.com/sparkmicro/Ki-nTree/blob/main/kintree/kicad/templates/LICENSE) which means that "you can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission" ([reference](https://creativecommons.org/publicdomain/zero/1.0/)).
The [KiCad templates](https://github.com/sparkmicro/Ki-nTree/tree/main/kintree/kicad/templates) are licensed under the [Creative Commons CC0 1.0 license](https://github.com/sparkmicro/Ki-nTree/blob/main/kintree/kicad/templates/LICENSE) which means that "you can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission" ([reference](https://creativecommons.org/publicdomain/zero/1.0/)).
6 changes: 5 additions & 1 deletion kintree/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ def reload_enable_flags():
global ENABLE_INVENTREE
global ENABLE_ALTERNATE
global UPDATE_INVENTREE
global CHECK_EXISTING

try:
ENABLE_KICAD = CONFIG_GENERAL.get('ENABLE_KICAD', False)
ENABLE_INVENTREE = CONFIG_GENERAL.get('ENABLE_INVENTREE', False)
ENABLE_ALTERNATE = CONFIG_GENERAL.get('ENABLE_ALTERNATE', False)
UPDATE_INVENTREE = CONFIG_GENERAL.get('UPDATE_INVENTREE', False)
CHECK_EXISTING = CONFIG_GENERAL.get('CHECK_EXISTING', True)
return True
except TypeError:
pass
Expand Down Expand Up @@ -370,7 +372,7 @@ def set_enable_flag(key: str, value: bool):
global CONFIG_GENERAL

user_settings = CONFIG_GENERAL
if key in ['kicad', 'inventree', 'alternate', 'update']:
if key in ['kicad', 'inventree', 'alternate', 'update', 'check_existing']:
if key == 'kicad':
user_settings['ENABLE_KICAD'] = value
elif key == 'inventree':
Expand All @@ -379,6 +381,8 @@ def set_enable_flag(key: str, value: bool):
user_settings['ENABLE_ALTERNATE'] = value
elif key == 'update':
user_settings['UPDATE_INVENTREE'] = value
elif key == 'check_existing':
user_settings['CHECK_EXISTING'] = value

# Save
config_interface.dump_file(
Expand Down
12 changes: 8 additions & 4 deletions kintree/database/inventree_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,16 @@ def get_category_parameters(category_id: int) -> list:
return parameter_templates


def get_part_number(part_id: int) -> str:
''' Get InvenTree part number from specified Part ID '''
def get_part_info(part_id: int) -> str:
''' Get InvenTree part info from specified Part ID '''
global inventree_api

part = Part(inventree_api, part_id)
return part.IPN
part_info = {'IPN': part.IPN}
attachment = part.getAttachments()
if attachment:
part_info['datasheet'] = f'{inventree_api.base_url.strip("/")}{attachment[0]["attachment"]}'
return part_info


def set_part_number(part_id: int, ipn: str) -> bool:
Expand Down Expand Up @@ -390,7 +394,7 @@ def upload_part_datasheet(datasheet_url: str, part_id: int) -> str:
try:
attachment = part.uploadAttachment(attachment=datasheet_location)
os.remove(datasheet_location)
return inventree_api.base_url.strip('/') + attachment['attachment']
return f'{inventree_api.base_url.strip("/")}{attachment["attachment"]}'
except Exception:
return ''
else:
Expand Down
42 changes: 24 additions & 18 deletions kintree/database/inventree_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def build_tree(tree, left_to_go, level) -> list:
elif left_to_go is None:
pass
return

if reload:
categories = inventree_api.get_categories()
category_data.update({'CATEGORIES': categories})
Expand Down Expand Up @@ -515,21 +515,22 @@ def inventree_create(part_info: dict, kicad=False, symbol=None, footprint=None,
if category_pk <= 0:
cprint(f'[ERROR]\tCategory ({category_tree}) does not exist in InvenTree', silent=settings.SILENT)
else:
# Check if part already exists
part_pk = inventree_api.is_new_part(category_pk, inventree_part)
# Part exists
if part_pk > 0:
cprint('[INFO]\tPart already exists, skipping.', silent=settings.SILENT)
ipn = inventree_api.get_part_number(part_pk)
if ipn:
# Update InvenTree part number
inventree_part['IPN'] = ipn
# Update InvenTree URL
inventree_part['inventree_url'] = f'{settings.PART_URL_ROOT}{inventree_part["IPN"]}/'
else:
inventree_part['inventree_url'] = f'{settings.PART_URL_ROOT}{part_pk}/'
if settings.CHECK_EXISTING:
# Check if part already exists
part_pk = inventree_api.is_new_part(category_pk, inventree_part)
# Part exists
if part_pk > 0:
cprint('[INFO]\tPart already exists, skipping.', silent=settings.SILENT)
info = inventree_api.get_part_info(part_pk)
if info:
# Update InvenTree part number
inventree_part = {**inventree_part, **info}
# Update InvenTree URL
inventree_part['inventree_url'] = f'{settings.PART_URL_ROOT}{inventree_part["IPN"]}/'
else:
inventree_part['inventree_url'] = f'{settings.PART_URL_ROOT}{part_pk}/'
# Part is new
else:
if not part_pk:
new_part = True
# Create a new Part
# Use the pk (primary-key) of the category
Expand Down Expand Up @@ -757,11 +758,16 @@ def inventree_create_alternate(part_info: dict, part_id='', part_ipn='', show_pr
manufacturer_mpn = part_info.get('manufacturer_part_number', '')
datasheet = part_info.get('datasheet', '')

attachment = part.getAttachments()
# if datasheet upload is enabled and no attachment present yet then upload
if settings.DATASHEET_UPLOAD and not part.getAttachments():
if settings.DATASHEET_UPLOAD and not attachment:
if datasheet:
inventree_api.upload_part_datasheet(part_id=part_pk,
datasheet_url=datasheet)
part_info['datasheet'] = inventree_api.upload_part_datasheet(
part_id=part_pk,
datasheet_url=datasheet)
# if an attachment is present, set it as the datasheet field
if attachment:
part_info['datasheet'] = f'{inventree_api.inventree_api.base_url.strip("/")}{attachment[0]["attachment"]}'

# Create manufacturer part
if manufacturer_name and manufacturer_mpn:
Expand Down
2 changes: 1 addition & 1 deletion kintree/gui/views/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def handle_transition(page: ft.Page, transition: bool, update_page=False, timeou
# Update
if update_page:
page.update()


def update_theme(page: ft.Page, mode='light', transition=False, compact=True):
# Color theme
Expand Down
102 changes: 90 additions & 12 deletions kintree/gui/views/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class PartSearchView(MainView):
label="Part Number",
dense=True,
hint_text="Part Number",
width=300,
width=250,
expand=True,
),
'supplier': ft.Dropdown(
Expand All @@ -268,25 +268,33 @@ class PartSearchView(MainView):
width=48,
tooltip="Submit",
),
'parameter_view': ft.Switch(
label='View Parameters',
disabled=True
),
'search_form': {},
'parameter_form': {},
}

def reset_view(self, e, ignore=['enable']):
hidden_fields = {
'searched_part_number': '',
'custom_part': None,
}
self.fields['parameter_form'] = {}
return super().reset_view(e, ignore=ignore, hidden=hidden_fields)

def enable_search_fields(self):
for form_field in self.fields['search_form'].values():
form_field.disabled = False
self.fields['parameter_view'].disabled = False
self.page.update()
return

def run_search(self, e):
# Reset view
self.reset_view(e, ignore=['part_number', 'supplier'])
self.switch_view()
# Validate form
if bool(self.fields['part_number'].value) != bool(self.fields['supplier'].value):
if not self.fields['part_number'].value:
Expand Down Expand Up @@ -323,23 +331,30 @@ def run_search(self, e):
supplier=supplier,
part_info=part_supplier_info,
)
if part_supplier_form:
for field_idx, field_name in enumerate(self.fields['search_form'].keys()):
# print(field_idx, field_name, get_default_search_keys()[field_idx], search_form_field[field_name])
try:
self.fields['search_form'][field_name].value = part_supplier_form.get(field_name, '')
except IndexError:
pass
# Enable editing
self.enable_search_fields()
# Stitch parameters
if part_supplier_info.get('parameters', None):
self.data['parameters'] = part_supplier_info['parameters']
for parameter, value in self.data['parameters'].items():
text_field = ft.TextField(
label=parameter,
value=value,
expand=True,
on_change=self.push_data,
)
self.fields['parameter_form'][parameter] = text_field
# and pricing
if part_supplier_info.get('pricing', None):
self.data['pricing'] = part_supplier_info['pricing']

if part_supplier_form:
for field_idx, field_name in enumerate(self.fields['search_form'].keys()):
# print(field_idx, field_name, get_default_search_keys()[field_idx], search_form_field[field_name])
try:
self.fields['search_form'][field_name].value = part_supplier_form.get(field_name, '')
except IndexError:
pass
# Enable editing
self.enable_search_fields()

# Add to data buffer
self.push_data()
self.page.splash.visible = False
Expand All @@ -364,6 +379,8 @@ def push_data(self, e=None):
}
for key, field in self.fields['search_form'].items():
self.data[key] = field.value
for key, field in self.fields['parameter_form'].items():
self.data['parameters'][key] = field.value
return super().push_data(e, hidden=hidden_fields)

def partial_update(self):
Expand All @@ -385,10 +402,34 @@ def update_suppliers(self):
# Control not added to page yet
pass

def switch_view(self, e=None):
# show parameters instead of part information
parameters_view = self.fields['parameter_view'].value
self.column.controls[0].content.controls = [
ft.Row(),
ft.Row(
controls=[
self.fields['part_number'],
self.fields['supplier'],
self.fields['search_button'],
self.fields['parameter_view'],
],
),
ft.Divider(),
]
if not parameters_view:
for field, text_field in self.fields['search_form'].items():
self.column.controls[0].content.controls.append(ft.Row([text_field]))
else:
for field, text_field in self.fields['parameter_form'].items():
self.column.controls[0].content.controls.append(ft.Row([text_field]))
self.page.update()

def build_column(self):
self.update_suppliers()
# Enable search method
self.fields['search_button'].on_click = self.run_search
self.fields['parameter_view'].on_change = self.switch_view

self.column = ft.Column(
controls=[
Expand All @@ -401,6 +442,7 @@ def build_column(self):
self.fields['part_number'],
self.fields['supplier'],
self.fields['search_button'],
self.fields['parameter_view'],
],
),
ft.Divider(),
Expand Down Expand Up @@ -480,6 +522,11 @@ class InventreeView(MainView):
'Create New Code': SwitchWithRefs(
label='Create New Code',
),
'check_existing': ft.Switch(
label='Check for existing Parts',
value=settings.CHECK_EXISTING if settings.ENABLE_INVENTREE else False,
disabled=not settings.ENABLE_INVENTREE,
),
'New Category Code': ft.TextField(
label='New Category Code',
width=GUI_PARAMS['textfield_width'] / 2 - 5,
Expand Down Expand Up @@ -586,6 +633,20 @@ def process_update(self, e, value=None):
settings.set_enable_flag('update', update_enabled)
self.push_data(e)

def process_button(self, e, value=None):
if value is not None:
button_enabled = value
else:
# Get switch value
button_enabled = False
if e.data.lower() == 'true':
button_enabled = True
if e.control.label == 'Update existing':
settings.set_enable_flag('update', button_enabled)
elif e.control.label == 'Check for existing Parts':
settings.set_enable_flag('check_existing', button_enabled)
self.push_data(e)

def process_category(self, e=None, label=None, value=None):
parent_category = None
if isinstance(self.fields['Category'].value, str):
Expand Down Expand Up @@ -654,6 +715,8 @@ def build_column(self):
self.fields['IPN: Category Code'].on_change = self.push_data
self.fields['Create New Code'].on_change = self.create_ipn_code
self.fields['New Category Code'].on_change = self.push_data
# Other Settings
self.fields['check_existing'].on_change = self.process_button
# Alternate fields
self.fields['alternate'].on_change = self.process_alternate
self.fields['Existing Part ID'].on_change = self.push_data
Expand Down Expand Up @@ -694,6 +757,11 @@ def build_column(self):
),
],
),
ft.Row(
[
self.fields['check_existing'],
],
),
],
),
],
Expand Down Expand Up @@ -1289,12 +1357,22 @@ def create_part(self, e=None):
progress.CREATE_PART_PROGRESS = 0
# Add part symbol to KiCAD
cprint('\n[MAIN]\tAdding part to KiCad', silent=settings.SILENT)
kicad_success, kicad_new_part = kicad_interface.inventree_to_kicad(
kicad_success, kicad_new_part, kicad_part_name = kicad_interface.inventree_to_kicad(
part_data=part_info,
library_path=symbol_library_path,
show_progress=self.fields['kicad_progress'],
)
# print(kicad_success, kicad_new_part)
# Update symbol name in InvenTree
if settings.ENABLE_INVENTREE and part_pk:
old_state = settings.UPDATE_INVENTREE
settings.UPDATE_INVENTREE = True
inventree_interface.inventree_process_parameters(
part_pk,
{'Symbol': f"{symbol_lib}:{kicad_part_name}"},
show_progress=self.fields['inventree_progress'],
)
settings.UPDATE_INVENTREE = old_state

# Complete add operation
if kicad_success:
Expand Down
Loading

0 comments on commit d5c1461

Please sign in to comment.