feat / AEB-66 create dashboard api
This commit is contained in:
135
backend/Pipfile.lock
generated
135
backend/Pipfile.lock
generated
@@ -179,11 +179,11 @@
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
|
||||
"sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
|
||||
"sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea",
|
||||
"sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.10"
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==3.11"
|
||||
},
|
||||
"inflection": {
|
||||
"hashes": [
|
||||
@@ -348,78 +348,65 @@
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
"sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff",
|
||||
"sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5",
|
||||
"sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f",
|
||||
"sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5",
|
||||
"sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0",
|
||||
"sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c",
|
||||
"sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c",
|
||||
"sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341",
|
||||
"sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f",
|
||||
"sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7",
|
||||
"sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d",
|
||||
"sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007",
|
||||
"sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142",
|
||||
"sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92",
|
||||
"sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb",
|
||||
"sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5",
|
||||
"sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5",
|
||||
"sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8",
|
||||
"sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1",
|
||||
"sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68",
|
||||
"sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73",
|
||||
"sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1",
|
||||
"sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53",
|
||||
"sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d",
|
||||
"sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906",
|
||||
"sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0",
|
||||
"sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2",
|
||||
"sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a",
|
||||
"sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b",
|
||||
"sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44",
|
||||
"sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648",
|
||||
"sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7",
|
||||
"sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f",
|
||||
"sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa",
|
||||
"sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697",
|
||||
"sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d",
|
||||
"sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b",
|
||||
"sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526",
|
||||
"sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4",
|
||||
"sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287",
|
||||
"sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e",
|
||||
"sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673",
|
||||
"sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0",
|
||||
"sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30",
|
||||
"sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3",
|
||||
"sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e",
|
||||
"sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92",
|
||||
"sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a",
|
||||
"sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c",
|
||||
"sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8",
|
||||
"sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909",
|
||||
"sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47",
|
||||
"sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864",
|
||||
"sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc",
|
||||
"sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00",
|
||||
"sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb",
|
||||
"sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539",
|
||||
"sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b",
|
||||
"sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481",
|
||||
"sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5",
|
||||
"sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4",
|
||||
"sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64",
|
||||
"sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392",
|
||||
"sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4",
|
||||
"sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1",
|
||||
"sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1",
|
||||
"sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567",
|
||||
"sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863"
|
||||
"sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1",
|
||||
"sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10",
|
||||
"sha256:20e7fb94e20b03dcc783f76c0865f9da39559dcc0c28dd1a3fce0d01902a6b9c",
|
||||
"sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee",
|
||||
"sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4",
|
||||
"sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03",
|
||||
"sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a",
|
||||
"sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee",
|
||||
"sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e",
|
||||
"sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316",
|
||||
"sha256:41360b01c140c2a03d346cec3280cf8a71aa07d94f3b1509fa0161c366af66b4",
|
||||
"sha256:44fc5c2b8fa871ce7f0023f619f1349a0aa03a0857f2c96fbc01c657dcbbdb49",
|
||||
"sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c",
|
||||
"sha256:4bdab48575b6f870f465b397c38f1b415520e9879fdf10a53ee4f49dcbdf8a21",
|
||||
"sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b",
|
||||
"sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3",
|
||||
"sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b",
|
||||
"sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d",
|
||||
"sha256:691c807d94aecfbc76a14e1408847d59ff5b5906a04a23e12a89007672b9e819",
|
||||
"sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a",
|
||||
"sha256:875039274f8a2361e5207857899706da840768e2a775bf8c65e82f60b197df02",
|
||||
"sha256:8b81627b691f29c4c30a8f322546ad039c40c328373b11dff7490a3e1b517855",
|
||||
"sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0",
|
||||
"sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd",
|
||||
"sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1",
|
||||
"sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5",
|
||||
"sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f",
|
||||
"sha256:9c55460033867b4622cda1b6872edf445809535144152e5d14941ef591980edf",
|
||||
"sha256:9d3a9edcfbe77a3ed4bc72836d466dfce4174beb79eda79ea155cc77237ed9e8",
|
||||
"sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2",
|
||||
"sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087",
|
||||
"sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a",
|
||||
"sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c",
|
||||
"sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d",
|
||||
"sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c",
|
||||
"sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c",
|
||||
"sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4",
|
||||
"sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4",
|
||||
"sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e",
|
||||
"sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766",
|
||||
"sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d",
|
||||
"sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d",
|
||||
"sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39",
|
||||
"sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908",
|
||||
"sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7",
|
||||
"sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2",
|
||||
"sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8",
|
||||
"sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f",
|
||||
"sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f",
|
||||
"sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3",
|
||||
"sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa",
|
||||
"sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94",
|
||||
"sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc",
|
||||
"sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db",
|
||||
"sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.9.10"
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==2.9.11"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
|
||||
19
backend/api/account/serializers/dashboard_serializers.py
Normal file
19
backend/api/account/serializers/dashboard_serializers.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from rest_framework import serializers
|
||||
from api.account.serializers.alert_serializers import AlertSerializer
|
||||
|
||||
class ChartDataSerializer(serializers.Serializer):
|
||||
timestamp = serializers.DateTimeField()
|
||||
value = serializers.IntegerField()
|
||||
|
||||
class DashboardSerializer(serializers.Serializer):
|
||||
chart_data = ChartDataSerializer(many=True, required=False)
|
||||
table_data = AlertSerializer(many=True, read_only=True)
|
||||
|
||||
def to_representation(self, instance):
|
||||
return {
|
||||
'chart_data': instance['chart_data'],
|
||||
'table_data': AlertSerializer(instance['table_data'], many=True).data
|
||||
}
|
||||
|
||||
class Meta:
|
||||
fields = ('chart_data', 'table_data')
|
||||
@@ -3,6 +3,7 @@ from .views.UserDataView import UserDataView
|
||||
from .views.objects_views import ObjectView
|
||||
from .views.sensors_views import SensorView
|
||||
from .views.alert_views import AlertView, ReportView
|
||||
from .views.dashboard_views import DashboardView
|
||||
from drf_spectacular.views import (
|
||||
SpectacularAPIView,
|
||||
SpectacularSwaggerView,
|
||||
@@ -33,4 +34,6 @@ urlpatterns = [
|
||||
path("update-alert/<int:pk>/", AlertView.as_view({'patch': 'change_alert_status'}), name="update-alert"),
|
||||
|
||||
path("get-reports/", ReportView.as_view({'post': 'get_reports'}), name="reports"),
|
||||
|
||||
path("get-dashboard/", DashboardView.as_view(), name="dashboard"),
|
||||
]
|
||||
|
||||
108
backend/api/account/views/dashboard_views.py
Normal file
108
backend/api/account/views/dashboard_views.py
Normal file
@@ -0,0 +1,108 @@
|
||||
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, OpenApiParameter
|
||||
from api.utils.decorators import handle_exceptions
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
from django.db.models import Count
|
||||
from django.db.models.functions import TruncHour
|
||||
from sitemanagement.models import Alert
|
||||
from api.account.serializers.dashboard_serializers import DashboardSerializer
|
||||
|
||||
@extend_schema(tags=['Дашборд'])
|
||||
class DashboardView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
summary="Получение статистики алертов",
|
||||
description="Возвращает статистику алертов за выбранный период времени",
|
||||
parameters=[OpenApiParameter(
|
||||
name='time_period',
|
||||
type=str,
|
||||
location=OpenApiParameter.QUERY,
|
||||
description='Период времени в часах',
|
||||
required=False,
|
||||
enum=['24', '72', '168', '720'],
|
||||
default='168'
|
||||
)],
|
||||
responses = {
|
||||
200: OpenApiResponse(response=DashboardSerializer, description="Данные для дашборда успешно получены",
|
||||
examples=[OpenApiExample(
|
||||
'Успешный ответ',
|
||||
value={
|
||||
"chart_data": [
|
||||
{"timestamp": "2025-10-07T10:00:00+03:00", "value": 5},
|
||||
{"timestamp": "2025-10-07T11:00:00+03:00", "value": 3}
|
||||
],
|
||||
"table_data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "GA-1",
|
||||
"object": "Объект 1",
|
||||
"metric_value": "23.5 °C",
|
||||
"sensor_type_name": "Датчик температуры",
|
||||
"message": "Превышение температуры",
|
||||
"severity": "warning",
|
||||
"created_at": "2025-10-07T10:30:00+03:00",
|
||||
"resolved": False
|
||||
}
|
||||
]
|
||||
}
|
||||
)])
|
||||
}
|
||||
|
||||
)
|
||||
@handle_exceptions
|
||||
def get(self, request):
|
||||
time_period = request.query_params.get('time_period', '168')
|
||||
|
||||
if time_period not in ['720', '168', '72', '24']:
|
||||
return Response(
|
||||
{"error": "Неверный период. Допустимые значения: 720, 168, 72, 24"},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# определяем начальную дату
|
||||
start_date = timezone.now() - timedelta(hours=int(time_period))
|
||||
|
||||
# получаем все алерты за период с фронта
|
||||
alerts = Alert.objects.filter(created_at__gte=start_date)
|
||||
|
||||
# данные для графиков (группировка по часам)
|
||||
alerts_by_hour = alerts.annotate(
|
||||
hour=TruncHour('created_at')
|
||||
).values('hour').annotate(
|
||||
count=Count('id')
|
||||
).order_by('hour')
|
||||
|
||||
# !! обязательно локальное время
|
||||
chart_data = []
|
||||
for entry in alerts_by_hour:
|
||||
local_time = timezone.localtime(entry['hour'])
|
||||
chart_data.append({
|
||||
"timestamp": local_time.isoformat(),
|
||||
"value": entry['count']
|
||||
})
|
||||
|
||||
# данные для таблицы с алертами
|
||||
alerts_list = alerts.select_related(
|
||||
'sensor',
|
||||
'sensor_type',
|
||||
'metric',
|
||||
'sensor__signal_format'
|
||||
).prefetch_related(
|
||||
'sensor__zones',
|
||||
'sensor__zones__object'
|
||||
).order_by('-created_at')
|
||||
|
||||
|
||||
dashboard_data = {
|
||||
"chart_data": chart_data,
|
||||
"table_data": alerts_list
|
||||
}
|
||||
|
||||
serializer = DashboardSerializer(instance=dashboard_data)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
@@ -101,6 +101,7 @@ SPECTACULAR_SETTINGS = {
|
||||
{'name': 'Объекты', 'description': 'Метод для получения данных об объектах'},
|
||||
{'name': 'Датчики', 'description': 'Методы для работы с датчиками'},
|
||||
{'name': 'Алерты', 'description': 'Методы для работы с алертами'},
|
||||
{'name': 'Дашборд', 'description': 'Получение данных для дашборда'},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ djangorestframework==3.16.1
|
||||
djangorestframework_simplejwt==5.5.1
|
||||
dotenv==0.9.9
|
||||
drf-spectacular==0.28.0
|
||||
idna==3.10
|
||||
idna==3.11
|
||||
inflection==0.5.1
|
||||
iniconfig==2.1.0
|
||||
jsonschema==4.25.1
|
||||
@@ -16,7 +16,7 @@ jsonschema-specifications==2025.9.1
|
||||
packaging==25.0
|
||||
pillow==11.3.0
|
||||
pluggy==1.6.0
|
||||
psycopg2-binary==2.9.10
|
||||
psycopg2-binary==2.9.11
|
||||
pycodestyle==2.14.0
|
||||
Pygments==2.19.2
|
||||
PyJWT==2.10.1
|
||||
|
||||
Reference in New Issue
Block a user