Implemented geofencing logic for student_status and study_groups with landmark labels

This commit is contained in:
Keannu Bernasol 2023-07-14 23:55:54 +08:00
parent 7938c3ceef
commit a67ea5cd8a
9 changed files with 85 additions and 27 deletions

View file

@ -15,6 +15,7 @@ daphne = "*"
psycopg2 = "*" psycopg2 = "*"
gdal = {path = "./packages/GDAL-3.4.3-cp311-cp311-win_amd64.whl"} gdal = {path = "./packages/GDAL-3.4.3-cp311-cp311-win_amd64.whl"}
django-leaflet = "*" django-leaflet = "*"
django-extra-fields = "*"
[dev-packages] [dev-packages]

53
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "efd1134f98df71c3c1209f70d5d962b6609fe23b044b57f80bba89db477b549f" "sha256": "975af7eaaedebb1c31a1828fdf307574edc293e0de15ecfd9cf7107f98d642f5"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -221,28 +221,32 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db", "sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711",
"sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a", "sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7",
"sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039", "sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd",
"sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c", "sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e",
"sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3", "sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58",
"sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485", "sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0",
"sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c", "sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d",
"sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca", "sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83",
"sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5", "sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831",
"sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5", "sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766",
"sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3", "sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b",
"sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb", "sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c",
"sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43", "sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182",
"sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31", "sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f",
"sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc", "sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa",
"sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b", "sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4",
"sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006", "sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a",
"sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a", "sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2",
"sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699" "sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76",
"sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5",
"sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee",
"sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f",
"sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==41.0.1" "version": "==41.0.2"
}, },
"daphne": { "daphne": {
"hashes": [ "hashes": [
@ -268,6 +272,13 @@
"index": "pypi", "index": "pypi",
"version": "==4.2.3" "version": "==4.2.3"
}, },
"django-extra-fields": {
"hashes": [
"sha256:2334e914b346c0a19a7765bf0ff7895c46cf35d5f40315a68418f44b7ddbb33b"
],
"index": "pypi",
"version": "==3.0.2"
},
"django-leaflet": { "django-leaflet": {
"hashes": [ "hashes": [
"sha256:2f6dc8c7187fd22e62b6f2b7b42eed86920f81bec312aa654981ebe5bc8e0866", "sha256:2f6dc8c7187fd22e62b6f2b7b42eed86920f81bec312aa654981ebe5bc8e0866",

View file

@ -8,7 +8,7 @@ from django.dispatch import receiver
class Landmark(models.Model): class Landmark(models.Model):
name = models.CharField(max_length=64) name = models.CharField(max_length=64)
location = gis_models.PolygonField() location = gis_models.PolygonField(srid=4326)
def __str__(self): def __str__(self):
return self.name return self.name
@ -178,7 +178,7 @@ def populate_landmarks(sender, **kwargs):
'POLYGON ((124.655534 8.485857, 124.655629 8.485588, 124.655795 8.485647, 124.655755 8.485757, 124.656271 8.485946, 124.656212 8.486104, 124.655534 8.485857))', 'POLYGON ((124.655534 8.485857, 124.655629 8.485588, 124.655795 8.485647, 124.655755 8.485757, 124.656271 8.485946, 124.656212 8.486104, 124.655534 8.485857))',
srid=SRID srid=SRID
) )
) )
Landmark.objects.get_or_create( Landmark.objects.get_or_create(
name='Science Complex', name='Science Complex',
location=GEOSGeometry( location=GEOSGeometry(

View file

@ -2,4 +2,14 @@ from django.contrib import admin
from .models import StudentStatus from .models import StudentStatus
from leaflet.admin import LeafletGeoAdmin from leaflet.admin import LeafletGeoAdmin
admin.site.register(StudentStatus, LeafletGeoAdmin)
class StudentStatusAdmin(LeafletGeoAdmin):
# define which fields are required
def get_form(self, request, obj=None, **kwargs):
form = super(StudentStatusAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['landmark'].required = False
return form
# Register the new StudentStatus model
admin.site.register(StudentStatus, StudentStatusAdmin)

View file

@ -0,0 +1,20 @@
# Generated by Django 4.2.3 on 2023-07-14 14:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('landmarks', '0001_initial'),
('student_status', '0002_initial'),
]
operations = [
migrations.AddField(
model_name='studentstatus',
name='landmark',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='landmarks.landmark'),
),
]

View file

@ -9,11 +9,13 @@ from django.contrib.gis.geos import Point
class StudentStatus(models.Model): class StudentStatus(models.Model):
user = models.OneToOneField( user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True) CustomUser, on_delete=models.CASCADE, primary_key=True)
location = gis_models.PointField(blank=True, null=True) location = gis_models.PointField(blank=True, null=True, srid=4326)
subject = models.ForeignKey( subject = models.ForeignKey(
'subjects.Subject', on_delete=models.SET_NULL, null=True) 'subjects.Subject', on_delete=models.SET_NULL, null=True)
active = models.BooleanField(default=False) active = models.BooleanField(default=False)
timestamp = models.DateField(auto_now_add=True) timestamp = models.DateField(auto_now_add=True)
landmark = models.ForeignKey(
'landmarks.Landmark', on_delete=models.SET_NULL, null=True)
study_group = models.ManyToManyField( study_group = models.ManyToManyField(
'study_groups.StudyGroup', through='study_groups.StudyGroupMembership', blank=True) 'study_groups.StudyGroup', through='study_groups.StudyGroupMembership', blank=True)

View file

@ -2,17 +2,22 @@ from rest_framework import serializers
from .models import StudentStatus from .models import StudentStatus
from subjects.models import Subject from subjects.models import Subject
from django.contrib.gis.geos import Point from django.contrib.gis.geos import Point
from drf_extra_fields.geo_fields import PointField
from landmarks.models import Landmark
class StudentStatusSerializer(serializers.ModelSerializer): class StudentStatusSerializer(serializers.ModelSerializer):
subject = serializers.SlugRelatedField( subject = serializers.SlugRelatedField(
queryset=Subject.objects.all(), slug_field='name', required=True) queryset=Subject.objects.all(), slug_field='name', required=True)
user = serializers.CharField(source='user.full_name', read_only=True) user = serializers.CharField(source='user.full_name', read_only=True)
location = PointField()
landmark = serializers.SlugRelatedField(
queryset=Landmark.objects.all(), many=False, slug_field='name', required=False, allow_null=True)
class Meta: class Meta:
model = StudentStatus model = StudentStatus
fields = '__all__' fields = '__all__'
read_only_fields = ['user'] read_only_fields = ['user', 'landmark']
def create(self, validated_data): def create(self, validated_data):
user = self.context['request'].user user = self.context['request'].user
@ -26,5 +31,12 @@ class StudentStatusSerializer(serializers.ModelSerializer):
if active is not None and active is False: if active is not None and active is False:
validated_data['location'] = Point(0, 0) validated_data['location'] = Point(0, 0)
validated_data['subject'] = None validated_data['subject'] = None
validated_data['landmark'] = None
else:
# Check each landmark to see if our location is within it
for landmark in Landmark.objects.all():
if landmark.location.contains(validated_data['location']):
validated_data['landmark'] = landmark
break
return super().update(instance, validated_data) return super().update(instance, validated_data)

View file

@ -9,7 +9,7 @@ class StudyGroup(models.Model):
name = models.CharField(max_length=48) name = models.CharField(max_length=48)
users = models.ManyToManyField( users = models.ManyToManyField(
'student_status.StudentStatus', through='StudyGroupMembership') 'student_status.StudentStatus', through='StudyGroupMembership')
location = gis_models.PointField(blank=True, null=True) location = gis_models.PointField(blank=True, null=True, srid=4326)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE) subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
active = models.BooleanField(default=False) active = models.BooleanField(default=False)
timestamp = models.DateField(auto_now_add=True) timestamp = models.DateField(auto_now_add=True)

View file

@ -2,6 +2,7 @@ from rest_framework import serializers
from .models import StudyGroup, StudyGroupMembership from .models import StudyGroup, StudyGroupMembership
from accounts.models import CustomUser from accounts.models import CustomUser
from subjects.models import Subject from subjects.models import Subject
from drf_extra_fields.geo_fields import PointField
class StudyGroupSerializer(serializers.ModelSerializer): class StudyGroupSerializer(serializers.ModelSerializer):
@ -9,6 +10,7 @@ class StudyGroupSerializer(serializers.ModelSerializer):
queryset=CustomUser.objects.all(), many=True, slug_field='name', required=False, allow_null=True) queryset=CustomUser.objects.all(), many=True, slug_field='name', required=False, allow_null=True)
subject = serializers.SlugRelatedField( subject = serializers.SlugRelatedField(
many=False, slug_field='name', queryset=Subject.objects.all(), required=True, allow_null=False) many=False, slug_field='name', queryset=Subject.objects.all(), required=True, allow_null=False)
location = PointField()
class Meta: class Meta:
model = StudyGroup model = StudyGroup