mirror of
https://github.com/lemeow125/StudE-Backend.git
synced 2025-06-28 16:25:44 +08:00
commit
2a4123cc75
18 changed files with 470 additions and 208 deletions
|
@ -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:
|
||||
|
|
|
@ -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)],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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
14
stude/wait_for_redis.py
Normal 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)
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue