Compare commits

...

3 Commits

Author SHA1 Message Date
92d98a299c prije uspostave API documentation 2024-01-16 08:49:37 +01:00
ade8ea32e2 dodani testovi 2024-01-15 11:49:58 +01:00
9908fcad02 prije restarta 2024-01-12 10:11:50 +01:00
18 changed files with 388 additions and 19 deletions

34
Dockerfile.dev Normal file
View File

@ -0,0 +1,34 @@
# Use an official Python runtime as a parent image
FROM python:3.8-slim
# Set environment variables for Django
ENV DJANGO_SETTINGS_MODULE=plovidba_projekt.settings
ENV PYTHONUNBUFFERED 1
# Install PostgreSQL development headers, build tools, and other dependencies
RUN apt-get update && \
apt-get install -y libpq-dev gcc && \
apt-get clean
# Install GDAL
RUN apt-get update \
&& apt-get install -y binutils libproj-dev gdal-bin
# Install the PostgreSQL client
RUN apt-get update && apt-get install -y postgresql-client
# Create and set the working directory in the container
WORKDIR /app/plovidba_projekt
# Copy the requirements file into the container and install dependencies
COPY requirements.txt /app/plovidba_projekt/
RUN pip install -r requirements.txt
# Copy the rest of the application code into the container
COPY . /app/plovidba_projekt/
# Expose the port the application will run on (if necessary)
# EXPOSE 8000
# Define the default command to run when starting the container
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

40
Dockerfile.prod Normal file
View File

@ -0,0 +1,40 @@
# Use an official Python runtime as a parent image
FROM python:3.8-slim
# Set environment variables for Django
ENV DJANGO_SETTINGS_MODULE=plovidba_projekt.settings
ENV PYTHONUNBUFFERED 1
# Install PostgreSQL development headers, build tools, and other dependencies
RUN apt-get update && \
apt-get install -y libpq-dev gcc && \
apt-get clean
# Install GDAL
RUN apt-get update \
&& apt-get install -y binutils libproj-dev gdal-bin
# Install PostgreSQL client and PostGIS extension
RUN apt-get update && \
apt-get install -y postgresql-client postgis && \
apt-get clean
# .Create and set the working directory in the container
RUN mkdir -p /app/plovidba_projekt
WORKDIR /app/plovidba_projekt
# .Copy the requirements file into the container and install dependencies
COPY requirements.txt /app/plovidba_projekt/
RUN pip install -r requirements.txt
# .Copy the rest of the application code into the container
COPY . /app/plovidba_projekt/
VOLUME /app/media
VOLUME /app/static
# Expose the port the application will run on (if necessary)
EXPOSE 8000
# Run the Django application using Gunicorn in production mode
CMD ["gunicorn", "plovidba_projekt.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]

33
docker-compose.yaml Normal file
View File

@ -0,0 +1,33 @@
version: "3"
volumes:
media:
static:
services:
# Development Environment
api_dev:
build:
context: .
dockerfile: Dockerfile.dev
container_name: plovidba_dev_container10
restart: on-failure
ports:
- "8000:8000" # Map host port 8000 to container port 8000
volumes:
- .:/app # Mount your application code into the container
env_file:
- .env # Specify the location of your .env files
# Production Environment
api_prod:
build:
context: .
dockerfile: Dockerfile.prod
container_name: plovidba_prod_container
restart: on-failure
ports:
- "8000:8000" # Map host port 8000 to container port 8000
env_file:
- C:\Users\Student1\Desktop\plovidba\myenv\plovidba_projekt\plovidba_projekt # Specify the location of your .env file
volumes:
- static:/app/static
- media:/app/media

Binary file not shown.

View File

@ -18,10 +18,16 @@ class ObjektSigurnosti(models.Model):
return self.naziv
# class Log(models.Model):
# user = models.ForeignKey(User, on_delete=models.CASCADE)
# akcija = models.CharField(max_length=255)
# AKCIJA_CHOICES = (
# ('Brisanje', 'Brisanje'),
# ('Unos', 'Unos'),
# ('Uređivanje', 'Uređivanje')
# )
# user = models.ForeignKey("user.User", null=True, related_name='user_logs', on_delete=models.SET_NULL)
# timestamp = models.DateTimeField(auto_now_add=True)
# akcija = models.CharField(max_length=255, choices=AKCIJA_CHOICES)
# opis = models.TextField()
# vrijeme = models.DateTimeField(default=timezone.now)
# def __str__(self):
# return f"{self.user.username} - {self.akcija} - {self.opis}"
# return self.opis

View File

@ -1,9 +1,43 @@
# serializers.py
from rest_framework import serializers
from django.contrib.gis.geos import Point
from .models import ObjektSigurnosti
# class ObjektSigurnostiSerializer(serializers.ModelSerializer):
# class Meta:
# model = ObjektSigurnosti
# fields = '__all__'
# class LogSerializer(serializers.ModelSerializer):
# user_full_name = serializers.ReadOnlyField(source='user.full_name')
# class Meta:
# model = Log
# fields = ('id', 'user', 'user_full_name', 'timestamp', 'akcija', 'opis')
class PointSerializer(serializers.Serializer):
lat = serializers.FloatField()
lon = serializers.FloatField()
def to_representation(self, instance):
return {'lat': instance.y, 'lon': instance.x}
class ObjektSigurnostiSerializer(serializers.ModelSerializer):
lokacija = PointSerializer()
class ObjektSigurnostiSerializer(serializers.ModelSerializer):
lokacija = PointSerializer()
class Meta:
model = ObjektSigurnosti
fields = '__all__'
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['lokacija'] = PointSerializer(instance.lokacija).data
return representation
def create(self, validated_data):
lokacija_data = validated_data.pop('lokacija')
lokacija = Point(lokacija_data['lon'], lokacija_data['lat'], srid=3765)
objekt = ObjektSigurnosti.objects.create(lokacija=lokacija, **validated_data)
return objekt

View File

@ -1,3 +1,69 @@
from django.test import TestCase
from django.contrib.gis.geos import Point
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from plovidba_aplikacija.models import ObjektSigurnosti
from plovidba_aplikacija.serializers import PointSerializer
# Create your tests here.
# Testiranje listanja objekata
class ObjektSigurnostiListTest(APITestCase):
def test_list_objekti_sigurnosti(self):
url = reverse('objektisigurnosti-list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('results', response.data)
# Testiranje stvaranja objekata
class ObjektSigurnostiCreateTest(APITestCase):
def setUp(self):
self.url = reverse('objektisigurnosti-list')
def test_create_objekt_sigurnosti(self):
data = {
'lokacija': {'lat': 45.123, 'lon': 18.456},
'naziv': 'test-naziv',
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# Testiranje dohvaćanja pojedinog objekata
class ObjektSigurnostiDetailTest(APITestCase):
def setUp(self):
self.objekt = ObjektSigurnosti.objects.create(
lokacija=Point(18.456, 45.123),
naziv='test-naziv',
)
self.url = reverse('objektisigurnosti-detail', args=[self.objekt.pk])
def test_get_objekt_sigurnosti(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# def test_update_objekt_sigurnosti(self):
# data = {
# 'lokacija': {'lat': 15.17517, 'lon': 44.01113},
# 'naziv' : 'updated-naziv',
# }
# response = self.client.patch(self.url, data, format='json')
# self.assertEqual(response.status_code, status.HTTP_200_OK)
# # Reload the object from the database
# updated_objekt = ObjektSigurnosti.objects.get(pk=self.objekt.pk)
# # Check if the values are updated correctly
# self.assertEqual(updated_objekt.lokacija.x, 13.79758)
# self.assertEqual(updated_objekt.lokacija.y, 44.9254)
# self.assertEqual(updated_objekt.naziv, 'updated-naziv')
# def test_retrieve_objekt_sigurnosti(self):
# url = reverse('objektisigurnosti-detail', args=[self.objekt.id])
# response = self.client.get(url)
# self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -1,8 +1,10 @@
from django.urls import path, include
from .views import ObjektSigurnostiList, ObjektSigurnostiDetail
from plovidba_aplikacija import views
urlpatterns = [
path('objekti/', ObjektSigurnostiList.as_view(), name='objektisigurnosti-list'),
path('objekti/<int:pk>/', ObjektSigurnostiDetail.as_view(), name='objektisigurnosti-detail' ),
# path('logs/', views.Log.as_view(), name='log-list')
]

View File

@ -10,31 +10,45 @@ from django.shortcuts import get_object_or_404
from rest_framework.pagination import LimitOffsetPagination
class CustomObjektSigurnostiPagination(LimitOffsetPagination):
default_limit = 20
class ObjektSigurnostiList(generics.ListCreateAPIView):
queryset = ObjektSigurnosti.objects.all().order_by("naziv")
serializer_class = ObjektSigurnostiSerializer
pagination_class = CustomObjektSigurnostiPagination
permission_classes = []
def get_queryset(self): #queryset je data iz database, listing and creating objects
queryset = ObjektSigurnosti.objects.all()
location = self.request.query_params.get('lokacija')
if location is not None:
queryset = queryset.filter(lokacija__icontains=location)
return queryset
def get_queryset(self):
user = self.request.user
return user.accounts.all()
# def get_queryset(self): #queryset je data iz database, listing and creating objects
# queryset = ObjektSigurnosti.objects.all()
# location = self.request.query_params.get('lokacija')
# if location is not None:
# queryset = queryset.filter(lokacija__icontains=location)
# return queryset
# def get_serializer_class(self):
# if self.request.method == "GET":
# return ObjektSigurnostiSerializer
# return self.serializer_class()
def perform_create(self, serializer):
#used to customize the behavior when creating an object, and in this case,
#it sets the operater field and creates a log entry for the created object.
serializer.save(operater=self.request.user)
# instance = serializer.instance
# self.create_log(instance)
def get_serializer_class(self):
if self.request.method == "GET":
return ObjektSigurnostiSerializer
return self.serializer_class()
class ObjektSigurnostiDetail(generics.RetrieveUpdateDestroyAPIView): #retrieving, updating, and deleting a specific object
queryset = ObjektSigurnosti.objects.all()
serializer_class = ObjektSigurnostiSerializer
# def perform_update(self, serializer):
# instance = serializer.save()
# opis = "Korisnik je uredio objekt sigurnosti {} (ID: {})".format(
@ -48,3 +62,4 @@ class ObjektSigurnostiDetail(generics.RetrieveUpdateDestroyAPIView): #ret
# instance.vrsta.naziv, instance.id
# )
# Log.objects.create(user=self.request.user, akcija="Brisanje", opis=opis)

View File

@ -49,6 +49,8 @@ INSTALLED_APPS = [
'django_extensions',
# 3rd party
'rest_framework',
'drf_yasg',
'rest_framework_swagger',
# Custom apps:
'plovidba_aplikacija',
@ -99,6 +101,10 @@ DATABASES = {
}
}
DATABASES["default"]["TEST"] = {
"NAME": ENV_STR("DATABASE_TEST_NAME", "test_plovidba_dev_db")
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
@ -118,6 +124,21 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
# Define DRF settings
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"PAGE_SIZE": 100,
}
# API docs
SHOW_API_DOCS = ENV_BOOL("SHOW_API_DOCS", True)
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
@ -173,3 +194,5 @@ TEMP_DIR = os.path.join(BASE_DIR, "temp")
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View File

@ -14,11 +14,39 @@ 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.conf import settings
from django.contrib import admin
from django.urls import path, include
from plovidba_aplikacija import views
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions
api_schema_view = get_schema_view(
openapi.Info(
title="API docs",
default_version='v1',
description="Swagger docs for ListLabs API",
contact=openapi.Contact(email="elena.maric@listlabs.net")
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('plovidba_aplikacija.urls')),
]
path("swagger/", api_schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
path("redoc/", api_schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"),
]
if settings.SHOW_API_DOCS:
urlpatterns += [
path('api/docs/swagger/',
api_schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('api/docs/redoc/',
api_schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc-ui')
]

88
requirements.txt Normal file
View File

@ -0,0 +1,88 @@
asgiref==3.5.2
async-timeout==4.0.2
attrs==22.1.0
autobahn==23.1.2
Automat==22.10.0
backports.zoneinfo==0.2.1
black==22.8.0
certifi==2022.6.15
cffi==1.15.1
channels==4.0.0
channels-redis==4.0.0
charset-normalizer==2.1.1
click==8.1.3
click-plugins==1.1.1
cligj==0.7.2
constantly==15.1.0
coreapi==2.3.3
coreschema==0.0.4
cryptography
daphne==4.0.0
defusedxml==0.7.1
dj-database-url==1.2.0
dj-rest-auth==2.2.5
Django==4.1
django-allauth==0.50.0
django-cors-headers==3.13.0
django-environ==0.9.0
django-filter==22.1
django-leaflet==0.28.2
django-sslserver==0.22
djangorestframework==3.13.1
djangorestframework-gis==1.0
djangorestframework-simplejwt==5.2.0
drf-spectacular==0.24.2
drf-yasg==1.21.4
et-xmlfile==1.1.0
Fiona==1.9.3
flake8==6.0.0
hyperlink==21.0.0
idna==3.3
importlib-metadata==6.6.0
importlib-resources==5.10.0
incremental==22.10.0
inflection==0.5.1
itypes==1.2.0
Jinja2==3.1.2
jsonschema==4.16.0
MarkupSafe==2.1.2
mccabe==0.7.0
msgpack==1.0.4
munch==3.0.0
mypy-extensions==0.4.3
oauthlib==3.2.0
openpyxl==3.1.2
packaging==23.0
pathspec==0.10.1
pkgutil-resolve-name==1.3.10
platformdirs==2.5.2
psycopg2==2.9.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycodestyle==2.10.0
pycparser==2.21
pyflakes==3.0.1
PyJWT==2.4.0
pyOpenSSL==23.0.0
pyrsistent==0.18.1
python3-openid==3.2.0
pytz==2022.2.1
PyYAML==6.0
redis==4.4.2
requests==2.28.1
requests-oauthlib==1.3.1
ruamel.yaml==0.17.21
ruamel.yaml.clib==0.2.7
sentry-sdk==1.15.0
service-identity==21.1.0
six==1.16.0
sqlparse==0.4.2
tomli==2.0.1
Twisted==22.10.0
txaio==23.1.1
typing-extensions==4.3.0
uritemplate==4.1.1
urllib3==1.26.12
zipp==3.9.0
zope.interface==5.5.2
gunicorn