Skip to content

Commit

Permalink
metadata uploading and GRTS cells
Browse files Browse the repository at this point in the history
  • Loading branch information
BryonLewis committed Feb 23, 2024
1 parent 7a6fb01 commit ca8500d
Show file tree
Hide file tree
Showing 17 changed files with 384 additions and 10 deletions.
3 changes: 2 additions & 1 deletion bats_ai/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from ninja import NinjaAPI
from oauth2_provider.models import AccessToken

from bats_ai.core.views import RecordingRouter, SpeciesRouter
from bats_ai.core.views import GRTSCellsRouter, RecordingRouter, SpeciesRouter

logger = logging.getLogger(__name__)

Expand All @@ -26,3 +26,4 @@ def global_auth(request):

api.add_router('/recording/', RecordingRouter)
api.add_router('/species/', SpeciesRouter)
api.add_router('/grts/', GRTSCellsRouter)
2 changes: 2 additions & 0 deletions bats_ai/core/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .annotations import AnnotationsAdmin
from .grts_cells import GRTSCellsAdmin
from .image import ImageAdmin
from .recording import RecordingAdmin
from .species import SpeciesAdmin
Expand All @@ -12,4 +13,5 @@
'SpectrogramAdmin',
'TemporalAnnotationsAdmin',
'SpeciesAdmin',
'GRTSCellsAdmin',
]
8 changes: 8 additions & 0 deletions bats_ai/core/admin/grts_cells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.contrib import admin
from bats_ai.core.models import GRTSCells

@admin.register(GRTSCells)
class GRTSCellsAdmin(admin.ModelAdmin):
list_display = ('id', 'grts_cell_id', 'sample_frame_id', 'water_p', 'outside_p') # Add other fields you want to display in the list
search_fields = ('id', 'grts_cell_id', 'sample_frame_id') # Add fields for searching
list_filter = ('location_1_type', 'location_2_type') # Add fields for filtering
1 change: 1 addition & 0 deletions bats_ai/core/admin/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class RecordingAdmin(admin.ModelAdmin):
'spectrogram_status',
'owner',
'recorded_date',
'recorded_time',
'public',
'equipment',
'comments',
Expand Down
57 changes: 57 additions & 0 deletions bats_ai/core/management/commands/importGRTSCells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import csv
from django.core.management.base import BaseCommand
from bats_ai.core.models import GRTSCells
from django.core.exceptions import ValidationError


class Command(BaseCommand):
help = 'Import data from CSV file'

def add_arguments(self, parser):
parser.add_argument('csv_file', type=str, help='Path to the CSV file')

def handle(self, *args, **options):
csv_file = options['csv_file']

# Get all field names of the GRTSCells model
model_fields = [field.name for field in GRTSCells._meta.get_fields()]

with open(csv_file, 'r') as file:
reader = csv.DictReader(file)
total_rows = sum(1 for _ in reader) # Get total number of rows in the CSV
file.seek(0) # Reset file pointer to start
next(reader) # Skip header row
counter = 0 # Initialize progress counter

for row in reader:
# Filter row dictionary to include only keys that exist in the model fields
filtered_row = {key: row[key] for key in row if key in model_fields}

for key, value in filtered_row.items():
if value == '':
filtered_row[key] = None

# Convert boolean fields from string to boolean values
for boolean_field in ['priority_frame', 'priority_state', 'clipped']:
if filtered_row.get(boolean_field):
if filtered_row[boolean_field].lower() == 'true':
filtered_row[boolean_field] = True
elif filtered_row[boolean_field].lower() == 'false':
filtered_row[boolean_field] = False
else:
raise ValidationError(f'Invalid boolean value for field {boolean_field}: {filtered_row[boolean_field]}')

# Check if a record with all the data already exists
if GRTSCells.objects.filter(**filtered_row).exists():
#self.stdout.write(f'Skipping row because it already exists: {filtered_row}')
counter += 1
self.stdout.write(f'Processed {counter} of {total_rows} rows')
continue

try:
GRTSCells.objects.create(**filtered_row)
counter += 1
self.stdout.write(f'Processed {counter} of {total_rows} rows')
except ValidationError as e:
self.stderr.write(str(e))
continue
48 changes: 48 additions & 0 deletions bats_ai/core/migrations/0008_grtscells_recording_recorded_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 4.1.13 on 2024-02-23 17:06

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


class Migration(migrations.Migration):

dependencies = [
('core', '0007_temporalannotations'),
]

operations = [
migrations.CreateModel(
name='GRTSCells',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('grts_cell_id', models.IntegerField()),
('sample_frame_id', models.IntegerField(blank=True, null=True)),
('grts_geom', django.contrib.gis.db.models.fields.GeometryField(blank=True, null=True, srid=4326)),
('water_p', models.FloatField(blank=True, null=True)),
('outside_p', models.FloatField(blank=True, null=True)),
('location_1_type', models.CharField(blank=True, max_length=255, null=True)),
('location_1_name', models.CharField(blank=True, max_length=255, null=True)),
('location_1_p', models.FloatField(blank=True, null=True)),
('location_2_type', models.CharField(blank=True, max_length=255, null=True)),
('location_2_name', models.CharField(blank=True, max_length=255, null=True)),
('location_2_p', models.FloatField(blank=True, null=True)),
('sub_location_1_type', models.CharField(blank=True, max_length=255, null=True)),
('sub_location_1_name', models.CharField(blank=True, max_length=255, null=True)),
('sub_location_1_p', models.FloatField(blank=True, null=True)),
('sub_location_2_type', models.CharField(blank=True, max_length=255, null=True)),
('sub_location_2_name', models.CharField(blank=True, max_length=255, null=True)),
('sub_location_2_p', models.FloatField(blank=True, null=True)),
('own_1_name', models.CharField(blank=True, max_length=255, null=True)),
('own_1_p', models.FloatField(blank=True, null=True)),
('priority_frame', models.BooleanField(blank=True, null=True)),
('priority_state', models.BooleanField(blank=True, null=True)),
('geom_4326', django.contrib.gis.db.models.fields.GeometryField(srid=4326)),
('clipped', models.BooleanField(blank=True, null=True)),
],
),
migrations.AddField(
model_name='recording',
name='recorded_time',
field=models.TimeField(blank=True, null=True),
),
]
2 changes: 2 additions & 0 deletions bats_ai/core/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .annotations import Annotations
from .grts_cells import GRTSCells
from .image import Image
from .recording import Recording
from .recording_annotation_status import RecordingAnnotationStatus
Expand All @@ -14,4 +15,5 @@
'Species',
'Spectrogram',
'TemporalAnnotations',
'GRTSCells',
]
52 changes: 52 additions & 0 deletions bats_ai/core/models/grts_cells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from django.contrib.gis.db import models

sample_frame_map = {
12: 'Mexico',
14: 'Contintental US',
19: 'Canada',
20: 'Alaska',
21: 'Puerto Rico',
15: 'Hawaii',
22: 'Offshore Caribbean',
23: 'Offshore AKCAN',
24: 'Offshore CONUS',
25: 'Offshore Hawaii',
26: 'Offshore Mexico',
}

class GRTSCells(models.Model):
id = models.IntegerField(primary_key=True)
grts_cell_id = models.IntegerField()
sample_frame_id = models.IntegerField(blank=True, null=True)
grts_geom = models.GeometryField(blank=True, null=True)
water_p = models.FloatField(blank=True, null=True)
outside_p = models.FloatField(blank=True, null=True)
location_1_type = models.CharField(max_length=255, blank=True, null=True)
location_1_name = models.CharField(max_length=255, blank=True, null=True)
location_1_p = models.FloatField(blank=True, null=True)
location_2_type = models.CharField(max_length=255, blank=True, null=True)
location_2_name = models.CharField(max_length=255, blank=True, null=True)
location_2_p = models.FloatField(blank=True, null=True)
sub_location_1_type = models.CharField(max_length=255, blank=True, null=True)
sub_location_1_name = models.CharField(max_length=255, blank=True, null=True)
sub_location_1_p = models.FloatField(blank=True, null=True)
sub_location_2_type = models.CharField(max_length=255, blank=True, null=True)
sub_location_2_name = models.CharField(max_length=255, blank=True, null=True)
sub_location_2_p = models.FloatField(blank=True, null=True)
own_1_name = models.CharField(max_length=255, blank=True, null=True)
own_1_p = models.FloatField(blank=True, null=True)
# continue defining all fields similarly
priority_frame = models.BooleanField(blank=True, null=True)
priority_state = models.BooleanField(blank=True, null=True)
geom_4326 = models.GeometryField()
clipped = models.BooleanField(blank=True, null=True)

@property
def sampleFrameMapping(self):
return sample_frame_map[self.sample_frame_id]

@staticmethod
def sort_order():
return [
14, 20, 15, 24, 21, 19, 12, 22, 23, 25, 26
]
1 change: 1 addition & 0 deletions bats_ai/core/models/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Recording(TimeStampedModel, models.Model):
audio_file = models.FileField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
recorded_date = models.DateField(blank=True, null=True)
recorded_time = models.TimeField(blank=True, null=True)
equipment = models.TextField(blank=True, null=True)
comments = models.TextField(blank=True, null=True)
recording_location = models.GeometryField(srid=4326, blank=True, null=True)
Expand Down
2 changes: 2 additions & 0 deletions bats_ai/core/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .annotations import router as AnnotationRouter
from .grts_cells import router as GRTSCellsRouter
from .recording import router as RecordingRouter
from .species import router as SpeciesRouter
from .temporal_annotations import router as TemporalAnnotationRouter
Expand All @@ -8,4 +9,5 @@
'SpeciesRouter',
'AnnotationRouter',
'TemporalAnnotationRouter',
'GRTSCellsRouter',
]
83 changes: 83 additions & 0 deletions bats_ai/core/views/grts_cells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from django.http import JsonResponse
from bats_ai.core.models import GRTSCells
from django.http import HttpRequest
from ninja.pagination import RouterPaginated
from django.contrib.gis.geos import Polygon

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from ninja import Query

router = RouterPaginated()

@router.get('/grid_cell_id')
def get_grid_cell_id(request: HttpRequest, latitude: float = Query(...), longitude: float = Query(...)):
try:
# Create a point object from the provided latitude and longitude
point = Point(longitude, latitude, srid=4326)

# Query the grid cell that contains the provided point
cell = GRTSCells.objects.filter(geom_4326__contains=point).first()

if cell:
# Return the grid cell ID
return JsonResponse({'grid_cell_id': cell.grts_cell_id})
else:
return JsonResponse({'error': 'No grid cell found for the provided latitude and longitude'}, status=200)
except Exception as e:
return JsonResponse({'error': str(e)}, status=200)


@router.get('/{id}')
def get_cell_center(request: HttpRequest, id: int, quadrant: str = None):
try:
cells = GRTSCells.objects.filter(grts_cell_id=id)

# Define a custom order for sample_frame_id
custom_order = GRTSCells.sort_order() # Define your custom order here

# Define a custom key function to sort cells based on the custom order
def custom_sort_key(cell):
return custom_order.index(cell.sample_frame_id)

# Sort the cells queryset based on the custom order
sorted_cells = sorted(cells, key=custom_sort_key)
cell = sorted_cells[0]
geom_4326 = cell.geom_4326


# Get the centroid of the entire cell polygon
center = geom_4326.centroid

if quadrant:
# If quadrant is specified, divide the cell polygon into quadrants
min_x, min_y, max_x, max_y = geom_4326.extent
mid_x = (min_x + max_x) / 2
mid_y = (min_y + max_y) / 2

# Determine the bounding box coordinates of the specified quadrant
if quadrant.upper() == 'NW':
bbox = (min_x, mid_y, mid_x, max_y)
elif quadrant.upper() == 'SE':
bbox = (mid_x, min_y, max_x, mid_y)
elif quadrant.upper() == 'SW':
bbox = (min_x, min_y, mid_x, mid_y)
elif quadrant.upper() == 'NE':
bbox = (mid_x, mid_y, max_x, max_y)

quadrant_polygon = Polygon.from_bbox(bbox)

# Intersect the cell polygon with the specified quadrant's polygon
quadrant_polygon = geom_4326.intersection(quadrant_polygon)

# Get the centroid of the intersected polygon
center = quadrant_polygon.centroid

# Get the latitude and longitude of the centroid
center_latitude = center.y
center_longitude = center.x

return JsonResponse({'latitude': center_latitude, 'longitude': center_longitude})
except GRTSCells.DoesNotExist:
return JsonResponse({'error': f'Cell with cellId={id} does not exist'}, status=200)

6 changes: 6 additions & 0 deletions bats_ai/core/views/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class RecordingSchema(Schema):
class RecordingUploadSchema(Schema):
name: str
recorded_date: str
recorded_time: str
equipment: str | None
comments: str | None
latitude: float = None
Expand Down Expand Up @@ -90,6 +91,7 @@ def create_recording(
publicVal: bool = False,
):
converted_date = datetime.strptime(payload.recorded_date, '%Y-%m-%d')
converted_time = datetime.strptime(payload.recorded_time, '%H%M%S')
point = None
if payload.latitude and payload.longitude:
point = Point(payload.longitude, payload.latitude)
Expand All @@ -98,6 +100,7 @@ def create_recording(
owner_id=request.user.pk,
audio_file=audio_file,
recorded_date=converted_date,
recorded_time=converted_time,
equipment=payload.equipment,
grts_cell_id=payload.gridCellId,
recording_location=point,
Expand Down Expand Up @@ -128,6 +131,9 @@ def update_recording(request: HttpRequest, id: int, recording_data: RecordingUpl
if recording_data.recorded_date:
converted_date = datetime.strptime(recording_data.recorded_date, '%Y-%m-%d')
recording.recorded_date = converted_date
if recording_data.recorded_time:
converted_time = datetime.strptime(recording_data.recorded_time, '%H%M%S')
recording.recorded_time = converted_time
if recording_data.publicVal is not None and recording_data.publicVal != recording.public:
recording.public = recording_data.publicVal
if recording_data.latitude and recording_data.longitude:
Expand Down
Loading

0 comments on commit ca8500d

Please sign in to comment.