simo 1.7.20__py3-none-any.whl → 2.0.1__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 simo might be problematic. Click here for more details.
- simo/__pycache__/asgi.cpython-38.pyc +0 -0
- simo/__pycache__/settings.cpython-38.pyc +0 -0
- simo/__pycache__/urls.cpython-38.pyc +0 -0
- simo/__pycache__/wsgi.cpython-38.pyc +0 -0
- simo/core/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
- simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/core/__pycache__/context.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/events.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/core/__pycache__/managers.cpython-38.pyc +0 -0
- simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
- simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/__pycache__/views.cpython-38.pyc +0 -0
- simo/core/admin.py +28 -18
- simo/core/api.py +157 -16
- simo/core/api_meta.py +87 -0
- simo/core/auto_urls.py +4 -1
- simo/core/autocomplete_views.py +8 -4
- simo/core/base_types.py +1 -0
- simo/core/context.py +3 -1
- simo/core/controllers.py +134 -36
- simo/core/db_backend/base.py +7 -22
- simo/core/drf_braces/README +3 -0
- simo/core/drf_braces/__init__.py +7 -0
- simo/core/drf_braces/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/__pycache__/utils.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__init__.py +5 -0
- simo/core/drf_braces/fields/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/_fields.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/custom.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/mixins.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/modified.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/_fields.py +48 -0
- simo/core/drf_braces/fields/custom.py +107 -0
- simo/core/drf_braces/fields/mixins.py +58 -0
- simo/core/drf_braces/fields/modified.py +41 -0
- simo/core/drf_braces/forms/__init__.py +0 -0
- simo/core/drf_braces/forms/fields.py +20 -0
- simo/core/drf_braces/forms/serializer_form.py +156 -0
- simo/core/drf_braces/mixins.py +52 -0
- simo/core/drf_braces/models.py +0 -0
- simo/core/drf_braces/parsers.py +72 -0
- simo/core/drf_braces/renderers.py +37 -0
- simo/core/drf_braces/serializers/__init__.py +0 -0
- simo/core/drf_braces/serializers/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/form_serializer.cpython-38.pyc +0 -0
- simo/core/drf_braces/serializers/enforce_validation_serializer.py +214 -0
- simo/core/drf_braces/serializers/form_serializer.py +391 -0
- simo/core/drf_braces/serializers/swapping.py +48 -0
- simo/core/drf_braces/tests/__init__.py +0 -0
- simo/core/drf_braces/tests/fields/__init__.py +0 -0
- simo/core/drf_braces/tests/fields/test_custom.py +94 -0
- simo/core/drf_braces/tests/fields/test_fields.py +13 -0
- simo/core/drf_braces/tests/fields/test_mixins.py +96 -0
- simo/core/drf_braces/tests/fields/test_modified.py +40 -0
- simo/core/drf_braces/tests/forms/__init__.py +0 -0
- simo/core/drf_braces/tests/forms/test_fields.py +46 -0
- simo/core/drf_braces/tests/forms/test_serializer_form.py +256 -0
- simo/core/drf_braces/tests/serializers/__init__.py +0 -0
- simo/core/drf_braces/tests/serializers/test_enforce_validation_serializer.py +169 -0
- simo/core/drf_braces/tests/serializers/test_form_serializer.py +387 -0
- simo/core/drf_braces/tests/serializers/test_swapping.py +40 -0
- simo/core/drf_braces/tests/test_mixins.py +111 -0
- simo/core/drf_braces/tests/test_parsers.py +73 -0
- simo/core/drf_braces/tests/test_renderers.py +23 -0
- simo/core/drf_braces/tests/test_utils.py +73 -0
- simo/core/drf_braces/utils.py +209 -0
- simo/core/events.py +3 -3
- simo/core/forms.py +79 -37
- simo/core/gateways.py +31 -14
- simo/core/management/commands/gateways_manager.py +0 -1
- simo/core/managers.py +81 -0
- simo/core/middleware.py +25 -0
- simo/core/migrations/0026_category_instance.py +20 -0
- simo/core/migrations/0027_remove_component_tags.py +17 -0
- simo/core/migrations/0028_rename_subcomponents_component_slaves.py +18 -0
- simo/core/migrations/0029_auto_20240229_1331.py +33 -0
- simo/core/migrations/__pycache__/0026_category_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0027_remove_component_tags.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-38.pyc +0 -0
- simo/core/models.py +103 -66
- simo/core/permissions.py +28 -2
- simo/core/serializers.py +330 -26
- simo/core/socket_consumers.py +5 -14
- simo/core/tasks.py +11 -1
- simo/core/templates/admin/base.html +37 -10
- simo/core/templates/admin/wizard/discovery.html +188 -0
- simo/core/templates/admin/wizard/wizard_add.html +5 -5
- simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
- simo/core/utils/admin.py +9 -2
- simo/core/utils/formsets.py +17 -16
- simo/core/utils/helpers.py +1 -0
- simo/core/utils/serialization.py +56 -0
- simo/core/utils/type_constants.py +1 -1
- simo/core/utils/validators.py +14 -1
- simo/core/views.py +13 -0
- simo/fleet/__pycache__/admin.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/api.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/managers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
- simo/fleet/admin.py +53 -29
- simo/fleet/api.py +59 -3
- simo/fleet/auto_urls.py +2 -3
- simo/fleet/controllers.py +199 -16
- simo/fleet/forms.py +325 -483
- simo/fleet/gateways.py +44 -2
- simo/fleet/managers.py +32 -0
- simo/fleet/migrations/0025_auto_20240130_1334.py +27 -0
- simo/fleet/migrations/0026_rename_i2cinterface_scl_pin_and_more.py +64 -0
- simo/fleet/migrations/0027_auto_20240306_0802.py +170 -0
- simo/fleet/migrations/0028_remove_i2cinterface_scl_pin_no_and_more.py +21 -0
- simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py +24 -0
- simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py +24 -0
- simo/fleet/migrations/0031_alter_colonel_type.py +18 -0
- simo/fleet/migrations/__pycache__/0025_auto_20240130_1334.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0026_rename_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0027_auto_20240306_0802.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-38.pyc +0 -0
- simo/fleet/models.py +140 -82
- simo/fleet/serializers.py +35 -1
- simo/fleet/socket_consumers.py +239 -76
- simo/fleet/utils.py +15 -53
- simo/fleet/views.py +28 -14
- simo/generic/controllers.py +13 -89
- simo/generic/forms.py +29 -18
- simo/generic/gateways.py +73 -2
- simo/generic/models.py +3 -3
- simo/multimedia/controllers.py +9 -8
- simo/settings.py +7 -4
- simo/urls.py +4 -8
- simo/users/__pycache__/admin.cpython-38.pyc +0 -0
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/users/__pycache__/sso_urls.cpython-38.pyc +0 -0
- simo/users/admin.py +8 -1
- simo/users/api.py +38 -2
- simo/users/auto_urls.py +2 -2
- simo/users/migrations/0025_rename_name_fingerprint_type_and_more.py +22 -0
- simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc +0 -0
- simo/users/models.py +2 -3
- simo/users/serializers.py +15 -1
- simo/users/sso_urls.py +3 -3
- simo/wsgi.py +7 -0
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/METADATA +8 -9
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/RECORD +173 -189
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/WHEEL +1 -1
- simo/core/db_backend/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/db_backend/__pycache__/base.cpython-38.pyc +0 -0
- simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0004_create_generic.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0005_component_subcomponents.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0006_alter_component_subcomponents.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0007_component_change_init_to.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0008_alter_component_change_init_to.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0009_auto_20220707_1404.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0010_historyaggregate.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0011_component_last_change.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0012_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0014_zone_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0015_auto_20231004_1113.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0016_auto_20231004_1113.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0017_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0019_alter_gateway_type.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0020_component_meta.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/templatetags/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/templatetags/__pycache__/components_list.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/config_values.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/easing.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/form_fields.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/form_widgets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/logs.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/mixins.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/model_helpers.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/relay.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/type_constants.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/validators.cpython-38.pyc +0 -0
- simo/fleet/tasks.py +0 -25
- simo/generic/__pycache__/__init__.cpython-37.pyc +0 -0
- simo/generic/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/generic/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/generic/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-37.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/generic/__pycache__/models.cpython-38.pyc +0 -0
- simo/generic/__pycache__/routing.cpython-38.pyc +0 -0
- simo/generic/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/widgets.cpython-37.pyc +0 -0
- simo/multimedia/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/admin.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/api.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/forms.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/models.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/admin.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/api.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0004_user_secret_key.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0005_permissionsrole_instance.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0006_auto_20231003_0850.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0007_auto_20231003_1228.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0008_auto_20231003_1229.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0009_remove_user_role.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0010_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0011_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0012_alter_userinstancerole_unique_together.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0013_remove_user_roles.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0014_user_roles.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0015_remove_user_at_home.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0016_auto_20231005_1050.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/LICENSE.md +0 -0
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/admin.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.utils.translation import gettext_lazy as _
|
|
2
2
|
from django.contrib import admin
|
|
3
|
+
from django.urls import reverse
|
|
3
4
|
from easy_thumbnails.fields import ThumbnailerField
|
|
4
5
|
from adminsortable2.admin import SortableAdminMixin
|
|
5
6
|
from django.template.loader import render_to_string
|
|
@@ -21,6 +22,7 @@ from .forms import (
|
|
|
21
22
|
)
|
|
22
23
|
from .filters import ZonesFilter
|
|
23
24
|
from .widgets import AdminImageWidget
|
|
25
|
+
from .middleware import get_current_instance
|
|
24
26
|
from simo.conf import dynamic_settings
|
|
25
27
|
|
|
26
28
|
csrf_protect_m = method_decorator(csrf_protect)
|
|
@@ -79,12 +81,6 @@ class ZoneAdmin(SortableAdminMixin, admin.ModelAdmin):
|
|
|
79
81
|
search_fields = 'name',
|
|
80
82
|
list_filter = 'instance',
|
|
81
83
|
|
|
82
|
-
def get_queryset(self, request):
|
|
83
|
-
qs = super().get_queryset(request)
|
|
84
|
-
if request.user.is_master:
|
|
85
|
-
return qs
|
|
86
|
-
return qs.filter(instance__in=request.user.instances)
|
|
87
|
-
|
|
88
84
|
def get_fields(self, request, obj=None):
|
|
89
85
|
if request.user.is_master:
|
|
90
86
|
return super().get_fields(request, obj)
|
|
@@ -271,7 +267,7 @@ class ComponentAdmin(admin.ModelAdmin):
|
|
|
271
267
|
'control', 'value', 'arm_status', 'history', 'meta'
|
|
272
268
|
)
|
|
273
269
|
list_filter = (
|
|
274
|
-
'gateway', 'base_type',
|
|
270
|
+
'gateway', 'base_type', ('zone', ZonesFilter), 'category', 'alive',
|
|
275
271
|
'alarm_category', 'arm_status'
|
|
276
272
|
)
|
|
277
273
|
|
|
@@ -280,12 +276,6 @@ class ComponentAdmin(admin.ModelAdmin):
|
|
|
280
276
|
change_list_template = 'admin/component_change_list.html'
|
|
281
277
|
inlines = ComponentPermissionInline,
|
|
282
278
|
|
|
283
|
-
def get_queryset(self, request):
|
|
284
|
-
qs = super().get_queryset(request)
|
|
285
|
-
if request.user.is_master:
|
|
286
|
-
return qs
|
|
287
|
-
return qs.filter(zone__instance__in=request.user.instances)
|
|
288
|
-
|
|
289
279
|
def get_fieldsets(self, request, obj=None):
|
|
290
280
|
form = self._get_form_for_get_fields(request, obj)
|
|
291
281
|
fieldsets = form.get_admin_fieldsets(request, obj)
|
|
@@ -338,7 +328,6 @@ class ComponentAdmin(admin.ModelAdmin):
|
|
|
338
328
|
)[request.session['add_comp_type']]
|
|
339
329
|
except:
|
|
340
330
|
request.session.pop('add_comp_type')
|
|
341
|
-
print("No such controller type!")
|
|
342
331
|
return redirect(request.path)
|
|
343
332
|
|
|
344
333
|
add_form = controller_cls.add_form
|
|
@@ -350,6 +339,8 @@ class ComponentAdmin(admin.ModelAdmin):
|
|
|
350
339
|
):
|
|
351
340
|
if field_neme in form.fields:
|
|
352
341
|
form.fields.pop(field_neme)
|
|
342
|
+
if 'slaves' not in form.declared_fields:
|
|
343
|
+
form.fields.pop('slaves')
|
|
353
344
|
|
|
354
345
|
ctx['is_last'] = True
|
|
355
346
|
ctx['current_step'] = 3
|
|
@@ -359,13 +350,33 @@ class ComponentAdmin(admin.ModelAdmin):
|
|
|
359
350
|
if request.method == 'POST':
|
|
360
351
|
ctx['form'] = add_form(
|
|
361
352
|
request=request,
|
|
362
|
-
|
|
363
|
-
controller_cls=controller_cls,
|
|
353
|
+
controller_uid=controller_cls.uid,
|
|
364
354
|
data=request.POST, files=request.FILES,
|
|
365
355
|
initial=request.session.get('c_add_init'),
|
|
366
356
|
)
|
|
367
357
|
pop_fields_from_form(ctx['form'])
|
|
368
358
|
if ctx['form'].is_valid():
|
|
359
|
+
if ctx['form'].controller.is_discoverable:
|
|
360
|
+
ctx['form'].controller.init_discovery(
|
|
361
|
+
ctx['form'].cleaned_data
|
|
362
|
+
)
|
|
363
|
+
ctx['discovery_msg'] = ctx['form'].controller.discovery_msg
|
|
364
|
+
instance = ctx['form'].cleaned_data['zone'].instance
|
|
365
|
+
ctx['api_check_url'] = reverse(
|
|
366
|
+
'discoveries-list',
|
|
367
|
+
kwargs={'instance_slug': instance.slug}
|
|
368
|
+
) + f"?controller_uid={ctx['form'].controller.uid}"
|
|
369
|
+
ctx['api_retry_url'] = reverse(
|
|
370
|
+
'discoveries-list',
|
|
371
|
+
kwargs={'instance_slug': instance.slug}
|
|
372
|
+
) + f"retry/?controller_uid={ctx['form'].controller.uid}"
|
|
373
|
+
ctx['api_components_url'] = reverse(
|
|
374
|
+
'components-list',
|
|
375
|
+
kwargs={'instance_slug': instance.slug}
|
|
376
|
+
)
|
|
377
|
+
return render(
|
|
378
|
+
request, 'admin/wizard/discovery.html', ctx
|
|
379
|
+
)
|
|
369
380
|
new_comp = ctx['form'].save()
|
|
370
381
|
request.session.pop('add_comp_gateway')
|
|
371
382
|
request.session.pop('add_comp_type')
|
|
@@ -373,8 +384,7 @@ class ComponentAdmin(admin.ModelAdmin):
|
|
|
373
384
|
else:
|
|
374
385
|
ctx['form'] = add_form(
|
|
375
386
|
request=request,
|
|
376
|
-
|
|
377
|
-
controller_cls=controller_cls,
|
|
387
|
+
controller_uid=controller_cls.uid,
|
|
378
388
|
initial=request.session.get('c_add_init'),
|
|
379
389
|
)
|
|
380
390
|
pop_fields_from_form(ctx['form'])
|
simo/core/api.py
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
from calendar import monthrange
|
|
3
3
|
import pytz
|
|
4
|
-
import
|
|
4
|
+
import time
|
|
5
5
|
from django.db.models import Q, Prefetch
|
|
6
6
|
from django.utils.translation import gettext_lazy as _
|
|
7
7
|
from django.utils import timezone
|
|
8
8
|
from django.shortcuts import get_object_or_404
|
|
9
|
-
from
|
|
10
|
-
from simo.core.utils.helpers import get_self_ip
|
|
9
|
+
from simo.core.utils.helpers import get_self_ip, search_queryset
|
|
11
10
|
from rest_framework.pagination import PageNumberPagination
|
|
12
11
|
from rest_framework import viewsets
|
|
13
12
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
14
13
|
from rest_framework.decorators import action
|
|
15
14
|
from rest_framework.response import Response as RESTResponse
|
|
16
|
-
from django.core.exceptions import ValidationError
|
|
17
15
|
from rest_framework.exceptions import ValidationError as APIValidationError
|
|
18
|
-
from simo.conf import dynamic_settings
|
|
19
16
|
from simo.core.utils.config_values import ConfigException
|
|
20
17
|
from .models import (
|
|
21
18
|
Instance, Category, Zone, Component, Icon, ComponentHistory,
|
|
22
|
-
HistoryAggregate
|
|
19
|
+
HistoryAggregate, Gateway
|
|
23
20
|
)
|
|
24
21
|
from .serializers import (
|
|
25
22
|
IconSerializer, CategorySerializer, ZoneSerializer,
|
|
26
23
|
ComponentSerializer, ComponentHistorySerializer
|
|
27
24
|
)
|
|
25
|
+
from .permissions import IsInstanceSuperuser, InstanceSuperuserCanEdit
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
class InstanceMixin:
|
|
31
29
|
|
|
32
30
|
def dispatch(self, request, *args, **kwargs):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
try:
|
|
32
|
+
self.instance = Instance.objects.get(
|
|
33
|
+
slug=self.request.resolver_match.kwargs.get('instance_slug')
|
|
34
|
+
)
|
|
35
|
+
except Instance.DoesNotExist:
|
|
36
|
+
raise APIValidationError(
|
|
37
|
+
f"Instance {self.request.resolver_match.kwargs.get('instance_slug')} "
|
|
38
|
+
"is not found on this SIMO.io hub!",
|
|
39
|
+
code=400
|
|
40
|
+
)
|
|
36
41
|
return super().dispatch(request, *args, **kwargs)
|
|
37
42
|
|
|
38
43
|
def get_serializer_context(self):
|
|
@@ -50,18 +55,51 @@ class IconViewSet(viewsets.ReadOnlyModelViewSet):
|
|
|
50
55
|
def get_queryset(self):
|
|
51
56
|
queryset = super().get_queryset()
|
|
52
57
|
if 'slugs' in self.request.GET:
|
|
53
|
-
queryset = queryset.filter(
|
|
58
|
+
queryset = queryset.filter(
|
|
59
|
+
slug__in=self.request.GET['slugs'].split(',')
|
|
60
|
+
)
|
|
61
|
+
if 'q' in self.request.GET:
|
|
62
|
+
queryset = search_queryset(
|
|
63
|
+
queryset, self.request.GET['q'], ['slug', 'keywords']
|
|
64
|
+
)[:10]
|
|
54
65
|
return queryset
|
|
55
66
|
|
|
67
|
+
def get_view_name(self):
|
|
68
|
+
singular = "Icon"
|
|
69
|
+
plural = "Icons"
|
|
70
|
+
suffix = getattr(self, 'suffix', None)
|
|
71
|
+
if suffix and suffix.lower() == 'list':
|
|
72
|
+
return plural
|
|
73
|
+
return singular
|
|
56
74
|
|
|
57
|
-
|
|
75
|
+
|
|
76
|
+
class CategoryViewSet(InstanceMixin, viewsets.ModelViewSet):
|
|
58
77
|
url = 'core/categories'
|
|
59
78
|
basename = 'categories'
|
|
60
|
-
queryset = Category.objects.all()
|
|
61
79
|
serializer_class = CategorySerializer
|
|
62
80
|
|
|
81
|
+
def get_permissions(self):
|
|
82
|
+
permissions = super().get_permissions()
|
|
83
|
+
permissions.append(InstanceSuperuserCanEdit())
|
|
84
|
+
return permissions
|
|
85
|
+
|
|
86
|
+
def get_queryset(self):
|
|
87
|
+
return Category.objects.filter(instance=self.instance)
|
|
88
|
+
|
|
89
|
+
def get_view_name(self):
|
|
90
|
+
singular = "Category"
|
|
91
|
+
plural = "Categories"
|
|
92
|
+
suffix = getattr(self, 'suffix', None)
|
|
93
|
+
if suffix and suffix.lower() == 'list':
|
|
94
|
+
return plural
|
|
95
|
+
return singular
|
|
63
96
|
|
|
64
|
-
|
|
97
|
+
def perform_create(self, serializer):
|
|
98
|
+
serializer.validated_data['instance'] = self.instance
|
|
99
|
+
serializer.save()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ZoneViewSet(InstanceMixin, viewsets.ModelViewSet):
|
|
65
103
|
url = 'core/zones'
|
|
66
104
|
basename = 'zones'
|
|
67
105
|
serializer_class = ZoneSerializer
|
|
@@ -69,6 +107,23 @@ class ZoneViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
|
|
|
69
107
|
def get_queryset(self):
|
|
70
108
|
return Zone.objects.filter(instance=self.instance)
|
|
71
109
|
|
|
110
|
+
def get_permissions(self):
|
|
111
|
+
permissions = super().get_permissions()
|
|
112
|
+
permissions.append(InstanceSuperuserCanEdit())
|
|
113
|
+
return permissions
|
|
114
|
+
|
|
115
|
+
def get_view_name(self):
|
|
116
|
+
singular = "Zone"
|
|
117
|
+
plural = "Zones"
|
|
118
|
+
suffix = getattr(self, 'suffix', None)
|
|
119
|
+
if suffix and suffix.lower() == 'list':
|
|
120
|
+
return plural
|
|
121
|
+
return singular
|
|
122
|
+
|
|
123
|
+
def perform_create(self, serializer):
|
|
124
|
+
serializer.validated_data['instance'] = self.instance
|
|
125
|
+
serializer.save()
|
|
126
|
+
|
|
72
127
|
|
|
73
128
|
def get_components_queryset(instance, user):
|
|
74
129
|
qs = Component.objects.filter(zone__instance=instance)
|
|
@@ -102,17 +157,31 @@ def get_components_queryset(instance, user):
|
|
|
102
157
|
return qs
|
|
103
158
|
|
|
104
159
|
|
|
105
|
-
class ComponentViewSet(InstanceMixin, viewsets.
|
|
160
|
+
class ComponentViewSet(InstanceMixin, viewsets.ModelViewSet):
|
|
106
161
|
url = 'core/components'
|
|
107
162
|
basename = 'components'
|
|
108
163
|
serializer_class = ComponentSerializer
|
|
109
164
|
|
|
165
|
+
def get_permissions(self):
|
|
166
|
+
permissions = super().get_permissions()
|
|
167
|
+
permissions.append(InstanceSuperuserCanEdit())
|
|
168
|
+
return permissions
|
|
169
|
+
|
|
110
170
|
def get_queryset(self):
|
|
111
171
|
return get_components_queryset(self.instance, self.request.user).filter(
|
|
112
172
|
zone__instance=self.instance
|
|
113
173
|
)
|
|
114
174
|
|
|
175
|
+
def get_view_name(self):
|
|
176
|
+
singular = "Component"
|
|
177
|
+
plural = "Components"
|
|
178
|
+
suffix = getattr(self, 'suffix', None)
|
|
179
|
+
if suffix and suffix.lower() == 'list':
|
|
180
|
+
return plural
|
|
181
|
+
return singular
|
|
182
|
+
|
|
115
183
|
def perform_controller_method(self, json_data, component):
|
|
184
|
+
component.prepare_controller()
|
|
116
185
|
for method_name, param in json_data.items():
|
|
117
186
|
if not hasattr(component, method_name):
|
|
118
187
|
raise APIValidationError(
|
|
@@ -152,7 +221,7 @@ class ComponentViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
|
|
|
152
221
|
json_data = request.data
|
|
153
222
|
subcomponent_id = json_data.pop('id', -1)
|
|
154
223
|
try:
|
|
155
|
-
subcomponent = component.
|
|
224
|
+
subcomponent = component.slaves.get(pk=subcomponent_id)
|
|
156
225
|
except Component.DoesNotExist:
|
|
157
226
|
raise APIValidationError(
|
|
158
227
|
_('Subcomponent with id %d does not exist!' % str(subcomponent_id)),
|
|
@@ -463,7 +532,6 @@ class InfoViewSet(InstanceMixin, viewsets.GenericViewSet):
|
|
|
463
532
|
permission_classes = []
|
|
464
533
|
|
|
465
534
|
def list(self, request, format=None, *args, **kwargs):
|
|
466
|
-
from simo.conf import dynamic_settings
|
|
467
535
|
resp = RESTResponse({'uid': self.instance.uid})
|
|
468
536
|
resp["Access-Control-Allow-Origin"] = "*"
|
|
469
537
|
return resp
|
|
@@ -502,3 +570,76 @@ class StatesViewSet(InstanceMixin, viewsets.GenericViewSet):
|
|
|
502
570
|
}
|
|
503
571
|
).data
|
|
504
572
|
})
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class ControllerTypes(InstanceMixin, viewsets.GenericViewSet):
|
|
576
|
+
url = 'core/controller-types'
|
|
577
|
+
basename = 'controller-types'
|
|
578
|
+
queryset = []
|
|
579
|
+
|
|
580
|
+
def get_permissions(self):
|
|
581
|
+
permissions = super().get_permissions()
|
|
582
|
+
permissions.append(IsInstanceSuperuser())
|
|
583
|
+
return permissions
|
|
584
|
+
|
|
585
|
+
def list(self, request, *args, **kwargs):
|
|
586
|
+
from .utils.type_constants import get_controller_types_map
|
|
587
|
+
data = {}
|
|
588
|
+
|
|
589
|
+
for uid, cls in get_controller_types_map().items():
|
|
590
|
+
if cls.gateway_class.name not in data:
|
|
591
|
+
data[cls.gateway_class.name] = []
|
|
592
|
+
data[cls.gateway_class.name].append({
|
|
593
|
+
'uid': uid,
|
|
594
|
+
'name': cls.name,
|
|
595
|
+
'is_discoverable': cls.is_discoverable
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
return RESTResponse(data)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
class RunningDiscoveries(InstanceMixin, viewsets.GenericViewSet):
|
|
602
|
+
url = 'core/discoveries'
|
|
603
|
+
basename = 'discoveries'
|
|
604
|
+
queryset = []
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def get_permissions(self):
|
|
608
|
+
permissions = super().get_permissions()
|
|
609
|
+
permissions.append(IsInstanceSuperuser())
|
|
610
|
+
return permissions
|
|
611
|
+
|
|
612
|
+
def get_data(self, gateways):
|
|
613
|
+
data = []
|
|
614
|
+
for gateway in gateways:
|
|
615
|
+
data.append({
|
|
616
|
+
'gateway': gateway.id,
|
|
617
|
+
'start': gateway.discovery['start'],
|
|
618
|
+
'controller_uid': gateway.discovery['controller_uid'],
|
|
619
|
+
'result': gateway.discovery['result'],
|
|
620
|
+
'finished': gateway.discovery.get('finished'),
|
|
621
|
+
})
|
|
622
|
+
return data
|
|
623
|
+
|
|
624
|
+
def get_gateways(self, request):
|
|
625
|
+
gateways = Gateway.objects.filter(
|
|
626
|
+
discovery__start__gt=time.time() - 60 * 60 # no more than an hour
|
|
627
|
+
)
|
|
628
|
+
if 'controller_uid' in request.GET:
|
|
629
|
+
gateways = gateways.filter(
|
|
630
|
+
discovery__controller_uid=request.GET['controller_uid']
|
|
631
|
+
)
|
|
632
|
+
return gateways
|
|
633
|
+
|
|
634
|
+
def list(self, request, *args, **kwargs):
|
|
635
|
+
return RESTResponse(self.get_data(self.get_gateways(request)))
|
|
636
|
+
|
|
637
|
+
@action(detail=False, methods=['get'])
|
|
638
|
+
def retry(self, request, *args, **kwargs):
|
|
639
|
+
gateways = self.get_gateways(request)
|
|
640
|
+
if 'controller_uid' in request.GET:
|
|
641
|
+
for gateway in gateways:
|
|
642
|
+
gateway.retry_discovery()
|
|
643
|
+
return RESTResponse(self.get_data(gateways))
|
|
644
|
+
|
|
645
|
+
|
simo/core/api_meta.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from django.utils.encoding import force_str
|
|
3
|
+
from rest_framework import serializers
|
|
4
|
+
from rest_framework.metadata import SimpleMetadata
|
|
5
|
+
from rest_framework.utils.field_mapping import ClassLookupDict
|
|
6
|
+
from .serializers import ComponentManyToManyRelatedField
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SIMOAPIMetadata(SimpleMetadata):
|
|
10
|
+
|
|
11
|
+
label_lookup = ClassLookupDict({
|
|
12
|
+
serializers.Field: 'field',
|
|
13
|
+
serializers.BooleanField: 'boolean',
|
|
14
|
+
serializers.CharField: 'string',
|
|
15
|
+
serializers.UUIDField: 'string',
|
|
16
|
+
serializers.URLField: 'url',
|
|
17
|
+
serializers.EmailField: 'email',
|
|
18
|
+
serializers.RegexField: 'regex',
|
|
19
|
+
serializers.SlugField: 'slug',
|
|
20
|
+
serializers.IntegerField: 'integer',
|
|
21
|
+
serializers.FloatField: 'float',
|
|
22
|
+
serializers.DecimalField: 'decimal',
|
|
23
|
+
serializers.DateField: 'date',
|
|
24
|
+
serializers.DateTimeField: 'datetime',
|
|
25
|
+
serializers.TimeField: 'time',
|
|
26
|
+
serializers.ChoiceField: 'choice',
|
|
27
|
+
serializers.MultipleChoiceField: 'multiple choice',
|
|
28
|
+
serializers.FileField: 'file upload',
|
|
29
|
+
serializers.ImageField: 'image upload',
|
|
30
|
+
serializers.ListField: 'list',
|
|
31
|
+
serializers.DictField: 'nested object',
|
|
32
|
+
serializers.Serializer: 'nested object',
|
|
33
|
+
serializers.RelatedField: 'related object',
|
|
34
|
+
serializers.ManyRelatedField: 'many related objects',
|
|
35
|
+
ComponentManyToManyRelatedField: 'many related objects'
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
def get_field_info(self, field):
|
|
39
|
+
"""
|
|
40
|
+
Given an instance of a serializer field, return a dictionary
|
|
41
|
+
of metadata about it.
|
|
42
|
+
"""
|
|
43
|
+
field_info = OrderedDict()
|
|
44
|
+
field_info['type'] = self.label_lookup[field]
|
|
45
|
+
field_info['required'] = getattr(field, 'required', False)
|
|
46
|
+
|
|
47
|
+
form_field = field.style.get('form_field')
|
|
48
|
+
if form_field:
|
|
49
|
+
if hasattr(form_field, 'queryset'):
|
|
50
|
+
model = form_field.queryset.model
|
|
51
|
+
field_info['related_object'] = ".".join(
|
|
52
|
+
[model.__module__, model.__name__]
|
|
53
|
+
)
|
|
54
|
+
if hasattr(form_field, 'filter_by'):
|
|
55
|
+
field_info['filter_by'] = form_field.filter_by
|
|
56
|
+
|
|
57
|
+
attrs = [
|
|
58
|
+
'read_only', 'label', 'help_text',
|
|
59
|
+
'min_length', 'max_length',
|
|
60
|
+
'min_value', 'max_value',
|
|
61
|
+
'initial'
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
for attr in attrs:
|
|
65
|
+
value = getattr(field, attr, None)
|
|
66
|
+
if value is not None and value != '':
|
|
67
|
+
field_info[attr] = force_str(value, strings_only=True)
|
|
68
|
+
|
|
69
|
+
if getattr(field, 'child', None):
|
|
70
|
+
field_info['child'] = self.get_field_info(field.child)
|
|
71
|
+
elif getattr(field, 'fields', None):
|
|
72
|
+
field_info['children'] = self.get_serializer_info(field)
|
|
73
|
+
|
|
74
|
+
if not field_info.get('read_only') and hasattr(field, 'choices'):
|
|
75
|
+
add_choices = True
|
|
76
|
+
queryset = getattr(field, 'queryset', None)
|
|
77
|
+
if queryset and queryset.count() > 1000:
|
|
78
|
+
add_choices = False
|
|
79
|
+
if add_choices:
|
|
80
|
+
field_info['choices'] = [
|
|
81
|
+
{
|
|
82
|
+
'value': choice_value,
|
|
83
|
+
'display_name': force_str(choice_name, strings_only=True)
|
|
84
|
+
}
|
|
85
|
+
for choice_value, choice_name in field.choices.items()
|
|
86
|
+
]
|
|
87
|
+
return field_info
|
simo/core/auto_urls.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from django.urls import path
|
|
2
|
-
from .views import
|
|
2
|
+
from .views import (
|
|
3
|
+
get_timestamp, setup_wizard, update, restart, reboot, set_instance
|
|
4
|
+
)
|
|
3
5
|
from .autocomplete_views import (
|
|
4
6
|
IconModelAutocomplete, #IconSlugAutocomplete,
|
|
5
7
|
CategoryAutocomplete, ZoneAutocomplete,
|
|
@@ -28,6 +30,7 @@ urlpatterns = [
|
|
|
28
30
|
'autocomplete-component',
|
|
29
31
|
ComponentAutocomplete.as_view(), name='autocomplete-component'
|
|
30
32
|
),
|
|
33
|
+
path('set-instance/<slug:instance_slug>/', set_instance, name='set-instance'),
|
|
31
34
|
path('setup/', setup_wizard, name='setup-wizard'),
|
|
32
35
|
path('update/', update, name='update'),
|
|
33
36
|
path('restart/', restart, name='restart'),
|
simo/core/autocomplete_views.py
CHANGED
|
@@ -18,7 +18,7 @@ class IconModelAutocomplete(autocomplete.Select2QuerySetView):
|
|
|
18
18
|
|
|
19
19
|
if self.q:
|
|
20
20
|
qs = search_queryset(qs, self.q, ('slug', 'keywords'))
|
|
21
|
-
return qs
|
|
21
|
+
return qs.distinct()
|
|
22
22
|
|
|
23
23
|
def get_result_label(self, item):
|
|
24
24
|
return render_to_string(
|
|
@@ -55,6 +55,7 @@ class CategoryAutocomplete(autocomplete.Select2QuerySetView):
|
|
|
55
55
|
def get_queryset(self):
|
|
56
56
|
qs = Category.objects.all()
|
|
57
57
|
|
|
58
|
+
|
|
58
59
|
if not self.request.user.is_staff:
|
|
59
60
|
return qs.none()
|
|
60
61
|
|
|
@@ -64,7 +65,7 @@ class CategoryAutocomplete(autocomplete.Select2QuerySetView):
|
|
|
64
65
|
qs = qs.filter(all=False)
|
|
65
66
|
if self.q:
|
|
66
67
|
qs = search_queryset(qs, self.q, ('name'))
|
|
67
|
-
return qs
|
|
68
|
+
return qs.distinct()
|
|
68
69
|
|
|
69
70
|
def get_result_label(self, item):
|
|
70
71
|
return render_to_string(
|
|
@@ -90,7 +91,7 @@ class ZoneAutocomplete(autocomplete.Select2QuerySetView):
|
|
|
90
91
|
|
|
91
92
|
if self.q:
|
|
92
93
|
qs = search_queryset(qs, self.q, ('name',))
|
|
93
|
-
return qs
|
|
94
|
+
return qs.distinct()
|
|
94
95
|
|
|
95
96
|
def get_result_label(self, item):
|
|
96
97
|
return render_to_string(
|
|
@@ -119,6 +120,9 @@ class ComponentAutocomplete(autocomplete.Select2QuerySetView):
|
|
|
119
120
|
if 'base_type' in self.forwarded:
|
|
120
121
|
qs = qs.filter(base_type__in=self.forwarded['base_type'])
|
|
121
122
|
|
|
123
|
+
if 'controller_uid' in self.forwarded:
|
|
124
|
+
qs = qs.filter(controller_uid__in=self.forwarded['controller_uid'])
|
|
125
|
+
|
|
122
126
|
if 'alarm_category' in self.forwarded:
|
|
123
127
|
qs = qs.filter(
|
|
124
128
|
Q(base_type='alarm-group') |
|
|
@@ -127,7 +131,7 @@ class ComponentAutocomplete(autocomplete.Select2QuerySetView):
|
|
|
127
131
|
|
|
128
132
|
if self.q:
|
|
129
133
|
qs = search_queryset(qs, self.q, ('name',))
|
|
130
|
-
return qs
|
|
134
|
+
return qs.distinct()
|
|
131
135
|
|
|
132
136
|
def get_result_label(self, item):
|
|
133
137
|
return render_to_string(
|
simo/core/base_types.py
CHANGED
simo/core/context.py
CHANGED
|
@@ -6,6 +6,7 @@ from django.apps import apps
|
|
|
6
6
|
from simo.core.models import Instance
|
|
7
7
|
from simo.conf import dynamic_settings
|
|
8
8
|
from simo.core.utils.helpers import is_update_available
|
|
9
|
+
from simo.core.middleware import get_current_instance
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def additional_templates_context(request):
|
|
@@ -14,7 +15,8 @@ def additional_templates_context(request):
|
|
|
14
15
|
'dynamic_settings': dynamic_settings,
|
|
15
16
|
'current_version': pkg_resources.get_distribution('simo').version,
|
|
16
17
|
'update_available': is_update_available(True),
|
|
17
|
-
'instances': Instance.objects.all()
|
|
18
|
+
'instances': Instance.objects.all(),
|
|
19
|
+
'current_instance': get_current_instance()
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
if request.path.endswith('/admin/'):
|