Skip to content

Commit

Permalink
Merge pull request #275 from seanmorley15/development
Browse files Browse the repository at this point in the history
Merge for v0.6.0 update
  • Loading branch information
seanmorley15 authored Aug 24, 2024
2 parents eaab740 + bed96b1 commit 030cacf
Show file tree
Hide file tree
Showing 19 changed files with 267 additions and 39 deletions.
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ WORKDIR /code

# Install system dependencies
RUN apt-get update \
&& apt-get install -y git postgresql-client \
&& apt-get install -y git postgresql-client gdal-bin libgdal-dev \
&& apt-get clean

# Install Python dependencies
Expand Down
3 changes: 3 additions & 0 deletions backend/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ done

>&2 echo "PostgreSQL is up - continuing..."

# run sql commands
# psql -h "$PGHOST" -U "$PGUSER" -d "$PGDATABASE" -f /app/backend/init-postgis.sql

# Apply Django migrations
python manage.py migrate

Expand Down
10 changes: 3 additions & 7 deletions backend/server/main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
'adventures',
'worldtravel',
'users',
# 'django_apscheduler',
'django.contrib.gis',

)

Expand Down Expand Up @@ -101,7 +101,7 @@

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': getenv('PGDATABASE'),
'USER': getenv('PGUSER'),
'PASSWORD': getenv('PGPASSWORD'),
Expand Down Expand Up @@ -228,11 +228,9 @@
'LOGOUT_URL': 'logout',
}


# For demo purposes only. Use a white list in the real world.
CORS_ORIGIN_ALLOW_ALL = True


from os import getenv

CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
Expand Down Expand Up @@ -261,6 +259,4 @@
'propagate': False,
},
},
}

SCHEDULER_AUTOSTART = True
}
2 changes: 1 addition & 1 deletion backend/server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ psycopg2-binary
Pillow
whitenoise
django-resized
django-apscheduler
django-geojson
2 changes: 1 addition & 1 deletion backend/server/static/data/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"type": "MultiPolygon",
"coordinates": [
[
[1.9221462784913, 48.457599361977],
Expand Down
2 changes: 1 addition & 1 deletion backend/server/static/data/mx.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"ISOCODE": "MX-CMX"
},
"geometry": {
"type": "Polygon",
"type": "MultiPolygon",
"coordinates": [
[
[-99.111241, 19.561498],
Expand Down
2 changes: 1 addition & 1 deletion backend/server/static/data/us.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"AWATER": 23736382213
},
"geometry": {
"type": "Polygon",
"type": "MultiPolygon",
"coordinates": [
[
[-94.0430515276176, 32.6930299766656],
Expand Down
60 changes: 60 additions & 0 deletions backend/server/worldtravel/management/commands/worldtravel-seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,66 @@
import requests
from worldtravel.models import Country, Region
from django.db import transaction
from django.contrib.gis.geos import GEOSGeometry, Polygon, MultiPolygon
from django.contrib.gis.geos.error import GEOSException
import json

from django.conf import settings

media_root = settings.MEDIA_ROOT


def setGeometry(region_code):
# Assuming the file name is the country code (e.g., 'AU.json' for Australia)
country_code = region_code.split('-')[0]
json_file = os.path.join('static/data', f'{country_code.lower()}.json')

if not os.path.exists(json_file):
print(f'File {country_code}.json does not exist (it probably hasn''t been added, contributors are welcome!)')
return None

try:
with open(json_file, 'r') as f:
geojson_data = json.load(f)
except json.JSONDecodeError as e:
print(f"Invalid JSON in file for {country_code}: {e}")
return None

if 'type' not in geojson_data or geojson_data['type'] != 'FeatureCollection':
print(f"Invalid GeoJSON structure for {country_code}: missing or incorrect 'type'")
return None

if 'features' not in geojson_data or not geojson_data['features']:
print(f"Invalid GeoJSON structure for {country_code}: missing or empty 'features'")
return None

for feature in geojson_data['features']:
try:
properties = feature.get('properties', {})
isocode = properties.get('ISOCODE')

if isocode == region_code:
geometry = feature['geometry']
geos_geom = GEOSGeometry(json.dumps(geometry))

if isinstance(geos_geom, Polygon):
Region.objects.filter(id=region_code).update(geometry=MultiPolygon([geos_geom]))
print(f"Updated geometry for region {region_code}")
return MultiPolygon([geos_geom])
elif isinstance(geos_geom, MultiPolygon):
Region.objects.filter(id=region_code).update(geometry=geos_geom)
print(f"Updated geometry for region {region_code}")
return geos_geom
else:
print(f"Unexpected geometry type for region {region_code}: {type(geos_geom)}")
return None

except (KeyError, ValueError, GEOSException) as e:
print(f"Error processing region {region_code}: {e}")

print(f"No matching region found for {region_code}")
return None

def saveCountryFlag(country_code):
flags_dir = os.path.join(media_root, 'flags')

Expand Down Expand Up @@ -616,7 +671,9 @@ def sync_regions(self, regions):
)
if created:
self.stdout.write(f'Inserted {name} into worldtravel regions')
setGeometry(id)
else:
setGeometry(id)
self.stdout.write(f'Updated {name} in worldtravel regions')

def insert_countries(self, countries):
Expand All @@ -627,6 +684,7 @@ def insert_countries(self, countries):
)
if created:
saveCountryFlag(country_code)

self.stdout.write(f'Inserted {name} into worldtravel countries')
else:
saveCountryFlag(country_code)
Expand All @@ -641,5 +699,7 @@ def insert_regions(self, regions):
)
if created:
self.stdout.write(f'Inserted {name} into worldtravel regions')
setGeometry(id)
else:
setGeometry(id)
self.stdout.write(f'{name} already exists in worldtravel regions')
19 changes: 19 additions & 0 deletions backend/server/worldtravel/migrations/0004_country_geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.0.8 on 2024-08-23 17:01

import django.contrib.gis.db.models.fields
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('worldtravel', '0003_alter_region_name_en'),
]

operations = [
migrations.AddField(
model_name='country',
name='geometry',
field=django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.0.8 on 2024-08-23 17:47

import django.contrib.gis.db.models.fields
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('worldtravel', '0004_country_geometry'),
]

operations = [
migrations.RemoveField(
model_name='country',
name='geometry',
),
migrations.AddField(
model_name='region',
name='geometry',
field=django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326),
),
]
3 changes: 3 additions & 0 deletions backend/server/worldtravel/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db import models
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.contrib.gis.db import models as gis_models


User = get_user_model()
Expand Down Expand Up @@ -34,6 +35,7 @@ class Country(models.Model):
choices=CONTINENT_CHOICES,
default=AFRICA
)


class Meta:
verbose_name = "Country"
Expand All @@ -47,6 +49,7 @@ class Region(models.Model):
name = models.CharField(max_length=100)
name_en = models.CharField(max_length=100, blank=True, null=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
geometry = gis_models.MultiPolygonField(srid=4326, null=True, blank=True)

def __str__(self):
return self.name
Expand Down
2 changes: 1 addition & 1 deletion backend/server/worldtravel/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class RegionSerializer(serializers.ModelSerializer):
class Meta:
model = Region
fields = '__all__' # Serialize all fields of the Adventure model
read_only_fields = ['id', 'name', 'country', 'name_en']
read_only_fields = ['id', 'name', 'country', 'name_en', 'geometry']

class VisitedRegionSerializer(serializers.ModelSerializer):
class Meta:
Expand Down
37 changes: 37 additions & 0 deletions backend/server/worldtravel/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
from rest_framework.decorators import api_view, permission_classes
import os
import json
from django.http import JsonResponse
from django.contrib.gis.geos import Point
from django.conf import settings
from rest_framework.decorators import action
from django.contrib.staticfiles import finders
from adventures.models import Adventure

@api_view(['GET'])
@permission_classes([IsAuthenticated])
Expand All @@ -34,6 +38,39 @@ class CountryViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = CountrySerializer
permission_classes = [IsAuthenticated]

@action(detail=False, methods=['get'])
def check_point_in_region(self, request):
lat = float(request.query_params.get('lat'))
lon = float(request.query_params.get('lon'))
point = Point(lon, lat, srid=4326)

region = Region.objects.filter(geometry__contains=point).first()

if region:
return Response({'in_region': True, 'region_name': region.name, 'region_id': region.id})
else:
return Response({'in_region': False})

# make a post action that will get all of the users adventures and check if the point is in any of the regions if so make a visited region object for that user if it does not already exist
@action(detail=False, methods=['post'])
def region_check_all_adventures(self, request):
adventures = Adventure.objects.filter(user_id=request.user.id, type='visited')
count = 0
for adventure in adventures:
if adventure.latitude is not None and adventure.longitude is not None:
try:
print(f"Adventure {adventure.id}: lat={adventure.latitude}, lon={adventure.longitude}")
point = Point(float(adventure.longitude), float(adventure.latitude), srid=4326)
region = Region.objects.filter(geometry__contains=point).first()
if region:
if not VisitedRegion.objects.filter(user_id=request.user.id, region=region).exists():
VisitedRegion.objects.create(user_id=request.user, region=region)
count += 1
except Exception as e:
print(f"Error processing adventure {adventure.id}: {e}")
continue
return Response({'regions_visited': count})

class RegionViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Region.objects.all()
serializer_class = RegionSerializer
Expand Down
2 changes: 2 additions & 0 deletions backup.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# This script will create a backup of the adventurelog_media volume and store it in the current directory as adventurelog-backup.tar.gz

docker run --rm \
-v adventurelog_adventurelog_media:/backup-volume \
-v "$(pwd)":/backup \
Expand Down
2 changes: 2 additions & 0 deletions deploy.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# This script is used to deploy the latest version of AdventureLog to the server. It pulls the latest version of the Docker images and starts the containers. It is a simple script that can be run on the server, possibly as a cron job, to keep the server up to date with the latest version of the application.

echo "Deploying latest version of AdventureLog"
docker compose pull
echo "Stating containers"
Expand Down
6 changes: 5 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ services:
web:
#build: ./frontend/
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
container_name: adventurelog-frontend
environment:
- PUBLIC_SERVER_URL=http://server:8000 # MOST DOCKER USERS WILL NEVER NEED TO CHANGE THIS, EVEN IF YOU CHANGE THE PORTS
- ORIGIN=http://localhost:8080
Expand All @@ -14,7 +15,8 @@ services:
- server

db:
image: postgres:latest
image: postgis/postgis:15-3.3
container_name: adventurelog-db
environment:
POSTGRES_DB: database
POSTGRES_USER: adventure
Expand All @@ -25,6 +27,7 @@ services:
server:
#build: ./backend/
image: ghcr.io/seanmorley15/adventurelog-backend:latest
container_name: adventurelog-backend
environment:
- PGHOST=db
- PGDATABASE=database
Expand All @@ -47,6 +50,7 @@ services:

nginx:
image: nginx:latest
container_name: adventurelog-nginx
ports:
- "81:80"
volumes:
Expand Down
Loading

0 comments on commit 030cacf

Please sign in to comment.