Skip to content

Commit

Permalink
Merge pull request #9 from Kitware/recording-display
Browse files Browse the repository at this point in the history
Recording Displaying
  • Loading branch information
BryonLewis authored Jan 3, 2024
2 parents 2ae1ecb + 9ba6504 commit 1671098
Show file tree
Hide file tree
Showing 19 changed files with 5,346 additions and 669 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ maintenance. To non-destructively update your development stack at any time:
This configuration still uses Docker to run attached services in the background,
but allows developers to run Python code on their native system.

### Dev Tool Endpoints
1. Main Site Interface [http://localhost:3000/](http://localhost:3000/)
2. Site Administration [http://localhost:8000/admin/](http://localhost:8000/admin/)
3. Swagger API (These are default swagger endpoints using Django-REST) [http://localhost:8000/api/docs/swagger/](http://localhost:8000/api/docs/swagger/)
4. Django Ninja API [http://localhost:8000/api/v1/docs#/](http://localhost:8000/api/v1/docs#/)
5. MinIO (S3 local management) [http://localhost:9001/browser](http://localhost:9001/browser)
Username: 'minioAccessKey'
Password: 'minioSecretKey'

### Initial Setup
1. Run `docker compose -f ./docker-compose.yml up -d`
2. Install Python 3.10
Expand Down
2 changes: 2 additions & 0 deletions bats_ai/core/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .image import Image
from .recording import Recording
from .recording_annotation_status import RecordingAnnotationStatus
from .species import Species

__all__ = [
'Image',
'Recording',
'RecordingAnnotationStatus',
'Species',
]
16 changes: 16 additions & 0 deletions bats_ai/core/models/annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.contrib.auth.models import User
from django.db import models

from .recording import Recording
from .species import Species


class Species(models.Model):
recording = models.ForeignKey(Recording, on_delete=models.CASCADE)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
start_time = models.IntegerField(blank=True, null=True)
end_time = models.IntegerField(blank=True, null=True)
low_freq = models.IntegerField(blank=True, null=True)
high_freq = models.IntegerField(blank=True, null=True)
species = models.ManyToManyField(Species)
comments = models.TextField(blank=True, null=True)
36 changes: 36 additions & 0 deletions bats_ai/core/models/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,39 @@ class Recording(TimeStampedModel, models.Model):
recording_location = models.GeometryField(srid=0, blank=True, null=True)
grts_cell_id = models.IntegerField(blank=True, null=True)
grts_cell = models.IntegerField(blank=True, null=True)

def generate_spectrogram(self):
import base64
from io import BytesIO

import librosa
import librosa.display
import matplotlib.pyplot as plt
import numpy as np

# Load audio file
bytefile = self.audio_file.read()

y, sr = librosa.load(BytesIO(bytefile))

# Generate spectrogram
D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)

# Plot and save the spectrogram
plt.figure(figsize=(10, 4))
librosa.display.specshow(D, sr=sr, x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Spectrogram')
plt.xlabel('Time')
plt.ylabel('Frequency')
plt.tight_layout()

# Convert the plot to base64
buffer = BytesIO()
plt.savefig(buffer, format='png')
buffer.seek(0)
base64_image = base64.b64encode(buffer.read()).decode('utf-8')

plt.close()

return base64_image
10 changes: 10 additions & 0 deletions bats_ai/core/models/species.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.db import models


class Species(models.Model):
species_code = models.CharField(max_length=10, blank=True, null=True)
family = models.CharField(max_length=50, blank=True, null=True)
genus = models.CharField(max_length=50, blank=True, null=True)
species = models.CharField(max_length=100, blank=True, null=True)
common_name = models.CharField(max_length=100, blank=True, null=True)
species_code_6 = models.CharField(max_length=10, blank=True, null=True)
26 changes: 19 additions & 7 deletions bats_ai/core/views/recording.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
import logging

from django.contrib.auth.models import User
Expand Down Expand Up @@ -31,6 +32,7 @@ class RecordingSchema(Schema):

class RecordingUploadSchema(Schema):
name: str
recorded_date: str
equipment: str | None
comments: str | None

Expand All @@ -45,14 +47,16 @@ def get_owner_id(request: HttpRequest):


@router.post('/')
def update_recording(
def create_recording(
request: HttpRequest, payload: Form[RecordingUploadSchema], audio_file: File[UploadedFile]
):
user_id = get_owner_id(request)
converted_date = datetime.strptime(payload.recorded_date, '%Y-%m-%d')
recording = Recording(
name=payload.name,
owner_id=user_id,
audio_file=audio_file,
recorded_date=converted_date,
equipment=payload.equipment,
comments=payload.comments,
)
Expand All @@ -64,14 +68,10 @@ def update_recording(
@router.get('/')
def get_recordings(request: HttpRequest):
# Check if the user is authenticated and get userId
token = request.headers.get('Authorization').replace('Bearer ', '')
token_found = AccessToken.objects.get(token=token)
logger.warning(token_found.user.pk)
if not token_found:
raise HttpError(401, 'Authentication credentials were not provided.')
user_id = get_owner_id(request)

# Filter recordings based on the owner's id
recordings = Recording.objects.filter(owner=token_found.user.pk).values()
recordings = Recording.objects.filter(owner=user_id).values()

for recording in recordings:
user = User.objects.get(id=recording['owner_id'])
Expand All @@ -80,3 +80,15 @@ def get_recordings(request: HttpRequest):

# Return the serialized data
return list(recordings)


@router.get('/{id}/spectrogram')
def get_spectrogram(request: HttpRequest, id: int):
try:
recording = Recording.objects.get(pk=id)
except Recording.DoesNotExist:
return {'error': 'Recording not found'}

base64_spectrogram = recording.generate_spectrogram()

return {'base64_spectrogram': base64_spectrogram}
3 changes: 2 additions & 1 deletion client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
'vue/script-setup-uses-vars': 'error',
"vue/valid-v-slot": ["error", {
"allowModifiers": true
}]
}],
'vue/multi-word-component-names': 'off'
},
};
Loading

0 comments on commit 1671098

Please sign in to comment.