django-bom 1.262__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.

Potentially problematic release.


This version of django-bom might be problematic. Click here for more details.

Files changed (191) hide show
  1. bom/__init__.py +1 -0
  2. bom/admin.py +207 -0
  3. bom/apps.py +8 -0
  4. bom/auth_backends.py +47 -0
  5. bom/base_classes.py +31 -0
  6. bom/constants.py +217 -0
  7. bom/context_processors.py +9 -0
  8. bom/csv_headers.py +252 -0
  9. bom/decorators.py +32 -0
  10. bom/form_fields.py +59 -0
  11. bom/forms.py +1328 -0
  12. bom/helpers.py +367 -0
  13. bom/local_settings.py +35 -0
  14. bom/migrations/0001_initial.py +135 -0
  15. bom/migrations/0002_auto_20180908_2151.py +24 -0
  16. bom/migrations/0003_sellerpart_data_source.py +18 -0
  17. bom/migrations/0004_auto_20180911_0011.py +18 -0
  18. bom/migrations/0005_auto_20181007_1934.py +56 -0
  19. bom/migrations/0006_auto_20181007_1949.py +41 -0
  20. bom/migrations/0007_auto_20181009_0256.py +19 -0
  21. bom/migrations/0008_auto_20181030_0427.py +19 -0
  22. bom/migrations/0009_subpart_reference.py +18 -0
  23. bom/migrations/0010_auto_20181202_0733.py +23 -0
  24. bom/migrations/0011_auto_20181202_2113.py +22 -0
  25. bom/migrations/0012_partchangehistory.py +30 -0
  26. bom/migrations/0013_auto_20190222_1631.py +19 -0
  27. bom/migrations/0014_auto_20190223_2353.py +18 -0
  28. bom/migrations/0015_auto_20190303_1915.py +136 -0
  29. bom/migrations/0016_auto_20190405_2308.py +58 -0
  30. bom/migrations/0017_auto_20190616_1912.py +19 -0
  31. bom/migrations/0018_auto_20190616_2143.py +24 -0
  32. bom/migrations/0019_auto_20190624_1246.py +45 -0
  33. bom/migrations/0020_auto_20190627_0207.py +38 -0
  34. bom/migrations/0021_auto_20190627_0428.py +23 -0
  35. bom/migrations/0022_auto_20190811_2140.py +35 -0
  36. bom/migrations/0023_auto_20191205_2351.py +255 -0
  37. bom/migrations/0024_auto_20191214_1342.py +89 -0
  38. bom/migrations/0025_auto_20191221_1907.py +38 -0
  39. bom/migrations/0026_auto_20191222_2258.py +22 -0
  40. bom/migrations/0027_auto_20191222_2347.py +17 -0
  41. bom/migrations/0028_partrevision_displayable_synopsis.py +74 -0
  42. bom/migrations/0029_auto_20191231_1630.py +23 -0
  43. bom/migrations/0030_auto_20200101_2253.py +22 -0
  44. bom/migrations/0031_auto_20200104_1352.py +38 -0
  45. bom/migrations/0032_auto_20200126_1806.py +27 -0
  46. bom/migrations/0033_auto_20200203_0618.py +29 -0
  47. bom/migrations/0034_auto_20200222_0359.py +30 -0
  48. bom/migrations/0035_auto_20200303_0111.py +34 -0
  49. bom/migrations/0036_auto_20200303_0538.py +17 -0
  50. bom/migrations/0037_auto_20200405_1642.py +44 -0
  51. bom/migrations/0038_auto_20200422_0504.py +19 -0
  52. bom/migrations/0039_auto_20200929_2315.py +41 -0
  53. bom/migrations/0040_alter_organization_currency.py +19 -0
  54. bom/migrations/0041_organization_subscription_quantity.py +18 -0
  55. bom/migrations/0042_auto_20210720_2137.py +23 -0
  56. bom/migrations/0043_auto_20211123_0157.py +24 -0
  57. bom/migrations/0044_auto_20220831_1241.py +23 -0
  58. bom/migrations/0045_sellerpart_link.py +18 -0
  59. bom/migrations/0046_alter_sellerpart_unique_together.py +17 -0
  60. bom/migrations/0047_sellerpart_seller_part_number.py +18 -0
  61. bom/migrations/0048_rename_part_organization_number_class_bom_part_organiz_b333d6_idx_and_more.py +1017 -0
  62. bom/migrations/0049_alter_assembly_id_alter_assemblysubparts_id_and_more.py +99 -0
  63. bom/migrations/0050_alter_organization_options.py +17 -0
  64. bom/migrations/0051_alter_manufacturer_organization_and_more.py +41 -0
  65. bom/migrations/0052_remove_partrevision_attribute_and_more.py +584 -0
  66. bom/migrations/__init__.py +0 -0
  67. bom/models.py +886 -0
  68. bom/part_bom.py +192 -0
  69. bom/settings.py +262 -0
  70. bom/static/bom/css/dashboard.css +17 -0
  71. bom/static/bom/css/jquery.treetable.css +28 -0
  72. bom/static/bom/css/materialize.min.css +13 -0
  73. bom/static/bom/css/part-info.css +15 -0
  74. bom/static/bom/css/style.css +482 -0
  75. bom/static/bom/css/tablesorter-theme.materialize.css +176 -0
  76. bom/static/bom/css/treetable-theme.css +42 -0
  77. bom/static/bom/doc/sample_part_classes.csv +38 -0
  78. bom/static/bom/doc/test_bom.csv +6 -0
  79. bom/static/bom/doc/test_bom_5_intelligent.csv +4 -0
  80. bom/static/bom/doc/test_full_bom.csv +37 -0
  81. bom/static/bom/doc/test_new_parts.csv +5 -0
  82. bom/static/bom/doc/test_new_parts_5_intelligent.csv +5 -0
  83. bom/static/bom/img/_ionicons_svg_md-arrow-dropdown.svg +1 -0
  84. bom/static/bom/img/_ionicons_svg_md-arrow-dropright.svg +1 -0
  85. bom/static/bom/img/favicon.ico +0 -0
  86. bom/static/bom/img/google/web/1x/btn_google_signin_dark_disabled_web.png +0 -0
  87. bom/static/bom/img/google/web/1x/btn_google_signin_dark_focus_web.png +0 -0
  88. bom/static/bom/img/google/web/1x/btn_google_signin_dark_normal_web.png +0 -0
  89. bom/static/bom/img/google/web/1x/btn_google_signin_dark_pressed_web.png +0 -0
  90. bom/static/bom/img/google/web/1x/btn_google_signin_light_disabled_web.png +0 -0
  91. bom/static/bom/img/google/web/1x/btn_google_signin_light_focus_web.png +0 -0
  92. bom/static/bom/img/google/web/1x/btn_google_signin_light_normal_web.png +0 -0
  93. bom/static/bom/img/google/web/1x/btn_google_signin_light_pressed_web.png +0 -0
  94. bom/static/bom/img/google/web/2x/btn_google_signin_dark_disabled_web@2x.png +0 -0
  95. bom/static/bom/img/google/web/2x/btn_google_signin_dark_focus_web@2x.png +0 -0
  96. bom/static/bom/img/google/web/2x/btn_google_signin_dark_normal_web@2x.png +0 -0
  97. bom/static/bom/img/google/web/2x/btn_google_signin_dark_pressed_web@2x.png +0 -0
  98. bom/static/bom/img/google/web/2x/btn_google_signin_light_disabled_web@2x.png +0 -0
  99. bom/static/bom/img/google/web/2x/btn_google_signin_light_focus_web@2x.png +0 -0
  100. bom/static/bom/img/google/web/2x/btn_google_signin_light_normal_web@2x.png +0 -0
  101. bom/static/bom/img/google/web/2x/btn_google_signin_light_pressed_web@2x.png +0 -0
  102. bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.eps +814 -0
  103. bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.svg +24 -0
  104. bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.eps +1866 -0
  105. bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.svg +51 -0
  106. bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.eps +1031 -0
  107. bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.svg +50 -0
  108. bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.eps +1031 -0
  109. bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.svg +50 -0
  110. bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.eps +814 -0
  111. bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.svg +24 -0
  112. bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.eps +1837 -0
  113. bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.svg +44 -0
  114. bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.eps +1002 -0
  115. bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.svg +43 -0
  116. bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.eps +1002 -0
  117. bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.svg +43 -0
  118. bom/static/bom/img/google_drive_logo.svg +1 -0
  119. bom/static/bom/img/indabom.png +0 -0
  120. bom/static/bom/img/mouser.png +0 -0
  121. bom/static/bom/img/octopart_blue.svg +19 -0
  122. bom/static/bom/js/formset-handler.js +65 -0
  123. bom/static/bom/js/jquery-3.4.1.min.js +2 -0
  124. bom/static/bom/js/jquery.ba-floatingscrollbar.min.js +10 -0
  125. bom/static/bom/js/jquery.treetable.js +629 -0
  126. bom/static/bom/js/materialize.min.js +6 -0
  127. bom/templates/bom/account-delete.html +23 -0
  128. bom/templates/bom/add-manufacturer-part.html +66 -0
  129. bom/templates/bom/add-sellerpart.html +93 -0
  130. bom/templates/bom/base-menu.html +16 -0
  131. bom/templates/bom/base.html +129 -0
  132. bom/templates/bom/bom-action-btn.html +23 -0
  133. bom/templates/bom/bom-action-table.html +57 -0
  134. bom/templates/bom/bom-base-menu.html +6 -0
  135. bom/templates/bom/bom-base.html +24 -0
  136. bom/templates/bom/bom-form-modal.html +36 -0
  137. bom/templates/bom/bom-form.html +30 -0
  138. bom/templates/bom/bom-modal-add-users.html +49 -0
  139. bom/templates/bom/bom-signup.html +12 -0
  140. bom/templates/bom/components/bom-flat.html +131 -0
  141. bom/templates/bom/components/bom-indented.html +237 -0
  142. bom/templates/bom/components/manufacturer-part-list.html +270 -0
  143. bom/templates/bom/components/seller-part-list.html +62 -0
  144. bom/templates/bom/create-part.html +65 -0
  145. bom/templates/bom/dashboard-menu.html +15 -0
  146. bom/templates/bom/dashboard.html +303 -0
  147. bom/templates/bom/edit-manufacturer-part.html +72 -0
  148. bom/templates/bom/edit-part-class.html +120 -0
  149. bom/templates/bom/edit-part.html +67 -0
  150. bom/templates/bom/edit-quantity-of-measure.html +119 -0
  151. bom/templates/bom/edit-user-meta.html +70 -0
  152. bom/templates/bom/help.html +1356 -0
  153. bom/templates/bom/manufacturer-info.html +82 -0
  154. bom/templates/bom/manufacturers.html +97 -0
  155. bom/templates/bom/nothing-to-see.html +15 -0
  156. bom/templates/bom/organization-create.html +135 -0
  157. bom/templates/bom/part-info.html +448 -0
  158. bom/templates/bom/part-revision-display.html +50 -0
  159. bom/templates/bom/part-revision-edit.html +39 -0
  160. bom/templates/bom/part-revision-manage-bom.html +115 -0
  161. bom/templates/bom/part-revision-new.html +57 -0
  162. bom/templates/bom/part-revision-release.html +41 -0
  163. bom/templates/bom/search-help.html +101 -0
  164. bom/templates/bom/seller-info.html +82 -0
  165. bom/templates/bom/sellers.html +97 -0
  166. bom/templates/bom/settings.html +734 -0
  167. bom/templates/bom/signup.html +28 -0
  168. bom/templates/bom/subscription_panel.html +16 -0
  169. bom/templates/bom/table_of_contents.html +47 -0
  170. bom/templates/bom/upload-bom.html +111 -0
  171. bom/templates/bom/upload-parts-help.html +103 -0
  172. bom/templates/bom/upload-parts.html +50 -0
  173. bom/templates/registration/login.html +39 -0
  174. bom/tests.py +1592 -0
  175. bom/third_party_apis/__init__.py +0 -0
  176. bom/third_party_apis/base_api.py +51 -0
  177. bom/third_party_apis/google_drive.py +166 -0
  178. bom/third_party_apis/mouser.py +132 -0
  179. bom/third_party_apis/test_apis.py +24 -0
  180. bom/urls.py +100 -0
  181. bom/utils.py +228 -0
  182. bom/validators.py +23 -0
  183. bom/views/__init__.py +0 -0
  184. bom/views/json_views.py +55 -0
  185. bom/views/views.py +1773 -0
  186. bom/wsgi.py +16 -0
  187. django_bom-1.262.dist-info/METADATA +206 -0
  188. django_bom-1.262.dist-info/RECORD +191 -0
  189. django_bom-1.262.dist-info/WHEEL +5 -0
  190. django_bom-1.262.dist-info/licenses/LICENSE +674 -0
  191. django_bom-1.262.dist-info/top_level.txt +1 -0
bom/utils.py ADDED
@@ -0,0 +1,228 @@
1
+ # This file is to have no project dependencies
2
+
3
+
4
+ def increment_char(c):
5
+ """
6
+ Increment an uppercase character, returning 'A' if 'Z' is given
7
+ """
8
+ return chr(ord(c) + 1) if c != 'Z' else 'A'
9
+
10
+
11
+ def increment_str(s):
12
+ lpart = s.rstrip('Z')
13
+ num_replacements = len(s) - len(lpart)
14
+ new_s = lpart[:-1] + increment_char(lpart[-1]) if lpart else 'A'
15
+ new_s += 'A' * num_replacements
16
+ return new_s
17
+
18
+
19
+ # The following function is based upon code from Jeff Atwood, see:
20
+ #
21
+ # https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
22
+ #
23
+ # Code has been adapted for for use as sort function for Python sorted(). Enables sorting an
24
+ # iterable whose items are strings represented by a mix of alphanumeric characters. For the
25
+ # default sort for {'R14', 'R5'} is:
26
+ #
27
+ # R14 R5
28
+ #
29
+ # but with prep_for_sorting_nicely the sort will be what is more naturally expected:
30
+ #
31
+ # R5 R14
32
+ #
33
+
34
+ import re
35
+
36
+
37
+ def prep_for_sorting_nicely(item):
38
+ convert = lambda text: int(text) if text.isdigit() else text
39
+ alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
40
+ return alphanum_key(item)
41
+
42
+
43
+ # Convert a string with delimited fields into a list of fields. Delimiters are comma,
44
+ # semi-colon, colon, tab, or blank space. Fields may contain any printable character.
45
+ def listify_string(st):
46
+ if st is None:
47
+ return []
48
+ ss = re.split(' |:|;|,|\t|\n', st)
49
+ split_st = []
50
+ for s in ss:
51
+ s_strip = s.strip()
52
+ if len(s_strip) != 0:
53
+ split_st.append(s_strip)
54
+ return split_st
55
+
56
+
57
+ # Convert a list of items into a comma-separated string without any surrounding brackets,
58
+ # for example:
59
+ #
60
+ # list = [1, 2, 3, 4]
61
+ #
62
+ # becomes '1, 2, 3, 4'
63
+ #
64
+ # as compared to str(list) which
65
+ #
66
+ # becomes '[1, 2, 3 4]'
67
+ def stringify_list(li):
68
+ return ', '.join(str(x) for x in li)
69
+
70
+
71
+ # Check a string reference designator for duplicates as compared to a running set of
72
+ # reference already seen. A reference designator may contain multiple delimited references,
73
+ # so need to check the new designator for duplicates before checking against references
74
+ # already seen. All duplicate references are added to the set duplicate_refs.
75
+ def check_references_for_duplicates(new_refs, seen_refs, duplicate_refs):
76
+ new_refs_list = listify_string(new_refs)
77
+ new_refs_set = set()
78
+ for r in new_refs_list:
79
+ if r in new_refs_set:
80
+ duplicate_refs.add(r)
81
+ else:
82
+ new_refs_set.add(r)
83
+ if r in seen_refs:
84
+ duplicate_refs.add(r)
85
+ seen_refs.add(r)
86
+
87
+
88
+ # Given a string that represents a number, returns a string that eliminates trailing zeros
89
+ # and decimal point if any from the input. For example, 25.000 become 25. If the input
90
+ # string that does not represent a number then the original string is returned.
91
+ def strip_trailing_zeros(num):
92
+ found = False
93
+ for c in num:
94
+ if c.isdigit():
95
+ found = True
96
+ elif c not in ['-', '+', '.']:
97
+ found = False
98
+ break
99
+ return ('%f' % float(num)).rstrip('0').rstrip('.') if found else num
100
+
101
+
102
+ # Input a dict with a list of key options, return the value if it exists, else None
103
+ def get_from_dict(input_dict, key_options):
104
+ for key in key_options:
105
+ val = input_dict.get(key, None)
106
+ if val:
107
+ return val
108
+ return None
109
+
110
+ # via https://github.com/hayj/SystemTools/blob/master/systemtools/number.py
111
+ def parse_number(text):
112
+ """
113
+ Return the first number in the given text for any locale.
114
+ TODO we actually don't take into account spaces for only
115
+ 3-digited numbers (like "1 000") so, for now, "1 0" is 10.
116
+ TODO parse cases like "125,000.1,0.2" (125000.1).
117
+ :example:
118
+ >>> parseNumber("a 125,00 €")
119
+ 125
120
+ >>> parseNumber("100.000,000")
121
+ 100000
122
+ >>> parseNumber("100 000,000")
123
+ 100000
124
+ >>> parseNumber("100,000,000")
125
+ 100000000
126
+ >>> parseNumber("100 000 000")
127
+ 100000000
128
+ >>> parseNumber("100.001 001")
129
+ 100.001
130
+ >>> parseNumber("$.3")
131
+ 0.3
132
+ >>> parseNumber(".003")
133
+ 0.003
134
+ >>> parseNumber(".003 55")
135
+ 0.003
136
+ >>> parseNumber("3 005")
137
+ 3005
138
+ >>> parseNumber("1.190,00 €")
139
+ 1190
140
+ >>> parseNumber("1190,00 €")
141
+ 1190
142
+ >>> parseNumber("1,190.00 €")
143
+ 1190
144
+ >>> parseNumber("$1190.00")
145
+ 1190
146
+ >>> parseNumber("$1 190.99")
147
+ 1190.99
148
+ >>> parseNumber("$-1 190.99")
149
+ -1190.99
150
+ >>> parseNumber("1 000 000.3")
151
+ 1000000.3
152
+ >>> parseNumber('-151.744122')
153
+ -151.744122
154
+ >>> parseNumber('-1')
155
+ -1
156
+ >>> parseNumber("1 0002,1.2")
157
+ 10002.1
158
+ >>> parseNumber("")
159
+ >>> parseNumber(None)
160
+ >>> parseNumber(1)
161
+ 1
162
+ >>> parseNumber(1.1)
163
+ 1.1
164
+ >>> parseNumber("rrr1,.2o")
165
+ 1
166
+ >>> parseNumber("rrr1rrr")
167
+ 1
168
+ >>> parseNumber("rrr ,.o")
169
+ """
170
+ try:
171
+ # First we return None if we don't have something in the text:
172
+ if text is None:
173
+ return None
174
+ if isinstance(text, int) or isinstance(text, float):
175
+ return text
176
+ text = text.strip()
177
+ if text == "":
178
+ return None
179
+ # Next we get the first "[0-9,. ]+":
180
+ n = re.search("-?[0-9]*([,. ]?[0-9]+)+", text).group(0)
181
+ n = n.strip()
182
+ if not re.match(".*[0-9]+.*", text):
183
+ return None
184
+ # Then we cut to keep only 2 symbols:
185
+ while " " in n and "," in n and "." in n:
186
+ index = max(n.rfind(','), n.rfind(' '), n.rfind('.'))
187
+ n = n[0:index]
188
+ n = n.strip()
189
+ # We count the number of symbols:
190
+ symbolsCount = 0
191
+ for current in [" ", ",", "."]:
192
+ if current in n:
193
+ symbolsCount += 1
194
+ # If we don't have any symbol, we do nothing:
195
+ if symbolsCount == 0:
196
+ pass
197
+ # With one symbol:
198
+ elif symbolsCount == 1:
199
+ # If this is a space, we just remove all:
200
+ if " " in n:
201
+ n = n.replace(" ", "")
202
+ # Else we set it as a "." if one occurence, or remove it:
203
+ else:
204
+ theSymbol = "," if "," in n else "."
205
+ if n.count(theSymbol) > 1:
206
+ n = n.replace(theSymbol, "")
207
+ else:
208
+ n = n.replace(theSymbol, ".")
209
+ else:
210
+ # Now replace symbols so the right symbol is "." and all left are "":
211
+ rightSymbolIndex = max(n.rfind(','), n.rfind(' '), n.rfind('.'))
212
+ rightSymbol = n[rightSymbolIndex:rightSymbolIndex+1]
213
+ if rightSymbol == " ":
214
+ return parseNumber(n.replace(" ", "_"))
215
+ n = n.replace(rightSymbol, "R")
216
+ leftSymbolIndex = max(n.rfind(','), n.rfind(' '), n.rfind('.'))
217
+ leftSymbol = n[leftSymbolIndex:leftSymbolIndex+1]
218
+ n = n.replace(leftSymbol, "L")
219
+ n = n.replace("L", "")
220
+ n = n.replace("R", ".")
221
+ # And we cast the text to float or int:
222
+ n = float(n)
223
+ if n.is_integer():
224
+ return int(n)
225
+ else:
226
+ return n
227
+ except: pass
228
+ return None
bom/validators.py ADDED
@@ -0,0 +1,23 @@
1
+ from django.core.validators import MaxValueValidator, RegexValidator
2
+ from django.core.exceptions import ValidationError
3
+ from django.utils.translation import gettext_lazy as _
4
+ import re
5
+
6
+ alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', 'Only alphanumeric characters are allowed.')
7
+ numeric = RegexValidator(r'^[0-9]*$', 'Only numeric characters are allowed.')
8
+ decimal = RegexValidator(r'^[0-9]\d*(\.\d+)?$', 'Only decimal number characters are allowed.')
9
+
10
+
11
+ def validate_pct(value):
12
+ if value is not None and len(value) > 1:
13
+ try:
14
+ if value.endswith("%"):
15
+ return float(value[:-1]) / 100
16
+ else:
17
+ return float(value)
18
+ except (TypeError, ValueError):
19
+ raise ValidationError(
20
+ _('%(value)s is not a valid pct'),
21
+ params={'value': value},
22
+ )
23
+ return None
bom/views/__init__.py ADDED
File without changes
@@ -0,0 +1,55 @@
1
+ from django.contrib.auth.decorators import login_required
2
+ from django.core.cache import cache
3
+ from django.http import JsonResponse
4
+ from django.shortcuts import get_object_or_404
5
+ from django.utils.decorators import method_decorator
6
+ from django.views import View
7
+
8
+ from bom.models import PartRevision
9
+ from bom.third_party_apis.base_api import BaseApiError
10
+ from bom.third_party_apis.mouser import Mouser
11
+
12
+
13
+ class BomJsonResponse(View):
14
+ response = {'errors': [], 'content': {}}
15
+
16
+
17
+ @method_decorator(login_required, name='dispatch')
18
+ class MouserPartMatchBOM(BomJsonResponse):
19
+ def get(self, request, part_revision_id):
20
+ part_revision = get_object_or_404(PartRevision, pk=part_revision_id) # get all of the pricing for manufacturer parts, marked with mouser in this part
21
+ user = request.user
22
+ profile = user.bom_profile()
23
+ organization = profile.organization
24
+
25
+ # Goal is to search mouser for anything that we want from mouser, then update the part revision in the bom with that
26
+ # To do that we can just get the manufacturer parts in this BOM
27
+ part = part_revision.part
28
+ qty_cache_key = str(part.id) + '_qty'
29
+ assy_quantity = cache.get(qty_cache_key, 100)
30
+
31
+ flat_bom = part_revision.flat(assy_quantity)
32
+
33
+ mouser = Mouser()
34
+ manufacturer_parts = flat_bom.mouser_parts()
35
+ # Quantity is the same on flat and indented bom WRT sourcing, so we should only need to look up by part revision, or even part
36
+ for bom_id, mp in manufacturer_parts.items():
37
+ bom_part = flat_bom.parts[bom_id]
38
+ bom_part_quantity = bom_part.total_extended_quantity
39
+
40
+ try:
41
+ part_seller_info = mouser.search_and_match(mp, quantity=bom_part_quantity, currency=organization.currency)
42
+ except BaseApiError as err:
43
+ self.response['errors'].append(str(err))
44
+ continue
45
+
46
+ try:
47
+ bom_part.seller_part = part_seller_info['optimal_seller_part']
48
+ bom_part.api_info = part_seller_info['mouser_parts'][0]
49
+ except (KeyError, IndexError):
50
+ continue
51
+
52
+ flat_bom.update()
53
+ flat_bom_dict = flat_bom.as_dict()
54
+ self.response['content'].update({'flat_bom': flat_bom_dict})
55
+ return JsonResponse(self.response)