feat: enhance project data handling with new Jinja filters and update UI labels for clarity
This commit is contained in:
@@ -2851,17 +2851,20 @@ async def upload_all_project_data(
|
|||||||
Determine the grouping key for a file path.
|
Determine the grouping key for a file path.
|
||||||
Files inside Auto_####/Auto_Leq/ or Auto_####/Auto_Lp_01/ are collapsed
|
Files inside Auto_####/Auto_Leq/ or Auto_####/Auto_Lp_01/ are collapsed
|
||||||
up to their Auto_#### parent so they all land in the same session.
|
up to their Auto_#### parent so they all land in the same session.
|
||||||
|
Only folder components are examined (not the filename, which is parts[-1]).
|
||||||
"""
|
"""
|
||||||
# Find the deepest Auto_#### component (case-insensitive)
|
# Only look at folder components — exclude the filename (last part)
|
||||||
|
folder_parts = parts[:-1]
|
||||||
auto_idx = None
|
auto_idx = None
|
||||||
for i, p in enumerate(parts):
|
for i, p in enumerate(folder_parts):
|
||||||
if p.lower().startswith("auto_") and not p.lower().startswith("auto_leq") and not p.lower().startswith("auto_lp"):
|
p_lower = p.lower()
|
||||||
|
if p_lower.startswith("auto_") and not p_lower.startswith("auto_leq") and not p_lower.startswith("auto_lp"):
|
||||||
auto_idx = i
|
auto_idx = i
|
||||||
if auto_idx is not None:
|
if auto_idx is not None:
|
||||||
# Group key = everything up to and including Auto_####
|
# Group key = everything up to and including Auto_####
|
||||||
return "/".join(parts[:auto_idx + 1])
|
return "/".join(folder_parts[:auto_idx + 1])
|
||||||
# Fallback: use the immediate parent folder
|
# Fallback: use the immediate parent folder
|
||||||
return "/".join(parts[:-1]) if len(parts) > 1 else ""
|
return "/".join(folder_parts) if folder_parts else ""
|
||||||
|
|
||||||
# --- Group files by session key ---
|
# --- Group files by session key ---
|
||||||
groups: dict[str, list[tuple[str, bytes]]] = defaultdict(list)
|
groups: dict[str, list[tuple[str, bytes]]] = defaultdict(list)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ All routers should import `templates` from this module to get consistent
|
|||||||
filter and global function registration.
|
filter and global function registration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json as _json
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
# Import timezone utilities
|
# Import timezone utilities
|
||||||
@@ -32,8 +33,38 @@ def jinja_timezone_abbr():
|
|||||||
# Create templates instance
|
# Create templates instance
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
|
def jinja_local_date(dt, fmt="%m-%d-%y"):
|
||||||
|
"""Jinja filter: format a UTC datetime as a local date string (e.g. 02-19-26)."""
|
||||||
|
return format_local_datetime(dt, fmt)
|
||||||
|
|
||||||
|
|
||||||
|
def jinja_fromjson(s):
|
||||||
|
"""Jinja filter: parse a JSON string into a dict (returns {} on failure)."""
|
||||||
|
if not s:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
return _json.loads(s)
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def jinja_same_date(dt1, dt2) -> bool:
|
||||||
|
"""Jinja global: True if two datetimes fall on the same local date."""
|
||||||
|
if not dt1 or not dt2:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
d1 = format_local_datetime(dt1, "%Y-%m-%d")
|
||||||
|
d2 = format_local_datetime(dt2, "%Y-%m-%d")
|
||||||
|
return d1 == d2
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Register Jinja filters and globals
|
# Register Jinja filters and globals
|
||||||
templates.env.filters["local_datetime"] = jinja_local_datetime
|
templates.env.filters["local_datetime"] = jinja_local_datetime
|
||||||
templates.env.filters["local_time"] = jinja_local_time
|
templates.env.filters["local_time"] = jinja_local_time
|
||||||
|
templates.env.filters["local_date"] = jinja_local_date
|
||||||
|
templates.env.filters["fromjson"] = jinja_fromjson
|
||||||
templates.env.globals["timezone_abbr"] = jinja_timezone_abbr
|
templates.env.globals["timezone_abbr"] = jinja_timezone_abbr
|
||||||
templates.env.globals["get_user_timezone"] = get_user_timezone
|
templates.env.globals["get_user_timezone"] = get_user_timezone
|
||||||
|
templates.env.globals["same_date"] = jinja_same_date
|
||||||
|
|||||||
@@ -23,12 +23,26 @@
|
|||||||
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"></path>
|
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<div>
|
<div>
|
||||||
|
{% set meta = session.session_metadata|fromjson if session.session_metadata else {} %}
|
||||||
|
{% set is_manual = meta.get('source') in ('manual_upload', 'bulk_upload') %}
|
||||||
<div class="font-semibold text-gray-900 dark:text-white">
|
<div class="font-semibold text-gray-900 dark:text-white">
|
||||||
{{ session.started_at|local_datetime if session.started_at else 'Unknown Date' }}
|
{% if location %}{{ location.name }}{% else %}Unknown Location{% endif %}
|
||||||
|
{% if session.started_at %}
|
||||||
|
—
|
||||||
|
{% if session.stopped_at and not same_date(session.started_at, session.stopped_at) %}
|
||||||
|
{{ session.started_at|local_date }} to {{ session.stopped_at|local_date }}
|
||||||
|
{% else %}
|
||||||
|
{{ session.started_at|local_date }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
{% if unit %}{{ unit.id }}{% else %}Unknown Unit{% endif %}
|
{% if is_manual %}
|
||||||
{% if location %} @ {{ location.name }}{% endif %}
|
{% set store = meta.get('store_name') %}
|
||||||
|
Manual upload{% if store %} — Store {{ store }}{% endif %}
|
||||||
|
{% elif unit %}
|
||||||
|
{{ unit.id }}
|
||||||
|
{% endif %}
|
||||||
<span class="mx-2">•</span>
|
<span class="mx-2">•</span>
|
||||||
{{ files|length }} file{{ 's' if files|length != 1 else '' }}
|
{{ files|length }} file{{ 's' if files|length != 1 else '' }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -235,7 +235,7 @@
|
|||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Upload All
|
Upload Days
|
||||||
</button>
|
</button>
|
||||||
<button onclick="htmx.trigger('#unified-files', 'refresh')"
|
<button onclick="htmx.trigger('#unified-files', 'refresh')"
|
||||||
class="px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
|
class="px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
|
||||||
@@ -248,7 +248,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Upload All Panel -->
|
<!-- Upload Days Panel -->
|
||||||
<div id="upload-all-panel" class="hidden border-b border-gray-200 dark:border-gray-700">
|
<div id="upload-all-panel" class="hidden border-b border-gray-200 dark:border-gray-700">
|
||||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-800/50">
|
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-800/50">
|
||||||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Bulk Import — Select Folder</p>
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Bulk Import — Select Folder</p>
|
||||||
@@ -1575,7 +1575,7 @@ document.getElementById('schedule-modal')?.addEventListener('click', function(e)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Upload All ───────────────────────────────────────────────────────────────
|
// ── Upload Days ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function toggleUploadAll() {
|
function toggleUploadAll() {
|
||||||
const panel = document.getElementById('upload-all-panel');
|
const panel = document.getElementById('upload-all-panel');
|
||||||
|
|||||||
Reference in New Issue
Block a user