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.
- bom/__init__.py +1 -0
- bom/admin.py +207 -0
- bom/apps.py +8 -0
- bom/auth_backends.py +47 -0
- bom/base_classes.py +31 -0
- bom/constants.py +217 -0
- bom/context_processors.py +9 -0
- bom/csv_headers.py +252 -0
- bom/decorators.py +32 -0
- bom/form_fields.py +59 -0
- bom/forms.py +1328 -0
- bom/helpers.py +367 -0
- bom/local_settings.py +35 -0
- bom/migrations/0001_initial.py +135 -0
- bom/migrations/0002_auto_20180908_2151.py +24 -0
- bom/migrations/0003_sellerpart_data_source.py +18 -0
- bom/migrations/0004_auto_20180911_0011.py +18 -0
- bom/migrations/0005_auto_20181007_1934.py +56 -0
- bom/migrations/0006_auto_20181007_1949.py +41 -0
- bom/migrations/0007_auto_20181009_0256.py +19 -0
- bom/migrations/0008_auto_20181030_0427.py +19 -0
- bom/migrations/0009_subpart_reference.py +18 -0
- bom/migrations/0010_auto_20181202_0733.py +23 -0
- bom/migrations/0011_auto_20181202_2113.py +22 -0
- bom/migrations/0012_partchangehistory.py +30 -0
- bom/migrations/0013_auto_20190222_1631.py +19 -0
- bom/migrations/0014_auto_20190223_2353.py +18 -0
- bom/migrations/0015_auto_20190303_1915.py +136 -0
- bom/migrations/0016_auto_20190405_2308.py +58 -0
- bom/migrations/0017_auto_20190616_1912.py +19 -0
- bom/migrations/0018_auto_20190616_2143.py +24 -0
- bom/migrations/0019_auto_20190624_1246.py +45 -0
- bom/migrations/0020_auto_20190627_0207.py +38 -0
- bom/migrations/0021_auto_20190627_0428.py +23 -0
- bom/migrations/0022_auto_20190811_2140.py +35 -0
- bom/migrations/0023_auto_20191205_2351.py +255 -0
- bom/migrations/0024_auto_20191214_1342.py +89 -0
- bom/migrations/0025_auto_20191221_1907.py +38 -0
- bom/migrations/0026_auto_20191222_2258.py +22 -0
- bom/migrations/0027_auto_20191222_2347.py +17 -0
- bom/migrations/0028_partrevision_displayable_synopsis.py +74 -0
- bom/migrations/0029_auto_20191231_1630.py +23 -0
- bom/migrations/0030_auto_20200101_2253.py +22 -0
- bom/migrations/0031_auto_20200104_1352.py +38 -0
- bom/migrations/0032_auto_20200126_1806.py +27 -0
- bom/migrations/0033_auto_20200203_0618.py +29 -0
- bom/migrations/0034_auto_20200222_0359.py +30 -0
- bom/migrations/0035_auto_20200303_0111.py +34 -0
- bom/migrations/0036_auto_20200303_0538.py +17 -0
- bom/migrations/0037_auto_20200405_1642.py +44 -0
- bom/migrations/0038_auto_20200422_0504.py +19 -0
- bom/migrations/0039_auto_20200929_2315.py +41 -0
- bom/migrations/0040_alter_organization_currency.py +19 -0
- bom/migrations/0041_organization_subscription_quantity.py +18 -0
- bom/migrations/0042_auto_20210720_2137.py +23 -0
- bom/migrations/0043_auto_20211123_0157.py +24 -0
- bom/migrations/0044_auto_20220831_1241.py +23 -0
- bom/migrations/0045_sellerpart_link.py +18 -0
- bom/migrations/0046_alter_sellerpart_unique_together.py +17 -0
- bom/migrations/0047_sellerpart_seller_part_number.py +18 -0
- bom/migrations/0048_rename_part_organization_number_class_bom_part_organiz_b333d6_idx_and_more.py +1017 -0
- bom/migrations/0049_alter_assembly_id_alter_assemblysubparts_id_and_more.py +99 -0
- bom/migrations/0050_alter_organization_options.py +17 -0
- bom/migrations/0051_alter_manufacturer_organization_and_more.py +41 -0
- bom/migrations/0052_remove_partrevision_attribute_and_more.py +584 -0
- bom/migrations/__init__.py +0 -0
- bom/models.py +886 -0
- bom/part_bom.py +192 -0
- bom/settings.py +262 -0
- bom/static/bom/css/dashboard.css +17 -0
- bom/static/bom/css/jquery.treetable.css +28 -0
- bom/static/bom/css/materialize.min.css +13 -0
- bom/static/bom/css/part-info.css +15 -0
- bom/static/bom/css/style.css +482 -0
- bom/static/bom/css/tablesorter-theme.materialize.css +176 -0
- bom/static/bom/css/treetable-theme.css +42 -0
- bom/static/bom/doc/sample_part_classes.csv +38 -0
- bom/static/bom/doc/test_bom.csv +6 -0
- bom/static/bom/doc/test_bom_5_intelligent.csv +4 -0
- bom/static/bom/doc/test_full_bom.csv +37 -0
- bom/static/bom/doc/test_new_parts.csv +5 -0
- bom/static/bom/doc/test_new_parts_5_intelligent.csv +5 -0
- bom/static/bom/img/_ionicons_svg_md-arrow-dropdown.svg +1 -0
- bom/static/bom/img/_ionicons_svg_md-arrow-dropright.svg +1 -0
- bom/static/bom/img/favicon.ico +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_disabled_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_focus_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_normal_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_pressed_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_disabled_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_focus_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_normal_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_pressed_web.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_disabled_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_focus_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_normal_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_pressed_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_disabled_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_focus_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_normal_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_pressed_web@2x.png +0 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.eps +814 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.svg +24 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.eps +1866 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.svg +51 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.eps +1031 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.svg +50 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.eps +1031 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.svg +50 -0
- bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.eps +814 -0
- bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.svg +24 -0
- bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.eps +1837 -0
- bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.svg +44 -0
- bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.eps +1002 -0
- bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.svg +43 -0
- bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.eps +1002 -0
- bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.svg +43 -0
- bom/static/bom/img/google_drive_logo.svg +1 -0
- bom/static/bom/img/indabom.png +0 -0
- bom/static/bom/img/mouser.png +0 -0
- bom/static/bom/img/octopart_blue.svg +19 -0
- bom/static/bom/js/formset-handler.js +65 -0
- bom/static/bom/js/jquery-3.4.1.min.js +2 -0
- bom/static/bom/js/jquery.ba-floatingscrollbar.min.js +10 -0
- bom/static/bom/js/jquery.treetable.js +629 -0
- bom/static/bom/js/materialize.min.js +6 -0
- bom/templates/bom/account-delete.html +23 -0
- bom/templates/bom/add-manufacturer-part.html +66 -0
- bom/templates/bom/add-sellerpart.html +93 -0
- bom/templates/bom/base-menu.html +16 -0
- bom/templates/bom/base.html +129 -0
- bom/templates/bom/bom-action-btn.html +23 -0
- bom/templates/bom/bom-action-table.html +57 -0
- bom/templates/bom/bom-base-menu.html +6 -0
- bom/templates/bom/bom-base.html +24 -0
- bom/templates/bom/bom-form-modal.html +36 -0
- bom/templates/bom/bom-form.html +30 -0
- bom/templates/bom/bom-modal-add-users.html +49 -0
- bom/templates/bom/bom-signup.html +12 -0
- bom/templates/bom/components/bom-flat.html +131 -0
- bom/templates/bom/components/bom-indented.html +237 -0
- bom/templates/bom/components/manufacturer-part-list.html +270 -0
- bom/templates/bom/components/seller-part-list.html +62 -0
- bom/templates/bom/create-part.html +65 -0
- bom/templates/bom/dashboard-menu.html +15 -0
- bom/templates/bom/dashboard.html +303 -0
- bom/templates/bom/edit-manufacturer-part.html +72 -0
- bom/templates/bom/edit-part-class.html +120 -0
- bom/templates/bom/edit-part.html +67 -0
- bom/templates/bom/edit-quantity-of-measure.html +119 -0
- bom/templates/bom/edit-user-meta.html +70 -0
- bom/templates/bom/help.html +1356 -0
- bom/templates/bom/manufacturer-info.html +82 -0
- bom/templates/bom/manufacturers.html +97 -0
- bom/templates/bom/nothing-to-see.html +15 -0
- bom/templates/bom/organization-create.html +135 -0
- bom/templates/bom/part-info.html +448 -0
- bom/templates/bom/part-revision-display.html +50 -0
- bom/templates/bom/part-revision-edit.html +39 -0
- bom/templates/bom/part-revision-manage-bom.html +115 -0
- bom/templates/bom/part-revision-new.html +57 -0
- bom/templates/bom/part-revision-release.html +41 -0
- bom/templates/bom/search-help.html +101 -0
- bom/templates/bom/seller-info.html +82 -0
- bom/templates/bom/sellers.html +97 -0
- bom/templates/bom/settings.html +734 -0
- bom/templates/bom/signup.html +28 -0
- bom/templates/bom/subscription_panel.html +16 -0
- bom/templates/bom/table_of_contents.html +47 -0
- bom/templates/bom/upload-bom.html +111 -0
- bom/templates/bom/upload-parts-help.html +103 -0
- bom/templates/bom/upload-parts.html +50 -0
- bom/templates/registration/login.html +39 -0
- bom/tests.py +1592 -0
- bom/third_party_apis/__init__.py +0 -0
- bom/third_party_apis/base_api.py +51 -0
- bom/third_party_apis/google_drive.py +166 -0
- bom/third_party_apis/mouser.py +132 -0
- bom/third_party_apis/test_apis.py +24 -0
- bom/urls.py +100 -0
- bom/utils.py +228 -0
- bom/validators.py +23 -0
- bom/views/__init__.py +0 -0
- bom/views/json_views.py +55 -0
- bom/views/views.py +1773 -0
- bom/wsgi.py +16 -0
- django_bom-1.262.dist-info/METADATA +206 -0
- django_bom-1.262.dist-info/RECORD +191 -0
- django_bom-1.262.dist-info/WHEEL +5 -0
- django_bom-1.262.dist-info/licenses/LICENSE +674 -0
- django_bom-1.262.dist-info/top_level.txt +1 -0
bom/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
default_app_config = 'bom.apps.BomConfig'
|
bom/admin.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
from django.contrib.auth import get_user_model
|
|
4
|
+
from django.contrib.auth.admin import UserAdmin
|
|
5
|
+
|
|
6
|
+
from .models import (
|
|
7
|
+
Assembly,
|
|
8
|
+
Manufacturer,
|
|
9
|
+
ManufacturerPart,
|
|
10
|
+
Part,
|
|
11
|
+
PartClass,
|
|
12
|
+
PartRevision,
|
|
13
|
+
PartRevisionProperty,
|
|
14
|
+
PartRevisionPropertyDefinition,
|
|
15
|
+
Seller,
|
|
16
|
+
SellerPart,
|
|
17
|
+
Subpart,
|
|
18
|
+
UnitDefinition,
|
|
19
|
+
QuantityOfMeasure,
|
|
20
|
+
get_organization_model,
|
|
21
|
+
get_user_meta_model
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
User = get_user_model()
|
|
25
|
+
UserMeta = get_user_meta_model()
|
|
26
|
+
Organization = get_organization_model()
|
|
27
|
+
|
|
28
|
+
class UserMetaInline(admin.TabularInline):
|
|
29
|
+
model = UserMeta
|
|
30
|
+
verbose_name = 'BOM User Meta'
|
|
31
|
+
raw_id_fields = ('organization',)
|
|
32
|
+
can_delete = False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OrganizationAdmin(admin.ModelAdmin):
|
|
36
|
+
list_display = ('name',)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SubpartInline(admin.TabularInline):
|
|
40
|
+
model = Subpart
|
|
41
|
+
fk_name = 'part_revision'
|
|
42
|
+
raw_id_fields = ('part_revision',)
|
|
43
|
+
# readonly_fields = ('get_full_part_number', )
|
|
44
|
+
#
|
|
45
|
+
# def get_full_part_number(self, obj):
|
|
46
|
+
# return obj.assembly_subpart.part.full_part_number()
|
|
47
|
+
# get_full_part_number.short_description = 'PartNumber'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SellerAdmin(admin.ModelAdmin):
|
|
51
|
+
list_display = ('name',)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SellerPartAdmin(admin.ModelAdmin):
|
|
55
|
+
list_display = (
|
|
56
|
+
'manufacturer_part',
|
|
57
|
+
'seller',
|
|
58
|
+
'seller_part_number',
|
|
59
|
+
'minimum_order_quantity',
|
|
60
|
+
'minimum_pack_quantity',
|
|
61
|
+
'unit_cost',
|
|
62
|
+
'lead_time_days',
|
|
63
|
+
'nre_cost',
|
|
64
|
+
'ncnr')
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class SellerPartAdminInline(admin.TabularInline):
|
|
68
|
+
model = SellerPart
|
|
69
|
+
raw_id_fields = ('seller', 'manufacturer_part',)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ManufacturerPartAdmin(admin.ModelAdmin):
|
|
73
|
+
list_display = (
|
|
74
|
+
'manufacturer_part_number',
|
|
75
|
+
'manufacturer',
|
|
76
|
+
'part',)
|
|
77
|
+
raw_id_fields = ('manufacturer', 'part',)
|
|
78
|
+
inlines = [
|
|
79
|
+
SellerPartAdminInline,
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ManufacturerPartAdminInline(admin.TabularInline):
|
|
84
|
+
model = ManufacturerPart
|
|
85
|
+
raw_id_fields = ('part', 'manufacturer',)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class PartClassAdmin(admin.ModelAdmin):
|
|
89
|
+
list_display = ('code', 'name', 'comment',)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class PartRevisionAdminInline(admin.TabularInline):
|
|
93
|
+
model = PartRevision
|
|
94
|
+
extra = 0
|
|
95
|
+
raw_id_fields = ('assembly',)
|
|
96
|
+
readonly_fields = ('timestamp',)
|
|
97
|
+
show_change_link = True
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class PartAdmin(admin.ModelAdmin):
|
|
101
|
+
ordering = ('organization', 'number_class__code', 'number_item', 'number_variation')
|
|
102
|
+
readonly_fields = ('get_full_part_number',)
|
|
103
|
+
list_display = (
|
|
104
|
+
'organization',
|
|
105
|
+
'get_full_part_number',
|
|
106
|
+
)
|
|
107
|
+
list_filter = ('organization', 'number_class',)
|
|
108
|
+
raw_id_fields = ('number_class', 'primary_manufacturer_part',)
|
|
109
|
+
inlines = [
|
|
110
|
+
PartRevisionAdminInline,
|
|
111
|
+
ManufacturerPartAdminInline,
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
def get_full_part_number(self, obj):
|
|
115
|
+
return obj.full_part_number()
|
|
116
|
+
|
|
117
|
+
get_full_part_number.short_description = 'PartNumber'
|
|
118
|
+
get_full_part_number.admin_order_field = 'number_class__part_number'
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class QuantityOfMeasureAdmin(admin.ModelAdmin):
|
|
122
|
+
list_display = ('name', 'organization')
|
|
123
|
+
list_filter = ('organization',)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class PartRevisionPropertyInline(admin.TabularInline):
|
|
127
|
+
model = PartRevisionProperty
|
|
128
|
+
extra = 1
|
|
129
|
+
raw_id_fields = ('property_definition', 'part_revision', 'unit_definition')
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class PartRevisionAdmin(admin.ModelAdmin):
|
|
133
|
+
list_display = ('part', 'revision', 'description', 'get_assembly_size', 'timestamp',)
|
|
134
|
+
raw_id_fields = ('assembly',)
|
|
135
|
+
readonly_fields = ('timestamp',)
|
|
136
|
+
inlines = [PartRevisionPropertyInline]
|
|
137
|
+
|
|
138
|
+
def get_assembly_size(self, obj):
|
|
139
|
+
return None if obj.assembly is None else obj.assembly.subparts.count()
|
|
140
|
+
|
|
141
|
+
get_assembly_size.short_description = 'AssemblySize'
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class ManufacturerAdmin(admin.ModelAdmin):
|
|
145
|
+
list_display = ('name', 'organization',)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class SubpartAdmin(admin.ModelAdmin):
|
|
149
|
+
list_display = ('part_revision', 'count', 'reference',)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class SubpartsInline(admin.TabularInline):
|
|
153
|
+
model = Assembly.subparts.through
|
|
154
|
+
raw_id_fields = ('subpart',)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class AssemblyAdmin(admin.ModelAdmin):
|
|
158
|
+
list_display = ('id',)
|
|
159
|
+
exclude = ('subparts',)
|
|
160
|
+
inlines = [
|
|
161
|
+
SubpartsInline,
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class UnitDefinitionAdmin(admin.ModelAdmin):
|
|
166
|
+
list_display = ('name', 'symbol', 'quantity_of_measure', 'base_multiplier', 'organization')
|
|
167
|
+
list_filter = ('organization',)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class PartRevisionPropertyDefinitionAdmin(admin.ModelAdmin):
|
|
171
|
+
list_display = ('code', 'name', 'type', 'organization', 'quantity_of_measure')
|
|
172
|
+
list_filter = ('organization',)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if settings.BOM_USER_META_MODEL == 'bom.UserMeta':
|
|
176
|
+
current_user_admin = admin.site._registry.get(User)
|
|
177
|
+
|
|
178
|
+
if current_user_admin:
|
|
179
|
+
user_admin_class = current_user_admin.__class__
|
|
180
|
+
existing_inlines = list(user_admin_class.inlines or [])
|
|
181
|
+
if UserMetaInline not in existing_inlines:
|
|
182
|
+
existing_inlines.append(UserMetaInline)
|
|
183
|
+
user_admin_class.inlines = existing_inlines
|
|
184
|
+
else:
|
|
185
|
+
class BomUserAdmin(UserAdmin):
|
|
186
|
+
inlines = [UserMetaInline]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
admin.site.register(User, BomUserAdmin)
|
|
190
|
+
|
|
191
|
+
if settings.BOM_ORGANIZATION_MODEL == 'bom.Organization':
|
|
192
|
+
from .models import Organization
|
|
193
|
+
|
|
194
|
+
admin.site.register(Organization, OrganizationAdmin)
|
|
195
|
+
|
|
196
|
+
admin.site.register(Seller, SellerAdmin)
|
|
197
|
+
admin.site.register(SellerPart, SellerPartAdmin)
|
|
198
|
+
admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
|
|
199
|
+
admin.site.register(PartClass, PartClassAdmin)
|
|
200
|
+
admin.site.register(Part, PartAdmin)
|
|
201
|
+
admin.site.register(PartRevision, PartRevisionAdmin)
|
|
202
|
+
admin.site.register(Manufacturer, ManufacturerAdmin)
|
|
203
|
+
admin.site.register(Assembly, AssemblyAdmin)
|
|
204
|
+
admin.site.register(Subpart, SubpartAdmin)
|
|
205
|
+
admin.site.register(UnitDefinition, UnitDefinitionAdmin)
|
|
206
|
+
admin.site.register(PartRevisionPropertyDefinition, PartRevisionPropertyDefinitionAdmin)
|
|
207
|
+
admin.site.register(QuantityOfMeasure, QuantityOfMeasureAdmin)
|
bom/apps.py
ADDED
bom/auth_backends.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from . import constants
|
|
4
|
+
from .models import get_organization_model
|
|
5
|
+
|
|
6
|
+
Organization = get_organization_model()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OrganizationPermissionBackend:
|
|
10
|
+
"""
|
|
11
|
+
Object-level permission backend for django-bom.
|
|
12
|
+
|
|
13
|
+
- Uses Django's has_perm(user, perm, obj) to evaluate permissions tied to an Organization.
|
|
14
|
+
- Superusers are granted all permissions.
|
|
15
|
+
- For `bom.manage_members`: User must be owner or admin within the organization.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def authenticate(self, request, **credentials): # pragma: no cover - not used for auth
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
def has_perm(self, user_obj, perm: str, obj: Optional[object] = None):
|
|
22
|
+
# Only handle our specific object-level permission. Let other backends process others.
|
|
23
|
+
if not user_obj or not user_obj.is_authenticated:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
if user_obj.is_superuser:
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
if perm != 'bom.manage_members':
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
if obj is None or not isinstance(obj, Organization):
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
profile = user_obj.bom_profile()
|
|
36
|
+
if not profile or profile.organization_id != obj.id:
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
if obj.subscription != constants.SUBSCRIPTION_TYPE_PRO:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
is_owner = obj.owner_id == user_obj.id
|
|
43
|
+
is_admin = getattr(profile, 'role', None) == constants.ROLE_TYPE_ADMIN
|
|
44
|
+
if not (is_owner or is_admin):
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
return True
|
bom/base_classes.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from django.forms.models import model_to_dict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AsDictModel:
|
|
6
|
+
def as_dict(self):
|
|
7
|
+
try:
|
|
8
|
+
return model_to_dict(self)
|
|
9
|
+
except (TypeError, AttributeError):
|
|
10
|
+
return dict(self)
|
|
11
|
+
|
|
12
|
+
def __iter__(self):
|
|
13
|
+
for key in dir(self):
|
|
14
|
+
if not key.startswith("_") and not key == "objects":
|
|
15
|
+
value = getattr(self, key)
|
|
16
|
+
if not callable(value):
|
|
17
|
+
if isinstance(value, (dict, OrderedDict)):
|
|
18
|
+
for subkey, subvalue in value.items():
|
|
19
|
+
try:
|
|
20
|
+
value[subkey] = subvalue.as_dict()
|
|
21
|
+
except AttributeError:
|
|
22
|
+
pass
|
|
23
|
+
yield key, value
|
|
24
|
+
else:
|
|
25
|
+
try:
|
|
26
|
+
yield key, value.as_dict()
|
|
27
|
+
except AttributeError:
|
|
28
|
+
if isinstance(value, (int, float, complex, bool)):
|
|
29
|
+
yield key, value
|
|
30
|
+
else:
|
|
31
|
+
yield key, str(value)
|
bom/constants.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
NO_CHOICE = ('', '-----')
|
|
2
|
+
|
|
3
|
+
SUBSCRIPTION_TYPE_FREE = 'F'
|
|
4
|
+
SUBSCRIPTION_TYPE_PRO = 'P'
|
|
5
|
+
SUBSCRIPTION_TYPES = ((SUBSCRIPTION_TYPE_FREE, 'Free'), (SUBSCRIPTION_TYPE_PRO, 'Pro'),)
|
|
6
|
+
|
|
7
|
+
ROLE_TYPE_ADMIN = 'A'
|
|
8
|
+
ROLE_TYPE_VIEWER = 'V'
|
|
9
|
+
ROLE_TYPES = ((ROLE_TYPE_ADMIN, 'Admin'), (ROLE_TYPE_VIEWER, 'Viewer'),)
|
|
10
|
+
|
|
11
|
+
CONFIGURATION_TYPE_RELEASED = 'R'
|
|
12
|
+
CONFIGURATION_TYPE_WORKING = 'W'
|
|
13
|
+
CONFIGURATION_TYPES = ((CONFIGURATION_TYPE_RELEASED, 'Released'), (CONFIGURATION_TYPE_WORKING, 'Working'),)
|
|
14
|
+
|
|
15
|
+
NUMBER_SCHEME_INTELLIGENT = 'I'
|
|
16
|
+
NUMBER_SCHEME_SEMI_INTELLIGENT = 'S'
|
|
17
|
+
NUMBER_SCHEMES = ((NUMBER_SCHEME_INTELLIGENT, 'Intelligent'), (NUMBER_SCHEME_SEMI_INTELLIGENT, 'Semi-Intelligent'),)
|
|
18
|
+
|
|
19
|
+
NUMBER_CLASS_CODE_LEN_MIN = 2
|
|
20
|
+
NUMBER_CLASS_CODE_LEN_MAX = 16
|
|
21
|
+
NUMBER_CLASS_CODE_LEN_DEFAULT = 3
|
|
22
|
+
NUMBER_ITEM_LEN_MIN = 3
|
|
23
|
+
NUMBER_ITEM_LEN_MAX = 128
|
|
24
|
+
NUMBER_ITEM_LEN_DEFAULT = 4
|
|
25
|
+
NUMBER_VARIATION_LEN_MIN = 0
|
|
26
|
+
NUMBER_VARIATION_LEN_MAX = 16
|
|
27
|
+
NUMBER_VARIATION_LEN_DEFAULT = 2
|
|
28
|
+
|
|
29
|
+
DATA_SOURCE_OCTOPART = 'octopart'
|
|
30
|
+
DATA_SOURCE_MOUSER = 'mouser'
|
|
31
|
+
DATA_SOURCES = (
|
|
32
|
+
NO_CHOICE,
|
|
33
|
+
(DATA_SOURCE_OCTOPART, 'octopart'),
|
|
34
|
+
(DATA_SOURCE_MOUSER, 'mouser'),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
PART_REVISION_PROPERTY_TYPE_STRING = 'S'
|
|
38
|
+
PART_REVISION_PROPERTY_TYPE_DECIMAL = 'D'
|
|
39
|
+
PART_REVISION_PROPERTY_TYPE_BOOLEAN = 'B'
|
|
40
|
+
PART_REVISION_PROPERTY_TYPES = ((PART_REVISION_PROPERTY_TYPE_STRING, 'String'),
|
|
41
|
+
(PART_REVISION_PROPERTY_TYPE_DECIMAL, 'Number'),
|
|
42
|
+
(PART_REVISION_PROPERTY_TYPE_BOOLEAN, 'Boolean'),)
|
|
43
|
+
|
|
44
|
+
VALUE_UNITS = (
|
|
45
|
+
NO_CHOICE,
|
|
46
|
+
('Ohms', '\u03A9'),
|
|
47
|
+
('mOhms', 'm\u03A9'),
|
|
48
|
+
('kOhms', 'k\u03A9'),
|
|
49
|
+
('MOhms', 'M\u03A9'),
|
|
50
|
+
('F', 'F'),
|
|
51
|
+
('pF', 'pF'),
|
|
52
|
+
('nF', 'nF'),
|
|
53
|
+
('uF', '\u03BCF'),
|
|
54
|
+
('V', 'V'),
|
|
55
|
+
('uV', '\u03BCV'),
|
|
56
|
+
('mV', 'mV'),
|
|
57
|
+
('A', 'A'),
|
|
58
|
+
('uA', '\u03BCA'),
|
|
59
|
+
('mA', 'mA'),
|
|
60
|
+
('C', '\u00B0C'),
|
|
61
|
+
('F', '\u00B0F'),
|
|
62
|
+
('H', 'H'),
|
|
63
|
+
('nH', 'nH'),
|
|
64
|
+
('mH', 'mH'),
|
|
65
|
+
('uH', '\u03BCH'),
|
|
66
|
+
('Hz', 'Hz'),
|
|
67
|
+
('kHz', 'kHz'),
|
|
68
|
+
('MHz', 'MHz'),
|
|
69
|
+
('GHz', 'GHz'),
|
|
70
|
+
('Other', 'Other'),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
PACKAGE_TYPES = (
|
|
74
|
+
NO_CHOICE,
|
|
75
|
+
('0201 smd', '0201 smd'),
|
|
76
|
+
('0402 smd', '0402 smd'),
|
|
77
|
+
('0603 smd', '0603 smd'),
|
|
78
|
+
('0805 smd', '0805 smd'),
|
|
79
|
+
('1206 smd', '1206 smd'),
|
|
80
|
+
('1210 smd', '1210 smd'),
|
|
81
|
+
('1812 smd', '1812 smd'),
|
|
82
|
+
('2010 smd', '2010 smd'),
|
|
83
|
+
('2512 smd', '2512 smd'),
|
|
84
|
+
('1/8 radial', '1/8 radial'),
|
|
85
|
+
('1/4 radial', '1/4 radial'),
|
|
86
|
+
('1/2 radial', '1/2 radial'),
|
|
87
|
+
('Size A', 'Size A'),
|
|
88
|
+
('Size B', 'Size B'),
|
|
89
|
+
('Size C', 'Size C'),
|
|
90
|
+
('Size D', 'Size D'),
|
|
91
|
+
('Size E', 'Size E'),
|
|
92
|
+
('SOT-23', 'SOT-23'),
|
|
93
|
+
('SOT-223', 'SOT-233'),
|
|
94
|
+
('DIL', 'DIL'),
|
|
95
|
+
('SOP', 'SOP'),
|
|
96
|
+
('SOIC', 'SOIC'),
|
|
97
|
+
('QFN', 'QFN'),
|
|
98
|
+
('QFP', 'QFP'),
|
|
99
|
+
('QFT', 'QFT'),
|
|
100
|
+
('PLCC', 'PLCC'),
|
|
101
|
+
('VGA', 'VGA'),
|
|
102
|
+
('BGA', 'BGA'),
|
|
103
|
+
('CSP', 'CSP'),
|
|
104
|
+
('Other', 'Other'),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
DISTANCE_UNITS = (
|
|
108
|
+
NO_CHOICE,
|
|
109
|
+
('mil', 'mil'),
|
|
110
|
+
('in', 'in'),
|
|
111
|
+
('ft', 'ft'),
|
|
112
|
+
('yd', 'yd'),
|
|
113
|
+
('km', 'km'),
|
|
114
|
+
('m', 'm'),
|
|
115
|
+
('cm', 'cm'),
|
|
116
|
+
('mm', 'mm'),
|
|
117
|
+
('um', '\u03BCm'),
|
|
118
|
+
('nm', 'nm'),
|
|
119
|
+
('Other', 'Other'),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
WEIGHT_UNITS = (
|
|
123
|
+
NO_CHOICE,
|
|
124
|
+
('mg', 'mg'),
|
|
125
|
+
('g', 'g'),
|
|
126
|
+
('kg', 'kg'),
|
|
127
|
+
('oz', 'oz'),
|
|
128
|
+
('lb', 'lb'),
|
|
129
|
+
('Other', 'Other'),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
TEMPERATURE_UNITS = (
|
|
133
|
+
NO_CHOICE,
|
|
134
|
+
('C', '\u00B0C'),
|
|
135
|
+
('F', '\u00B0F'),
|
|
136
|
+
('Other', 'Other'),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
WAVELENGTH_UNITS = (
|
|
140
|
+
NO_CHOICE,
|
|
141
|
+
('km', 'km'),
|
|
142
|
+
('m', 'm'),
|
|
143
|
+
('cm', 'cm'),
|
|
144
|
+
('mm', 'mm'),
|
|
145
|
+
('um', '\u03BCm'),
|
|
146
|
+
('nm', 'nm'),
|
|
147
|
+
('A', '\u212B'),
|
|
148
|
+
('Other', 'Other'),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
FREQUENCY_UNITS = (
|
|
152
|
+
NO_CHOICE,
|
|
153
|
+
('Hz', 'Hz'),
|
|
154
|
+
('kHz', 'kHz'),
|
|
155
|
+
('MHz', 'MHz'),
|
|
156
|
+
('GHz', 'GHz'),
|
|
157
|
+
('Other', 'Other'),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
MEMORY_UNITS = (
|
|
161
|
+
NO_CHOICE,
|
|
162
|
+
('KB', 'KB'),
|
|
163
|
+
('MB', 'MB'),
|
|
164
|
+
('GB', 'GB'),
|
|
165
|
+
('TB', 'TB'),
|
|
166
|
+
('Other', 'Other'),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
INTERFACE_TYPES = (
|
|
170
|
+
NO_CHOICE,
|
|
171
|
+
('I2C', 'I2C'),
|
|
172
|
+
('SPI', 'SPI'),
|
|
173
|
+
('CAN', 'CAN'),
|
|
174
|
+
('One-Wire', '1-Wire'),
|
|
175
|
+
('RS485', 'RS-485'),
|
|
176
|
+
('RS232', 'RS-232'),
|
|
177
|
+
('WiFi', 'Wi-Fi'),
|
|
178
|
+
('4G', '4G'),
|
|
179
|
+
('BT', 'Bluetooth'),
|
|
180
|
+
('BTLE', 'Bluetooth LE'),
|
|
181
|
+
('Z_Wave', 'Z-Wave'),
|
|
182
|
+
('Zigbee', 'Zigbee'),
|
|
183
|
+
('LAN', 'LAN'),
|
|
184
|
+
('USB', 'USB'),
|
|
185
|
+
('HDMI', 'HDMI'),
|
|
186
|
+
('Other', 'Other'),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
POWER_UNITS = (
|
|
190
|
+
NO_CHOICE,
|
|
191
|
+
('W', 'W'),
|
|
192
|
+
('uW', '\u03BCW'),
|
|
193
|
+
('mW', 'mW'),
|
|
194
|
+
('kW', 'kW'),
|
|
195
|
+
('MW', 'MW'),
|
|
196
|
+
('Other', 'Other'),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
VOLTAGE_UNITS = (
|
|
200
|
+
NO_CHOICE,
|
|
201
|
+
('V', 'V'),
|
|
202
|
+
('uV', '\u03BCV'),
|
|
203
|
+
('mV', 'mV'),
|
|
204
|
+
('kV', 'kV'),
|
|
205
|
+
('MV', 'MV'),
|
|
206
|
+
('Other', 'Other'),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
CURRENT_UNITS = (
|
|
210
|
+
NO_CHOICE,
|
|
211
|
+
('A', 'A'),
|
|
212
|
+
('uA', '\u03BCV'),
|
|
213
|
+
('mA', 'mA'),
|
|
214
|
+
('kA', 'kA'),
|
|
215
|
+
('MA', 'MA'),
|
|
216
|
+
('Other', 'Other'),
|
|
217
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from django.conf import settings # import the settings file
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def bom_config(request):
|
|
5
|
+
base_template = 'bom/base.html'
|
|
6
|
+
if settings.BOM_CONFIG:
|
|
7
|
+
if 'base_template' in settings.BOM_CONFIG:
|
|
8
|
+
base_template = settings.BOM_CONFIG['base_template']
|
|
9
|
+
return {'BASE_TEMPLATE': base_template}
|