Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish-image-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches:
- "main"
- "test-bucketstore"
- "ctd-case-sensitive-filenames"

env:
IMAGE_NAME: nes-lter-api-2
Expand Down
18 changes: 18 additions & 0 deletions api/core/migrations/0013_alter_cruise_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 6.0.5 on 2026-06-09 20:18

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0012_alter_cruise_type'),
]

operations = [
migrations.AlterField(
model_name='cruise',
name='type',
field=models.CharField(choices=[('NESLTER', 'Neslter'), ('JP Student', 'Jp Student'), ('OOI NES', 'Ooi Nes'), ('OOI MAB', 'Ooi Mab'), ('Opportunistic', 'Opportunistic')], default='NESLTER', max_length=32),
),
]
18 changes: 18 additions & 0 deletions api/core/migrations/0014_alter_cruise_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 6.0.6 on 2026-06-17 19:57

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0013_alter_cruise_type'),
]

operations = [
migrations.AlterField(
model_name='cruise',
name='type',
field=models.CharField(choices=[('NES-LTER', 'Neslter'), ('JP Student', 'Jp Student'), ('OOI-NES', 'Ooi Nes'), ('OOI-MAB', 'Ooi Mab'), ('Opportunistic', 'Opportunistic')], default='NES-LTER', max_length=32),
),
]
5 changes: 3 additions & 2 deletions api/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,10 @@ def __str__(self):

class Cruise(models.Model):
class CruiseType(models.TextChoices):
NESLTER = "NESLTER"
NESLTER = "NES-LTER"
JP_STUDENT = "JP Student"
OOI_PIONEER = "OOI Pioneer"
OOI_NES = "OOI-NES" # pioneer
OOI_MAB = "OOI-MAB" # pioneer
OPPORTUNISTIC = "Opportunistic"

name = models.CharField(max_length=100, unique=True) # e.g. "EN627"
Expand Down
1 change: 1 addition & 0 deletions api/core/templates/upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ <h1>📂 Drag and Drop Your Files</h1>
<option value="nutrient">LTERnut.xlsx</option>
<option value="sample_log">LTER_sample_log.xlsx</option>
<option value="station_list">NES-LTER_station_list_compilation.xlsx</option>
<option value="cruise_types">NES-LTER_cruise_types.csv</option>
<option value="hplc">Sosik*report.xlsx</option>
<option value="chlorophyll">NESLTERchl.xlsx</option>
</select>
Expand Down
6 changes: 5 additions & 1 deletion api/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def file_upload_view(request):
'nutrient': Path('/data/raw/all/nut'),
'sample_log': Path('/data/raw/all'),
'station_list' : Path('/data/raw/all/metadata'),
'cruise_types' : Path('/data/raw/all/metadata'),
'hplc' : Path('/data/raw/all/hplc'),
'chlorophyll': Path('/data/raw/all/chl'),
}
Expand All @@ -42,6 +43,7 @@ def file_upload_view(request):
'nutrient': Path('/vast/raw/all/nut'),
'sample_log': Path('/vast/raw/all'),
'station_list' : Path('/vast/raw/all/metadata'),
'cruise_types' : Path('/vast/raw/all/metadata'),
'hplc' : Path('/vast/raw/all/hplc'),
'chlorophyll': Path('/vast/raw/all/chl'),
}
Expand Down Expand Up @@ -101,6 +103,7 @@ def file_upload_view(request):
'nutrient' : ['importnut'],
'sample_log' : ['importnut'],
'station_list' : ['importstations'],
'cruise_types' : ['importcruise'],
'hplc' : ['importhplc'],
'chlorophyll' : ['importchl']
}
Expand Down Expand Up @@ -267,7 +270,8 @@ def cruises_for_season(request, season: str):
TYPE_PREFIXES = [
(Cruise.CruiseType.NESLTER, "NESLTER", "🚢"),
(Cruise.CruiseType.JP_STUDENT, "JP Student", "🛳️"),
(Cruise.CruiseType.OOI_PIONEER, "OOI Pioneer", "🛥️"),
(Cruise.CruiseType.OOI_NES, "OOI NES", "🛥️"),
(Cruise.CruiseType.OOI_MAB, "OOI MAB", "⛴️"),
(Cruise.CruiseType.OPPORTUNISTIC, "Opportunistic", "⛵"),
]

Expand Down
4 changes: 4 additions & 0 deletions api/ctd/management/commands/importall.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ def handle(self, *args, **options):
logger.error("Importing underway data...")
call_command('importunderwaydata', cruise_name=cruise_name)

self.stdout.write("Importing cruises again to set missing cruise start and end times from underway")
logger.error("Importing cruises again to set missing cruise start and end times from underway...")
call_command('importcruise', cruise_name=cruise_name)

self.stdout.write("Importing casts...")
logger.error("Importing casts...")
call_command('importcast', cruise_name=cruise_name)
Expand Down
28 changes: 16 additions & 12 deletions api/ctd/management/commands/importcast.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,19 @@ def parse_asc_fixed_width(self, asc_path):

def create_cast_file(self, file, cruise, cast, time):
delimiter = ';'
base_dir = os.path.dirname(file.name)
base_name = os.path.splitext(os.path.basename(file.name))[0]
hdr_path = Path(file)
base_dir = hdr_path.parent
base_name = hdr_path.stem.lower()

# Look for matching .asc file (case-sensitive)
# Look for matching .asc file, regardless of filename case
ascfile = None
for f in os.listdir(base_dir):
if f.lower() == f"{base_name.lower()}.asc":
ascfile = os.path.join(base_dir, f)
for f in base_dir.iterdir():
if (
f.is_file()
and f.stem.lower() == base_name
and f.suffix.lower() == ".asc"
):
ascfile = f
break

if not ascfile:
Expand Down Expand Up @@ -110,14 +115,13 @@ def create_cast_file(self, file, cruise, cast, time):
self.logger.error((f"{ascfile} not parsable."))

def handle(self, *args, **options):
#self.stdout = options.get('stdout', sys.stdout) # removes /n's
cruise_name = options['cruise_name']

if cruise_name is None:
parent_dir = Path('/vast/raw')
cruises = [f.name for f in parent_dir.iterdir() if f.is_dir() and f.name != "all"]
else:
cruises = [cruise_name]
cruises = [cruise_name.lower()]

self.stdout.write(self.style.SUCCESS(f'For each .hdr file, look for a matching .asc file.'))
self.logger.error((f'For each .hdr file, look for a matching .asc file.'))
Expand All @@ -127,7 +131,7 @@ def handle(self, *args, **options):
cruise = Cruise.objects.get(name__iexact=cruise_name)
directory = f'/vast/raw/{cruise_name}/ctd/'
hdr_files = sorted(glob.glob(os.path.join(directory, '*.hdr')))
if cruise_name.lower() == "en627":
if cruise_name == "en627":
added_dir = os.path.join(directory, "cast_1_files_used_for_corrected_cast_2")
hdr_files += sorted(glob.glob(os.path.join(added_dir, '*.hdr')))

Expand All @@ -148,12 +152,12 @@ def handle(self, *args, **options):
cast = cast.lstrip('0')

# get lat, lon and time from .hdr file
with open(file, 'r', encoding='utf-8', errors='ignore') as file:
content = file.read()
with open(file, "r", encoding="utf-8", errors="ignore") as fh:
content = fh.read()

latitude, longitude = parse_lat_lon(content)
start_time = parse_time(content)

if latitude != None and longitude != None and start_time != None:
Cast.objects.update_or_create(
cruise=cruise,
Expand Down
Loading