Skip to content
Open
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
34 changes: 22 additions & 12 deletions ifcbdb/assets/js/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,20 +135,30 @@ function showWorkspace(workspace) {
}
}

function updateDateTimeLabel(selector, value) {
if (!value) {
$(selector).closest(".flex-row").hide();
return;
}

const timestamp = moment.utc(value);
const dateString = timestamp.format("YYYY-MM-DD");
const timeString = timestamp.format("HH:mm:ss z");
const relativeTime = timestamp.fromNow();

const text =
`${dateString}<br/>${timeString}<br/>` +
`(<span data-livestamp='${value}'>${relativeTime}</span>)`;

$(selector).closest(".flex-row").show();
$(selector).html(text);
}

//************* Bin Methods ***********************/
function updateBinStats(data) {
var timestamp_iso = data["timestamp_iso"];
var timestamp = moment.utc(timestamp_iso);

var date_string = timestamp.format("YYYY-MM-DD");
var time_string = timestamp.format("HH:mm:ss z");
var initial_relative_time = timestamp.fromNow();

$("#stat-date-time").html(
date_string + "<br/>" +
time_string + "<br/>" +
"(<span data-livestamp='"+timestamp_iso+"'>"+initial_relative_time+"</span>)"
);
updateDateTimeLabel("#stat-date-time", data.timestamp_iso);
updateDateTimeLabel("#stat-modified", data.modified);
updateDateTimeLabel("#stat-accessioned", data.accessioned);

function showField(id, text) {
$("#show-"+id).removeClass("d-none").addClass("d-flex");
Expand Down
5 changes: 5 additions & 0 deletions ifcbdb/dashboard/accession.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.db import IntegrityError, transaction
from django.db.models import Count, Max
from django.contrib.postgres.aggregates.general import StringAgg
from django.utils import timezone

import pandas as pd
import numpy as np
Expand Down Expand Up @@ -96,6 +97,8 @@ def sync_one(self, pid):
'data_directory': dd_found,
'skip': True, # in case accession is interrupted
'team': team,
'modified': timezone.now(),
'accessioned': timezone.now(),
})

# For existing bins, if the team value is not set, and there is one, save that value. This handles pre-existing
Expand Down Expand Up @@ -167,6 +170,8 @@ def sync(self, progress_callback=do_nothing, log_callback=do_nothing):
'data_directory': dd,
'skip': True, # in case accession is interrupted
'team': team,
'modified': timezone.now(),
'accessioned': timezone.now(),
})

# For existing bins, if the team value is not set, and there is one, save that value. This handles pre-existing
Expand Down
3 changes: 3 additions & 0 deletions ifcbdb/dashboard/management/commands/bintool.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def handle(self, *args, **options):
except Dataset.DoesNotExist:
self.stderr.write(f"Dataset '{add_dataset_name}' does not exist.")

# Update timestamp on affected bins
bins.update(modified=timezone.now())

if options['cache_paths']:
for b in bins:
b._get_bin() # this will cache the path if it isn't already
Expand Down
8 changes: 5 additions & 3 deletions ifcbdb/dashboard/management/commands/updatetriggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils import timezone
from tqdm import tqdm
from tqdm._utils import _term_move_up

Expand Down Expand Up @@ -42,8 +43,10 @@ def bulk_update(self, chunk, pbar):
pbar.write(self.border)
continue
bin.n_triggers = self.n_triggers(self.last_line(bin.adc_path()))
bin.modified = timezone.now()
objs.append(bin)
res = Bin.objects.bulk_update(objs, ['n_triggers'])

res = Bin.objects.bulk_update(objs, ['n_triggers', 'modified'])
pbar.update(res)
if res == 0:
pbar.write(self.clear_border + ("Error: Bins, " + str(objs) + " not updated! Continuing ..."))
Expand Down Expand Up @@ -77,8 +80,7 @@ def parse_input_csv(self, input_csv):
row = next(reader)
with transaction.atomic():
for row in reader:
res = 0
res = Bin.objects.filter(pid=row[0]).update(n_triggers=row[1])
res = Bin.objects.filter(pid=row[0]).update(n_triggers=row[1], modified=timezone.now())
if res == 0:
print("Error: Bin, " + bin.pid + " not updated! Continuing ...")

Expand Down
18 changes: 18 additions & 0 deletions ifcbdb/dashboard/migrations/0053_bin_modified.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.29 on 2026-03-25 03:44

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0052_team_short_description'),
]

operations = [
migrations.AddField(
model_name='bin',
name='modified',
field=models.DateTimeField(auto_now=True, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.30 on 2026-04-23 14:17

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0053_bin_modified'),
]

operations = [
migrations.AddField(
model_name='bin',
name='accessioned',
field=models.DateTimeField(null=True),
),
migrations.AlterField(
model_name='bin',
name='modified',
field=models.DateTimeField(null=True),
),
]
16 changes: 16 additions & 0 deletions ifcbdb/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ class Bin(models.Model):
data_directory = models.ForeignKey('DataDirectory', null=True, blank=True, on_delete=models.SET_NULL)
# accession
added = models.DateTimeField(auto_now_add=True, null=True)
modified = models.DateTimeField(auto_now=False, null=True)
accessioned = models.DateTimeField(auto_now=False, null=True)
# qaqc flags
qc_bad = models.BooleanField(default=False) # is this bin invalid
qc_no_rois = models.BooleanField(default=False)
Expand Down Expand Up @@ -816,6 +818,11 @@ def add_tag(self, tag_name, user=None):
event, created = TagEvent.objects.get_or_create(bin=self, tag=tag)
if created and user is not None:
event.user = user

# Update the timestamp on the bin
if created:
self.save(update_fields=['modified'])

return event

def delete_tag(self, tag_name, normalize=True):
Expand All @@ -825,6 +832,9 @@ def delete_tag(self, tag_name, normalize=True):
event = TagEvent.objects.get(bin=self, tag=tag)
event.delete()

# Update the timestamp on the bin
self.save(update_fields=['modified'])

# comments

def add_comment(self, content, user=None, skip_duplicates=False):
Expand All @@ -835,11 +845,17 @@ def add_comment(self, content, user=None, skip_duplicates=False):
comment = Comment(bin=self, content=content, user=user)
comment.save()

# Update the timestamp on the bin
self.save(update_fields=['modified'])

def delete_comment(self, comment_id, user):
try:
comment = Comment.objects.get(bin=self, pk=comment_id)
if user.is_staff:
comment.delete()

# Update the timestamp on the bin
self.save(update_fields=['modified'])
except:
pass

Expand Down
2 changes: 2 additions & 0 deletions ifcbdb/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,8 @@ def _bin_details(bin, dataset=None, view_size=None, scale_factor=None, preload_a
"cruise": bin.cruise,
"cast": bin.cast,
"niskin": bin.niskin,
"modified": bin.modified,
"accessioned": bin.accessioned,
}

def _mosaic_page_image(request, bin_id):
Expand Down
16 changes: 16 additions & 0 deletions ifcbdb/secure/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.contrib.auth.models import User, Group
from django.db.models import Count
from django import forms
from django.utils import timezone
from django.views.decorators.http import require_POST, require_GET
from django.http import JsonResponse, Http404, HttpResponseForbidden, HttpResponse, StreamingHttpResponse
from django.shortcuts import render, get_object_or_404, redirect, reverse
Expand Down Expand Up @@ -399,12 +400,18 @@ def merge_tag(request, id):
# Get the list of bins already assigned to the target tag to prevent creating duplicates
assigned_bins = TagEvent.query(tag=target, dataset=dataset).values("bin")

# Get the bins that will be affected so we can update the modified date
affected_bin_ids = list(tag_events.values_list("bin_id", flat=True))

# Update the tag on any records not already assigned to the target tag
tag_events.exclude(bin__in=assigned_bins).update(tag=target)

# Remove any records already assigned to the target tag
tag_events.filter(bin__in=assigned_bins).delete()

# Update the timestamp on affected bins
Bin.objects.filter(id__in=affected_bin_ids).update(modified=timezone.now())

# If the tag is no longer in use on any bins, remove it
if TagEvent.objects.filter(tag=tag).count() == 0:
tag.delete()
Expand Down Expand Up @@ -721,6 +728,9 @@ def update_comment(request, bin_id):
comment.content = content
comment.save()

# Update the timestamp on the bin
bin.save(update_fields=['modified'])

return JsonResponse({
"id": comment.id,
"comments": bin.comment_list,
Expand Down Expand Up @@ -1126,6 +1136,9 @@ def assign_dataset(bin_qs, dataset):
num_already_assigned = dataset.bins.filter(id__in=bin_qs.values_list("id", flat=True)).count()

dataset.bins.add(*bin_qs)

# Update the timestamp on all updated bins
bin_qs.update(modified=timezone.now())

total = bin_qs.count()
num_assigned = total - num_already_assigned
Expand Down Expand Up @@ -1155,6 +1168,9 @@ def unassign_dataset(bin_qs, dataset):
})

dataset.bins.remove(*bin_qs)

# Update the timestamp on all updated bins
bin_qs.update(modified=timezone.now())

label = "bins" if num_assigned != 1 else "bin"
return JsonResponse({
Expand Down
6 changes: 6 additions & 0 deletions ifcbdb/templates/dashboard/bin.html
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,12 @@
<a {% if user.is_authenticated %}href="javascript:;"{% endif %} id="stat-skip"></a>
</div>
</div>
<div class="d-flex flex-row">
<div class="flex-column">Modified: <span id="stat-modified"></span></div>
</div>
<div class="d-flex flex-row">
<div class="flex-column">Accessioned: <span id="stat-accessioned"></span></div>
</div>
<!-- Download links -->
<p class="h5-responsive">Download:</p>
<div class="d-flex flex-row">
Expand Down