Skip to content

UN-3320 [FIX] Show days in TTL time display instead of only hours#1847

Merged
chandrasekharan-zipstack merged 8 commits intomainfrom
fix/ttl-display-days
Mar 12, 2026
Merged

UN-3320 [FIX] Show days in TTL time display instead of only hours#1847
chandrasekharan-zipstack merged 8 commits intomainfrom
fix/ttl-display-days

Conversation

@vishnuszipstack
Copy link
Contributor

@vishnuszipstack vishnuszipstack commented Mar 11, 2026

What

  • Fix formatTimeDisplay helper to include days in TTL time display
  • Add batch_resolve_user_ids utility in utils/user_utils.py for resolving stringified user PKs to display names

Why

  • TTL remaining was showing large hour values like "2011h 45m" instead of "83d 19h 45m" in the HITL manual review header
  • The user ID resolution logic was duplicated in cloud manual_review_v2/views.py; extracted into a shared OSS utility for reuse

How

  • GetStaticData.js: Added days calculation (Math.floor(seconds / 86400)) and adjusted hours to use remainder
  • utils/user_utils.py: New generic utility that batch resolves stringified integer PKs to {'name', 'email'} dicts via a single DB query

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No. The TTL fix is display-only. The new user_utils.py is a new file with no existing callers in OSS — it's consumed by the cloud repo.

Database Migrations

  • None

Env Config

  • None

Relevant Docs

  • N/A

Related Issues or PRs

Dependencies Versions

  • None

Notes on Testing

  • Verify TTL display in HITL review header shows days (e.g. "83d 19h 45m") instead of only hours
  • Cloud PR depends on utils.user_utils.batch_resolve_user_ids — merge this first

Screenshots

N/A

Checklist

I have read and understood the Contribution Guidelines.

The formatTimeDisplay helper was computing hours from total seconds
without extracting days first, resulting in large hour values like
"2011h 45m" instead of "83d 19h 45m".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5b0e2a17-26f7-4dda-869b-67458c150cd4

📥 Commits

Reviewing files that changed from the base of the PR and between a37c4a8 and 821cdc9.

📒 Files selected for processing (1)
  • backend/utils/user_utils.py

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting


Summary by CodeRabbit

  • New Features

    • Time display now includes day-level granularity, showing durations in days, hours, minutes, and seconds format (e.g., "1d 2h 30m") for improved readability.
  • Improvements

    • Enhanced backend capability to efficiently batch resolve multiple user identifiers and retrieve associated user information including names and email addresses.

Walkthrough

Adds day-level granularity to frontend time formatting and introduces a backend utility to batch-resolve string user IDs to user name/email mappings.

Changes

Cohort / File(s) Summary
Time Formatting Enhancement
frontend/src/helpers/GetStaticData.js
Compute days from total time and use the remainder for hours; push a days segment (e.g., "1d") when days > 0 while keeping existing hours/minutes/seconds logic.
User ID Batch Resolver
backend/utils/user_utils.py
Add batch_resolve_user_ids(user_ids: Iterable[str]) -> dict[str, dict[str, str]]: normalize string IDs to int PKs, query User.objects.filter(pk__in=...), and return mapping from original string IDs to {'name', 'email'}; skips invalid/empty IDs and omits unresolved IDs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly summarizes the main change: fixing TTL display to show days instead of only hours.
Description check ✅ Passed The PR description comprehensively covers all template sections with relevant details: What, Why, How, impact analysis, and testing notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ttl-display-days
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use OpenGrep to find security vulnerabilities and bugs across 17+ programming languages.

OpenGrep is compatible with Semgrep configurations. Add an opengrep.yml or semgrep.yml configuration file to your project to enable OpenGrep analysis.

@vishnuszipstack vishnuszipstack changed the title [FIX] Show days in TTL time display instead of only hours UN-3320 [FIX] Show days in TTL time display instead of only hours Mar 11, 2026
Generic utility to batch resolve stringified user PKs to display names
and emails in a single query. Used by HITL manual review for resolving
reviewer/approver IDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
backend/utils/user_utils.py (1)

30-35: Consider using get_full_name() for more user-friendly display names.

The current approach derives the display name from the email prefix (user.email.split("@")[0]). However, the User model extends Django's AbstractUser, which provides first_name and last_name fields and a get_full_name() method specifically for this purpose. Seed data in the repository already populates these fields when creating users.

Using get_full_name() would produce more readable names (e.g., "John Doe" instead of "john.doe.123") when available.

♻️ Proposed refactor to use get_full_name()
     lookup = {}
     for user in User.objects.filter(pk__in=pk_set):
-        display_name = user.email.split("@")[0] if user.email else user.username
+        display_name = (
+            user.get_full_name()
+            or (user.email.split("@")[0] if user.email else None)
+            or user.username
+        )
         info = {"name": display_name, "email": user.email}
         lookup[str(user.pk)] = info
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/utils/user_utils.py` around lines 30 - 35, Replace the current
display name logic inside the loop that builds lookup (the for user in
User.objects.filter(...) block) to prefer Django's user.get_full_name() when
available; specifically, set display_name = user.get_full_name() if that returns
a non-empty string, otherwise fall back to user.email.split("@")[0] if
user.email exists, and finally to user.username — update only the assignment of
display_name so lookup[str(user.pk)] = info remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/utils/user_utils.py`:
- Around line 30-35: Replace the current display name logic inside the loop that
builds lookup (the for user in User.objects.filter(...) block) to prefer
Django's user.get_full_name() when available; specifically, set display_name =
user.get_full_name() if that returns a non-empty string, otherwise fall back to
user.email.split("@")[0] if user.email exists, and finally to user.username —
update only the assignment of display_name so lookup[str(user.pk)] = info
remains unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5123f356-5bd7-4c6c-8f30-e60a28968b6b

📥 Commits

Reviewing files that changed from the base of the PR and between 4d79c4f and cb1a697.

📒 Files selected for processing (1)
  • backend/utils/user_utils.py

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 12, 2026

Greptile Summary

This PR delivers two independent improvements: a display-only fix for the HITL TTL countdown (adds a days tier to formatTimeDisplay) and a new shared backend utility (batch_resolve_user_ids) that centralises the user-ID-to-display-name resolution logic for reuse by a companion cloud PR.

Key changes:

  • frontend/src/helpers/GetStaticData.js: formatTimeDisplay now correctly breaks seconds into days (/ 86400), hours-of-day (% 86400 / 3600), minutes, and seconds. The change is arithmetically correct and has no side-effects on the rest of the helper.
  • backend/utils/user_utils.py: New batch_resolve_user_ids utility issues a single User.objects.filter(pk__in=…) query, builds {"name": str, "email": str} entries, and omits unresolvable PKs with clear docstring guidance to use .get(). Previous review feedback on type annotations, email-coercion (None → ""), docstring clarity, and the unreliable early-return guard for generators have all been addressed in the current revision.
  • No database migrations, no changes to existing callers, and no breaking changes.

Confidence Score: 5/5

  • This PR is safe to merge — both changes are narrow, low-risk, and well-reasoned.
  • The frontend change is a straightforward arithmetic correction with no API or state impact. The backend utility is a brand-new file with no existing OSS callers; all previously raised review concerns (type annotations, email coercion, docstring, generator guard) have been resolved in the current revision. No migrations, no config changes, and no breaking changes.
  • No files require special attention.

Important Files Changed

Filename Overview
backend/utils/user_utils.py New utility for batch-resolving stringified user PKs to display info in a single DB query; previous review concerns (type annotations, docstring, email coercion, guard) have all been addressed. One minor issue: display_name could be None in theory if user.username is somehow None, but Django's model guarantees make this safe in practice.
frontend/src/helpers/GetStaticData.js Correct fix to formatTimeDisplay — adds days tier using 86400s and adjusts hours to use the per-day remainder; no logic errors detected.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["formatTimeDisplay(seconds)"] --> B{seconds <= 0?}
    B -- Yes --> C["return 'Expired'"]
    B -- No --> D["days = floor(seconds / 86400)"]
    D --> E["hours = floor((seconds % 86400) / 3600)"]
    E --> F["minutes = floor((seconds % 3600) / 60)"]
    F --> G["secs = floor(seconds % 60)"]
    G --> H{days > 0?}
    H -- Yes --> I["parts.push(days + 'd')"]
    H -- No --> J{hours > 0?}
    I --> J
    J -- Yes --> K["parts.push(hours + 'h')"]
    J -- No --> L{minutes > 0?}
    K --> L
    L -- Yes --> M["parts.push(minutes + 'm')"]
    L -- No --> N{secs > 0 or parts empty?}
    M --> N
    N -- Yes --> O["parts.push(secs + 's')"]
    N -- No --> P["return parts.join(' ')"]
    O --> P

    Q["batch_resolve_user_ids(user_ids)"] --> R["Iterate user_ids or []"]
    R --> S["Try int(uid) → add to pk_set"]
    S --> T{pk_set empty?}
    T -- Yes --> U["return {}"]
    T -- No --> V["User.objects.filter(pk__in=pk_set)"]
    V --> W["Build display_name:\nget_full_name()\nor email prefix\nor username"]
    W --> X["lookup[str(user.pk)] = {name, email}"]
    X --> Y["return lookup"]
Loading

Last reviewed commit: 821cdc9

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com>
Reverts the Iterable[str] type annotation that was added without
importing Iterable, which broke the pre-commit check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add type annotations (Iterable[str] param, dict return type)
- Clarify docstring that unresolved PKs are omitted
- Coerce user.email None to empty string
- Fix early-return guard for generator inputs
- Prefer get_full_name() for display names

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
backend/utils/user_utils.py (1)

32-37: Consider limiting fetched fields for query efficiency.

Since only pk, first_name, last_name, email, and username are used, you could use only() to avoid fetching unused columns.

♻️ Proposed optimization
-    for user in User.objects.filter(pk__in=pk_set):
+    for user in User.objects.filter(pk__in=pk_set).only(
+        "pk", "first_name", "last_name", "email", "username"
+    ):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/utils/user_utils.py` around lines 32 - 37, Looping over
User.objects.filter(pk__in=pk_set) fetches full rows; restrict columns by adding
.only('pk', 'first_name', 'last_name', 'email', 'username') to the query used in
this loop in backend/utils/user_utils.py so that the display name computation
(user.get_full_name(), user.email, user.username) only loads those fields and
avoids pulling unused columns.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/utils/user_utils.py`:
- Around line 32-37: Looping over User.objects.filter(pk__in=pk_set) fetches
full rows; restrict columns by adding .only('pk', 'first_name', 'last_name',
'email', 'username') to the query used in this loop in
backend/utils/user_utils.py so that the display name computation
(user.get_full_name(), user.email, user.username) only loads those fields and
avoids pulling unused columns.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dc55e846-8b31-489b-8f62-9f1da5d8bdd3

📥 Commits

Reviewing files that changed from the base of the PR and between a37c4a8 and 95001a3.

📒 Files selected for processing (1)
  • backend/utils/user_utils.py

@github-actions
Copy link
Contributor

Frontend Lint Report (Biome)

All checks passed! No linting or formatting issues found.

@github-actions
Copy link
Contributor

Test Results

Summary
  • Runner Tests: 11 passed, 0 failed (11 total)

Runner Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_logs}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_for\_sidecar}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_sidecar\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{11}}$$ $$\textcolor{#23d18b}{\tt{11}}$$

@sonarqubecloud
Copy link

@chandrasekharan-zipstack chandrasekharan-zipstack merged commit 8a7db7c into main Mar 12, 2026
9 checks passed
@chandrasekharan-zipstack chandrasekharan-zipstack deleted the fix/ttl-display-days branch March 12, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants