diff --git a/README.md b/README.md index 1264739d218df74b44150ee3284bfc4c38b6d633..55e9805b1de6e94aa3027c7d8c362efc30b4dee0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ # lustra-django-app -## Running the Django server on Windows +This micro-service is the API used by multiple micro-services in the infrastructure. It is used by the Grafana dashboard as a data source, by the MQTT Client as a data input and by the Godot game as both input and output. + +## Requirements + +Python >=3.11 + +Other project dependencies are listed in the [`requirements.txt`](./requirements.txt) file. + +## Seting up the development environment Create a Python virtual environment. @@ -10,11 +18,19 @@ python -m venv env Activate the virtual enviornment. +- On Linux based systems + ``` -env\Scripts\activate.bat +source ./env/bin/activate ``` -Install the requirements. +- On Windows + +``` +.\env\Scripts\activate +``` + +Install the requirements using pip. ``` pip install -r requirements.txt @@ -22,6 +38,8 @@ pip install -r requirements.txt Apply the database migrations. +> By default, SQLite3 is used as a development database. Refer to Djang's documentation if you wish to modify the database driver used. + ``` python lustraDjangoApp\manage.py migrate ``` @@ -34,14 +52,9 @@ python lustraDjangoApp\manage.py runserver Run the server on default route. -``` -python lustraDjangoApp\manage.py 0.0.0.0:8000 -``` +## API Documentation -## Running the Django server with the launch script on Windows +The API documentation is served on two endpoints after running the app: -Run the `launch-script-windows.bat` through a terminal. - -``` -C:\...> launch-script-windows.bat -``` +- `/api/schema/swagger-ui` using Swagger +- `/api/schema/redoc` using Redoc diff --git a/lustraDjangoApp/lustraApi/models.py b/lustraDjangoApp/lustraApi/models.py index e28037b7c3b3e0aca63decf65cd309b22f99f430..f86ff3d098d4c3996568ddfcfd38333d1e19a790 100644 --- a/lustraDjangoApp/lustraApi/models.py +++ b/lustraDjangoApp/lustraApi/models.py @@ -4,10 +4,14 @@ from django.db import models class Message(models.Model): + """Messages that transit between the broker and the other micro-services. + """ payload = models.JSONField() class RecyclingCounters(models.Model): + """Numerical counters used by the game. + """ pet_counter = models.IntegerField() alu_counter = models.IntegerField() bad_recycling_counter = models.IntegerField(default=0) @@ -15,17 +19,23 @@ class RecyclingCounters(models.Model): class UserFeedback(models.Model): + """Feedback send by the user when a recycling was deemed as a bad. + """ container_type = models.IntegerField() is_system_wrong = models.BooleanField() timestamp = models.DateTimeField(auto_now_add=True) class DeviceMonitoringData(models.Model): + """Pings sent by the devices to monitor their activity. + """ device_id = models.IntegerField() timestamp = models.DateTimeField(auto_now_add=True) class RecyclingData(models.Model): + """Recycling data class used primarly for showing data. + """ device_id = models.IntegerField() container_type = models.IntegerField() game_code = models.BooleanField() diff --git a/lustraDjangoApp/lustraApi/views.py b/lustraDjangoApp/lustraApi/views.py index bf722e776821a77da74bb566f14b003298f5a02d..a834427b92c81e7f1991694dce030e44e0fb4a17 100644 --- a/lustraDjangoApp/lustraApi/views.py +++ b/lustraDjangoApp/lustraApi/views.py @@ -2,7 +2,11 @@ from lustraApi.models import Message, UserFeedback, RecyclingCounters, DeviceMon from lustraApi.serializers import MessagesSerializer, UserFeedbackSerializer, RecyclingCountersSerializer, DeviceMonitoringDataSerializer, RecyclingDataSerializer from rest_framework import viewsets, status from rest_framework.response import Response +from rest_framework.request import Request from rest_framework.decorators import action +from datetime import datetime +from itertools import groupby +import ciso8601 # Create your views here. @@ -11,6 +15,19 @@ class MessageViewSet(viewsets.ModelViewSet): queryset = Message.objects.all() serializer_class = MessagesSerializer + @action(detail=False, methods=["GET"], url_path='get-and-delete') + def get_and_delete(self, request): + """Retrieves and removes all messages. + """ + try: + response = self.list(request=request) + messages = self.queryset.all() + for message in messages: + message.delete() + return response + except RecyclingCounters.DoesNotExist: + return Response(status=status.HTTP_404_NOT_FOUND) + class UserFeedbackViewSet(viewsets.ModelViewSet): queryset = UserFeedback.objects.all() @@ -21,10 +38,22 @@ class RecyclingCountersViewSet(viewsets.ModelViewSet): queryset = RecyclingCounters.objects.all() serializer_class = RecyclingCountersSerializer + def create(self, request: Request, *args, **kwargs): + response = self.latest(request) + if response.status_code == status.HTTP_200_OK: + if 'pet_counter' in response.data and 'alu_counter' in response.data and 'bad_recycling_counter' in response.data: + if request.data['pet_counter'] >= response.data['pet_counter'] and \ + request.data['alu_counter'] >= response.data['alu_counter'] and \ + request.data['bad_recycling_counter'] >= response.data['bad_recycling_counter']: + return super().create(request, *args, **kwargs) + return Response(status=status.HTTP_204_NO_CONTENT) + @action( detail=False, methods=["GET"], url_path="latest" ) def latest(self, request): + """Returns the latest record in the database. + """ try: latest_recycling_counters = self.queryset.latest('timestamp') serializer = self.get_serializer(latest_recycling_counters) @@ -41,6 +70,8 @@ class DeviceMonitoringDataViewSet(viewsets.ModelViewSet): detail=False, methods=["GET"], url_path=r'latest/(?P<device_id>[^/.]+)' ) def latest(self, request, device_id=None): + """Returns the latest record in the database. + """ try: device_monitoring_data = self.queryset.filter( device_id__exact=device_id).latest('timestamp') @@ -53,3 +84,52 @@ class DeviceMonitoringDataViewSet(viewsets.ModelViewSet): class RecyclingDataViewSet(viewsets.ModelViewSet): queryset = RecyclingData.objects.all() serializer_class = RecyclingDataSerializer + + @action( + detail=False, methods=["GET"], url_path='metrics' + ) + def metrics(self, request): + """Get recycling data formatted in order to display it on a Grafana dashboard. + + Each recycling data is grouped by day, which is extracted from the timestamp. The data is expanded with additional counters that are computed in order to sepearte good / bad recycling for PET / ALU. + """ + recycling_data_list = list(self.queryset) + + def transform_data(data): + serialized_data = self.get_serializer(data).data + serialized_data['timestamp'] = datetime.fromtimestamp( + serialized_data['timestamp']).strftime('%Y-%m-%d') + return serialized_data + transformed_data = map(transform_data, recycling_data_list) + grouped_by_timestamp_data = groupby( + transformed_data, lambda x: x['timestamp']) + + return_data = list() + # Might need improving because this will be slow with a lot of data... + for key, value in grouped_by_timestamp_data: + good_pet_recycling = 0 + good_alu_recycling = 0 + bad_pet_recycling = 0 + bad_alu_recycling = 0 + for element in list(value): + if element['container_type'] == 0 and element['game_code']: + good_pet_recycling += 1 + elif element['container_type'] == 1 and element['game_code']: + good_alu_recycling += 1 + elif element['container_type'] == 0 and not element['game_code']: + bad_pet_recycling += 1 + elif element['container_type'] == 1 and not element['game_code']: + bad_alu_recycling += 1 + return_data.append( + { + 'good_pet_recycling': good_pet_recycling, + 'good_alu_recycling': good_alu_recycling, + 'bad_pet_recycling': bad_pet_recycling, + 'bad_alu_recycling': bad_alu_recycling, + 'total_pet_recycling': good_pet_recycling + bad_pet_recycling, + 'total_alu_recycling': good_alu_recycling + bad_alu_recycling, + 'time': ciso8601.parse_datetime(key).timestamp() + } + ) + + return Response(return_data) diff --git a/lustraDjangoApp/lustraDjangoApp/settings.py b/lustraDjangoApp/lustraDjangoApp/settings.py index b68e96efc04698cc8ab4aa377fe355fe8b08597d..282126023660d02da8738630130eaa2f2d8b8b36 100644 --- a/lustraDjangoApp/lustraDjangoApp/settings.py +++ b/lustraDjangoApp/lustraDjangoApp/settings.py @@ -48,6 +48,8 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'drf_spectacular', + 'drf_spectacular_sidecar', ] MIDDLEWARE = [ @@ -130,7 +132,8 @@ AUTH_PASSWORD_VALIDATORS = [ REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', - ) + ), + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', } # Internationalization @@ -155,4 +158,14 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static/") # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = 'django.db.models.Bi gAutoField' + +SPECTACULAR_SETTINGS = { + 'TITLE': 'Lustra Django API', + 'DESCRIPTION': 'This API serves as a data source for the Godot game and the Grafana dashboard, and as a data input for the information coming from the MQTT Broker.', + 'VERSION': '1.0.0', + 'SERVE_INCLUDE_SCHEMA': False, + 'SWAGGER_UI_DIST': 'SIDECAR', # shorthand to use the sidecar instead + 'SWAGGER_UI_FAVICON_HREF': 'SIDECAR', + 'REDOC_DIST': 'SIDECAR', +} diff --git a/lustraDjangoApp/lustraDjangoApp/urls.py b/lustraDjangoApp/lustraDjangoApp/urls.py index 0f54d34027595f5ed65a1649f5e449175b4a5902..68786f8a58ab0f24ed4dbe782b9285d722d4d7be 100644 --- a/lustraDjangoApp/lustraDjangoApp/urls.py +++ b/lustraDjangoApp/lustraDjangoApp/urls.py @@ -14,10 +14,10 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.contrib import admin from rest_framework import routers from django.urls import path, include from lustraApi import views +from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView router = routers.DefaultRouter() router.register(r'messages', views.MessageViewSet) @@ -29,5 +29,12 @@ router.register(r'recycling-data', views.RecyclingDataViewSet) urlpatterns = [ path('', include(router.urls)), + # YOUR PATTERNS + path('api/schema/', SpectacularAPIView.as_view(), name='schema'), + # Optional UI: + path('api/schema/swagger-ui/', + SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), + path('api/schema/redoc/', + SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # path('admin/', admin.site.urls), ] diff --git a/lustraDjangoApp/schema.yml b/lustraDjangoApp/schema.yml new file mode 100644 index 0000000000000000000000000000000000000000..271aa82e783d53837ee518300a46384eb11e2a69 --- /dev/null +++ b/lustraDjangoApp/schema.yml @@ -0,0 +1,1003 @@ +openapi: 3.0.3 +info: + title: Lustra Django API + version: 1.0.0 + description: This API serves as a data source for the Godot + game and the Grafana dashboard, and as a data input for the information coming + from the MQTT Broker. +paths: + /device-monitoring-data/: + get: + operationId: device_monitoring_data_list + tags: + - device-monitoring-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DeviceMonitoringData" + description: "" + post: + operationId: device_monitoring_data_create + tags: + - device-monitoring-data + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + multipart/form-data: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + description: "" + /device-monitoring-data/{id}/: + get: + operationId: device_monitoring_data_retrieve + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this device monitoring data. + required: true + tags: + - device-monitoring-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + description: "" + put: + operationId: device_monitoring_data_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this device monitoring data. + required: true + tags: + - device-monitoring-data + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + multipart/form-data: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + description: "" + patch: + operationId: device_monitoring_data_partial_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this device monitoring data. + required: true + tags: + - device-monitoring-data + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedDeviceMonitoringData" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedDeviceMonitoringData" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedDeviceMonitoringData" + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + description: "" + delete: + operationId: device_monitoring_data_destroy + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this device monitoring data. + required: true + tags: + - device-monitoring-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "204": + description: No response body + /device-monitoring-data/latest/{device_id}/: + get: + operationId: device_monitoring_data_latest_retrieve + parameters: + - in: path + name: device_id + schema: + type: integer + required: true + tags: + - device-monitoring-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/DeviceMonitoringData" + description: "" + /messages/: + get: + operationId: messages_list + tags: + - messages + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Messages" + description: "" + post: + operationId: messages_create + tags: + - messages + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Messages" + multipart/form-data: + schema: + $ref: "#/components/schemas/Messages" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + description: "" + /messages/{id}/: + get: + operationId: messages_retrieve + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this message. + required: true + tags: + - messages + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + description: "" + put: + operationId: messages_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this message. + required: true + tags: + - messages + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Messages" + multipart/form-data: + schema: + $ref: "#/components/schemas/Messages" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + description: "" + patch: + operationId: messages_partial_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this message. + required: true + tags: + - messages + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedMessages" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedMessages" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedMessages" + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + description: "" + delete: + operationId: messages_destroy + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this message. + required: true + tags: + - messages + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "204": + description: No response body + /messages/get-and-delete/: + get: + operationId: messages_get_and_delete_retrieve + tags: + - messages + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Messages" + description: "" + /recycling-counters/: + get: + operationId: recycling_counters_list + tags: + - recycling-counters + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RecyclingCounters" + description: "" + post: + operationId: recycling_counters_create + tags: + - recycling-counters + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/RecyclingCounters" + multipart/form-data: + schema: + $ref: "#/components/schemas/RecyclingCounters" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + description: "" + /recycling-counters/{id}/: + get: + operationId: recycling_counters_retrieve + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling counters. + required: true + tags: + - recycling-counters + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + description: "" + put: + operationId: recycling_counters_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling counters. + required: true + tags: + - recycling-counters + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/RecyclingCounters" + multipart/form-data: + schema: + $ref: "#/components/schemas/RecyclingCounters" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + description: "" + patch: + operationId: recycling_counters_partial_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling counters. + required: true + tags: + - recycling-counters + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedRecyclingCounters" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedRecyclingCounters" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedRecyclingCounters" + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + description: "" + delete: + operationId: recycling_counters_destroy + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling counters. + required: true + tags: + - recycling-counters + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "204": + description: No response body + /recycling-counters/latest/: + get: + operationId: recycling_counters_latest_retrieve + tags: + - recycling-counters + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingCounters" + description: "" + /recycling-data/: + get: + operationId: recycling_data_list + tags: + - recycling-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RecyclingData" + description: "" + post: + operationId: recycling_data_create + tags: + - recycling-data + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/RecyclingData" + multipart/form-data: + schema: + $ref: "#/components/schemas/RecyclingData" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + description: "" + /recycling-data/{id}/: + get: + operationId: recycling_data_retrieve + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling data. + required: true + tags: + - recycling-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + description: "" + put: + operationId: recycling_data_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling data. + required: true + tags: + - recycling-data + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/RecyclingData" + multipart/form-data: + schema: + $ref: "#/components/schemas/RecyclingData" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + description: "" + patch: + operationId: recycling_data_partial_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling data. + required: true + tags: + - recycling-data + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedRecyclingData" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedRecyclingData" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedRecyclingData" + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + description: "" + delete: + operationId: recycling_data_destroy + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this recycling data. + required: true + tags: + - recycling-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "204": + description: No response body + /recycling-data/metrics/: + get: + operationId: recycling_data_metrics_retrieve + tags: + - recycling-data + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/RecyclingData" + description: "" + /user-feedbacks/: + get: + operationId: user_feedbacks_list + tags: + - user-feedbacks + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UserFeedback" + description: "" + post: + operationId: user_feedbacks_create + tags: + - user-feedbacks + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserFeedback" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/UserFeedback" + multipart/form-data: + schema: + $ref: "#/components/schemas/UserFeedback" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/UserFeedback" + description: "" + /user-feedbacks/{id}/: + get: + operationId: user_feedbacks_retrieve + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this user feedback. + required: true + tags: + - user-feedbacks + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/UserFeedback" + description: "" + put: + operationId: user_feedbacks_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this user feedback. + required: true + tags: + - user-feedbacks + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserFeedback" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/UserFeedback" + multipart/form-data: + schema: + $ref: "#/components/schemas/UserFeedback" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/UserFeedback" + description: "" + patch: + operationId: user_feedbacks_partial_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this user feedback. + required: true + tags: + - user-feedbacks + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedUserFeedback" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedUserFeedback" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedUserFeedback" + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/UserFeedback" + description: "" + delete: + operationId: user_feedbacks_destroy + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this user feedback. + required: true + tags: + - user-feedbacks + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + "204": + description: No response body +components: + schemas: + DeviceMonitoringData: + type: object + properties: + device_id: + type: integer + timestamp: + type: string + format: date-time + readOnly: true + required: + - device_id + - timestamp + Messages: + type: object + properties: + id: + type: integer + readOnly: true + payload: {} + required: + - id + - payload + PatchedDeviceMonitoringData: + type: object + properties: + device_id: + type: integer + timestamp: + type: string + format: date-time + readOnly: true + PatchedMessages: + type: object + properties: + id: + type: integer + readOnly: true + payload: {} + PatchedRecyclingCounters: + type: object + properties: + id: + type: integer + readOnly: true + pet_counter: + type: integer + alu_counter: + type: integer + bad_recycling_counter: + type: integer + timestamp: + type: string + format: date-time + readOnly: true + PatchedRecyclingData: + type: object + properties: + device_id: + type: integer + container_type: + type: integer + game_code: + type: boolean + sensors: {} + wav_file: + type: string + bin_file: + type: string + timestamp: + type: number + format: double + PatchedUserFeedback: + type: object + properties: + id: + type: integer + readOnly: true + container_type: + type: integer + is_system_wrong: + type: boolean + timestamp: + type: string + format: date-time + readOnly: true + RecyclingCounters: + type: object + properties: + id: + type: integer + readOnly: true + pet_counter: + type: integer + alu_counter: + type: integer + bad_recycling_counter: + type: integer + timestamp: + type: string + format: date-time + readOnly: true + required: + - alu_counter + - id + - pet_counter + - timestamp + RecyclingData: + type: object + properties: + device_id: + type: integer + container_type: + type: integer + game_code: + type: boolean + sensors: {} + wav_file: + type: string + bin_file: + type: string + timestamp: + type: number + format: double + required: + - bin_file + - container_type + - device_id + - game_code + - sensors + - timestamp + - wav_file + UserFeedback: + type: object + properties: + id: + type: integer + readOnly: true + container_type: + type: integer + is_system_wrong: + type: boolean + timestamp: + type: string + format: date-time + readOnly: true + required: + - container_type + - id + - is_system_wrong + - timestamp + securitySchemes: + basicAuth: + type: http + scheme: basic + cookieAuth: + type: apiKey + in: cookie + name: sessionid diff --git a/requirements.txt b/requirements.txt index b78b20bf4fddad6b30e5798cacafed2e3f21f39a..9452eeeb7b6864e6adf97164267fb9646343bc6c 100644 Binary files a/requirements.txt and b/requirements.txt differ