OpenPartsLibrary 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ class PartsLibraryCLI:
2
+ def __init__(self):
3
+ ''' CLI to be moved to its own object OpenPartsLibraryCLI in cli.py
4
+ '''
5
+ '''
6
+ command_history = []
7
+ while True:
8
+ os.system('cls')
9
+ print('************************************************************')
10
+ print('* OpenPartsLibrary *')
11
+ print('* Aleksander Sadowski, Nandana Gopala Krishnan (C) 2025 *')
12
+ print('************************************************************')
13
+ pl.display()
14
+ commands = 'add part', 'add supplier', 'modify part', 'modify supplier', 'remove part', 'remove supplier'
15
+ commands_str = ''
16
+ for command in commands:
17
+ commands_str = commands_str + '[' + str(command) + '] '
18
+ print('Commands: ' + commands_str)
19
+ print('Last commands:' + str([command for command in command_history][-5:]))
20
+ input_cmd = input('Enter command: ')
21
+ command_history.append(input_cmd)
22
+ if input_cmd in commands:
23
+ if input_cmd == 'add part':
24
+ pass
25
+ if input_cmd == 'add supplier':
26
+ pass
27
+ if input_cmd == 'modify part':
28
+ os.system('cls')
29
+ print('************************************************************')
30
+ print('* OpenPartsLibrary *')
31
+ print('* Aleksander Sadowski, Nandana Gopala Krishnan (C) 2025 *')
32
+ print('************************************************************')
33
+ pl.display_parts()
34
+ selected_part = int(input('Enter part id: '))
35
+ pass
36
+ if input_cmd == 'modify supplier':
37
+ os.system('cls')
38
+ print('************************************************************')
39
+ print('* OpenPartsLibrary *')
40
+ print('* Aleksander Sadowski, Nandana Gopala Krishnan (C) 2025 *')
41
+ print('************************************************************')
42
+ print()
43
+ pl.display_suppliers()
44
+ selected_part = int(input('Enter supplier id: '))
45
+ pass
46
+ if input_cmd == 'remove part':
47
+ pass
48
+ if input_cmd == 'remove supplier':
49
+ pass
50
+ '''
51
+ pass
openpartslibrary/db.py CHANGED
@@ -5,7 +5,11 @@ import pandas as pd
5
5
 
6
6
  from datetime import datetime
7
7
 
8
- from .models import Base, Part
8
+ from .models import Base, Part, Supplier, File, Component, ComponentComponent
9
+
10
+ import uuid
11
+
12
+ import os
9
13
 
10
14
 
11
15
  class PartsLibrary:
@@ -21,25 +25,96 @@ class PartsLibrary:
21
25
  self.session = self.session_factory()
22
26
 
23
27
  def display(self):
28
+ # Print the components table to the terminal
29
+ component_component_table = pd.read_sql_table(table_name="component_component", con=self.engine)
30
+ print('ComponentComponent:')
31
+ print('===================')
32
+ print(component_component_table)
33
+ print('')
34
+
35
+ # Print the components table to the terminal
36
+ components_table = pd.read_sql_table(table_name="components", con=self.engine)
37
+ print('Components:')
38
+ print('===========')
39
+ print(components_table)
40
+ print('')
41
+
42
+ # Print the parts table to the terminal
24
43
  part_table = pd.read_sql_table(table_name="parts", con=self.engine)
25
-
26
- pd.set_option('display.max_columns', 8)
27
- pd.set_option('display.width', 240)
28
-
44
+ print('Parts:')
45
+ print('======')
29
46
  print(part_table)
47
+ print('')
48
+
49
+ # Print the suppliers table to the terminal
50
+ supplier_table = pd.read_sql_table(table_name="suppliers", con=self.engine)
51
+ print('Suppliers:')
52
+ print('==========')
53
+ print(supplier_table)
54
+ print('')
55
+
56
+ # Print the files table to the terminal
57
+ files_table = pd.read_sql_table(table_name="files", con=self.engine)
58
+ print('Files:')
59
+ print('==========')
60
+ print(files_table)
61
+ print('')
30
62
 
31
63
  def display_reduced(self):
64
+ # Print the parts table to the terminal in reduced form
65
+ pass
66
+
67
+ def display_parts(self):
68
+ # Print the parts table to the terminal
32
69
  part_table = pd.read_sql_table(table_name="parts", con=self.engine)
33
- reduced_part_table = part_table[["id", "number", "name", "quantity", "mass", "lead_time", "supplier", "unit_price", "currency"]]
34
- pd.set_option('display.max_columns', 9)
35
- pd.set_option('display.width', 200)
36
- print(reduced_part_table)
37
-
70
+ print('Parts:')
71
+ print('======')
72
+ print(part_table)
73
+ print('')
74
+
75
+ def display_suppliers(self):
76
+ # Print the suppliers table to the terminal
77
+ supplier_table = pd.read_sql_table(table_name="suppliers", con=self.engine)
78
+ print('Suppliers:')
79
+ print('==========')
80
+ print(supplier_table)
81
+ print('')
82
+
83
+ def display_files(self):
84
+ # Print the files table to the terminal
85
+ files_table = pd.read_sql_table(table_name="files", con=self.engine)
86
+ print('Files:')
87
+ print('==========')
88
+ print(files_table)
89
+ print('')
38
90
 
39
91
  def delete_all(self):
92
+ print('[ INFO ] Clearing the parts library.')
93
+ self.session.query(ComponentComponent).delete()
94
+ self.session.query(Component).delete()
40
95
  self.session.query(Part).delete()
96
+ self.session.query(Supplier).delete()
97
+ self.session.query(File).delete()
41
98
  self.session.commit()
42
99
 
100
+ directory_to_empty = os.path.join(os.path.dirname(__file__), 'data', 'files')
101
+
102
+ for filename in os.listdir(directory_to_empty):
103
+ filepath = os.path.join(directory_to_empty, filename)
104
+ if os.path.isfile(filepath) and filename != "README.md":
105
+ os.remove(filepath)
106
+ print(f"[ INFO ] Deleted: {filename}")
107
+
108
+ def total_value(self):
109
+ from decimal import Decimal
110
+ all_parts = self.session.query(Part).all()
111
+
112
+ total_value = Decimal(0.0)
113
+ for part in all_parts:
114
+ total_value = Decimal(total_value) + (Decimal(part.unit_price) * part.quantity)
115
+
116
+ return total_value
117
+
43
118
  def create_parts_from_spreadsheet(self, file_path):
44
119
  df = pd.read_excel(file_path)
45
120
 
@@ -65,7 +140,6 @@ class PartsLibrary:
65
140
  attached_documents_reference=row.get("attached_documents_reference"),
66
141
  lead_time=row.get("lead_time"),
67
142
  make_or_buy=row.get("make_or_buy"),
68
- supplier=row.get("supplier"),
69
143
  manufacturer_number=row.get("manufacturer_number"),
70
144
  unit_price=row.get("unit_price"),
71
145
  currency=row.get("currency")
@@ -75,13 +149,70 @@ class PartsLibrary:
75
149
  self.session.add_all(parts)
76
150
  self.session.commit()
77
151
  print(f"Imported {len(parts)} parts successfully from {file_path}")
152
+
153
+ def create_suppliers_from_spreadsheet(self, file_path):
154
+ self.session.query(Supplier).delete()
155
+ self.session.commit()
78
156
 
79
- def total_value(self):
80
- from decimal import Decimal
81
- all_parts = self.session.query(Part).all()
157
+ df = pd.read_excel(file_path)
82
158
 
83
- total_value = Decimal(0.0)
84
- for part in all_parts:
85
- total_value = Decimal(total_value) + (Decimal(part.unit_price) * part.quantity)
159
+ suppliers = []
160
+ for _, row in df.iterrows():
161
+ supplier = Supplier(
162
+ uuid=row.get("uuid", str(uuid.uuid4())),
163
+ name=row["name"],
164
+ description=row.get("description", "No description"),
165
+ street=row.get("street"),
166
+ city=row.get("city"),
167
+ postal_code=row.get("postal_code"),
168
+ house_number=row.get("house_number"),
169
+ country=row.get("country")
170
+ )
171
+ suppliers.append(supplier)
86
172
 
87
- return total_value
173
+ self.session.add_all(suppliers)
174
+ self.session.commit()
175
+ print(f"Imported {len(suppliers)} suppliers successfully from {file_path}")
176
+
177
+ def display_suppliers_table(self):
178
+ from tabulate import tabulate
179
+ import textwrap
180
+ query="SELECT * FROM suppliers"
181
+ suppliers_table = pd.read_sql_query(sql=query, con=self.engine)
182
+ suppliers_table["house_number"] = suppliers_table["house_number"].astype(str)
183
+ suppliers_table["postal_code"] = suppliers_table["postal_code"].astype(str)
184
+ pd.set_option('display.max_columns', 7)
185
+ pd.set_option('display.width', 200)
186
+ print(tabulate(suppliers_table, headers='keys', tablefmt='github'))
187
+
188
+ def add_sample_suppliers(self):
189
+ from .models import Supplier
190
+ siemens = Supplier(
191
+ uuid=str(uuid.uuid4()),
192
+ name="Siemens AG",
193
+ description="Siemens AG is a global powerhouse focusing on the areas of electrification, automation, and digitalization. One of the world's largest producers of energy-efficient, resource-saving technologies",
194
+ street="Werner-von-Siemens-Straße",
195
+ house_number="1",
196
+ postal_code="80333",
197
+ city="Munich",
198
+ country="Germany",
199
+ date_created=datetime.utcnow(),
200
+ date_modified=datetime.utcnow()
201
+ )
202
+
203
+ kuka = Supplier(
204
+ uuid=str(uuid.uuid4()),
205
+ name="KUKA AG",
206
+ description="The KUKA Group is an internationally active automation group with revenue of approximately EUR 3.7 billion and approximately 15,000 employees. As one of the world's leading providers of intelligent, resource-efficient automation solutions, KUKA offers industrial robots, autonomous mobile robots (AMR) including controllers, software, and cloud-based digital services, as well as fully networked production systems for various industries and markets, such as automotive with a focus on e-mobility and batteries, electronics, metal and plastics, consumer goods, food, e-commerce, retail, and healthcare",
207
+ street="Zugspitzstraße",
208
+ house_number="140",
209
+ postal_code="86165",
210
+ city="Augsburg",
211
+ country="Germany",
212
+ date_created=datetime.utcnow(),
213
+ date_modified=datetime.utcnow()
214
+ )
215
+ self.session.add(siemens)
216
+ self.session.add(kuka)
217
+ self.session.commit()
218
+ print("Added sample suppliers: Siemens AG and KUKA AG")
@@ -1,17 +1,62 @@
1
- from sqlalchemy import Column, Integer, String, Float, DateTime, Numeric, Enum
2
- from sqlalchemy.orm import DeclarativeBase
1
+ from sqlalchemy import Column, Integer, String, Float, DateTime, Numeric, Enum, ForeignKey, UniqueConstraint
2
+ from sqlalchemy.orm import DeclarativeBase, relationship, backref
3
3
  from datetime import datetime
4
4
 
5
- import uuid
6
5
 
7
6
  class Base(DeclarativeBase):
8
7
  pass
9
8
 
9
+ class ComponentComponent(Base):
10
+ __tablename__ = 'component_component'
11
+
12
+ id = Column(Integer, primary_key=True)
13
+
14
+ parent_component_id = Column(Integer, ForeignKey("components.id"), nullable=False)
15
+ child_component_id = Column(Integer, ForeignKey("components.id"), nullable=False)
16
+
17
+ __table_args__ = (UniqueConstraint("parent_component_id", "child_component_id", name="uq_parent_child"),)
18
+
19
+ def __repr__(self):
20
+ return f"<ComponentComponent(id={self.id}, parent_component_id={self.parent_component_id}, child_component_id={self.child_component_id})>"
21
+
22
+ class Component(Base):
23
+ __tablename__ = 'components'
24
+
25
+ id = Column(Integer, primary_key=True)
26
+ uuid = Column(String(32), unique=True, nullable=False)
27
+ name = Column(String(200), nullable=False)
28
+
29
+ part = relationship('Part', back_populates='component', uselist=False)
30
+
31
+ # children: Components that this component is parent of
32
+ children = relationship(
33
+ "Component",
34
+ secondary="component_component",
35
+ primaryjoin=id == ComponentComponent.parent_component_id,
36
+ secondaryjoin=id == ComponentComponent.child_component_id,
37
+ backref=backref("parents", lazy="joined"),
38
+ lazy="joined",
39
+ )
40
+
41
+ class File(Base):
42
+ __tablename__ = 'files'
43
+
44
+ id = Column(Integer, primary_key=True)
45
+ uuid = Column(String(32), unique=True, nullable=False)
46
+ name = Column(String(200), nullable=False)
47
+ description = Column(String(1000))
48
+ date_created = Column(DateTime, default=datetime.utcnow)
49
+ date_modified = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
50
+
51
+ part_id = Column(ForeignKey('parts.id'))
52
+ part = relationship('Part', back_populates='cad_reference')
53
+
54
+
10
55
  class Part(Base):
11
56
  __tablename__ = 'parts'
12
57
 
13
58
  id = Column(Integer, primary_key=True)
14
- uuid = Column(String(32), unique=True, nullable=False, default=str(uuid.uuid4()))
59
+ uuid = Column(String(32), unique=True, nullable=False)
15
60
  number = Column(String(50), nullable=False)
16
61
  name = Column(String(200), nullable=False)
17
62
  description = Column(String(1000), default="No description")
@@ -26,46 +71,83 @@ class Part(Base):
26
71
  dimension_y = Column(Float)
27
72
  dimension_z = Column(Float)
28
73
  quantity = Column(Integer, default=0)
29
- cad_reference = Column(String(200))
30
74
  attached_documents_reference = Column(String(200))
31
75
  lead_time = Column(Integer)
32
76
  make_or_buy = Column(Enum('make', 'buy', name='make_or_buy_enum'))
33
- supplier = Column(String(100))
34
77
  manufacturer_number = Column(String(100))
35
78
  unit_price = Column(Numeric(10, 2))
36
79
  currency = Column(String(3))
37
80
 
81
+ cad_reference = relationship('File', back_populates='part', uselist=False)
82
+
83
+ supplier_id = Column(ForeignKey('suppliers.id'))
84
+ supplier = relationship('Supplier', back_populates='parts')
85
+
86
+ component_id = Column(ForeignKey('components.id'))
87
+ component = relationship('Component', back_populates='part')
88
+
38
89
  def __repr__(self):
39
90
  return f"<Part(id={self.id}, number={self.number}, name={self.name})>"
40
91
 
41
92
  def to_dict(self):
42
93
  return {column.name: getattr(self, column.name) for column in self.__table__.columns}
43
-
44
-
94
+
45
95
  class Supplier(Base):
46
96
  __tablename__ = 'suppliers'
47
97
 
48
98
  id = Column(Integer, primary_key=True)
49
99
  uuid = Column(String(32), unique=True, nullable=False)
50
100
  name = Column(String(200), nullable=False)
51
- description = Column(String(1000))
101
+ description = Column(String(1000), default="No description")
102
+ street = Column(String(200))
103
+ house_number = Column(String(20))
104
+ postal_code = Column(String(20))
105
+ city = Column(String(100))
106
+ country = Column(String(100))
52
107
  date_created = Column(DateTime, default=datetime.utcnow)
53
108
  date_modified = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
54
109
 
55
- class File(Base):
56
- __tablename__ = 'files'
110
+ parts = relationship(Part)
111
+
112
+ def to_dict(self):
113
+ return {column.name: getattr(self, column.name) for column in self.__table__.columns}
114
+
115
+ class Adress(Base):
116
+ __tablename__ = 'adresses'
57
117
 
58
118
  id = Column(Integer, primary_key=True)
59
119
  uuid = Column(String(32), unique=True, nullable=False)
60
- name = Column(String(200), nullable=False)
61
- description = Column(String(1000))
62
- date_created = Column(DateTime, default=datetime.utcnow)
63
- date_modified = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
120
+ street = Column(String(200))
121
+ house_number = Column(String(20))
122
+ postal_code = Column(String(20))
123
+ city = Column(String(100))
124
+ country = Column(String(100))
125
+
126
+
127
+ '''
128
+ Relationship tables
129
+ '''
64
130
 
65
- class NumberRange(Base):
66
- __tablename__ = 'number_ranges'
131
+ class PartSupplier(Base):
132
+ __tablename__ = 'part_supplier'
67
133
 
68
134
  id = Column(Integer, primary_key=True)
69
- name = Column(String(200), nullable=False)
70
- description = Column(String(1000))
71
- date_created = Column(DateTime, default=datetime.utcnow)
135
+
136
+ class PartFile(Base):
137
+ __tablename__ = 'part_file'
138
+
139
+ id = Column(Integer, primary_key=True)
140
+
141
+ class SupplierAdress(Base):
142
+ __tablename__ = 'supplier_adress'
143
+
144
+ id = Column(Integer, primary_key=True)
145
+
146
+ class SupplierFile(Base):
147
+ __tablename__ = 'supplier_file'
148
+
149
+ id = Column(Integer, primary_key=True)
150
+
151
+
152
+
153
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenPartsLibrary
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Python library for creating a database of hardware components for manufacturing
5
5
  Home-page: https://github.com/alekssadowski95/OpenPartsLibrary
6
6
  Author: Aleksander Sadowski
@@ -130,6 +130,8 @@ Creating parts from a parts list in a Excel-spreadsheet (*.xlsx). Take note, tha
130
130
  ```python
131
131
  pl.create_parts_from_spreadsheet('C:/Users/Work/Documents/Github/OpenPartsLibrary/openpartslibrary/sample/parts_data_sample.xlsx')
132
132
  ```
133
+ ## Database structure
134
+ <img src="./openpartslibrary/images/Database-structure-openpartslibrary.png" width="100%" alt="OpenPartsLibrary database structure"></img>
133
135
 
134
136
  ## Part schema
135
137
  This table outlines the `Part` properties used in the OpenPartsLibrary.
@@ -0,0 +1,9 @@
1
+ openpartslibrary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ openpartslibrary/cli.py,sha256=4QZi-Gub33jWsUPnVmIv2N_iq8fd9WTGePscLwMujHY,2614
3
+ openpartslibrary/db.py,sha256=pW2QtaluIEqVUMDU3kj7EUSE-WzfH9Q3BtiUdy1Fnjo,8572
4
+ openpartslibrary/models.py,sha256=tadjKKi-ChLaqwYM96biZ2DiRSiaszrX5e76dhFHKGQ,5163
5
+ openpartslibrary-0.1.9.dist-info/licenses/LICENSE,sha256=d0dlaVkR5u5bZVrTL-FmUyCoiu3o87Vc-MF1X-NYSXA,1076
6
+ openpartslibrary-0.1.9.dist-info/METADATA,sha256=SkqvSZ2uM2qdgmBwFl50TUyjPiGH5n7_W_pXlBslXTA,7814
7
+ openpartslibrary-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ openpartslibrary-0.1.9.dist-info/top_level.txt,sha256=-26qZ2PdH7UxxjYQmnXDViKaFDd5Mb5e_Xp__WDTdKQ,17
9
+ openpartslibrary-0.1.9.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- openpartslibrary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- openpartslibrary/db.py,sha256=FYyQuFdJZzERniBOyWQNCuRQ_v8ZrzgXhjHUbKY1tTk,3157
3
- openpartslibrary/models.py,sha256=yVnyV0f7hSoTG30f1UVugeLNKLkk4osDEVUU2zIUQYk,2649
4
- openpartslibrary-0.1.7.dist-info/licenses/LICENSE,sha256=d0dlaVkR5u5bZVrTL-FmUyCoiu3o87Vc-MF1X-NYSXA,1076
5
- openpartslibrary-0.1.7.dist-info/METADATA,sha256=pO47xQvLlmLRkLqxYhGycAWGKX2hw3LtogeqTbLNs7U,7653
6
- openpartslibrary-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- openpartslibrary-0.1.7.dist-info/top_level.txt,sha256=-26qZ2PdH7UxxjYQmnXDViKaFDd5Mb5e_Xp__WDTdKQ,17
8
- openpartslibrary-0.1.7.dist-info/RECORD,,