Merge pull request #3 from lemeow125/feature/docker

Feature/docker
This commit is contained in:
lemeow125 2023-09-12 21:46:02 +08:00 committed by GitHub
commit 2a4123cc75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 470 additions and 208 deletions

View file

@ -5,14 +5,15 @@ from .models import CustomUser
from year_levels.models import Year_Level
from semesters.models import Semester
from courses.models import Course
from subjects.models import Subject
from subjects.models import SubjectInstance
from django.contrib.admin.widgets import FilteredSelectMultiple
class CustomUserForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CustomUserForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['subjects'].queryset = Subject.objects.filter(
self.fields['subjects'].queryset = SubjectInstance.objects.filter(
course=self.instance.course)
year_level = forms.ModelChoiceField(
@ -22,7 +23,7 @@ class CustomUserForm(forms.ModelForm):
course = forms.ModelChoiceField(
queryset=Course.objects.all(), required=False)
subjects = forms.ModelMultipleChoiceField(
queryset=Subject.objects.none(), required=False, widget=forms.CheckboxSelectMultiple)
queryset=SubjectInstance.objects.none(), required=False, widget=FilteredSelectMultiple("Subjects", is_stacked=False))
avatar = forms.ImageField(required=False)
class Meta:

View file

@ -21,7 +21,6 @@ load_dotenv() # loads the configs from .env
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
@ -141,12 +140,26 @@ REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '360/min',
'user': '1440/min'
},
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
WSGI_APPLICATION = 'config.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
@ -244,5 +257,34 @@ LEAFLET_CONFIG = {
'DEFAULT_ZOOM': 19,
'MAX_ZOOM': 20,
'MIN_ZOOM': 3,
'SCALE': 'both'
'SCALE': 'both',
'TILES': 'https://openstreetmap.keannu1.duckdns.org/tile/{z}/{x}/{y}.png'
}
REDIS_HOST = os.getenv('REDIS_HOST', 'localhost')
REDIS_PORT = os.getenv('REDIS_PORT', 6379)
# Django Redis Cache
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://redis:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
# Redis Cache for Django Channel Websockets
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("redis", 6379)],
},
},
}

View file

@ -1,3 +1,5 @@
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework import generics
from .models import Course
from .serializers import CourseSerializer
@ -6,3 +8,7 @@ from .serializers import CourseSerializer
class CourseListView(generics.ListAPIView):
serializer_class = CourseSerializer
queryset = Course.objects.all()
@method_decorator(cache_page(60*60))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)

View file

@ -1,3 +1,5 @@
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from .serializers import LandmarkSerializer
@ -8,3 +10,7 @@ class LandmarkListView(generics.ListAPIView):
serializer_class = LandmarkSerializer
# permission_classes = [IsAuthenticated]
queryset = Landmark.objects.all()
@method_decorator(cache_page(60*60))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)

View file

@ -1,3 +1,5 @@
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework import generics
from .models import Semester
from .serializers import SemesterSerializer
@ -6,3 +8,7 @@ from .serializers import SemesterSerializer
class SemesterListView(generics.ListAPIView):
serializer_class = SemesterSerializer
queryset = Semester.objects.all()
@method_decorator(cache_page(60*60))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)

View file

@ -0,0 +1,20 @@
# Generated by Django 4.2.3 on 2023-09-05 12:19
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('subjects', '0002_alter_subject_name_alter_subjectinstance_subject'),
('student_status', '0002_initial'),
]
operations = [
migrations.AlterField(
model_name='studentstatus',
name='subject',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='subjects.subject', to_field='name'),
),
]

View file

@ -11,7 +11,7 @@ class StudentStatus(models.Model):
CustomUser, on_delete=models.CASCADE, primary_key=True)
location = gis_models.PointField(blank=True, null=True, srid=4326)
subject = models.ForeignKey(
'subjects.Subject', on_delete=models.SET_NULL, null=True)
'subjects.Subject', on_delete=models.SET_NULL, null=True, to_field='name')
active = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
landmark = models.ForeignKey(

View file

@ -7,6 +7,7 @@ from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.geos import fromstr
from .models import StudentStatus
from .serializers import StudentStatusLocationSerializer, StudentStatusSerializer
from subjects.models import Subject, SubjectInstance
class StudentStatusAPIView(generics.RetrieveUpdateAPIView):
@ -26,7 +27,7 @@ class ActiveStudentStatusListAPIView(generics.ListAPIView):
def get_queryset(self):
user = self.request.user
return StudentStatus.objects.filter(active=True and user != user)
return StudentStatus.objects.exclude(user=user).filter(active=True)
class StudentStatusListByStudentStatusLocation(generics.ListAPIView):
@ -44,7 +45,15 @@ class StudentStatusListByStudentStatusLocation(generics.ListAPIView):
if user_status.active is False:
raise exceptions.ValidationError("Student Status is not active")
return StudentStatus.objects.filter(active=True and user != user).filter(subject__in=user.subjects.all()).annotate(distance=Distance('location', user_location)).filter(distance__lte=50)
# Get names of all subjects of the user
user_subject_names = user.subjects.values_list('subject', flat=True)
# Exclude user
# Filter those only with the same subjects as the user
# Annotate the queryset with distance to the user
# Then filter so that only those within 50m remain
return StudentStatus.objects.exclude(user=user).filter(active=True).filter(
subject__name__in=user_subject_names).annotate(distance=Distance('location', user_location)).filter(distance__lte=50)
class StudentStatusListByCurrentLocation(viewsets.ViewSet):
@ -60,8 +69,17 @@ class StudentStatusListByCurrentLocation(viewsets.ViewSet):
if not location_str:
raise exceptions.ValidationError("Location is required")
# Parse user location from the POST request
user_location = fromstr(location_str, srid=4326)
queryset = StudentStatus.objects.filter(active=True and user != user).filter(subject__in=user.subjects.all()).annotate(
# Get names of all subjects of the user
user_subject_names = user.subjects.values_list('subject', flat=True)
# Exclude user
# Filter those only with the same subjects as the user
# Annotate the queryset with distance to the user
# Then filter so that only those within 50m remain
queryset = StudentStatus.objects.exclude(user=user).filter(active=True).filter(subject__name__in=user_subject_names).annotate(
distance=Distance('location', user_location)).filter(distance__lte=50)
serializer = StudentStatusLocationSerializer(queryset, many=True)
return Response(serializer.data)

View file

@ -0,0 +1,24 @@
# Generated by Django 4.2.3 on 2023-09-05 12:19
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('subjects', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='subject',
name='name',
field=models.CharField(max_length=64, unique=True),
),
migrations.AlterField(
model_name='subjectinstance',
name='subject',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='subjects.subject', to_field='name'),
),
]

View file

@ -12,7 +12,7 @@ from semesters.models import Semester
class Subject(models.Model):
name = models.CharField(max_length=64)
name = models.CharField(max_length=64, unique=True)
students = models.ManyToManyField(
CustomUser, blank=True)
@ -22,7 +22,7 @@ class Subject(models.Model):
class SubjectInstance(models.Model):
subject = models.ForeignKey(
Subject, on_delete=models.CASCADE)
Subject, on_delete=models.CASCADE, to_field='name')
code = models.CharField(max_length=16)
course = models.ForeignKey(
Course, on_delete=models.CASCADE)

View file

@ -1,3 +1,5 @@
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework import generics, viewsets
from .models import Subject, SubjectInstance
from .serializers import SubjectInstanceSerializer
@ -10,6 +12,10 @@ class SubjectListAllView(generics.ListAPIView):
serializer_class = SubjectInstanceSerializer
queryset = SubjectInstance.objects.all()
@method_decorator(cache_page(60*60))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
class SubjectListView(generics.ListAPIView):
permission_classes = [IsAuthenticated]

14
stude/wait_for_redis.py Normal file
View file

@ -0,0 +1,14 @@
import time
import os
import redis
REDIS_HOST = os.getenv('REDIS_HOST', 'localhost')
REDIS_PORT = os.getenv('REDIS_PORT', 6379)
if __name__ == '__main__':
while True:
try:
redis.Redis(host=REDIS_HOST, port=REDIS_PORT)
print('Redis is up!')
break
except redis.ConnectionError:
time.sleep(0.1)

View file

@ -1,3 +1,5 @@
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework import generics
from .models import Year_Level
from .serializers import YearLevelSerializer
@ -6,3 +8,7 @@ from .serializers import YearLevelSerializer
class CourseListView(generics.ListAPIView):
serializer_class = YearLevelSerializer
queryset = Year_Level.objects.all()
@method_decorator(cache_page(60*60))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)