diff --git a/dbapp/mainapp/templates/mainapp/source_list.html b/dbapp/mainapp/templates/mainapp/source_list.html
index d355b65..b1c6ad1 100644
--- a/dbapp/mainapp/templates/mainapp/source_list.html
+++ b/dbapp/mainapp/templates/mainapp/source_list.html
@@ -227,6 +227,15 @@
placeholder="До" value="{{ date_to|default:'' }}">
+
+
+
+
+
+
+
@@ -812,8 +821,25 @@ function showSourceDetails(sourceId) {
const modal = new bootstrap.Modal(document.getElementById('sourceDetailsModal'));
modal.show();
+ // Build URL with filter parameters
+ const urlParams = new URLSearchParams(window.location.search);
+ const geoDateFrom = urlParams.get('geo_date_from');
+ const geoDateTo = urlParams.get('geo_date_to');
+
+ let apiUrl = '/api/source/' + sourceId + '/objitems/';
+ const params = new URLSearchParams();
+ if (geoDateFrom) {
+ params.append('geo_date_from', geoDateFrom);
+ }
+ if (geoDateTo) {
+ params.append('geo_date_to', geoDateTo);
+ }
+ if (params.toString()) {
+ apiUrl += '?' + params.toString();
+ }
+
// Fetch data from API
- fetch('/api/source/' + sourceId + '/objitems/')
+ fetch(apiUrl)
.then(response => {
if (!response.ok) {
if (response.status === 404) {
diff --git a/dbapp/mainapp/views/api.py b/dbapp/mainapp/views/api.py
index fc1528b..9c02782 100644
--- a/dbapp/mainapp/views/api.py
+++ b/dbapp/mainapp/views/api.py
@@ -176,9 +176,14 @@ class SourceObjItemsAPIView(LoginRequiredMixin, View):
"""API endpoint for getting ObjItems related to a Source."""
def get(self, request, source_id):
+ from datetime import datetime, timedelta
from ..models import Source
try:
+ # Get filter parameters from query string
+ geo_date_from = request.GET.get("geo_date_from", "").strip()
+ geo_date_to = request.GET.get("geo_date_to", "").strip()
+
# Load Source with prefetch_related for ObjItem
source = Source.objects.prefetch_related(
'source_objitems',
@@ -198,7 +203,26 @@ class SourceObjItemsAPIView(LoginRequiredMixin, View):
).get(id=source_id)
# Get all related ObjItems, sorted by created_at
- objitems = source.source_objitems.all().order_by('created_at')
+ objitems = source.source_objitems.all()
+
+ # Apply Geo timestamp filter if provided
+ if geo_date_from:
+ try:
+ geo_date_from_obj = datetime.strptime(geo_date_from, "%Y-%m-%d")
+ objitems = objitems.filter(geo_obj__timestamp__gte=geo_date_from_obj)
+ except (ValueError, TypeError):
+ pass
+
+ if geo_date_to:
+ try:
+ geo_date_to_obj = datetime.strptime(geo_date_to, "%Y-%m-%d")
+ # Add one day to include entire end date
+ geo_date_to_obj = geo_date_to_obj + timedelta(days=1)
+ objitems = objitems.filter(geo_obj__timestamp__lt=geo_date_to_obj)
+ except (ValueError, TypeError):
+ pass
+
+ objitems = objitems.order_by('created_at')
objitems_data = []
for objitem in objitems:
diff --git a/dbapp/mainapp/views/source.py b/dbapp/mainapp/views/source.py
index d541fe2..e36f362 100644
--- a/dbapp/mainapp/views/source.py
+++ b/dbapp/mainapp/views/source.py
@@ -40,6 +40,8 @@ class SourceListView(LoginRequiredMixin, View):
objitem_count_max = request.GET.get("objitem_count_max", "").strip()
date_from = request.GET.get("date_from", "").strip()
date_to = request.GET.get("date_to", "").strip()
+ geo_date_from = request.GET.get("geo_date_from", "").strip()
+ geo_date_to = request.GET.get("geo_date_to", "").strip()
selected_satellites = request.GET.getlist("satellite_id")
# Get all satellites for filter
@@ -50,6 +52,28 @@ class SourceListView(LoginRequiredMixin, View):
.order_by("name")
)
+ # Build Q object for geo date filtering
+ geo_date_q = Q()
+ has_geo_date_filter = False
+ if geo_date_from:
+ try:
+ geo_date_from_obj = datetime.strptime(geo_date_from, "%Y-%m-%d")
+ geo_date_q &= Q(source_objitems__geo_obj__timestamp__gte=geo_date_from_obj)
+ has_geo_date_filter = True
+ except (ValueError, TypeError):
+ pass
+
+ if geo_date_to:
+ try:
+ from datetime import timedelta
+ geo_date_to_obj = datetime.strptime(geo_date_to, "%Y-%m-%d")
+ # Add one day to include entire end date
+ geo_date_to_obj = geo_date_to_obj + timedelta(days=1)
+ geo_date_q &= Q(source_objitems__geo_obj__timestamp__lt=geo_date_to_obj)
+ has_geo_date_filter = True
+ except (ValueError, TypeError):
+ pass
+
# Get all Source objects with query optimization
# Using annotate to count ObjItems efficiently (single query with GROUP BY)
# Using prefetch_related for reverse ForeignKey relationships to avoid N+1 queries
@@ -61,7 +85,7 @@ class SourceListView(LoginRequiredMixin, View):
'marks',
'marks__created_by__user'
).annotate(
- objitem_count=Count('source_objitems')
+ objitem_count=Count('source_objitems', filter=geo_date_q) if has_geo_date_filter else Count('source_objitems')
)
# Apply filters
@@ -130,6 +154,10 @@ class SourceListView(LoginRequiredMixin, View):
except (ValueError, TypeError):
pass
+ # Filter by Geo timestamp range (only filter sources that have matching objitems)
+ if has_geo_date_filter:
+ sources = sources.filter(geo_date_q).distinct()
+
# Search by ID
if search_query:
try:
@@ -184,15 +212,33 @@ class SourceListView(LoginRequiredMixin, View):
coords_valid_str = format_coords(source.coords_valid)
coords_reference_str = format_coords(source.coords_reference)
- # Get count of related ObjItems
- objitem_count = source.objitem_count
+ # Filter objitems by geo date if filter is applied
+ objitems_to_display = source.source_objitems.all()
+ if geo_date_from or geo_date_to:
+ if geo_date_from:
+ try:
+ geo_date_from_obj = datetime.strptime(geo_date_from, "%Y-%m-%d")
+ objitems_to_display = objitems_to_display.filter(geo_obj__timestamp__gte=geo_date_from_obj)
+ except (ValueError, TypeError):
+ pass
+ if geo_date_to:
+ try:
+ from datetime import timedelta
+ geo_date_to_obj = datetime.strptime(geo_date_to, "%Y-%m-%d")
+ geo_date_to_obj = geo_date_to_obj + timedelta(days=1)
+ objitems_to_display = objitems_to_display.filter(geo_obj__timestamp__lt=geo_date_to_obj)
+ except (ValueError, TypeError):
+ pass
+
+ # Get count of related ObjItems (filtered)
+ objitem_count = objitems_to_display.count()
# Get satellites for this source and check for LyngSat
satellite_names = set()
has_lyngsat = False
lyngsat_id = None
- for objitem in source.source_objitems.all():
+ for objitem in objitems_to_display:
if hasattr(objitem, 'parameter_obj') and objitem.parameter_obj:
if hasattr(objitem.parameter_obj, 'id_satellite') and objitem.parameter_obj.id_satellite:
satellite_names.add(objitem.parameter_obj.id_satellite.name)
@@ -247,6 +293,8 @@ class SourceListView(LoginRequiredMixin, View):
'objitem_count_max': objitem_count_max,
'date_from': date_from,
'date_to': date_to,
+ 'geo_date_from': geo_date_from,
+ 'geo_date_to': geo_date_to,
'satellites': satellites,
'selected_satellites': [
int(x) if isinstance(x, str) else x for x in selected_satellites if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))