feat / AEB-62 add additional DB schema for objects logic
This commit is contained in:
@@ -16,6 +16,7 @@ pycodestyle = "*"
|
|||||||
requests = "*"
|
requests = "*"
|
||||||
pyjwt = "*"
|
pyjwt = "*"
|
||||||
drf-spectacular = "*"
|
drf-spectacular = "*"
|
||||||
|
pillow = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
|||||||
127
backend/Pipfile.lock
generated
127
backend/Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "c8a08d8710d5b37141e66db971439bf41996f2ea1330f2d5716f8be2a841a796"
|
"sha256": "9818bd0afdf9b9b9884ad0f207ea2fa3485e448d2a80cd079d604018130ae088"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"asgiref": {
|
"asgiref": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960",
|
"sha256:aef8a81283a34d0ab31630c9b7dfe70c812c95eba78171367ca8745e88124734",
|
||||||
"sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1"
|
"sha256:d89f2d8cd8b56dada7d52fa7dc8075baa08fb836560710d38c292a7a3f78c04e"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.9'",
|
"markers": "python_version >= '3.9'",
|
||||||
"version": "==3.9.2"
|
"version": "==3.10.0"
|
||||||
},
|
},
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -34,11 +34,11 @@
|
|||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407",
|
"sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de",
|
||||||
"sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"
|
"sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==2025.8.3"
|
"version": "==2025.10.5"
|
||||||
},
|
},
|
||||||
"charset-normalizer": {
|
"charset-normalizer": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -225,6 +225,119 @@
|
|||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==25.0"
|
"version": "==25.0"
|
||||||
},
|
},
|
||||||
|
"pillow": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2",
|
||||||
|
"sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214",
|
||||||
|
"sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e",
|
||||||
|
"sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59",
|
||||||
|
"sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50",
|
||||||
|
"sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632",
|
||||||
|
"sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06",
|
||||||
|
"sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a",
|
||||||
|
"sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51",
|
||||||
|
"sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced",
|
||||||
|
"sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f",
|
||||||
|
"sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12",
|
||||||
|
"sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8",
|
||||||
|
"sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6",
|
||||||
|
"sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580",
|
||||||
|
"sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f",
|
||||||
|
"sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac",
|
||||||
|
"sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860",
|
||||||
|
"sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd",
|
||||||
|
"sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722",
|
||||||
|
"sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8",
|
||||||
|
"sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4",
|
||||||
|
"sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673",
|
||||||
|
"sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788",
|
||||||
|
"sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542",
|
||||||
|
"sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e",
|
||||||
|
"sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd",
|
||||||
|
"sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8",
|
||||||
|
"sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523",
|
||||||
|
"sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967",
|
||||||
|
"sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809",
|
||||||
|
"sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477",
|
||||||
|
"sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027",
|
||||||
|
"sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae",
|
||||||
|
"sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b",
|
||||||
|
"sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c",
|
||||||
|
"sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f",
|
||||||
|
"sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e",
|
||||||
|
"sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b",
|
||||||
|
"sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7",
|
||||||
|
"sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27",
|
||||||
|
"sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361",
|
||||||
|
"sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae",
|
||||||
|
"sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d",
|
||||||
|
"sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc",
|
||||||
|
"sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58",
|
||||||
|
"sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad",
|
||||||
|
"sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6",
|
||||||
|
"sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024",
|
||||||
|
"sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978",
|
||||||
|
"sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb",
|
||||||
|
"sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d",
|
||||||
|
"sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0",
|
||||||
|
"sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9",
|
||||||
|
"sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f",
|
||||||
|
"sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874",
|
||||||
|
"sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa",
|
||||||
|
"sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081",
|
||||||
|
"sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149",
|
||||||
|
"sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6",
|
||||||
|
"sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d",
|
||||||
|
"sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd",
|
||||||
|
"sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f",
|
||||||
|
"sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c",
|
||||||
|
"sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31",
|
||||||
|
"sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e",
|
||||||
|
"sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db",
|
||||||
|
"sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6",
|
||||||
|
"sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f",
|
||||||
|
"sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494",
|
||||||
|
"sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69",
|
||||||
|
"sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94",
|
||||||
|
"sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77",
|
||||||
|
"sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d",
|
||||||
|
"sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7",
|
||||||
|
"sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a",
|
||||||
|
"sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438",
|
||||||
|
"sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288",
|
||||||
|
"sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b",
|
||||||
|
"sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635",
|
||||||
|
"sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3",
|
||||||
|
"sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d",
|
||||||
|
"sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe",
|
||||||
|
"sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0",
|
||||||
|
"sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe",
|
||||||
|
"sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a",
|
||||||
|
"sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805",
|
||||||
|
"sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8",
|
||||||
|
"sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36",
|
||||||
|
"sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a",
|
||||||
|
"sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b",
|
||||||
|
"sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e",
|
||||||
|
"sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25",
|
||||||
|
"sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12",
|
||||||
|
"sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada",
|
||||||
|
"sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c",
|
||||||
|
"sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71",
|
||||||
|
"sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d",
|
||||||
|
"sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c",
|
||||||
|
"sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6",
|
||||||
|
"sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1",
|
||||||
|
"sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50",
|
||||||
|
"sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653",
|
||||||
|
"sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c",
|
||||||
|
"sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4",
|
||||||
|
"sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==11.3.0"
|
||||||
|
},
|
||||||
"pluggy": {
|
"pluggy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3",
|
"sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3",
|
||||||
|
|||||||
13
backend/api/account/serializers/SensorSerializers.py
Normal file
13
backend/api/account/serializers/SensorSerializers.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from django.conf import settings
|
||||||
|
from sitemanagement.models import Sensor, Metric, Alert
|
||||||
|
|
||||||
|
class SensorSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Sensor
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class MetricSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Metric
|
||||||
|
fields = '__all__'
|
||||||
10
backend/api/account/views/GetSensorDataView.py
Normal file
10
backend/api/account/views/GetSensorDataView.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample
|
||||||
|
|
||||||
|
from api.auth.serializers import UserResponseSerializer
|
||||||
|
from api.models import UserProfile
|
||||||
|
|
||||||
|
from api.utils.decorators import handle_exceptions
|
||||||
@@ -8,6 +8,7 @@ load_dotenv(dotenv_path=BASE_DIR / './.env')
|
|||||||
|
|
||||||
SECRET_KEY = os.environ.get("SECRET_KEY")
|
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||||
DEBUG = os.environ.get("DEBUG_MODE")
|
DEBUG = os.environ.get("DEBUG_MODE")
|
||||||
|
BASE_URL = os.environ.get("BASE_URL", "http://127.0.0.1:8000")
|
||||||
|
|
||||||
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
|
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
|
||||||
CSRF_TRUSTED_ORIGINS = os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",")
|
CSRF_TRUSTED_ORIGINS = os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
asgiref==3.9.2
|
asgiref==3.10.0
|
||||||
attrs==25.3.0
|
attrs==25.3.0
|
||||||
certifi==2025.8.3
|
certifi==2025.10.5
|
||||||
charset-normalizer==3.4.3
|
charset-normalizer==3.4.3
|
||||||
Django==5.2.7
|
Django==5.2.7
|
||||||
django-cors-headers==4.9.0
|
django-cors-headers==4.9.0
|
||||||
@@ -14,6 +14,7 @@ iniconfig==2.1.0
|
|||||||
jsonschema==4.25.1
|
jsonschema==4.25.1
|
||||||
jsonschema-specifications==2025.9.1
|
jsonschema-specifications==2025.9.1
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
|
pillow==11.3.0
|
||||||
pluggy==1.6.0
|
pluggy==1.6.0
|
||||||
psycopg2-binary==2.9.10
|
psycopg2-binary==2.9.10
|
||||||
pycodestyle==2.14.0
|
pycodestyle==2.14.0
|
||||||
|
|||||||
@@ -1,31 +1,93 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Multiplexor, Channel, SensorType, SignalFormat, Sensor, Metric, Alert
|
from .models import *
|
||||||
|
|
||||||
@admin.register(Multiplexor)
|
@admin.register(Multiplexor)
|
||||||
class MultiplexorAdmin(admin.ModelAdmin):
|
class MultiplexorAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'ip', 'subnet', 'gateway', 'sd_path')
|
list_display = ('name', 'ip', 'subnet', 'gateway', 'sd_path')
|
||||||
|
list_filter = ('name', 'ip', 'subnet', 'gateway', 'sd_path')
|
||||||
|
search_fields = ('name', 'ip', 'subnet', 'gateway', 'sd_path')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('ip', 'subnet', 'gateway', 'sd_path')
|
||||||
|
list_display_links = ('name',)
|
||||||
|
|
||||||
@admin.register(Channel)
|
@admin.register(Channel)
|
||||||
class ChannelAdmin(admin.ModelAdmin):
|
class ChannelAdmin(admin.ModelAdmin):
|
||||||
list_display = ('multiplexor', 'number', 'position')
|
list_display = ('multiplexor', 'number', 'position')
|
||||||
|
list_filter = ('multiplexor', 'number', 'position')
|
||||||
|
search_fields = ('multiplexor', 'number', 'position')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('number', 'position')
|
||||||
|
list_display_links = ('multiplexor',)
|
||||||
|
|
||||||
@admin.register(SensorType)
|
@admin.register(SensorType)
|
||||||
class SensorTypeAdmin(admin.ModelAdmin):
|
class SensorTypeAdmin(admin.ModelAdmin):
|
||||||
list_display = ('code', 'name', 'description')
|
list_display = ('code', 'name', 'description')
|
||||||
|
list_filter = ('code', 'name', 'description')
|
||||||
|
search_fields = ('code', 'name', 'description')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('name', 'description')
|
||||||
|
list_display_links = ('code',)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Metric)
|
@admin.register(Metric)
|
||||||
class MetricAdmin(admin.ModelAdmin):
|
class MetricAdmin(admin.ModelAdmin):
|
||||||
list_display = ('timestamp', 'sensor', 'raw_value', 'value', 'status')
|
list_display = ('timestamp', 'sensor', 'raw_value', 'value', 'status')
|
||||||
|
list_filter = ('timestamp', 'sensor', 'status')
|
||||||
|
search_fields = ('timestamp', 'sensor', 'status')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('value', 'status')
|
||||||
|
list_display_links = ('timestamp',)
|
||||||
|
|
||||||
@admin.register(Alert)
|
@admin.register(Alert)
|
||||||
class AlertAdmin(admin.ModelAdmin):
|
class AlertAdmin(admin.ModelAdmin):
|
||||||
list_display = ('sensor', 'metric', 'sensor_type', 'message', 'severity', 'created_at', 'resolved')
|
list_display = ('sensor', 'metric', 'sensor_type', 'message', 'severity', 'created_at', 'resolved')
|
||||||
|
list_filter = ('sensor', 'metric', 'sensor_type', 'severity')
|
||||||
|
search_fields = ('sensor', 'metric', 'sensor_type', 'severity')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('message', 'severity', 'resolved')
|
||||||
|
list_display_links = ('sensor',)
|
||||||
|
|
||||||
@admin.register(SignalFormat)
|
@admin.register(SignalFormat)
|
||||||
class SignalFormatAdmin(admin.ModelAdmin):
|
class SignalFormatAdmin(admin.ModelAdmin):
|
||||||
list_display = ('sensor_type', 'code', 'unit', 'conversion_rule')
|
list_display = ('sensor_type', 'code', 'unit', 'conversion_rule')
|
||||||
|
list_filter = ('sensor_type', 'code', 'unit', 'conversion_rule')
|
||||||
|
search_fields = ('sensor_type', 'code', 'unit', 'conversion_rule')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('unit', 'conversion_rule')
|
||||||
|
list_display_links = ('sensor_type',)
|
||||||
|
|
||||||
@admin.register(Sensor)
|
@admin.register(Sensor)
|
||||||
class SensorAdmin(admin.ModelAdmin):
|
class SensorAdmin(admin.ModelAdmin):
|
||||||
list_display = ('channel', 'sensor_type', 'signal_format', 'serial_number', 'name', 'math_formula')
|
list_display = ('channel', 'sensor_type', 'signal_format', 'serial_number', 'name', 'math_formula')
|
||||||
|
list_filter = ('channel', 'sensor_type', 'signal_format', 'serial_number', 'name', 'math_formula')
|
||||||
|
search_fields = ('channel', 'sensor_type', 'signal_format', 'serial_number', 'name', 'math_formula')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('signal_format', 'serial_number', 'name', 'math_formula')
|
||||||
|
list_display_links = ('channel',)
|
||||||
|
|
||||||
|
@admin.register(Object)
|
||||||
|
class ObjectAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('title', 'description', 'image', 'address', 'floors', 'area')
|
||||||
|
list_filter = ('title', 'description', 'image', 'address', 'floors', 'area')
|
||||||
|
search_fields = ('title', 'description', 'image', 'address', 'floors', 'area')
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('image', 'address', 'floors', 'area')
|
||||||
|
list_display_links = ('title',)
|
||||||
|
|
||||||
|
@admin.register(Zone)
|
||||||
|
class ZoneAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('object', 'name') # Removed sensors from list_display
|
||||||
|
list_filter = ('object', 'name') # Removed sensors from list_filter
|
||||||
|
search_fields = ('object', 'name') # Removed sensors from search_fields
|
||||||
|
list_per_page = 10
|
||||||
|
list_max_show_all = 100
|
||||||
|
list_editable = ('name',) # Changed to tuple with name only
|
||||||
|
list_display_links = ('object',)
|
||||||
3
backend/sitemanagement/constants/image_file_path.py
Normal file
3
backend/sitemanagement/constants/image_file_path.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
def register_object_upload_path(instance, filename):
|
||||||
|
"""Генерирует путь: media/objects/{filename}"""
|
||||||
|
return f"objects/{filename}"
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-06 10:59
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('sitemanagement', '0004_sensor_math_formula'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='alert',
|
||||||
|
name='message',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='Сообщение'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='alert',
|
||||||
|
name='metric',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alerts', to='sitemanagement.metric', verbose_name='Метрика'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='alert',
|
||||||
|
name='resolved',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Статус обработки'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='alert',
|
||||||
|
name='sensor',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alerts', to='sitemanagement.sensor', verbose_name='Датчик'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='alert',
|
||||||
|
name='sensor_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alerts', to='sitemanagement.sensortype', verbose_name='Тип сенсора'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='alert',
|
||||||
|
name='severity',
|
||||||
|
field=models.CharField(choices=[('warning', 'Warning'), ('critical', 'Critical')], default='warning', max_length=20, verbose_name='Уровень тревоги'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='metric',
|
||||||
|
name='raw_value',
|
||||||
|
field=models.CharField(max_length=50, verbose_name='Исходное значение'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='metric',
|
||||||
|
name='sensor',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='metrics', to='sitemanagement.sensor', verbose_name='Датчик'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='metric',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Статус'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='metric',
|
||||||
|
name='value',
|
||||||
|
field=models.FloatField(blank=True, null=True, verbose_name='Преобразованное значение'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='sensor',
|
||||||
|
name='math_formula',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Математическая формула'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='sensor',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Название'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='sensor',
|
||||||
|
name='sensor_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sitemanagement.sensortype', verbose_name='Тип сенсора'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='sensor',
|
||||||
|
name='serial_number',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Серийный номер'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='sensor',
|
||||||
|
name='signal_format',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sitemanagement.signalformat', verbose_name='Формат сигнала'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='signalformat',
|
||||||
|
name='code',
|
||||||
|
field=models.CharField(max_length=50, verbose_name='Код'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='signalformat',
|
||||||
|
name='conversion_rule',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Правило преобразования'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='signalformat',
|
||||||
|
name='unit',
|
||||||
|
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Единица измерения'),
|
||||||
|
),
|
||||||
|
]
|
||||||
50
backend/sitemanagement/migrations/0006_object_zone.py
Normal file
50
backend/sitemanagement/migrations/0006_object_zone.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-06 11:28
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import sitemanagement.constants.image_file_path
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('sitemanagement', '0005_alter_alert_message_alter_alert_metric_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Object',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=255, verbose_name='Название')),
|
||||||
|
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
|
||||||
|
('image', models.ImageField(blank=True, null=True, upload_to=sitemanagement.constants.image_file_path.register_object_upload_path, verbose_name='Изображение')),
|
||||||
|
('address', models.CharField(max_length=255, verbose_name='Адрес')),
|
||||||
|
('floors', models.PositiveSmallIntegerField(verbose_name='Количество этажей')),
|
||||||
|
('area', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Площадь')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Объект',
|
||||||
|
'verbose_name_plural': 'Объекты',
|
||||||
|
'ordering': ['title'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Zone',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='Название')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='zones', to='sitemanagement.object', verbose_name='Объект')),
|
||||||
|
('sensors', models.ManyToManyField(related_name='zones', to='sitemanagement.sensor', verbose_name='Датчики')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Зона',
|
||||||
|
'verbose_name_plural': 'Зоны',
|
||||||
|
'ordering': ['object', 'name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from sitemanagement.constants.image_file_path import register_object_upload_path
|
||||||
|
|
||||||
class Multiplexor(models.Model):
|
class Multiplexor(models.Model):
|
||||||
"""Устройство-мультиплексор"""
|
"""Устройство-мультиплексор"""
|
||||||
@@ -61,9 +61,9 @@ class SensorType(models.Model):
|
|||||||
class SignalFormat(models.Model):
|
class SignalFormat(models.Model):
|
||||||
"""Формат сигнала и правило преобразования"""
|
"""Формат сигнала и правило преобразования"""
|
||||||
sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, related_name="formats")
|
sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, related_name="formats")
|
||||||
code = models.CharField(max_length=50) # например "4-20мА", "VW f<1600Hz", "NTC R>250Ohm"
|
code = models.CharField(max_length=50, verbose_name="Код") # например "4-20мА", "VW f<1600Hz", "NTC R>250Ohm"
|
||||||
unit = models.CharField(max_length=20, blank=True, null=True) # °C, мкм/м, мм и т.п.
|
unit = models.CharField(max_length=20, blank=True, null=True, verbose_name="Единица измерения") # °C, мкм/м, мм и т.п.
|
||||||
conversion_rule = models.CharField(max_length=255, blank=True, null=True)
|
conversion_rule = models.CharField(max_length=255, blank=True, null=True, verbose_name="Правило преобразования")
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -79,13 +79,13 @@ class SignalFormat(models.Model):
|
|||||||
class Sensor(models.Model):
|
class Sensor(models.Model):
|
||||||
"""Конкретный датчик, установленный в канале"""
|
"""Конкретный датчик, установленный в канале"""
|
||||||
channel = models.ForeignKey(Channel, on_delete=models.CASCADE, related_name="sensors")
|
channel = models.ForeignKey(Channel, on_delete=models.CASCADE, related_name="sensors")
|
||||||
sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE)
|
sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, verbose_name="Тип сенсора")
|
||||||
signal_format = models.ForeignKey(SignalFormat, on_delete=models.SET_NULL, null=True, blank=True)
|
signal_format = models.ForeignKey(SignalFormat, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Формат сигнала")
|
||||||
serial_number = models.CharField(max_length=50, blank=True, null=True) # CL 2106009
|
serial_number = models.CharField(max_length=50, blank=True, null=True, verbose_name="Серийный номер") # CL 2106009
|
||||||
name = models.CharField(max_length=50, blank=True, null=True) # GA-1, HLE-1 и т.п.
|
name = models.CharField(max_length=50, blank=True, null=True, verbose_name="Название") # GA-1, HLE-1 и т.п.
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
math_formula = models.CharField(null=True, blank=True, max_length=255)
|
math_formula = models.CharField(null=True, blank=True, max_length=255, verbose_name="Математическая формула")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Датчик"
|
verbose_name = "Датчик"
|
||||||
@@ -99,10 +99,10 @@ class Sensor(models.Model):
|
|||||||
class Metric(models.Model):
|
class Metric(models.Model):
|
||||||
"""Значения, которые приходят из CSV"""
|
"""Значения, которые приходят из CSV"""
|
||||||
timestamp = models.DateTimeField()
|
timestamp = models.DateTimeField()
|
||||||
sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name="metrics")
|
sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name="metrics", verbose_name="Датчик")
|
||||||
raw_value = models.CharField(max_length=50) # исходное значение из файла (например "11.964 (A)")
|
raw_value = models.CharField(max_length=50, verbose_name="Исходное значение") # исходное значение из файла (например "11.964 (A)")
|
||||||
value = models.FloatField(null=True, blank=True) # преобразованное значение
|
value = models.FloatField(null=True, blank=True, verbose_name="Преобразованное значение") # преобразованное значение
|
||||||
status = models.CharField(max_length=20, blank=True, null=True) # No Rx, Error, NotAv и т.д.
|
status = models.CharField(max_length=20, blank=True, null=True, verbose_name="Статус") # No Rx, Error, NotAv и т.д.
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
@@ -121,21 +121,22 @@ class Metric(models.Model):
|
|||||||
|
|
||||||
class Alert(models.Model):
|
class Alert(models.Model):
|
||||||
"""Тревоги по метрикам"""
|
"""Тревоги по метрикам"""
|
||||||
sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name="alerts")
|
sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name="alerts", verbose_name="Датчик")
|
||||||
metric = models.ForeignKey(Metric, on_delete=models.CASCADE, related_name="alerts")
|
metric = models.ForeignKey(Metric, on_delete=models.CASCADE, related_name="alerts", verbose_name="Метрика")
|
||||||
sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, related_name="alerts")
|
sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, related_name="alerts", verbose_name="Тип сенсора")
|
||||||
message = models.CharField(max_length=255)
|
message = models.CharField(max_length=255, verbose_name="Сообщение")
|
||||||
severity = models.CharField(
|
severity = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
choices=[
|
choices=[
|
||||||
("warning", "Warning"),
|
("warning", "Warning"),
|
||||||
("critical", "Critical"),
|
("critical", "Critical"),
|
||||||
],
|
],
|
||||||
default="warning"
|
default="warning",
|
||||||
|
verbose_name="Уровень тревоги"
|
||||||
)
|
)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
resolved = models.BooleanField(default=False)
|
resolved = models.BooleanField(default=False, verbose_name="Статус обработки")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
indexes = [
|
indexes = [
|
||||||
@@ -149,3 +150,39 @@ class Alert(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"ALERT {self.sensor} @ {self.metric.timestamp}: {self.message}"
|
return f"ALERT {self.sensor} @ {self.metric.timestamp}: {self.message}"
|
||||||
|
|
||||||
|
class Object(models.Model):
|
||||||
|
"""Объект"""
|
||||||
|
title = models.CharField(max_length=255, verbose_name="Название")
|
||||||
|
description = models.TextField(blank=True, null=True, verbose_name="Описание")
|
||||||
|
image = models.ImageField(upload_to=register_object_upload_path, null=True, blank=True, verbose_name="Изображение")
|
||||||
|
address = models.CharField(max_length=255, verbose_name="Адрес")
|
||||||
|
floors = models.PositiveSmallIntegerField(verbose_name="Количество этажей")
|
||||||
|
area = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Площадь")
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Объект"
|
||||||
|
verbose_name_plural = "Объекты"
|
||||||
|
ordering = ["title"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Zone(models.Model):
|
||||||
|
"""Зона"""
|
||||||
|
object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name="zones", verbose_name="Объект")
|
||||||
|
name = models.CharField(max_length=255, verbose_name="Название")
|
||||||
|
sensors = models.ManyToManyField(Sensor, related_name="zones", verbose_name="Датчики")
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Зона"
|
||||||
|
verbose_name_plural = "Зоны"
|
||||||
|
ordering = ["object", "name"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.object.title} - {self.name}"
|
||||||
|
|
||||||
Reference in New Issue
Block a user