diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml index 9cd630e7..0f5863b2 100644 --- a/.github/workflows/semantic-release.yml +++ b/.github/workflows/semantic-release.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 - name: Python Semantic Release - uses: relekang/python-semantic-release@v8.0.8 + uses: relekang/python-semantic-release@v9.4.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dba5c485..c02bb08f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ exclude: '^static/[bootstrap|htmx]/' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 + rev: v4.5.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 24.1.1 hooks: - id: black diff --git a/app/Entirety/.env.EXAMPLE b/app/Entirety/.env.EXAMPLE index e75be9d6..6e97b30e 100644 --- a/app/Entirety/.env.EXAMPLE +++ b/app/Entirety/.env.EXAMPLE @@ -2,6 +2,7 @@ ENTITIES_LOAD=True DEVICES_LOAD=True NOTIFICATIONS_LOAD=True +SEMANTICS_LOAD=True # Database DATABASE_PORT=5432 @@ -26,6 +27,7 @@ LOKI_PROTOCOL=http LOKI_SRC_HOST=entirety LOKI_TIMEZONE=Europe/Berlin +LOCAL_AUTH=True # OIDC OIDC_OP_AUTHORIZATION_ENDPOINT= OIDC_OP_JWKS_ENDPOINT= diff --git a/app/Entirety/devices/forms/servicegroups.py b/app/Entirety/devices/forms/servicegroups.py index 314fa47e..071e7f65 100644 --- a/app/Entirety/devices/forms/servicegroups.py +++ b/app/Entirety/devices/forms/servicegroups.py @@ -1,5 +1,7 @@ from crispy_forms.helper import FormHelper from django import forms +from smartdatamodels.models import SmartDataModel +import json class ServiceGroupBasic(forms.Form): @@ -10,7 +12,7 @@ class ServiceGroupBasic(forms.Form): attrs={ "data-bs-toggle": "tooltip", "title": "A string representing the southbound resource " - "that will be used to provision a device, e.g. /iot/json", + "that will be used to provision a device, e.g. /iot/json", } ), ) @@ -35,10 +37,36 @@ class ServiceGroupBasic(forms.Form): } ), ) - explicit_attrs = forms.BooleanField(label="Explicit Attributes", required=False, initial=False) - autoprovision = forms.BooleanField(label="Auto Provision", required=False, initial=True) + explicit_attrs = forms.BooleanField( + label="Explicit Attributes", required=False, initial=False + ) + autoprovision = forms.BooleanField( + label="Auto Provision", required=False, initial=True + ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.form_tag = False + + +class SmartDataModelServicesForm(forms.Form): + select_data_model = forms.ChoiceField(label="Select a Data Model", required=True) + only_required_attrs = forms.BooleanField( + label="Only use required attributes", required=False, initial=False + ) + + def __init__(self, *args, **kwargs): + super(SmartDataModelServicesForm, self).__init__(*args, **kwargs) + qs = SmartDataModel.objects.all() + list_of_schemas = [] + for set in qs: + list_of_schemas.append( + {"name": set.name, "value": json.dumps(set.jsonschema)} + ) + list_of_schemas.append({"name": "..", "value": ".."}) + self.fields["select_data_model"] = forms.ChoiceField( + choices=[(x.get("value"), x.get("name")) for x in list_of_schemas] + ) + self.helper = FormHelper(self) + self.helper.form_tag = False diff --git a/app/Entirety/devices/templates/devices/batch.html b/app/Entirety/devices/templates/devices/batch.html index 928ee3d8..9b2402d7 100644 --- a/app/Entirety/devices/templates/devices/batch.html +++ b/app/Entirety/devices/templates/devices/batch.html @@ -3,11 +3,10 @@ {% load compress %} {% load crispy_forms_tags %} - -{% block title %}Create Batch Devices {% endblock %} +{% block title %}Create Batch Devices{% endblock %} {% block content %} - Create Batch Devices + Create Batch Devices
{% csrf_token %} @@ -15,13 +14,11 @@ - +
{% compress js %} {% endcompress %} - - {% endblock %} diff --git a/app/Entirety/devices/templates/devices/datamodels.html b/app/Entirety/devices/templates/devices/datamodels.html new file mode 100644 index 00000000..4100287f --- /dev/null +++ b/app/Entirety/devices/templates/devices/datamodels.html @@ -0,0 +1,29 @@ +{% extends '_base.html' %} +{% load static %} +{% load compress %} +{% load crispy_forms_tags %} + + +{% block title %}Create device or service group from data model {% endblock %} + +{% block content %} +{# Create Device#} +
+ {% crispy smart_data_model_form %} + +
+{#
#} +{#
#} +{# {% csrf_token %}#} +{# {{ json_form.media }}#} +{# {{ json_form }}#} +{# #} +{#
#} +{#
#} + +{# {% compress js %}#} +{# #} +{# {% endcompress %}#} + + +{% endblock %} diff --git a/app/Entirety/devices/templates/devices/detail.html b/app/Entirety/devices/templates/devices/detail.html index 99ea626d..3f245142 100644 --- a/app/Entirety/devices/templates/devices/detail.html +++ b/app/Entirety/devices/templates/devices/detail.html @@ -1,4 +1,6 @@ {% extends '_base.html' %} +{% load crispy_forms_tags %} + {% block title %} {% if action == "Create" %} @@ -14,27 +16,31 @@ {% block content %} {% if action == "Create" %} -

Create Device

+ Create Device {% elif action == "Edit" %} -

Edit Device

+ Edit Device {% elif action == "Create_Group" %} -

Create Service Group

+ Create Service Group {% elif action == "Edit_Group" %} -

Edit Service Group

+ Edit Service Group {% endif %} {% if action == "Create" %}
+ {% crispy smart_data_model_form %} + {% elif action == "Edit" %} {% elif action == "Create_Group" %} + {% crispy smart_data_model_form %} + {% elif action == "Edit_Group" %} {% endif %} {% csrf_token %} {% include 'accordion.html' %} -
diff --git a/app/Entirety/devices/templates/devices/list.html b/app/Entirety/devices/templates/devices/list.html index 2ed33523..b966223e 100644 --- a/app/Entirety/devices/templates/devices/list.html +++ b/app/Entirety/devices/templates/devices/list.html @@ -52,29 +52,34 @@

Devices

-
- {% csrf_token %} -
- - -
+ + - - + + + + {% render_table tables.0 %} @@ -169,24 +174,33 @@

Services Group

- {% csrf_token %} -
- -
+ + - - + + + + {% render_table tables.1 %} diff --git a/app/Entirety/devices/urls.py b/app/Entirety/devices/urls.py index 9e68e9ac..81741031 100644 --- a/app/Entirety/devices/urls.py +++ b/app/Entirety/devices/urls.py @@ -7,7 +7,7 @@ DeviceEditSubmitView, DeviceEditView, DeviceDeleteView, - DeviceBatchCreateView + DeviceCreateBatchView ) from devices.views import ( ServiceGroupListSubmitView, @@ -15,13 +15,15 @@ ServiceGroupCreateSubmitView, ServiceGroupEditView, ServiceGroupEditSubmitView, - ServiceGroupDeleteView + ServiceGroupDeleteView, + ServiceGroupDataModelCreateView, + ServiceGroupDataModelCreateSubmitView ) app_name = "devices" urlpatterns = [ path("create", DeviceCreateView.as_view(), name="create"), - path("batchcreate", DeviceBatchCreateView.as_view(), name="batchcreate"), + path("create/batch", DeviceCreateBatchView.as_view(), name="create_batch"), path("edit", DeviceEditView.as_view(), name="edit"), path("edit-submit", DeviceEditSubmitView.as_view(), name="edit_submit"), path("create-submit", DeviceCreateSubmitView.as_view(), name="create_submit"), @@ -34,4 +36,7 @@ path("create-submit-group", ServiceGroupCreateSubmitView.as_view(), name="create_submit_group"), path("list-submit-group", ServiceGroupListSubmitView.as_view(), name="list_submit_group"), path("delete-group", ServiceGroupDeleteView.as_view(), name="delete_group"), + path("create-group-datamodel", ServiceGroupDataModelCreateView.as_view(), name="create_group_datamodel"), + path("create-submit-group-datamodel", ServiceGroupDataModelCreateSubmitView.as_view(), + name="create_submit_group_datamodel") ] diff --git a/app/Entirety/devices/utils.py b/app/Entirety/devices/utils.py index d0c5cbf0..1d47ca8c 100644 --- a/app/Entirety/devices/utils.py +++ b/app/Entirety/devices/utils.py @@ -5,13 +5,23 @@ from filip.clients.ngsi_v2 import IoTAClient from filip.models import FiwareHeader from filip.models.ngsi_v2.iot import Device, DeviceAttribute, DeviceCommand, ServiceGroup - +from filip.models.base import DataType # global settings prefix_attributes = "attributes" prefix_commands = "commands" +JSONSchemaMap = { + "string": DataType.TEXT.value, + "number": DataType.NUMBER.value, + "integer": DataType.INTEGER.value, + "object": DataType.STRUCTUREDVALUE.value, + "array": DataType.ARRAY.value, + "boolean": DataType.BOOLEAN.value +} + + def get_device_by_id(project: Project, device_id): """ Get device by id for current project @@ -379,3 +389,36 @@ def get_data_from_session(request, key): return request.session.pop(key) else: return None + + +# TODO deprecate +def _get_attributes_from_data_model(data_model, only_required_attrs): + properties = data_model.get("properties") + required_attrs = data_model.get("required") + print(f"required_attrs: {required_attrs}") + attributes = [] + if properties: + for prop_name in properties: + if prop_name in ("id", "type"): + continue + if only_required_attrs and prop_name not in required_attrs: + continue + try: + attr_type = JSONSchemaMap[properties[prop_name]["type"]] + except: + attr_type = DataType.TEXT.value # by default use text + attribute = { + "name": prop_name, + "type": attr_type, + "object_id": None + } + attributes.append(attribute) + return attributes + + + +# TODO deprecate +def _get_entity_type_from_data_model(data_model): + if data_model.get("properties").get("type").get("enum"): + if len(data_model.get("properties").get("type").get("enum")) == 1: + return data_model.get("properties").get("type").get("enum")[0] diff --git a/app/Entirety/devices/views/devices.py b/app/Entirety/devices/views/devices.py index 0664c12e..bff49920 100644 --- a/app/Entirety/devices/views/devices.py +++ b/app/Entirety/devices/views/devices.py @@ -28,6 +28,37 @@ from devices.tables import DevicesTable, GroupsTable from requests.exceptions import RequestException from pydantic import ValidationError +from requests.exceptions import RequestException +import logging +from django.shortcuts import render, redirect +from django.views.generic import TemplateView +from django.contrib import messages +from entities.forms import ( + EntityForm, + AttributeForm, + SubscriptionForm, + RelationshipForm, + SelectionForm, + DeviceForm, + JSONForm, +) +from smartdatamodels.forms import SmartDataModelQueryForm +from entities.requests import ( + get_entity, + post_entity, + update_entity, + get_relationships, + # get_devices, + get_subscriptions, + delete_subscription, + delete_relationship, + # delete_device, + delete_entity, + delete_entities, +) +from entities.tables import EntityTable +from projects.mixins import ProjectContextMixin +from utils.parser import parser, parse_entity, parse_device logger = logging.getLogger(__name__) @@ -161,11 +192,13 @@ def get(self, request, *args, **kwargs): attributes = Attributes(prefix=prefix_attributes) commands = Commands(prefix=prefix_commands) context: dict = super(DeviceCreateView, self).get_context_data(**kwargs) + smart_data_model_form = SmartDataModelQueryForm(initial={"data_model": ".."}) context = { "basic_info": basic_info, "attributes": attributes, "commands": commands, "action": "Create", + "smart_data_model_form": smart_data_model_form, **context, } return render(request, "devices/detail.html", context) @@ -227,68 +260,108 @@ def post(self, request, *args, **kwargs): class DeviceCreateSubmitView(ProjectContextMixin, TemplateView): def post(self, request: HttpRequest, **kwargs): - # preprocess the request query data - data_basic, data_attributes, data_commands = parse_request_data( - request.POST, BasicForm=DeviceBasic - ) + # Load data model + if "load" in self.request.POST: + # Load device data model + if self.request.POST.get("data_model") == "..": + device_dict = {} + else: + device_dict = parse_device(self.request.POST.get("data_model")) + # get the project context data + context: dict = super(DeviceCreateSubmitView, self).get_context_data( + **kwargs + ) - # create forms from query data - basic_info = DeviceBasic(data=data_basic) - attributes = Attributes(data=data_attributes, prefix=prefix_attributes) - commands = Commands(data=data_commands, prefix=prefix_commands) + # pass the json to devices form + basic_info = DeviceBasic(initial=device_dict) + attributes = Attributes( + initial=device_dict.get("attributes"), prefix=prefix_attributes + ) + commands = Commands(prefix=prefix_commands) - if basic_info.is_valid() and attributes.is_valid() and commands.is_valid(): - try: - device = build_device( - data_basic=data_basic, - data_attributes=data_attributes, - data_commands=data_commands, - ) - post_device(device, project=self.project) - logger.info( - "Device created by " - + str( - self.request.user.first_name - if self.request.user.first_name - else self.request.user.username + context["smart_data_model_form"] = SmartDataModelQueryForm( + initial=request.POST + ) + context = { + "basic_info": basic_info, + "attributes": attributes, + "commands": commands, + "action": "Create", + **context, + } + return render(request, "devices/detail.html", context) + elif "submit" in self.request.POST: + # preprocess the request query data + data_basic, data_attributes, data_commands = parse_request_data( + request.POST, BasicForm=DeviceBasic + ) + + # create forms from query data + basic_info = DeviceBasic(data=data_basic) + attributes = Attributes(data=data_attributes, prefix=prefix_attributes) + commands = Commands(data=data_commands, prefix=prefix_commands) + + if basic_info.is_valid() and attributes.is_valid() and commands.is_valid(): + try: + device = build_device( + data_basic=data_basic, + data_attributes=data_attributes, + data_commands=data_commands, + ) + post_device(device, project=self.project) + logger.info( + "Device created by " + + str( + self.request.user.first_name + if self.request.user.first_name + else self.request.user.username + ) + + f" in project {self.project.name}" ) - + f" in project {self.project.name}" - ) - return redirect("projects:devices:list", project_id=self.project.uuid) - # handel the error from server - except RequestException as e: - messages.error(request, e.response.content.decode("utf-8")) - if "DUPLICATE_DEVICE_ID" in e.response.content.decode("utf-8"): - device_id = device.device_id - add_data_to_session(request, "search-pattern", device_id) return redirect( "projects:devices:list", project_id=self.project.uuid ) - logger.error( - str( - self.request.user.first_name - if self.request.user.first_name - else self.request.user.username + # handel the error from server + except RequestException as e: + messages.error(request, e.response.content.decode("utf-8")) + if "DUPLICATE_DEVICE_ID" in e.response.content.decode("utf-8"): + device_id = device.device_id + add_data_to_session(request, "search-pattern", device_id) + return redirect( + "projects:devices:list", project_id=self.project.uuid + ) + logger.error( + str( + self.request.user.first_name + if self.request.user.first_name + else self.request.user.username + ) + + " tried creating device" + + " but failed with error " + + json.loads(e.response.content.decode("utf-8")).get("message") + + f" in project {self.project.name}" ) - + " tried creating device" - + " but failed with error " - + json.loads(e.response.content.decode("utf-8")).get("message") - + f" in project {self.project.name}" - ) - except ValidationError as e: - messages.error(request, e.raw_errors[0].exc.__str__()) + except ValidationError as e: + messages.error(request, e.raw_errors[0].exc.__str__()) - # get the project context data - context: dict = super(DeviceCreateSubmitView, self).get_context_data(**kwargs) + # get the project context data + context: dict = super(DeviceCreateSubmitView, self).get_context_data( + **kwargs + ) + context["smart_data_model_form"] = SmartDataModelQueryForm( + initial=request.POST + ) - context = { - "basic_info": basic_info, - "attributes": attributes, - "commands": commands, - "action": "Create", - **context, - } - return render(request, "devices/detail.html", context) + context = { + "basic_info": basic_info, + "attributes": attributes, + "commands": commands, + "action": "Create", + **context, + } + return render(request, "devices/detail.html", context) + else: + raise NotImplementedError # Edit devices @@ -438,3 +511,53 @@ def get(self, request: HttpRequest, *args, **kwargs): # if success, redirect to devices list view return redirect("projects:devices:list", project_id=self.project.uuid) + + +class DeviceCreateBatchView(ProjectContextMixin, TemplateView): + template_name = "devices/batch.html" + form_class = JSONForm + + def get_context_data(self, **kwargs): + json_form = self.form_class() + context = super(DeviceCreateBatchView, self).get_context_data(**kwargs) + context["json_form"] = json_form + return context + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST) + context = super(DeviceCreateBatchView, self).get_context_data(**kwargs) + context["json_form"] = form + if form.is_valid(): + devices_json = json.loads(self.request.POST.get("device_json")) + devices_to_add = [ + DeviceBasic(**device_json) for device_json in devices_json + ] + + try: + post_device(devices_to_add, project=self.project) + logger.info( + "Batch of devices created by " + + str( + self.request.user.first_name + if self.request.user.first_name + else self.request.user.username + ) + + f" in project {self.project.name}" + ) + return redirect("projects:devices:list", project_id=self.project.uuid) + except RequestException as e: + messages.error(self.request, e.response.content.decode("utf-8")) + logger.error( + str( + self.request.user.first_name + if self.request.user.first_name + else self.request.user.username + ) + + f" tried creating a batch of devices but failed with error " + + json.loads(e.response.content.decode("utf-8")).get("message") + + f" in project {self.project.name}" + ) + except ValidationError as e: + messages.error(self.request, e.raw_errors[0].exc.__str__()) + else: + return render(request, self.template_name, context) diff --git a/app/Entirety/devices/views/servicegroups.py b/app/Entirety/devices/views/servicegroups.py index ef294d9e..4323d42d 100644 --- a/app/Entirety/devices/views/servicegroups.py +++ b/app/Entirety/devices/views/servicegroups.py @@ -7,6 +7,15 @@ from entirety.utils import add_data_to_session, pop_data_from_session from projects.mixins import ProjectContextMixin, ProjectContextAndViewOnlyMixin from devices.forms import ServiceGroupBasic, Attributes, Commands +from smartdatamodels.forms import SmartDataModelQueryForm +from utils.json_schema_parser import EntiretyJsonSchemaParser +from projects.mixins import ProjectContextMixin +from devices.forms import ( + ServiceGroupBasic, + Attributes, + Commands, + SmartDataModelServicesForm, +) from devices.utils import ( prefix_attributes, prefix_commands, @@ -22,6 +31,8 @@ from pydantic import ValidationError import logging +from utils.parser import parse_device + logger = logging.getLogger(__name__) @@ -46,6 +57,12 @@ def post(self, request, *args, **kwargs): "projects:devices:create_group", project_id=self.project.uuid ) + # press create from data model button + elif request.POST.get("Create_Group_Data_Model"): + return redirect( + "projects:devices:create_group_datamodel", project_id=self.project.uuid + ) + # press edit button elif request.POST.get("Edit_Group"): if not request.POST.get("selection"): @@ -65,13 +82,26 @@ def post(self, request, *args, **kwargs): # Create service group class ServiceGroupCreateView(ProjectContextMixin, TemplateView): def get(self, request, *args, **kwargs): - basic_info = ServiceGroupBasic(initial={"resource": "/iot/json"}) + # if data_model: + # json_schema_parser = EntiretyJsonSchemaParser(data_model=data_model) + # only_required_attrs = pop_data_from_session(request, "only_required_attrs") + # service_group_template = json_schema_parser.parse_to_service_group( + # only_required_attrs=only_required_attrs) + # attributes_model = [attr.dict() for attr in service_group_template.attributes] + # attributes = Attributes( + # initial=attributes_model, prefix=prefix_attributes + # ) + # basic_info = ServiceGroupBasic(initial={"resource": service_group_template.resource, + # "entity_type": service_group_template.entity_type}) + smart_data_model_form = SmartDataModelQueryForm(initial={"data_model": ".."}) attributes = Attributes(prefix=prefix_attributes) + basic_info = ServiceGroupBasic(initial={"resource": "/iot/json"}) context: dict = super(ServiceGroupCreateView, self).get_context_data(**kwargs) context = { "basic_info": basic_info, "attributes": attributes, "action": "Create_Group", + "smart_data_model_form": smart_data_model_form, **context, } return render(request, "devices/detail.html", context) @@ -79,61 +109,114 @@ def get(self, request, *args, **kwargs): class ServiceGroupCreateSubmitView(ProjectContextMixin, TemplateView): def post(self, request: HttpRequest, **kwargs): - # preprocess the request query data - data_basic, data_attributes, _ = parse_request_data( - request.POST, BasicForm=ServiceGroupBasic - ) + if "load" in self.request.POST: + # Load device data model + if self.request.POST.get("data_model") == "..": + device_dict = {} + else: + device_dict = parse_device(self.request.POST.get("data_model")) + device_dict.update({"resource": "/iot/json"}) + basic_info = ServiceGroupBasic(initial=device_dict) + attributes = Attributes( + initial=device_dict.get("attributes"), prefix=prefix_attributes + ) - # create forms from query data - basic_info = ServiceGroupBasic(data=data_basic) - attributes = Attributes(data=data_attributes, prefix=prefix_attributes) + # get the project context data + context: dict = super(ServiceGroupCreateSubmitView, self).get_context_data( + **kwargs + ) + context["smart_data_model_form"] = SmartDataModelQueryForm( + initial=request.POST + ) + context = { + "basic_info": basic_info, + "attributes": attributes, + "action": "Create_Group", + **context, + } + return render(request, "devices/detail.html", context) + elif "submit" in self.request.POST: + # preprocess the request query data + data_basic, data_attributes, _ = parse_request_data( + request.POST, BasicForm=ServiceGroupBasic + ) - if basic_info.is_valid() and attributes.is_valid(): - try: - service_group = build_service_group( - data_basic=data_basic, data_attributes=data_attributes - ) - post_service_group(service_group, project=self.project) - add_data_to_session(request, "to_servicegroup", True) - logger.info( - "Service group created by " - + str( - self.request.user.first_name - if self.request.user.first_name - else self.request.user.username + # create forms from query data + basic_info = ServiceGroupBasic(data=data_basic) + attributes = Attributes(data=data_attributes, prefix=prefix_attributes) + + if basic_info.is_valid() and attributes.is_valid(): + try: + service_group = build_service_group( + data_basic=data_basic, data_attributes=data_attributes ) - + f" in project {self.project.name}" - ) - return redirect("projects:devices:list", project_id=self.project.uuid) - # handel the error from server - except RequestException as e: - messages.error(request, e.response.content.decode("utf-8")) - logger.error( - str( - self.request.user.first_name - if self.request.user.first_name - else self.request.user.username + post_service_group(service_group, project=self.project) + add_data_to_session(request, "to_servicegroup", True) + logger.info( + "Service group created by " + + str( + self.request.user.first_name + if self.request.user.first_name + else self.request.user.username + ) + + f" in project {self.project.name}" ) - + " tried creating service group" - + " but failed with error " - + json.loads(e.response.content.decode("utf-8")).get("message") - + f" in project {self.project.name}" - ) - except ValidationError as e: - messages.error(request, e.raw_errors[0].exc.__str__()) + return redirect( + "projects:devices:list", project_id=self.project.uuid + ) + # handel the error from server + except RequestException as e: + messages.error(request, e.response.content.decode("utf-8")) + logger.error( + str( + self.request.user.first_name + if self.request.user.first_name + else self.request.user.username + ) + + " tried creating service group" + + " but failed with error " + + json.loads(e.response.content.decode("utf-8")).get("message") + + f" in project {self.project.name}" + ) + except ValidationError as e: + messages.error(request, e.raw_errors[0].exc.__str__()) - # get the project context data - context: dict = super(ServiceGroupCreateSubmitView, self).get_context_data( + # get the project context data + context: dict = super(ServiceGroupCreateSubmitView, self).get_context_data( + **kwargs + ) + context["smart_data_model_form"] = SmartDataModelQueryForm( + initial=request.POST + ) + context = { + "basic_info": basic_info, + "attributes": attributes, + "action": "Create_Group", + **context, + } + return render(request, "devices/detail.html", context) + else: + raise NotImplementedError + + +class ServiceGroupDataModelCreateView(ProjectContextMixin, TemplateView): + template_name = "devices/datamodels.html" + + def get_context_data(self, **kwargs): + context = super(ServiceGroupDataModelCreateView, self).get_context_data( **kwargs ) + context["smart_data_model_form"] = SmartDataModelQueryForm() + return context - context = { - "basic_info": basic_info, - "attributes": attributes, - "action": "Create_Group", - **context, - } - return render(request, "devices/detail.html", context) + +class ServiceGroupDataModelCreateSubmitView(ProjectContextMixin, TemplateView): + def post(self, request: HttpRequest, **kwargs): + data_model = json.loads(request.POST.get(key="select_data_model")) + only_required_attrs = bool(request.POST.get(key="only_required_attrs")) + add_data_to_session(request, "data_model", data_model) + add_data_to_session(request, "only_required_attrs", only_required_attrs) + return redirect("projects:devices:create_group", project_id=self.project.uuid) # Edit service group diff --git a/app/Entirety/entirety/settings.py b/app/Entirety/entirety/settings.py index fa651188..ef8939b3 100644 --- a/app/Entirety/entirety/settings.py +++ b/app/Entirety/entirety/settings.py @@ -111,13 +111,16 @@ class Settings(PydanticSettings): "django.contrib.messages", "django.contrib.staticfiles", "django.forms", + "django_jsonforms", "django_tables2", "compressor", + "corsheaders", "crispy_forms", "crispy_bootstrap5", "projects", "examples", "users", + "smartdatamodels", ] CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" @@ -126,6 +129,7 @@ class Settings(PydanticSettings): CRISPY_TEMPLATE_PACK = "bootstrap5" MIDDLEWARE: List[str] = [ + "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", @@ -135,6 +139,8 @@ class Settings(PydanticSettings): "django.middleware.clickjacking.XFrameOptionsMiddleware", ] + CORS_ORIGIN_ALLOW_ALL = True + MESSAGE_TAGS = { messages.DEBUG: "alert-info", messages.INFO: "alert-info", diff --git a/app/Entirety/entities/templates/entities/batch.html b/app/Entirety/entities/templates/entities/batch.html index a54672fd..365fb1e5 100644 --- a/app/Entirety/entities/templates/entities/batch.html +++ b/app/Entirety/entities/templates/entities/batch.html @@ -15,7 +15,7 @@ - +
diff --git a/app/Entirety/entities/templates/entities/entity_list.html b/app/Entirety/entities/templates/entities/entity_list.html index 4f014189..c908ca9b 100644 --- a/app/Entirety/entities/templates/entities/entity_list.html +++ b/app/Entirety/entities/templates/entities/entity_list.html @@ -11,24 +11,26 @@
- +
- -
-
- + +
+
+ +
@@ -67,8 +69,10 @@ title="Inspect Entity" name="Edit" value="Edit"> - @@ -76,8 +80,8 @@ {% render_table table %} - -
",c},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.innerHTML="
",c},applyStyles:function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a.style[c]=b[c])},closest:function(a,b){for(;a&&a!==document;){if(!a[i])return!1;if(a[i](b))return a;a=a.parentNode}return!1},insertBasicTopTab:function(a,b){b.firstChild.insertBefore(a,b.firstChild.firstChild)},getTab:function(a,b){var c=document.createElement("div");return c.appendChild(a),c.id=b,c.style=c.style||{},this.applyStyles(c,{border:"1px solid #ccc",borderWidth:"1px 0 1px 1px",textAlign:"center",lineHeight:"30px",borderRadius:"5px",borderBottomRightRadius:0,borderTopRightRadius:0,fontWeight:"bold",cursor:"pointer"}),c},getTopTab:function(a,b){var c=document.createElement("div");return c.id=b,c.appendChild(a),c.style=c.style||{},this.applyStyles(c,{"float":"left",border:"1px solid #ccc",borderWidth:"1px 1px 0px 1px",textAlign:"center",lineHeight:"30px",borderRadius:"5px",paddingLeft:"5px",paddingRight:"5px",borderBottomRightRadius:0,borderBottomLeftRadius:0,fontWeight:"bold",cursor:"pointer"}),c},getTabContentHolder:function(a){return a.children[1]},getTopTabContentHolder:function(a){return a.children[1]},getTabContent:function(){return this.getIndentedPanel()},getTopTabContent:function(){return this.getTopIndentedPanel()},markTabActive:function(a){this.applyStyles(a.tab,{opacity:1,background:"white"}),"undefined"!=typeof a.rowPane?a.rowPane.style.display="":a.container.style.display=""},markTabInactive:function(a){this.applyStyles(a.tab,{opacity:.5,background:""}),"undefined"!=typeof a.rowPane?a.rowPane.style.display="none":a.container.style.display="none"},addTab:function(a,b){a.children[0].appendChild(b)},addTopTab:function(a,b){a.children[0].appendChild(b)},getBlockLink:function(){var a=document.createElement("a");return a.style.display="block",a},getBlockLinkHolder:function(){var a=document.createElement("div");return a},getLinksHolder:function(){var a=document.createElement("div");return a},createMediaLink:function(a,b,c){a.appendChild(b),c.style.width="100%",a.appendChild(c)},createImageLink:function(a,b,c){a.appendChild(b),b.appendChild(c)},getFirstTab:function(a){return a.firstChild.firstChild},getInputGroup:function(a,c){return b}}),h.defaults.themes.bootstrap2=h.AbstractTheme.extend({getRangeInput:function(a,b,c){return this._super(a,b,c)},getGridContainer:function(){var a=document.createElement("div");return a.classList.add("container-fluid"),a},getGridRow:function(){var a=document.createElement("div");return a.classList.add("row-fluid"),a},getFormInputLabel:function(a){var b=this._super(a);return b.style.display="inline-block",b.style.fontWeight="bold",b},setGridColumnSize:function(a,b){a.classList.add("span"+b)},getSelectInput:function(a){var b=this._super(a);return b.style.width="auto",b.style.maxWidth="98%",b},getFormInputField:function(a){var b=this._super(a);return b.style.width="98%",b},afterInputReady:function(a){if(!a.controlgroup&&(a.controlgroup=this.closest(a,".control-group"),a.controls=this.closest(a,".controls"),this.closest(a,".compact")&&(a.controlgroup.className=a.controlgroup.className.replace(/control-group/g,"").replace(/[ ]{2,}/g," "),a.controls.className=a.controlgroup.className.replace(/controls/g,"").replace(/[ ]{2,}/g," "),a.style.marginBottom=0),this.queuedInputErrorText)){var b=this.queuedInputErrorText;delete this.queuedInputErrorText,this.addInputError(a,b)}},getIndentedPanel:function(){var a=document.createElement("div");return a.classList.add("well","well-small"),a.style.paddingBottom=0,a},getInfoButton:function(a){var b=document.createElement("span");b.classList.add("icon-info-sign","pull-right"),b.style.padding=".25rem",b.style.position="relative",b.style.display="inline-block";var c=document.createElement("span");return c.style["font-family"]="sans-serif",c.style.visibility="hidden",c.style["background-color"]="rgba(50, 50, 50, .75)",c.style.margin="0 .25rem",c.style.color="#FAFAFA",c.style.padding=".5rem 1rem",c.style["border-radius"]=".25rem",c.style.width="25rem",c.style.transform="translateX(-27rem) translateY(-.5rem)",c.style.position="absolute",c.innerText=a,b.onmouseover=function(){c.style.visibility="visible"},b.onmouseleave=function(){c.style.visibility="hidden"},b.appendChild(c),b},getFormInputDescription:function(a){var b=document.createElement("p");return b.classList.add("help-inline"),b.textContent=a,b},getFormControl:function(a,b,c,d){var e=document.createElement("div");e.classList.add("control-group");var f=document.createElement("div");return f.classList.add("controls"),a&&"checkbox"===b.getAttribute("type")?(e.appendChild(f),a.classList.add("checkbox"),a.appendChild(b),f.appendChild(a),d&&f.appendChild(d),f.style.height="30px"):(a&&(a.classList.add("control-label"),e.appendChild(a)),d&&f.appendChild(d),f.appendChild(b),e.appendChild(f)),c&&f.appendChild(c),e},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a},getButtonHolder:function(){var a=document.createElement("div");return a.classList.add("btn-group"),a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.classList.add("btn","btn-default"),d},getTable:function(){var a=document.createElement("table");return a.classList.add("table","table-bordered"),a.style.width="auto",a.style.maxWidth="none",a},addInputError:function(a,b){return a.controlgroup?void(a.controlgroup&&a.controls&&(a.controlgroup.classList.add("error"),a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("p"),a.errmsg.classList.add("help-block","errormsg"),a.controls.appendChild(a.errmsg)),a.errmsg.textContent=b)):void(this.queuedInputErrorText=b)},removeInputError:function(a){a.controlgroup||delete this.queuedInputErrorText,a.errmsg&&(a.errmsg.style.display="none",a.controlgroup.classList.remove("error"))},getTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.classList.add("tabbable","tabs-left"),c.innerHTML="
",c},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.classList.add("tabbable","tabs-over"),c.innerHTML="
",c},getTab:function(a,b){var c=document.createElement("li");c.classList.add("nav-item");var d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTopTab:function(a,b){var c=document.createElement("li");c.classList.add("nav-item");var d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTabContentHolder:function(a){return a.children[1]},getTopTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-pane"),a},getTopTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-pane"),a},markTabActive:function(a){a.tab.classList.add("active"),"undefined"!=typeof a.rowPane?a.rowPane.classList.add("active"):a.container.classList.add("active")},markTabInactive:function(a){a.tab.classList.remove("active"),"undefined"!=typeof a.rowPane?a.rowPane.classList.remove("active"):a.container.classList.remove("active")},addTab:function(a,b){a.children[0].appendChild(b)},addTopTab:function(a,b){a.children[0].appendChild(b)},getProgressBar:function(){var a=document.createElement("div");a.classList.add("progress");var b=document.createElement("div");return b.classList.add("bar"),b.style.width="0%",a.appendChild(b),a},updateProgressBar:function(a,b){a&&(a.firstChild.style.width=b+"%")},updateProgressBarUnknown:function(a){a&&(a.classList.add("progress","progress-striped","active"),a.firstChild.style.width="100%")},getInputGroup:function(a,b){if(a){var c=document.createElement("div");c.classList.add("input-append"),c.appendChild(a);for(var d=0;d
",c},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.innerHTML="
",c},getTab:function(a,b){var c=document.createElement("li");c.setAttribute("role","presentation");var d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),d.setAttribute("aria-controls",b),d.setAttribute("role","tab"),d.setAttribute("data-toggle","tab"),c.appendChild(d),c},getTopTab:function(a,b){var c=document.createElement("li");c.setAttribute("role","presentation");var d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),d.setAttribute("aria-controls",b),d.setAttribute("role","tab"),d.setAttribute("data-toggle","tab"),c.appendChild(d),c},getTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-pane"),a.setAttribute("role","tabpanel"),a},getTopTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-pane"),a.setAttribute("role","tabpanel"),a},markTabActive:function(a){a.tab.classList.add("active"),"undefined"!=typeof a.rowPane?a.rowPane.classList.add("active"):a.container.classList.add("active")},markTabInactive:function(a){a.tab.classList.remove("active"),"undefined"!=typeof a.rowPane?a.rowPane.classList.remove("active"):a.container.classList.remove("active")},getProgressBar:function(){var a=0,b=100,c=0,d=document.createElement("div");d.classList.add("progress");var e=document.createElement("div");return e.classList.add("progress-bar"),e.setAttribute("role","progressbar"),e.setAttribute("aria-valuenow",c), +e.setAttribute("aria-valuemin",a),e.setAttribute("aria-valuenax",b),e.innerHTML=c+"%",d.appendChild(e),d},updateProgressBar:function(a,b){if(a){var c=a.firstChild,d=b+"%";c.setAttribute("aria-valuenow",b),c.style.width=d,c.innerHTML=d}},updateProgressBarUnknown:function(a){if(a){var b=a.firstChild;a.classList.add("progress","progress-striped","active"),b.removeAttribute("aria-valuenow"),b.style.width="100%",b.innerHTML=""}},getInputGroup:function(a,b){if(a){var c=document.createElement("div");c.classList.add("input-group"),c.appendChild(a);var d=document.createElement("div");d.classList.add("input-group-btn"),c.appendChild(d);for(var e=0;e
",b.classList.add("row"),b},addTab:function(a,b){a.children[0].children[0].appendChild(b)},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.innerHTML="
",c},getTab:function(a,b){var c=document.createElement("li");c.classList.add("nav-item");var d=document.createElement("a");return d.classList.add("nav-link"),d.setAttribute("style","padding:10px;"),d.setAttribute("href","#"+b),d.setAttribute("data-toggle","tab"),d.appendChild(a),c.appendChild(d),c},getTopTab:function(a,b){var c=document.createElement("li");c.classList.add("nav-item");var d=document.createElement("a");return d.classList.add("nav-link"),d.setAttribute("href","#"+b),d.setAttribute("data-toggle","tab"),d.appendChild(a),c.appendChild(d),c},getTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-pane"),a.setAttribute("role","tabpanel"),a},getTopTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-pane"),a.setAttribute("role","tabpanel"),a},markTabActive:function(a){a.tab.firstChild.classList.add("active"),"undefined"!=typeof a.rowPane?a.rowPane.classList.add("active"):a.container.classList.add("active")},markTabInactive:function(a){a.tab.firstChild.classList.remove("active"),"undefined"!=typeof a.rowPane?a.rowPane.classList.remove("active"):a.container.classList.remove("active")},getProgressBar:function(){var a=0,b=100,c=0,d=document.createElement("div");d.classList.add("progress");var e=document.createElement("div");return e.classList.add("progress-bar"),e.setAttribute("role","progressbar"),e.setAttribute("aria-valuenow",c),e.setAttribute("aria-valuemin",a),e.setAttribute("aria-valuenax",b),e.innerHTML=c+"%",d.appendChild(e),d},updateProgressBar:function(a,b){if(a){var c=a.firstChild,d=b+"%";c.setAttribute("aria-valuenow",b),c.style.width=d,c.innerHTML=d}},updateProgressBarUnknown:function(a){if(a){var b=a.firstChild;a.classList.add("progress","progress-striped","active"),b.removeAttribute("aria-valuenow"),b.style.width="100%",b.innerHTML=""}},getInputGroup:function(a,b){if(a){var c=document.createElement("div");c.classList.add("input-group"),c.appendChild(a);var d=document.createElement("div");d.classList.add("input-group-prepend"),c.appendChild(d);for(var e=0;e'),a.errmsg=a.parentNode.getElementsByClassName("error")[0]),void(a.errmsg.textContent=b)):void(this.queuedInputErrorText=b)},removeInputError:function(a){a.group||delete this.queuedInputErrorText,a.errmsg&&(a.group.classList.remove("error"),a.errmsg.style.display="none")},getProgressBar:function(){var a=document.createElement("div");a.classList.add("progress");var b=document.createElement("span");return b.classList.add("meter"),b.style.width="0%",a.appendChild(b),a},updateProgressBar:function(a,b){a&&(a.firstChild.style.width=b+"%")},updateProgressBarUnknown:function(a){a&&(a.firstChild.style.width="100%")},getInputGroup:function(a,c){if(!a)return b;var d=document.createElement("div");d.classList.add("input-group"),a.classList.add("input-group-field"),d.appendChild(a);for(var e=0;e
',c},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.classList.add("row"),c.innerHTML='
',c},setGridColumnSize:function(a,b){var c=["zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"];a.classList.add("columns",c[b])},getTab:function(a,b){var c=document.createElement("dd"),d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTopTab:function(a,b){var c=document.createElement("dd"),d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTabContentHolder:function(a){return a.children[1]},getTopTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.classList.add("content","active"),a.style.paddingLeft="5px",a},getTopTabContent:function(){var a=document.createElement("div");return a.classList.add("content","active"),a.style.paddingLeft="5px",a},markTabActive:function(a){a.tab.classList.add("active"),"undefined"!=typeof a.rowPane?a.rowPane.style.display="":a.container.style.display=""},markTabInactive:function(a){a.tab.classList.remove("active"),"undefined"!=typeof a.rowPane?a.rowPane.style.display="none":a.container.style.display="none"},addTab:function(a,b){a.children[0].appendChild(b)},addTopTab:function(a,b){a.children[0].appendChild(b)}}),h.defaults.themes.foundation4=h.defaults.themes.foundation.extend({getHeaderButtonHolder:function(){var a=this._super();return a.style.fontSize=".6em",a},setGridColumnSize:function(a,b){a.classList.add("columns","large-"+b)},getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8rem",b},getFormInputLabel:function(a){var b=this._super(a);return b.style.fontWeight="bold",b}}),h.defaults.themes.foundation5=h.defaults.themes.foundation.extend({getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8rem",b},setGridColumnSize:function(a,b){a.classList.add("columns","medium-"+b)},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className=d.className.replace(/\s*small/g,"")+" tiny",d},getTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.innerHTML='
',c},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.classList.add("row"),c.innerHTML='
',c},getTab:function(a,b){var c=document.createElement("dd"),d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTopTab:function(a,b){var c=document.createElement("dd"),d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTabContentHolder:function(a){return a.children[1]},getTopTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-content","active"),a.style.paddingLeft="5px",a},getTopTabContent:function(){var a=document.createElement("div");return a.classList.add("tab-content","active"),a.style.paddingLeft="5px",a},markTabActive:function(a){a.tab.classList.add("active"),"undefined"!=typeof a.rowPane?a.rowPane.style.display="":a.container.style.display=""},markTabInactive:function(a){a.tab.classList.remove("active"),"undefined"!=typeof a.rowPane?a.rowPane.style.display="none":a.container.style.display="none"},addTab:function(a,b){a.children[0].appendChild(b)},addTopTab:function(a,b){a.children[0].appendChild(b)}}),h.defaults.themes.foundation6=h.defaults.themes.foundation5.extend({getIndentedPanel:function(){var a=document.createElement("div");return a.classList.add("callout","secondary"),a.style="padding-left: 10px; margin-left: 10px;",a},getButtonHolder:function(){var a=document.createElement("div");return a.classList.add("button-group","tiny"),a.style.marginBottom=0,a},getFormInputLabel:function(a){var b=this._super(a);return b.style.display="block",b},getFormControl:function(a,b,c,d){var e=document.createElement("div");return e.classList.add("form-control"),a&&e.appendChild(a),"checkbox"===b.type?a.insertBefore(b,a.firstChild):a?(d&&a.appendChild(d),a.appendChild(b)):(d&&e.appendChild(d),e.appendChild(b)),c&&a.appendChild(c),e},addInputError:function(a,b){if(a.group){if(a.group.classList.add("error"),a.errmsg)a.errmsg.style.display="",a.className="";else{var c=document.createElement("span");c.classList.add("form-error","is-visible"),a.group.getElementsByTagName("label")[0].appendChild(c),a.classList.add("is-invalid-input"),a.errmsg=c}a.errmsg.textContent=b}},removeInputError:function(a){a.errmsg&&(a.classList.remove("is-invalid-input"),a.errmsg.parentNode&&a.errmsg.parentNode.removeChild(a.errmsg))},getTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.classList.add("grid-x"),c.innerHTML='
    ',c},getTopTabHolder:function(a){var b="undefined"==typeof a?"":a,c=document.createElement("div");return c.classList.add("grid-y"),c.innerHTML='
      ',c},insertBasicTopTab:function(a,b){b.firstChild.firstChild.insertBefore(a,b.firstChild.firstChild.firstChild)},getTab:function(a,b){var c=document.createElement("li");c.classList.add("tabs-title");var d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTopTab:function(a,b){var c=document.createElement("li");c.classList.add("tabs-title");var d=document.createElement("a");return d.setAttribute("href","#"+b),d.appendChild(a),c.appendChild(d),c},getTabContentHolder:function(a){return a.children[1].firstChild},getTopTabContentHolder:function(a){return a.firstChild.children[1]},getTabContent:function(){var a=document.createElement("div");return a.classList.add("tabs-panel"),a.style.paddingLeft="5px",a},getTopTabContent:function(){var a=document.createElement("div");return a.classList.add("tabs-panel"),a.style.paddingLeft="5px",a},markTabActive:function(a){a.tab.classList.add("is-active"),a.tab.firstChild.setAttribute("aria-selected","true"),"undefined"!=typeof a.rowPane?(a.rowPane.classList.add("is-active"),a.rowPane.setAttribute("aria-selected","true")):(a.container.classList.add("is-active"),a.container.setAttribute("aria-selected","true"))},markTabInactive:function(a){a.tab.classList.remove("is-active"),a.tab.firstChild.removeAttribute("aria-selected"),"undefined"!=typeof a.rowPane?(a.rowPane.classList.remove("is-active"),a.rowPane.removeAttribute("aria-selected")):(a.container.classList.remove("is-active"),a.container.removeAttribute("aria-selected"))},addTab:function(a,b){a.children[0].firstChild.appendChild(b)},addTopTab:function(a,b){a.firstChild.children[0].appendChild(b)},getFirstTab:function(a){return a.firstChild.firstChild.firstChild}}),h.defaults.themes.html=h.AbstractTheme.extend({getFormInputLabel:function(a){var b=this._super(a);return b.style.display="block",b.style.marginBottom="3px",b.style.fontWeight="bold",b},getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8em",b.style.margin=0,b.style.display="inline-block",b.style.fontStyle="italic",b},getIndentedPanel:function(){var a=this._super();return a.style.border="1px solid #ddd",a.style.padding="5px",a.style.margin="10px",a.style.borderRadius="3px",a},getTopIndentedPanel:function(){return this.getIndentedPanel()},getChildEditorHolder:function(){var a=this._super();return a.style.marginBottom="8px",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.display="inline-block",a.style.marginLeft="10px",a.style.fontSize=".8em",a.style.verticalAlign="middle",a},getTable:function(){var a=this._super();return a.style.borderBottom="1px solid #ccc",a.style.marginBottom="5px",a},addInputError:function(a,b){if(a.style.borderColor="red",a.errmsg)a.errmsg.style.display="block";else{var c=this.closest(a,".form-control");a.errmsg=document.createElement("div"),a.errmsg.setAttribute("class","errmsg"),a.errmsg.style=a.errmsg.style||{},a.errmsg.style.color="red",c.appendChild(a.errmsg)}a.errmsg.innerHTML="",a.errmsg.appendChild(document.createTextNode(b))},removeInputError:function(a){a.style.borderColor="",a.errmsg&&(a.errmsg.style.display="none")},getProgressBar:function(){var a=100,b=0,c=document.createElement("progress");return c.setAttribute("max",a),c.setAttribute("value",b),c},updateProgressBar:function(a,b){a&&a.setAttribute("value",b)},updateProgressBarUnknown:function(a){a&&a.removeAttribute("value")}}),h.defaults.themes.jqueryui=h.AbstractTheme.extend({getTable:function(){var a=this._super();return a.setAttribute("cellpadding",5),a.setAttribute("cellspacing",0),a},getTableHeaderCell:function(a){var b=this._super(a);return b.classList.add("ui-state-active"),b.style.fontWeight="bold",b},getTableCell:function(){var a=this._super();return a.classList.add("ui-widget-content"),a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a.style.fontSize=".6em",a.style.display="inline-block",a},getFormInputDescription:function(a){var b=this.getDescription(a);return b.style.marginLeft="10px",b.style.display="inline-block",b},getFormControl:function(a,b,c,d){var e=this._super(a,b,c,d);return"checkbox"===b.type?(e.style.lineHeight="25px",e.style.padding="3px 0"):e.style.padding="4px 0 8px 0",e},getDescription:function(a){var b=document.createElement("span");return b.style.fontSize=".8em",b.style.fontStyle="italic",b.textContent=a,b},getButtonHolder:function(){var a=document.createElement("div");return a.classList.add("ui-buttonset"),a.style.fontSize=".7em",a},getFormInputLabel:function(a){var b=document.createElement("label");return b.style.fontWeight="bold",b.style.display="block",b.textContent=a,b},getButton:function(a,b,c){var d=document.createElement("button");d.classList.add("ui-button","ui-widget","ui-state-default","ui-corner-all"),b&&!a?(d.classList.add("ui-button-icon-only"),b.classList.add("ui-button-icon-primary","ui-icon-primary"),d.appendChild(b)):b?(d.classList.add("ui-button-text-icon-primary"),b.classList.add("ui-button-icon-primary","ui-icon-primary"),d.appendChild(b)):d.classList.add("ui-button-text-only");var e=document.createElement("span");return e.classList.add("ui-button-text"),e.textContent=a||c||".",d.appendChild(e),d.setAttribute("title",c),d},setButtonText:function(a,b,c,d){a.innerHTML="",a.classList.add("ui-button","ui-widget","ui-state-default","ui-corner-all"),c&&!b?(a.classList.add("ui-button-icon-only"),c.classList.add("ui-button-icon-primary","ui-icon-primary"),a.appendChild(c)):c?(a.classList.add("ui-button-text-icon-primary"),c.classList.add("ui-button-icon-primary","ui-icon-primary"),a.appendChild(c)):a.classList.add("ui-button-text-only");var e=document.createElement("span");e.classList.add("ui-button-text"),e.textContent=b||d||".",a.appendChild(e),a.setAttribute("title",d)},getIndentedPanel:function(){var a=document.createElement("div");return a.classList.add("ui-widget-content","ui-corner-all"),a.style.padding="1em 1.4em",a.style.marginBottom="20px",a},afterInputReady:function(a){if(!a.controls&&(a.controls=this.closest(a,".form-control"),this.queuedInputErrorText)){var b=this.queuedInputErrorText;delete this.queuedInputErrorText,this.addInputError(a,b)}},addInputError:function(a,b){return a.controls?(a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("div"),a.errmsg.classList.add("ui-state-error"),a.controls.appendChild(a.errmsg)),void(a.errmsg.textContent=b)):void(this.queuedInputErrorText=b)},removeInputError:function(a){a.controls||delete this.queuedInputErrorText,a.errmsg&&(a.errmsg.style.display="none")},markTabActive:function(a){a.tab.classList.remove("ui-widget-header"),a.tab.classList.add("ui-state-active"),"undefined"!=typeof a.rowPane?a.rowPane.style.display="":a.container.style.display=""},markTabInactive:function(a){a.tab.classList.add("ui-widget-header"),a.tab.classList.remove("ui-state-active"),"undefined"!=typeof a.rowPane?a.rowPane.style.display="none":a.container.style.display="none"}}),h.defaults.themes.barebones=h.AbstractTheme.extend({getFormInputLabel:function(a){var b=this._super(a);return b},getFormInputDescription:function(a){var b=this._super(a);return b},getIndentedPanel:function(){var a=this._super();return a},getChildEditorHolder:function(){var a=this._super();return a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a},getTable:function(){var a=this._super();return a},addInputError:function(a,b){if(a.errmsg)a.errmsg.style.display="block";else{var c=this.closest(a,".form-control");a.errmsg=document.createElement("div"),a.errmsg.setAttribute("class","errmsg"),c.appendChild(a.errmsg)}a.errmsg.innerHTML="",a.errmsg.appendChild(document.createTextNode(b))},removeInputError:function(a){a.style.borderColor="",a.errmsg&&(a.errmsg.style.display="none")},getProgressBar:function(){var a=100,b=0,c=document.createElement("progress");return c.setAttribute("max",a),c.setAttribute("value",b),c},updateProgressBar:function(a,b){a&&a.setAttribute("value",b)},updateProgressBarUnknown:function(a){a&&a.removeAttribute("value")}}),h.defaults.themes.materialize=h.AbstractTheme.extend({setGridColumnSize:function(a,b){a.classList.add("col"),a.classList.add("s"+b)},getHeaderButtonHolder:function(){return this.getButtonHolder()},getButtonHolder:function(){return document.createElement("span")},getButton:function(a,b,c){a&&(b.classList.add("left"),b.style.marginRight="5px");var d=this._super(a,b,c);return d.classList.add("waves-effect","waves-light","btn"),d.style.fontSize="0.75rem",d.style.height="24px",d.style.lineHeight="24px",d.style.marginLeft="5px",d.style.padding="0 0.5rem",d},getFormControl:function(a,b,c,d){var e,f=b.type;if(f&&"checkbox"===f){if(e=document.createElement("p"),a){var g=document.createElement("span");g.innerHTML=a.innerHTML,a.innerHTML="",a.setAttribute("for",b.id),e.appendChild(a),a.appendChild(b),a.appendChild(g)}else e.appendChild(b);return e}return e=this._super(a,b,c,d),f&&f.startsWith("select")||e.classList.add("input-field"),f&&"color"===f&&(b.style.height="3rem",b.style.width="100%",b.style.margin="5px 0 20px 0",b.style.padding="3px",a&&(a.style.transform="translateY(-14px) scale(0.8)",a.style["-webkit-transform"]="translateY(-14px) scale(0.8)",a.style["-webkit-transform-origin"]="0 0",a.style["transform-origin"]="0 0")),e},getDescription:function(a){var b=document.createElement("div");return b.classList.add("grey-text"),b.style.marginTop="-15px",b.innerHTML=a,b},getHeader:function(a){var b=document.createElement("h5");return"string"==typeof a?b.textContent=a:b.appendChild(a),b},getChildEditorHolder:function(){var a=document.createElement("div");return a.marginBottom="10px",a},getIndentedPanel:function(){var a=document.createElement("div");return a.classList.add("card-panel"),a},getTable:function(){var a=document.createElement("table");return a.classList.add("striped","bordered"),a.style.marginBottom="10px",a},getTableRow:function(){return document.createElement("tr")},getTableHead:function(){return document.createElement("thead")},getTableBody:function(){return document.createElement("tbody")},getTableHeaderCell:function(a){var b=document.createElement("th");return b.textContent=a,b},getTableCell:function(){var a=document.createElement("td");return a},getTabHolder:function(){var a=['
      ','
        ',"
      ","
      ",'
      ',"
      "].join("\n"),b=document.createElement("div");return b.classList.add("row","card-panel"),b.innerHTML=a,b},addTab:function(a,b){a.children[0].children[0].appendChild(b)},getTab:function(a){var b=document.createElement("li");return b.classList.add("tab"),b.style=b.style||{},this.applyStyles(b,{width:"100%",textAlign:"left",lineHeight:"24px",height:"24px",fontSize:"14px",cursor:"pointer"}),b.appendChild(a),b},markTabActive:function(a){a.style=a.style||{},this.applyStyles(a,{width:"100%",textAlign:"left",lineHeight:"24px",height:"24px",fontSize:"14px",cursor:"pointer",color:"rgba(238,110,115,1)",transition:"border-color .5s ease",borderRight:"3px solid #424242"})},markTabInactive:function(a){a.style=a.style||{},this.applyStyles(a,{width:"100%",textAlign:"left",lineHeight:"24px",height:"24px",fontSize:"14px",cursor:"pointer",color:"rgba(238,110,115,0.7)"})},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){return document.createElement("div")},addInputError:function(a,b){var c,d=a.parentNode;d&&(this.removeInputError(a),c=document.createElement("div"),c.classList.add("error-text","red-text"),c.textContent=b,d.appendChild(c))},removeInputError:function(a){var b,c=a.parentElement;if(c){b=c.getElementsByClassName("error-text");for(var d=0;d1){var g;c=function(b){for(g=b,a=0;a=0){if(a.items["enum"])return"multiselect";if(h.plugins.selectize.enable&&"string"===a.items.type)return"arraySelectize"}}),h.defaults.resolvers.unshift(function(a){if(a.oneOf||a.anyOf)return"multiple"}),h.defaults.resolvers.unshift(function(a){if(["string","integer"].indexOf(a.type)!==-1&&["date","time","datetime-local"].indexOf(a.format)!==-1)return"datetime"}),h.defaults.resolvers.unshift(function(a){if("string"===a.type&&"starrating"===a.format)return"starrating"}),function(){if(window.jQuery||window.Zepto){var a=window.jQuery||window.Zepto;a.jsoneditor=h.defaults,a.fn.jsoneditor=function(a){var b=this,c=this.data("jsoneditor");if("value"===a){if(!c)throw"Must initialize jsoneditor before getting/setting the value";if(!(arguments.length>1))return c.getValue();c.setValue(arguments[1])}else{if("validate"===a){if(!c)throw"Must initialize jsoneditor before validating";return arguments.length>1?c.validate(arguments[1]):c.validate()}"destroy"===a?c&&(c.destroy(),this.data("jsoneditor",null)):(c&&c.destroy(),c=new h(this.get(0),a),this.data("jsoneditor",c),c.on("change",function(){b.trigger("change")}),c.on("ready",function(){b.trigger("ready")}))}return this}}}(),h}); +//# sourceMappingURL=jsoneditor.min.js.map diff --git a/app/Entirety/static/scss/card.scss b/app/Entirety/static/scss/card.scss new file mode 100644 index 00000000..d1399b07 --- /dev/null +++ b/app/Entirety/static/scss/card.scss @@ -0,0 +1,30 @@ +@import "colors"; + +.card-header { + .bi-power { + color: red; + &.active { + color: lawngreen; + } + } +} + +.card-footer { + .bi-gear { + color: $color-sidebar; + } + .bi-trash { + color: red; + } +} + +// card on hover +.card:hover{ + transform:scale(1.05); +} + +// card image small +.card-img-top{ + height: 200px; + object-fit: fill; +} diff --git a/app/Entirety/static/scss/style.scss b/app/Entirety/static/scss/style.scss index 447dc104..dd208ba4 100644 --- a/app/Entirety/static/scss/style.scss +++ b/app/Entirety/static/scss/style.scss @@ -7,36 +7,6 @@ @import "scrollbar"; - -.card-header { - .bi-power { - color: red; - &.active { - color: lawngreen; - } - } -} - -.card-footer { - .bi-gear { - color: $color-sidebar; - } - .bi-trash { - color: red; - } -} - -// card on hover -.card:hover{ - transform:scale(1.05); -} - -// card image small -.card-img-top{ - height: 200px; - object-fit: fill; -} - h1{ color: $primary } diff --git a/app/Entirety/templates/.release_notes.md.j2 b/app/Entirety/templates/.release_notes.md.j2 new file mode 100644 index 00000000..67304d48 --- /dev/null +++ b/app/Entirety/templates/.release_notes.md.j2 @@ -0,0 +1,12 @@ +## What's Changed +# {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) +{% for type_, commits in release["elements"] | dictsort %} +## {{ type_ | capitalize }} +{% for commit in commits %} +{% if commit.scope is not none %} +* {{ commit.scope | capitalize }}: {{ commit.descriptions[0] }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) +{% else %} +* {{ commit.descriptions[0] }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) +{% endif %} +{% endfor %} +{% endfor %} diff --git a/app/Entirety/templates/_base.html b/app/Entirety/templates/_base.html index 680568c3..06d41c54 100644 --- a/app/Entirety/templates/_base.html +++ b/app/Entirety/templates/_base.html @@ -41,6 +41,9 @@
      + + +
      {% include 'sidebar.html' %}
      diff --git a/app/Entirety/templates/sidebar.html b/app/Entirety/templates/sidebar.html index 7a400a57..f0cc1d71 100644 --- a/app/Entirety/templates/sidebar.html +++ b/app/Entirety/templates/sidebar.html @@ -45,6 +45,17 @@ {% endif %} + {% if smart_datamodels_load %} + + {% endif %} {% if semantics_load %}