Skip to content

Commit

Permalink
feat: implement subscriptions app (#49)
Browse files Browse the repository at this point in the history
Co-authored-by: dnikolay-ebc <dnikolay-ebc@users.noreply.github.com>
  • Loading branch information
dnikolay-ebc and dnikolay-ebc authored Oct 10, 2022
1 parent 1ef4850 commit 55e5bf8
Show file tree
Hide file tree
Showing 54 changed files with 1,423 additions and 172 deletions.
94 changes: 49 additions & 45 deletions app/Entirety/.env.EXAMPLE
Original file line number Diff line number Diff line change
@@ -1,45 +1,49 @@
# Application Load
ENTITIES_LOAD=True
DEVICES_LOAD=True
NOTIFICATIONS_LOAD=True

# Django
DJANGO_SECRET_KEY=
DJANGO_DEBUG=False
ALLOWED_HOSTS=["localhost","127.0.0.1"]
LANGUAGE_CODE=en-us
TIME_ZONE=Europe/Berlin

# JS/SCSS compression
COMPRESS_ENABLED=True

# LOGGING
LOKI_HOST=localhost
LOKI_LEVEL=DEBUG
LOKI_PORT=3100
LOKI_TIMEOUT=0.5
LOKI_PROTOCOL=http
LOKI_SRC_HOST=entirety
LOKI_TIMEZONE=Europe/Berlin

# Static files location
STATIC_ROOT=/var/entirety/static/
MEDIA_ROOT=/var/entirety/media/

# OIDC
LOGIN_URL=/oidc/authenticate
LOGIN_REDIRECT_URL=/oidc/callback/
LOGOUT_REDIRECT_URL=/
OIDC_OP_AUTHORIZATION_ENDPOINT=
OIDC_OP_JWKS_ENDPOINT=
OIDC_OP_TOKEN_ENDPOINT=
OIDC_OP_USER_ENDPOINT=
OIDC_RP_CLIENT_ID=
OIDC_RP_CLIENT_SECRET=
OIDC_SUPER_ADMIN_ROLE=super_admin
OIDC_SERVER_ADMIN_ROLE=server_admin
OIDC_PROJECT_ADMIN_ROLE=project_admin
OIDC_USER_ROLE=user
OIDC_TOKEN_ROLE_FIELD=roles

WEB_URL=localhost
# Application Load
ENTITIES_LOAD=True
DEVICES_LOAD=True
NOTIFICATIONS_LOAD=True

# Django
DJANGO_SECRET_KEY=
DJANGO_DEBUG=False
ALLOWED_HOSTS=["localhost","127.0.0.1"]
LANGUAGE_CODE=en-us
TIME_ZONE=Europe/Berlin

# JS/SCSS compression
COMPRESS_ENABLED=True

# LOGGING
LOKI_HOST=localhost
LOKI_LEVEL=DEBUG
LOKI_PORT=3100
LOKI_TIMEOUT=0.5
LOKI_PROTOCOL=http
LOKI_SRC_HOST=entirety
LOKI_TIMEZONE=Europe/Berlin

# Static files location
STATIC_ROOT=/var/entirety/static/
MEDIA_ROOT=/var/entirety/media/

# OIDC
LOGIN_URL=/oidc/authenticate
LOGIN_REDIRECT_URL=/oidc/callback/
LOGOUT_REDIRECT_URL=/
OIDC_OP_AUTHORIZATION_ENDPOINT=
OIDC_OP_JWKS_ENDPOINT=
OIDC_OP_TOKEN_ENDPOINT=
OIDC_OP_USER_ENDPOINT=
OIDC_RP_CLIENT_ID=
OIDC_RP_CLIENT_SECRET=
OIDC_SUPER_ADMIN_ROLE=super_admin
OIDC_SERVER_ADMIN_ROLE=server_admin
OIDC_PROJECT_ADMIN_ROLE=project_admin
OIDC_USER_ROLE=user
OIDC_TOKEN_ROLE_FIELD=roles

# FIWARE
CB_URL=http://localhost:1026
MQTT_BASE_TOPIC=/Entirety

WEB_URL=localhost
5 changes: 0 additions & 5 deletions app/Entirety/alarming/admin.py

This file was deleted.

Empty file.

This file was deleted.

11 changes: 0 additions & 11 deletions app/Entirety/alarming/urls.py

This file was deleted.

12 changes: 0 additions & 12 deletions app/Entirety/alarming/views.py

This file was deleted.

50 changes: 32 additions & 18 deletions app/Entirety/devices/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,28 @@
from django.shortcuts import render, redirect
from django.views.generic import View, TemplateView
from django.http import HttpRequest
from projects.mixins import ProjectContextMixin, ApplicationLoadMixin
from projects.mixins import ProjectContextMixin
from devices.forms import DeviceBasic, Attributes, Commands
from devices.utils import get_devices, post_device, \
update_device, prefix_attributes, prefix_commands, parse_request_data, \
build_device, get_device_by_id, delete_device, devices_filter, patern_devices_filter
from devices.utils import (
get_devices,
post_device,
update_device,
prefix_attributes,
prefix_commands,
parse_request_data,
build_device,
get_device_by_id,
delete_device,
devices_filter,
patern_devices_filter,
)
from devices.tables import DevicesTable
from requests.exceptions import RequestException
from pydantic import ValidationError


# Devices list
class DeviceListView(ProjectContextMixin, ApplicationLoadMixin, SingleTableMixin, TemplateView):
class DeviceListView(ProjectContextMixin, SingleTableMixin, TemplateView):
# TemplateView.as_view() will render the template. Do not need to invoke render function
template_name = "devices/list.html"
table_class = DevicesTable
Expand All @@ -34,7 +44,7 @@ def get_context_data(self, **kwargs):
return context


class DeviceListSubmitView(ProjectContextMixin, ApplicationLoadMixin, View):
class DeviceListSubmitView(ProjectContextMixin, View):
# Redirect the request to corresponding view
def post(self, request, *args, **kwargs):
# press delete button
Expand All @@ -45,7 +55,9 @@ def post(self, request, *args, **kwargs):
else:
# use session to cache the selected devices
request.session["devices"] = request.POST.get("selection")
request.session["delete_entity"] = True if request.POST.get("delete_entity") else False
request.session["delete_entity"] = (
True if request.POST.get("delete_entity") else False
)
return redirect("projects:devices:delete", project_id=self.project.uuid)

# press advanced delete button
Expand All @@ -69,7 +81,7 @@ def post(self, request, *args, **kwargs):
"projects:entities:delete",
project_id=self.project.uuid,
entity_id=entity_id,
entity_type=entity_type
entity_type=entity_type,
)

# press create button
Expand All @@ -86,7 +98,7 @@ def post(self, request, *args, **kwargs):


# Create devices
class DeviceCreateView(ProjectContextMixin, ApplicationLoadMixin, TemplateView):
class DeviceCreateView(ProjectContextMixin, TemplateView):
def get(self, request, *args, **kwargs):
basic_info = DeviceBasic()
attributes = Attributes(prefix=prefix_attributes)
Expand All @@ -97,12 +109,12 @@ def get(self, request, *args, **kwargs):
"attributes": attributes,
"commands": commands,
"action": "Create",
**context
**context,
}
return render(request, "devices/detail.html", context)


class DeviceCreateSubmitView(ProjectContextMixin, ApplicationLoadMixin, TemplateView):
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)
Expand Down Expand Up @@ -135,13 +147,13 @@ def post(self, request: HttpRequest, **kwargs):
"attributes": attributes,
"commands": commands,
"action": "Create",
**context
**context,
}
return render(request, "devices/detail.html", context)


# Edit devices
class DeviceEditView(ProjectContextMixin, ApplicationLoadMixin, TemplateView):
class DeviceEditView(ProjectContextMixin, TemplateView):
def get(self, request: HttpRequest, *args, **kwargs):
context = super(DeviceEditView, self).get_context_data()

Expand Down Expand Up @@ -174,12 +186,12 @@ def get(self, request: HttpRequest, *args, **kwargs):
"attributes": attributes,
"commands": commands,
"action": "Edit",
**context
**context,
}
return render(request, "devices/detail.html", context)


class DeviceEditSubmitView(ProjectContextMixin, ApplicationLoadMixin, TemplateView):
class DeviceEditSubmitView(ProjectContextMixin, TemplateView):
def post(self, request: HttpRequest, **kwargs):
context = super(DeviceEditSubmitView, self).get_context_data()

Expand Down Expand Up @@ -214,21 +226,23 @@ def post(self, request: HttpRequest, **kwargs):
"attributes": attributes,
"commands": commands,
"action": "Edit",
**context
**context,
}
return render(request, "devices/detail.html", context)


# Delete Device
class DeviceDeleteView(ProjectContextMixin, ApplicationLoadMixin, View):
class DeviceDeleteView(ProjectContextMixin, View):
def get(self, request: HttpRequest, *args, **kwargs):
# get the selected devices from session
device_id = request.session.get("devices")
delete_entity = bool(request.session.get("delete_entity"))

# delete the device and entity?
try:
delete_device(project=self.project, device_id=device_id, delete_entity=delete_entity)
delete_device(
project=self.project, device_id=device_id, delete_entity=delete_entity
)
except RequestException as e:
messages.error(request, e.response.content.decode("utf-8"))

Expand Down
44 changes: 44 additions & 0 deletions app/Entirety/entirety/fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.core.validators import URLValidator
from django.forms import MultiValueField, ChoiceField, CharField, URLField
from django.utils.translation import gettext_lazy as _

from entirety.widgets import SelectTextInputWidget
from entirety.validators import CustomURLValidator


class MQTTURLField(URLField):
"""URL Field that accepts URLs that start with mqtt:// only"""

default_validators = [CustomURLValidator(schemes=["mqtt"])]
default_error_messages = {
"invalid": _("Enter a valid MQTT URL."),
}


class HTTPURLField(URLField):
"""URL Field that accepts URLs that start with http(s):// only"""

default_validators = [CustomURLValidator(schemes=["http", "https"])]
default_error_messages = {
"invalid": _("Enter a valid URL."),
}


class SelectTextMultiField(MultiValueField):
def __init__(self, choices, *, require_all_fields=True, **kwargs):
self.widget = SelectTextInputWidget(choices)
fields = (
ChoiceField(
choices=choices,
),
CharField(),
)
super(SelectTextMultiField, self).__init__(
fields=fields, require_all_fields=require_all_fields, **kwargs
)

def compress(self, data_list):
if data_list:
return "|".join(data_list)

return ""
11 changes: 6 additions & 5 deletions app/Entirety/entirety/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ class Settings(PydanticSettings):

STATIC_URL = "static/"

STATICFILES_DIRS: List[DirectoryPath] = [
os.path.join(BASE_DIR, "static"),
]
# STATICFILES_DIRS: List[DirectoryPath] = [
# os.path.join(BASE_DIR, "static"),
# ]

STATICFILES_FINDERS: List[str] = [
"django.contrib.staticfiles.finders.FileSystemFinder",
Expand Down Expand Up @@ -208,7 +208,7 @@ class Settings(PydanticSettings):
},
},
"loggers": {
"mozilla_django_oidc": {"handlers": ["console"], "level": "DEBUG"},
"mozilla_django_oidc": {"handlers": ["loki"], "level": "DEBUG"},
"": {
"handlers": ["loki"],
"level": "INFO",
Expand Down Expand Up @@ -239,6 +239,7 @@ def secret_key_not_empty(cls, v) -> str:
ALLOWED_HOSTS: List = Field(default=["*"], env="ALLOWED_HOSTS")

CB_URL: AnyUrl = Field(default="http://localhost:1026", env="CB_URL")
MQTT_BASE_TOPIC: str = Field(default="/Entirety", env="MQTT_BASE_TOPIC")

QL_URL: AnyUrl = Field(default="http://localhost:8668", env="QL_URL")

Expand Down Expand Up @@ -296,7 +297,7 @@ def secret_key_not_empty(cls, v) -> str:
if AppLoadSettings().dict().get("DEVICES_LOAD") is True:
INSTALLED_APPS.append("devices")
if AppLoadSettings().dict().get("NOTIFICATIONS_LOAD") is True:
INSTALLED_APPS.append("alarming")
INSTALLED_APPS.append("subscriptions")

class Config:
case_sensitive = False
Expand Down
26 changes: 26 additions & 0 deletions app/Entirety/entirety/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import re

from django.core.validators import URLValidator
from django.utils.regex_helper import _lazy_re_compile


class CustomURLValidator(URLValidator):
def __init__(self, schemes=None, **kwargs):
super().__init__(schemes, **kwargs)
self.host_re = (
"("
+ self.hostname_re
+ "(?:"
+ self.domain_re
+ self.tld_re
+ ")?|localhost)"
)
self.regex = _lazy_re_compile(
r"^(?:[a-z0-9.+-]*)://" # scheme is validated separately
r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?" # user:pass authentication
r"(?:" + self.ipv4_re + "|" + self.ipv6_re + "|" + self.host_re + ")"
r"(?::\d{1,5})?" # port
r"(?:[/?#][^\s]*)?" # resource path
r"\Z",
re.IGNORECASE,
)
2 changes: 0 additions & 2 deletions app/Entirety/entirety/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import requests

from django.http import HttpResponseRedirect
from django.urls import reverse
from django.shortcuts import render
Expand Down
Loading

0 comments on commit 55e5bf8

Please sign in to comment.