Skip to content

Conversation

@fm3
Copy link
Member

@fm3 fm3 commented Nov 11, 2025

Description

  • Unified display of ids, allowing single-click copy.
  • Added link to workflow report to job list (superusers only for now).
  • Speed up loading of job list.
  • Fixed loading indicators for job and workflow lists. already done in Add loading state spinner for job and workflow list views #9095
  • Refactored APIJob type in frontend and backend to make them simpler and make them match

Steps to test:

  • yarn enable jobs
  • start a few jobs
  • check that workflow list view looks as expected
  • check that id rendering in job list view, annotation list view, etc is pretty.

TODOs:

  • Fix Loading indicator
  • also on workflow list view (loading indicator in topright is there, but table still says No Data)
  • Workflow link for superusers if available
  • Fix rendering of infer instances job
  • Speed up loading (old code ca 5s on scm orga)
    • complex query in list view
    • adapt types
    • get single job json should be superset of compact list json
    • test

Issues:


  • Added changelog entry (create a $PR_NUMBER.md file in unreleased_changes or use ./tools/create-changelog-entry.py)
  • Removed dev-only changes like prints and application.conf edits
  • Considered common edge cases

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 11, 2025

📝 Walkthrough

Walkthrough

Refactors job data flow: introduces a compact job shape (JobCompactInfo), extracts result-link logic into JobResultLinks, renames job commandArgs→args, changes timestamps to Instant, exposes findAllCompact for list retrieval, updates controller/service serialization, and migrates frontend types and usages from APIJobType to APIJobCommand.

Changes

Cohort / File(s) Summary
Backend: Job model & links
app/models/job/Job.scala, app/models/job/JobResultLinks.scala
Added JobResultLinks trait; introduced JobCompactInfo; renamed commandArgsargs; changed started/ended to Option[Instant]; added id accessor and made effectiveState accessible; added JSON format for JobCompactInfo.
Backend: DAO & persistence
app/models/job/JobDAO.scala (within Job.scala)
Added findAllCompact returning Seq[JobCompactInfo]; updated insertOne to persist args; adjusted date mapping to use Instant.fromSql; refactored list access helpers.
Backend: Controller & service
app/controllers/JobController.scala, app/models/job/JobService.scala
Controller.list now uses jobDAO.findAllCompact and calls .enrich per item; JobService.publicWrites/submitPaidJob now produce JsValue using JobCompactInfo and sanitize args/tokens; creditCost derivation adjusted.
Frontend: types & API shape
frontend/javascripts/types/api_types.ts
Introduced APIJobCommand enum, ApiJobArgs union, redefined APIJob (command, args, ownerFirstName/LastName/Email, created, returnValue, creditCost) and made APIJobState non-nullable.
Frontend: job list & hooks
frontend/javascripts/admin/api/jobs.ts, frontend/javascripts/admin/job/job_list_view.tsx, frontend/javascripts/admin/job/job_hooks.ts
transform and mapping now use job.args.* and job.command; removed previous sort by createdAt in favor of created; replaced APIJobType → APIJobCommand and adjusted rendering/columns (Cost, Date, Voxelytics).
Frontend: various UI updates (command/args migration)
multiple files under admin/, dashboard/, viewer/ (e.g., frontend/javascripts/dashboard/*, frontend/javascripts/viewer/view/*)
Replaced APIJobType usages with APIJobCommand; replaced top-level job fields with job.args.* accessors across dashboard, modals, action-bar, tabs, and download/export logic.
Frontend: ID display component & usage
frontend/javascripts/components/formatted_id.tsx, frontend/javascripts/admin/scripts/script_list_view.tsx, frontend/javascripts/admin/task/task_list_view.tsx, frontend/javascripts/admin/tasktype/task_type_list_view.tsx, frontend/javascripts/dashboard/explorative_annotations_view.tsx, frontend/javascripts/dashboard/folders/details_sidebar.tsx
Added FormattedId component for compact ID display + single-click copy; replaced previous copy UI and monospace ID rendering with FormattedId in multiple list/details views.
Frontend: AI job modals & forms
frontend/javascripts/viewer/view/action-bar/ai_job_modals/*, frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/*
Updated imports/props/types to use APIJobCommand instead of APIJobType; adjusted function/type signatures where StartJobForm/jobName typed.
Frontend: utilities & styles
frontend/javascripts/libs/format_utils.ts, frontend/stylesheets/main.less
Removed formatHash utility; removed .monospace-id CSS rule.
Misc / logging & docs
util/src/main/scala/com/scalableminds/util/requestlogging/RequestLogging.scala, unreleased_changes/9068.md
Log message formatting adjusted to wrap bodies in backticks; release notes added documenting UI/UX and performance changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • app/models/job/JobResultLinks.scala — verify all command→link branches and argument extraction.
    • app/models/job/Job.scala & DAO — ensure compact projection, enrich, and Instant mapping match frontend expectations.
    • app/controllers/JobController.scala & JobService.scala — confirm JSON shape produced matches new APIJob/JobCompactInfo schema and token-sanitization.
    • Frontend type changes in types/api_types.ts — ensure ApiJobArgs covers all consumed fields.
    • Widespread APIJobTypeAPIJobCommand replacements — check for any missed imports/uses.

Possibly related PRs

Suggested labels

jobs

Suggested reviewers

  • philippotto
  • MichaelBuessemeyer

Poem

🐰 I hopped through code both near and far,
Trimmed args, renamed each job command star.
Compact and tidy, links now find their way,
IDs copy quick — hip-hop hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Improve job list view, unify ID rendering' accurately summarizes the main changes: unifying ID rendering across the UI and improving the job list view performance and loading indicators.
Linked Issues check ✅ Passed The PR successfully addresses the objectives from linked issue #9051: the changes implement proper loading indicators for job and workflow lists (fixing the loading spinner visibility), and the refactored job types and performance improvements (compact fetch via JobDAO.findAllCompact) enable faster loading for large organizations.
Out of Scope Changes check ✅ Passed All code changes align with the stated objectives. Backend changes implement compact job fetching and type refactoring. Frontend changes migrate from APIJobType to APIJobCommand, update job list rendering to use new args structure, introduce FormattedId component for unified ID display, and add workflow link rendering—all directly supporting the main goals.
Description check ✅ Passed The PR description clearly outlines the changeset: unified ID display with copy functionality, workflow report link, job list performance improvements, loading indicator fixes, and APIJob type refactoring. It directly relates to the provided summary.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improve-job-list-view

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.

@fm3 fm3 changed the title WIP: Improve job list view WIP: Improve job list view, unify ID rendering Nov 12, 2025

useEffect(() => {
fetchData();
initialize();
Copy link
Member Author

Choose a reason for hiding this comment

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

Extracted this so I could make it async (and actually await the fetchData). React wouldn’t let me do that inline in useEffect.

@fm3 fm3 changed the title WIP: Improve job list view, unify ID rendering Improve job list view, unify ID rendering Nov 25, 2025
@fm3 fm3 marked this pull request as ready for review November 25, 2025 07:59

def exportFileName: Option[String] = argAsStringOpt("export_file_name")

def datasetName: Option[String] = argAsStringOpt("dataset_name")
Copy link
Member Author

Choose a reason for hiding this comment

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

these lines were moved to a trait, see below

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.

Actionable comments posted: 2

🧹 Nitpick comments (10)
frontend/javascripts/admin/voxelytics/workflow_list_view.tsx (1)

191-197: Table empty state now reflects loading vs. “no workflows yet”; consider optional error handling

Using locale.emptyText with isLoading ? <Spin size="large" /> : "There are no voxelytics workflow reports yet." cleanly solves the “loading shows as no data” problem when renderRuns is empty and the initial fetch is in progress, aligning with the issue’s objective.

One optional improvement: on fetch errors (where Toast.error is shown and workflows remains empty), this will still render the “no workflow reports yet” message. If distinguishing “error” from “truly empty” is important, you could add an error state flag and branch emptyText accordingly.

frontend/javascripts/admin/job/job_hooks.ts (1)

34-52: Confirm created is numeric and consider using state updater for the latest-job logic

The switch from createdAt to created in both the sort and the “most recent successful job” check looks correct, assuming APIJob["created"] is still a numeric timestamp.

To avoid any future drift in the type (e.g., moving to ISO strings or Date objects) and to make the async updater more robust, you could:

  • Ensure created is typed as number in APIJob.
  • Update the state write to rely on the previous value instead of the captured mostRecentSuccessfulJob, e.g.:
setMostRecentSuccessfulJob((prev) =>
  prev == null || job.created > prev.created ? job : prev,
);

This keeps the “latest job” invariant correct even if multiple updates race.

frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx (1)

196-198: Make access to trainingAnnotations resilient to missing args

The move to model.trainingJob.args.trainingAnnotations matches the new command/args model, but it will throw if trainingJob.args is ever undefined (e.g., for older or partially‑migrated jobs).

You can keep behavior identical while hardening against that case:

const { voxelyticsWorkflowHash, state: trainingJobState, args } = model.trainingJob;
const trainingAnnotations = args?.trainingAnnotations;

The existing trainingAnnotations != null check then safely handles “no annotations” without risking a runtime error.

frontend/javascripts/components/formatted_id.tsx (1)

1-27: Harden clipboard handling and improve click semantics

The component does what we want functionally, but two small tweaks would make it more robust:

  1. Guard clipboard access / handle errors

On some environments navigator.clipboard may be unavailable or writeText may reject. Wrapping the call avoids hard crashes and lets you surface a better message if copy fails:

onClick={async () => {
  try {
    if (!navigator.clipboard?.writeText) {
      throw new Error("Clipboard API not available");
    }
    await navigator.clipboard.writeText(id);
    Toast.success("Copied ID to clipboard.");
  } catch (e) {
    Toast.error("Could not copy ID to clipboard.");
    // optionally: console.error(e);
  }
}}
  1. Use button semantics for accessibility

A clickable <div> is not focusable or keyboard‑accessible. Consider rendering a <button type="button"> (or adding role="button" and tabIndex={0} plus an onKeyDown handler for Enter/Space) so keyboard users can trigger the copy interaction as well.

These keep the UX but make the component safer and more accessible across the app.

frontend/javascripts/types/api_types.ts (1)

808-820: Consider making trainingAnnotations nullable for consistency.

All other fields in ApiJobArgs are marked as | null | undefined, but trainingAnnotations is typed as a non-nullable array. If jobs without training annotations don't include this field in the response, accessing it could cause runtime issues.

 export type ApiJobArgs = {
   readonly datasetId: string | null | undefined;
   readonly datasetName: string | null | undefined;
   readonly mergeSegments: boolean | null | undefined;
-  readonly trainingAnnotations: Array<{ annotationId: string }>;
+  readonly trainingAnnotations: Array<{ annotationId: string }> | null | undefined;
   readonly datasetDirectoryName: string | null | undefined;
   readonly annotationId: string | null | undefined;
   readonly annotationLayerName: string | null | undefined;
   readonly layerName: string | null | undefined;
   readonly modelId: string | null | undefined;
   readonly boundingBox: string | null | undefined;
   readonly ndBoundingBox: WkLibsNdBoundingBox | null | undefined;
 };
frontend/javascripts/viewer/view/right-border-tabs/segments_tab/segments_view.tsx (1)

681-684: Use the enum constant instead of a string literal for consistency.

Line 683 uses the string literal "compute_mesh_file" while lines 437 and 762 use APIJobCommand.COMPUTE_MESH_FILE. Use the enum constant for consistency and type safety.

     const meshJobsForDataset = jobs.filter(
-      (job) =>
-        job.command === "compute_mesh_file" && job.args.datasetName === this.props.datasetName,
+      (job) =>
+        job.command === APIJobCommand.COMPUTE_MESH_FILE && job.args.datasetName === this.props.datasetName,
     );
frontend/javascripts/admin/job/job_list_view.tsx (1)

415-416: Use the enum constant instead of a string literal for consistency.

This line uses the string literal "find_largest_segment_id" while other comparisons in this file use APIJobCommand.* constants (e.g., lines 378-381, 393, 404, 418-424, 437-438).

-    } else if (job.command === "find_largest_segment_id") {
+    } else if (job.command === APIJobCommand.FIND_LARGEST_SEGMENT_ID) {
       return <span>{job.returnValue}</span>;
frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx (1)

276-279: Use the enum constant instead of a string literal for consistency.

Line 277 uses the string literal "find_largest_segment_id" while line 518 uses APIJobCommand.FIND_LARGEST_SEGMENT_ID. Use the enum constant for consistency.

     initialJobKeyExtractor: (job) =>
-      job.command === "find_largest_segment_id" && job.args.datasetName === dataset?.name
+      job.command === APIJobCommand.FIND_LARGEST_SEGMENT_ID && job.args.datasetName === dataset?.name
         ? (job.args.datasetName ?? "largest_segment_id")
         : null,
frontend/javascripts/dashboard/dataset_view.tsx (1)

428-472: NewJobsAlert: command/created migration looks correct; consider avoiding string literal

The migration to job.command, job.created, and job.args.datasetName is consistent with the new APIJob shape, and FormattedDate will handle created fine as long as it’s a timestamp or ISO string. To keep things type‑safe and aligned with the rest of the PR, consider replacing the hard‑coded "convert_to_wkw" with the corresponding APIJobCommand constant so refactors of command names stay centralized.

-import type { APIDatasetCompact, APIJob, APIUser, FolderItem } from "types/api_types";
+import type { APIDatasetCompact, APIJob, APIUser, FolderItem } from "types/api_types";
+import { APIJobCommand } from "types/api_types";
@@
-    .filter(
-      (job) =>
-        job.command === "convert_to_wkw" &&
+    .filter(
+      (job) =>
+        job.command === APIJobCommand.CONVERT_TO_WKW &&
         dayjs.duration(now.diff(job.created)).asDays() <= RECENT_DATASET_DAY_THRESHOLD,
     )
app/models/job/JobResultLinks.scala (1)

35-38: Simplify redundant map operation.

The .map { resultAnnotationLink => resultAnnotationLink } is a no-op identity transformation.

         case JobCommand.infer_neurons if this.argAsBooleanOpt("do_evaluation").getOrElse(false) =>
-          returnValue.map { resultAnnotationLink =>
-            resultAnnotationLink
-          }
+          returnValue
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9e33ef and 2640071.

📒 Files selected for processing (40)
  • app/controllers/JobController.scala (1 hunks)
  • app/models/job/Job.scala (8 hunks)
  • app/models/job/JobResultLinks.scala (1 hunks)
  • app/models/job/JobService.scala (5 hunks)
  • frontend/javascripts/admin/api/jobs.ts (2 hunks)
  • frontend/javascripts/admin/dataset/dataset_upload_view.tsx (2 hunks)
  • frontend/javascripts/admin/job/job_hooks.ts (2 hunks)
  • frontend/javascripts/admin/job/job_list_view.tsx (12 hunks)
  • frontend/javascripts/admin/scripts/script_list_view.tsx (2 hunks)
  • frontend/javascripts/admin/task/task_list_view.tsx (2 hunks)
  • frontend/javascripts/admin/tasktype/task_type_list_view.tsx (2 hunks)
  • frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx (1 hunks)
  • frontend/javascripts/admin/voxelytics/workflow_list_view.tsx (2 hunks)
  • frontend/javascripts/components/formatted_id.tsx (1 hunks)
  • frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx (5 hunks)
  • frontend/javascripts/dashboard/dataset_view.tsx (2 hunks)
  • frontend/javascripts/dashboard/explorative_annotations_view.tsx (2 hunks)
  • frontend/javascripts/dashboard/folders/details_sidebar.tsx (2 hunks)
  • frontend/javascripts/libs/format_utils.ts (0 hunks)
  • frontend/javascripts/types/api_types.ts (3 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/constants.ts (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/align_sections_form.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/custom_ai_model_inference_form.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/mitochondria_segmentation_form.tsx (3 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx (3 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/nuclei_detection_form.tsx (3 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/start_job_form.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/materialize_volume_annotation_modal.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/tabs/run_ai_model_tab.tsx (5 hunks)
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/utils.tsx (3 hunks)
  • frontend/javascripts/viewer/view/action-bar/create_animation_modal.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/download_modal_view.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action-bar/tools/skeleton_specific_ui.tsx (2 hunks)
  • frontend/javascripts/viewer/view/action_bar_view.tsx (3 hunks)
  • frontend/javascripts/viewer/view/left-border-tabs/layer_settings_tab.tsx (2 hunks)
  • frontend/javascripts/viewer/view/right-border-tabs/bounding_box_tab.tsx (2 hunks)
  • frontend/javascripts/viewer/view/right-border-tabs/segments_tab/segments_view.tsx (4 hunks)
  • frontend/stylesheets/main.less (1 hunks)
  • unreleased_changes/9068.md (1 hunks)
  • util/src/main/scala/com/scalableminds/util/requestlogging/RequestLogging.scala (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/javascripts/libs/format_utils.ts
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-08-27T11:32:54.033Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8796
File: frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx:68-77
Timestamp: 2025-08-27T11:32:54.033Z
Learning: In the webknossos frontend AI job forms, `datasetConfiguration.layers[colorLayer.name]` is assumed to always be non-null when accessing layer properties like `isInverted`. The selected color layer is guaranteed to exist in the dataset configuration.

Applied to files:

  • frontend/javascripts/viewer/view/left-border-tabs/layer_settings_tab.tsx
  • frontend/javascripts/viewer/view/action_bar_view.tsx
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/nuclei_detection_form.tsx
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.

Applied to files:

  • frontend/javascripts/dashboard/explorative_annotations_view.tsx
  • frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx
  • frontend/javascripts/viewer/view/action-bar/create_animation_modal.tsx
  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/materialize_volume_annotation_modal.tsx
📚 Learning: 2025-01-13T09:06:15.202Z
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.

Applied to files:

  • app/models/job/JobService.scala
  • app/models/job/JobResultLinks.scala
  • app/controllers/JobController.scala
  • app/models/job/Job.scala
📚 Learning: 2025-11-03T17:50:57.587Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9025
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:10-10
Timestamp: 2025-11-03T17:50:57.587Z
Learning: In the webknossos codebase, both `import { Component } from "antd";` and `import { Component } from "antd/lib";` are valid import paths for antd components. Do not flag imports from "antd/lib" as errors.

Applied to files:

  • frontend/javascripts/admin/job/job_list_view.tsx
📚 Learning: 2024-11-22T17:18:04.217Z
Learnt from: dieknolle3333
Repo: scalableminds/webknossos PR: 8168
File: frontend/javascripts/oxalis/model/sagas/proofread_saga.ts:1039-1039
Timestamp: 2024-11-22T17:18:04.217Z
Learning: In `frontend/javascripts/oxalis/model/sagas/proofread_saga.ts`, when calling `getMagInfo`, the use of `volumeTracingLayer.resolutions` is intentional and should not be changed to `volumeTracingLayer.mags`.

Applied to files:

  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/materialize_volume_annotation_modal.tsx
📚 Learning: 2025-08-27T11:34:16.411Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8796
File: frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx:68-77
Timestamp: 2025-08-27T11:34:16.411Z
Learning: The webknossos codebase uses `getDefaultLayerViewConfiguration` to ensure that layer configurations always exist in `datasetConfiguration.layers`, making direct access like `datasetConfiguration.layers[colorLayer.name]` safe across multiple files including api_latest.ts and load_histogram_data_saga.ts.

Applied to files:

  • frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx
📚 Learning: 2025-08-04T11:49:30.012Z
Learnt from: frcroth
Repo: scalableminds/webknossos PR: 8821
File: app/models/dataset/Dataset.scala:864-866
Timestamp: 2025-08-04T11:49:30.012Z
Learning: In WebKnossos Scala codebase, when querying database tables with Slick, explicit column listing in SELECT statements is preferred over SELECT * to ensure columns are returned in the exact order expected by case class mappings. This prevents parsing failures when the physical column order in the production database doesn't match the schema definition order.

Applied to files:

  • app/models/job/Job.scala
🧬 Code graph analysis (16)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/constants.ts (1)
frontend/javascripts/viewer/constants.ts (1)
  • Vector3 (14-14)
frontend/javascripts/admin/scripts/script_list_view.tsx (1)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/mitochondria_segmentation_form.tsx (1)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/utils.tsx (1)
  • isDatasetOrBoundingBoxTooSmall (105-124)
frontend/javascripts/admin/api/jobs.ts (2)
frontend/javascripts/types/api_types.ts (1)
  • APIJob (822-836)
frontend/javascripts/admin/api/api_utils.ts (1)
  • assertResponseLimit (6-12)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/utils.tsx (1)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/constants.ts (1)
  • MIN_BBOX_EXTENT (25-29)
frontend/javascripts/dashboard/explorative_annotations_view.tsx (2)
frontend/javascripts/types/api_types.ts (1)
  • APIAnnotationInfo (510-531)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
frontend/javascripts/admin/task/task_list_view.tsx (1)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
frontend/javascripts/viewer/view/action_bar_view.tsx (1)
frontend/javascripts/viewer/model/actions/ui_actions.ts (1)
  • setAIJobModalStateAction (144-148)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/tabs/run_ai_model_tab.tsx (5)
frontend/javascripts/viewer/model/actions/ui_actions.ts (1)
  • setAIJobModalStateAction (144-148)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx (1)
  • NeuronSegmentationForm (25-155)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/nuclei_detection_form.tsx (1)
  • NucleiDetectionForm (14-79)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/mitochondria_segmentation_form.tsx (1)
  • MitochondriaSegmentationForm (14-73)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/align_sections_form.tsx (1)
  • AlignSectionsForm (11-54)
frontend/javascripts/dashboard/folders/details_sidebar.tsx (1)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
frontend/javascripts/admin/job/job_list_view.tsx (5)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
  • getReadableURLPart (747-751)
frontend/javascripts/types/api_types.ts (1)
  • APIJob (822-836)
frontend/javascripts/libs/format_utils.ts (2)
  • formatWkLibsNdBBox (540-553)
  • formatCreditsString (555-559)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
frontend/javascripts/components/formatted_date.tsx (1)
  • FormattedDate (19-42)
frontend/javascripts/components/formatted_id.tsx (1)
frontend/javascripts/components/fast_tooltip.tsx (1)
  • FastTooltip (54-123)
frontend/javascripts/dashboard/dataset_view.tsx (3)
util/src/main/scala/com/scalableminds/util/time/Instant.scala (1)
  • now (48-48)
frontend/javascripts/viewer/constants.ts (1)
  • Unicode (279-283)
frontend/javascripts/components/formatted_date.tsx (1)
  • FormattedDate (19-42)
app/models/job/JobResultLinks.scala (3)
app/models/job/JobCommand.scala (1)
  • JobCommand (5-22)
app/models/job/Job.scala (3)
  • id (38-38)
  • effectiveState (51-51)
  • effectiveState (85-85)
util/src/main/scala/com/scalableminds/util/objectid/ObjectId.scala (1)
  • fromStringSync (22-26)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx (1)
frontend/javascripts/viewer/view/action-bar/ai_job_modals/utils.tsx (1)
  • isDatasetOrBoundingBoxTooSmall (105-124)
app/controllers/JobController.scala (1)
app/models/job/Job.scala (2)
  • findAllCompact (164-217)
  • enrich (87-90)
🔇 Additional comments (52)
util/src/main/scala/com/scalableminds/util/requestlogging/RequestLogging.scala (1)

18-23: Log formatting change looks good; just confirm no rigid log parsing downstream

Wrapping the result body in backticks improves readability and makes it clearer where the payload starts/ends. Since this slightly changes the log string format, double‑check that no log parsers/alerts rely on the previous : <body> shape without backticks.

frontend/javascripts/admin/voxelytics/workflow_list_view.tsx (1)

4-4: Spin import is correctly wired to the new loading state UI

Importing Spin alongside the existing antd components is consistent with its usage in the table’s emptyText below; no issues here.

unreleased_changes/9068.md (1)

1-7: Changelog accurately reflects the functional changes

The entries clearly summarize the unified ID display, workflow report link, performance work, and loading-indicator fixes; nothing to adjust from a technical perspective.

frontend/javascripts/admin/tasktype/task_type_list_view.tsx (1)

17-18: Nice reuse of FormattedId for task type IDs

Switching the ID column to <FormattedId id={id} /> cleanly aligns this view with the unified ID UX while preserving sorting/filtering behavior on the full ID value. No further changes needed here.

Also applies to: 151-158

frontend/javascripts/dashboard/folders/details_sidebar.tsx (1)

11-11: Consistent ID UX in dataset details

Using <FormattedId id={fullDataset.id} /> inside the Tag brings the dataset details sidebar in line with the new unified ID behavior, including copy‑to‑clipboard, without affecting surrounding layout or logic.

Also applies to: 174-179

frontend/javascripts/dashboard/explorative_annotations_view.tsx (1)

24-25: ID column refactor to FormattedId looks good

The ID column now:

  • Uses <FormattedId id={annotation.id} /> for a consistent short-ID + copy UX.
  • Keeps sorting based on the full ID (sorter: Utils.localeCompareBy((annotation) => annotation.id)).
  • Still renders the “read-only” and “locked” status lines under the ID.

Imports (FormattedId, updated AntD components, stringToColor) line up with the usage. No issues spotted.

Also applies to: 29-34, 625-639

frontend/stylesheets/main.less (1)

712-716: Cleanup verified as safe—no remaining .monospace-id usages found

The verification confirms that .monospace-id has been completely removed with no lingering references in the frontend code. The style cleanup is safe and complete.

frontend/javascripts/admin/api/jobs.ts (2)

15-20: LGTM on the simplified transformation.

The spread pattern with _.mapKeys for camelCasing args is clean and idiomatic. The approach correctly delegates structure to the backend while normalizing argument keys for frontend consumption.


22-26: Verify: Sorting removal is intentional.

The previous implementation sorted jobs by createdAt before returning. This change removes that sort, so the order now depends on the backend. Confirm this is intentional and that the backend provides jobs in a consistent/expected order (e.g., newest first).

frontend/javascripts/admin/dataset/dataset_upload_view.tsx (1)

67-67: LGTM on APIJobType → APIJobCommand migration.

The import and usage changes are consistent with the project-wide migration to command-based job modeling. The enum value name remains unchanged, so behavior is preserved.

Also applies to: 688-688

frontend/javascripts/types/api_types.ts (3)

780-798: LGTM on APIJobCommand enum.

The enum provides clear identifiers for all job commands with appropriate deprecation markers for backwards compatibility. The snake_case values align with backend conventions.


822-836: LGTM on flattened APIJob type.

The new structure with explicit owner fields (ownerFirstName, ownerLastName, ownerEmail) and centralized args payload improves clarity. The created timestamp and returnValue naming align with backend conventions.


778-778: No breaking changes detected. The APIJobState type is already non-nullable, and frontend code consistently treats job.state as a required field.

All usages throughout the codebase—direct string comparisons in segments_view.tsx, array lookups in dataset_view.tsx and job_list_view.tsx, and state checks in job_hooks.ts—assume state is always defined. No defensive null checks or type assertions exist, indicating the code was never designed to handle nullable state.

frontend/javascripts/viewer/view/right-border-tabs/bounding_box_tab.tsx (1)

10-10: LGTM on APIJobType → APIJobCommand migration.

The import and usage changes correctly update to the new command-based enum while preserving the export TIFF capability check logic.

Also applies to: 121-123

frontend/javascripts/viewer/view/left-border-tabs/layer_settings_tab.tsx (1)

51-51: LGTM on APIJobType → APIJobCommand migration.

The import and usage changes correctly update to the new command-based enum for the segment index file computation capability check.

Also applies to: 710-713

frontend/javascripts/viewer/view/right-border-tabs/segments_tab/segments_view.tsx (3)

52-52: LGTM!

Import correctly updated to use the new APIJobCommand enum.


436-441: LGTM!

Correctly uses the APIJobCommand.COMPUTE_MESH_FILE enum constant for the supported jobs check.


760-763: LGTM!

Correctly uses the APIJobCommand.COMPUTE_MESH_FILE enum constant for the tooltip condition.

frontend/javascripts/admin/job/job_list_view.tsx (5)

142-151: LGTM!

Good refactoring to extract the async initialization logic. This correctly ensures fetchData completes before setting isLoading to false, which addresses the loading indicator issue mentioned in the PR objectives.


169-179: LGTM!

The fallback logic for generating dataset links correctly handles both the new datasetId-based approach and the legacy organizationId/datasetName approach, ensuring backward compatibility.


330-334: LGTM!

The workflow link rendering is clean and correctly returns null when no workflow hash is available.


499-548: LGTM!

The table changes correctly implement:

  • Loading spinner wrapper
  • Search filtering using job.args.datasetName
  • FormattedId for copyable IDs
  • Flattened owner fields
  • New Cost in Credits and Date columns
  • Conditional Voxelytics column for superusers

181-327: LGTM!

The renderDescription function correctly migrates to the command-based approach using job.command and job.args.* fields. The conditional rendering logic properly handles all job types.

frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx (3)

283-291: LGTM!

The job start callback correctly uses job.args.datasetName to extract the dataset name from the new API structure.


517-518: LGTM!

Correctly uses APIJobCommand.FIND_LARGEST_SEGMENT_ID enum constant.


551-554: LGTM!

Correctly updated to use mostRecentSuccessfulJob.returnValue matching the new API field name.

frontend/javascripts/admin/scripts/script_list_view.tsx (2)

5-5: LGTM!

Import added for the new FormattedId component.


140-147: LGTM!

The ID column now uses FormattedId for consistent ID rendering with single-click copy functionality, aligning with the PR objective of unified ID display across views.

frontend/javascripts/admin/task/task_list_view.tsx (2)

30-30: LGTM!

Import added for the new FormattedId component.


251-259: LGTM!

The ID column now uses FormattedId for consistent ID rendering with single-click copy functionality. The width increase to 120 appropriately accommodates the new format (copy icon + shortened ID).

frontend/javascripts/viewer/view/action-bar/create_animation_modal.tsx (1)

23-23: LGTM! Enum migration is consistent.

The migration from APIJobType to APIJobCommand is correctly applied in both the import and feature flag check.

Also applies to: 333-333

frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/mitochondria_segmentation_form.tsx (1)

8-8: LGTM! Consistent enum usage throughout.

All references to the mitochondria inference job type have been correctly updated to use APIJobCommand.INFER_MITOCHONDRIA.

Also applies to: 36-49

frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/custom_ai_model_inference_form.tsx (1)

14-14: LGTM! Enum migration applied correctly.

The import and StartJobForm prop have been consistently updated to use APIJobCommand.INFER_NEURONS.

Also applies to: 100-100

frontend/javascripts/viewer/view/action-bar/ai_job_modals/constants.ts (1)

1-7: LGTM! Type definitions correctly updated.

Both the ModalJobTypes union and MEAN_VX_SIZE record type have been properly migrated to use APIJobCommand instead of APIJobType.

Also applies to: 31-34

frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/start_job_form.tsx (1)

11-11: LGTM! Public API signature updated correctly.

The StartJobFormProps.jobName type has been properly updated to use APIJobCommand, maintaining consistency with the broader migration.

Also applies to: 42-42

frontend/javascripts/viewer/view/action-bar/download_modal_view.tsx (1)

41-41: LGTM! Export feature check updated correctly.

The TIFF export availability check now uses APIJobCommand.EXPORT_TIFF consistently with the migration pattern.

Also applies to: 594-594

frontend/javascripts/viewer/view/action-bar/ai_job_modals/materialize_volume_annotation_modal.tsx (1)

7-7: LGTM! Materialization job correctly updated.

The import and StartJobForm usage have been consistently updated to use APIJobCommand.MATERIALIZE_VOLUME_ANNOTATION.

Also applies to: 131-131

frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/neuron_segmentation_form.tsx (1)

10-10: LGTM! All neuron inference references updated.

The migration is complete across all three usage sites: getBestFittingMagComparedToTrainingDS, isDatasetOrBoundingBoxTooSmall, and the StartJobForm prop.

Also applies to: 63-65, 123-123

frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/align_sections_form.tsx (1)

7-30: AlignSectionsForm: command enum migration looks consistent

Using APIJobCommand.ALIGN_SECTIONS for jobName and importing APIJobCommand keeps this form aligned with the new command-based job model; no functional regressions visible here.

frontend/javascripts/viewer/view/action-bar/tools/skeleton_specific_ui.tsx (1)

19-64: Materialize-volume capability check correctly switched to commands

Switching the capability check to APIJobCommand.MATERIALIZE_VOLUME_ANNOTATION matches the new command-based backend while preserving the previous boolean logic; no issues spotted.

app/controllers/JobController.scala (1)

87-92: Job list now uses compact projection; verify ordering and field parity

Switching list to jobDAO.findAllCompact and returning jobsCompact.map(_.enrich) is a good fit for the new compact job representation and should help with performance. One thing to double‑check: that findAllCompact (plus enrich) preserves the expected default ordering and field set compared to the previous list implementation, especially if any UI relied on “newest first” or additional fields from publicWrites.

frontend/javascripts/viewer/view/action-bar/ai_job_modals/utils.tsx (1)

5-21: AI job utils: command-based typing and branching remain consistent

Updating getMinimumDSSize and getBestFittingMagComparedToTrainingDS to use APIJobCommand (including the INFER_MITOCHONDRIA special case) keeps the behavior identical while aligning with the new enum. The interactions with MIN_BBOX_EXTENT and ModalJobTypes still look coherent; no adjustments needed here.

Also applies to: 42-47

frontend/javascripts/viewer/view/action-bar/ai_job_modals/forms/nuclei_detection_form.tsx (1)

6-39: NucleiDetectionForm: migration to APIJobCommand is straightforward

Passing APIJobCommand.INFER_NUCLEI through to mag selection, bounding‑box validation, and jobName keeps this form consistent with the other AI job forms and the new command‑centric model; no issues found.

Also applies to: 52-57

frontend/javascripts/viewer/view/action-bar/ai_job_modals/tabs/run_ai_model_tab.tsx (1)

4-5: RunAiModelTab: command-based selection wiring looks solid

The radio button checks, dispatch payloads, and conditional rendering have all been updated to APIJobCommand.* in a consistent way, so the tab continues to map each selection to the correct form (neurons, mitochondria, nuclei, align sections). Assuming StartAIJobModalState has been updated to accept these command values, this wiring looks good.

Also applies to: 66-133

frontend/javascripts/viewer/view/action_bar_view.tsx (1)

13-14: Action bar AI entrypoint: capability check and default command updated correctly

Using APIJobCommand.INFER_NEURONS for the AI Analysis button and checking dataset.dataStore.jobsSupportedByAvailableWorkers against the four relevant APIJobCommand values preserves the previous gating behavior while aligning with the new command enum. The conditions still ensure the button only appears and is enabled when at least one supported AI job is available.

Also applies to: 301-335

app/models/job/JobResultLinks.scala (1)

56-61: LGTM!

The resultLinkPublic method correctly handles prefixing relative URLs with the public URL while preserving absolute URLs.

app/models/job/JobService.scala (2)

171-196: LGTM! Clean refactor to use JobCompactInfo.

The publicWrites method now properly constructs a JobCompactInfo with appropriate field mappings. Token sanitization is correctly applied to remove sensitive credentials from the args.


186-186: Token sanitization is properly consistent across both code paths.

Verification confirms that both sanitization approaches are correctly implemented:

  • JobService.publicWrites (line 186): Constructs JobCompactInfo from a full Job object and removes tokens
  • JobCompactInfo.enrich (Job.scala lines 87-90): Enriches an existing JobCompactInfo and removes the same tokens

These are not redundant paths—they serve different purposes:

  1. publicWrites transforms a full Job into a serializable JobCompactInfo
  2. enrich is called on JobCompactInfo objects from findAllCompact to add calculated fields

Both remove identical tokens ("webknossos_token" and "user_auth_token"), ensuring consistent sanitization across all code paths. No issues found.

app/models/job/Job.scala (4)

67-92: JobCompactInfo extends JobResultLinks but lacks required abstract members.

JobResultLinks declares def args: JsObject, def command: JobCommand, def returnValue: Option[String], and protected def id: ObjectId. While JobCompactInfo has fields for args, command, returnValue, and id, it's missing the protected def id: ObjectId override since id is a public val. This works in Scala because a val satisfies a def, but the visibility differs (public vs protected).

Additionally, datasetName and datasetId methods from JobResultLinks are public, exposing internal parsing logic. This is acceptable if intentional.


83-90: Potential issue: enrich calls constructResultLink which accesses args before sanitization.

In the enrich method, constructResultLink(organizationId) is called on this, which still contains the unsanitized args (with tokens). The constructResultLink logic reads from args to extract dataset_name, dataset_id, and do_evaluation. While these specific keys are not sensitive, be aware that args isn't sanitized until after constructResultLink runs. This appears correct since the sensitive keys aren't used in link construction.


164-215: Well-designed compact query for improved performance.

The findAllCompact method joins all required tables in a single query, avoiding N+1 queries. This aligns with the PR objective of improving job list loading performance. The use of COALESCE(j.manualState, j.state) correctly handles the effective state logic at the database level.

Minor observation: The tuple type annotation is verbose but necessary for Slick's type inference. Consider adding a brief comment explaining the column mapping for maintainability.


149-155: No SQL injection risk identified—prefix parameter is properly controlled across all callers.

The review concern has been verified. In Job.scala, listAccessQWithPrefix accepts a prefix parameter that's interpolated via the q"" SQL interpolator. All callers of this method pass safe, hardcoded prefixes:

  • Line 150: empty prefix (q"")
  • Line 166: table alias (q"j.")

The same safe pattern is used in User.scala where buildSelectionPredicates is called only with hardcoded prefix SqlToken.raw("u."). The prefix parameter is never exposed to user-controlled input.

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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
app/models/job/JobResultLinks.scala (1)

35-38: Simplify redundant identity map.

The map operation on returnValue is redundant since it simply returns the value unchanged.

         case JobCommand.infer_neurons if this.argAsBooleanOpt("do_evaluation").getOrElse(false) =>
-          returnValue.map { resultAnnotationLink =>
-            resultAnnotationLink
-          }
+          returnValue
app/models/job/Job.scala (1)

164-215: Consider named tuple extraction for maintainability.

The positional tuple access (row._1, row._2, etc.) across 14 columns is fragile and harder to maintain if column order changes. This aligns with the learning that explicit column listing is preferred, but the parsing side could also benefit from named extraction or a helper structure.

The current approach works but consider defining an intermediate case class or using Slick's built-in mapping to improve readability and reduce risk of column misalignment during future modifications.

Additionally, Fox.serialCombined processes rows sequentially. If parsing performance becomes a concern, Fox.combined could parallelize parsing:

// Alternative if parallelism is acceptable
parsed <- Fox.combined(rows.map { row =>
  for {
    command <- JobCommand.fromString(row._2).toFox
    effectiveState <- JobState.fromString(row._8).toFox
    commandArgs <- JsonHelper.parseAs[JsObject](row._7).toFox
  } yield JobCompactInfo(...)
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2640071 and ab68dc2.

📒 Files selected for processing (3)
  • app/models/job/Job.scala (8 hunks)
  • app/models/job/JobResultLinks.scala (1 hunks)
  • app/models/job/JobService.scala (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-01-13T09:06:15.202Z
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.

Applied to files:

  • app/models/job/JobResultLinks.scala
  • app/models/job/JobService.scala
  • app/models/job/Job.scala
📚 Learning: 2025-08-04T11:49:30.012Z
Learnt from: frcroth
Repo: scalableminds/webknossos PR: 8821
File: app/models/dataset/Dataset.scala:864-866
Timestamp: 2025-08-04T11:49:30.012Z
Learning: In WebKnossos Scala codebase, when querying database tables with Slick, explicit column listing in SELECT statements is preferred over SELECT * to ensure columns are returned in the exact order expected by case class mappings. This prevents parsing failures when the physical column order in the production database doesn't match the schema definition order.

Applied to files:

  • app/models/job/Job.scala
🧬 Code graph analysis (2)
app/models/job/JobResultLinks.scala (3)
app/models/job/JobCommand.scala (1)
  • JobCommand (5-22)
app/models/job/Job.scala (3)
  • id (38-38)
  • effectiveState (51-51)
  • effectiveState (85-85)
util/src/main/scala/com/scalableminds/util/objectid/ObjectId.scala (1)
  • fromStringSync (22-26)
app/models/job/Job.scala (6)
app/models/job/JobState.scala (1)
  • JobState (5-8)
util/src/main/scala/com/scalableminds/util/time/Instant.scala (5)
  • Instant (14-45)
  • Instant (47-103)
  • now (48-48)
  • fromSql (58-58)
  • fromString (75-78)
app/models/job/JobResultLinks.scala (1)
  • constructResultLink (24-56)
app/utils/sql/SimpleSQLDAO.scala (1)
  • run (28-48)
app/models/job/JobCommand.scala (1)
  • JobCommand (5-22)
util/src/main/scala/com/scalableminds/util/tools/JsonHelper.scala (1)
  • JsonHelper (16-112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (14)
app/models/job/JobResultLinks.scala (3)

1-7: LGTM on imports and package declaration.

Clean imports with appropriate dependencies for the trait's functionality.


8-23: LGTM on trait definition and helpers.

Well-structured trait with clear abstraction boundaries. The protected accessors and JSON extraction helpers provide clean encapsulation.


56-61: LGTM on resultLinkPublic.

Defensive handling of both relative and absolute paths is appropriate given the varying return formats from constructResultLink.

app/models/job/JobService.scala (5)

17-17: LGTM on import addition.

Adding JsValue to imports is necessary for the updated publicWrites return type.


161-161: LGTM on field rename alignment.

Correctly updated to use the renamed args field.


171-196: LGTM on publicWrites refactoring.

The method now correctly constructs JobCompactInfo with sanitized args and proper token removal. The resultLink is computed via the trait method, maintaining consistency with the compact path.


208-208: LGTM on parameterWrites update.

Correctly uses the renamed args field while appending the authentication token for worker processing.


226-226: LGTM on return type alignment.

Return type correctly updated to match the new publicWrites signature.

app/models/job/Job.scala (6)

9-9: LGTM on OFormat import.

Required for the implicit JSON format definition in JobCompactInfo companion object.


67-96: LGTM on JobCompactInfo structure.

Well-designed compact representation with the enrich method properly computing the result link and sanitizing sensitive tokens from args. The effective state is correctly pre-resolved via COALESCE in the query.


125-126: LGTM on Instant mapping.

Correctly uses Instant.fromSql to convert SQL timestamps to the new Instant type.


149-155: LGTM on listAccessQ refactoring.

The prefix parameterization enables query reuse across both findAll and findAllCompact while maintaining consistent access control logic.


296-296: LGTM on insertOne field alignment.

Correctly uses the renamed args field for database insertion.


20-65: LGTM on Job case class refactoring.

The field rename (commandArgsargs) and type changes (Option[Long]Option[Instant]) improve consistency. The JobResultLinks trait integration properly provides the protected accessors and enables link construction. Verified that the custom Instant class correctly defines def -(other: Instant): FiniteDuration, confirming the duration method returns the expected type.

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Thanks for the improvements. I only checked out the backend code. If I should look at the frontend as well, please let me know:

Didn't really find anything, only two minor things and a mini question :)

@fm3
Copy link
Member Author

fm3 commented Nov 26, 2025

@philippotto when reviewing the frontend, could you also have a quick look at #9095? I’m not super familiar with react or antd, so that one might have better solutions for some of the problems. If you think so, I’m happy to incorporate those here.

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.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab68dc2 and 8edfac8.

📒 Files selected for processing (6)
  • app/models/job/Job.scala (7 hunks)
  • app/models/job/JobService.scala (5 hunks)
  • frontend/javascripts/admin/job/job_list_view.tsx (12 hunks)
  • frontend/javascripts/admin/task/task_list_view.tsx (2 hunks)
  • frontend/javascripts/admin/tasktype/task_type_list_view.tsx (2 hunks)
  • frontend/javascripts/dashboard/explorative_annotations_view.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/javascripts/admin/task/task_list_view.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.

Applied to files:

  • frontend/javascripts/dashboard/explorative_annotations_view.tsx
📚 Learning: 2025-01-13T09:06:15.202Z
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.

Applied to files:

  • app/models/job/Job.scala
  • app/models/job/JobService.scala
📚 Learning: 2025-08-04T11:49:30.012Z
Learnt from: frcroth
Repo: scalableminds/webknossos PR: 8821
File: app/models/dataset/Dataset.scala:864-866
Timestamp: 2025-08-04T11:49:30.012Z
Learning: In WebKnossos Scala codebase, when querying database tables with Slick, explicit column listing in SELECT statements is preferred over SELECT * to ensure columns are returned in the exact order expected by case class mappings. This prevents parsing failures when the physical column order in the production database doesn't match the schema definition order.

Applied to files:

  • app/models/job/Job.scala
📚 Learning: 2025-11-03T17:50:57.587Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9025
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:10-10
Timestamp: 2025-11-03T17:50:57.587Z
Learning: In the webknossos codebase, both `import { Component } from "antd";` and `import { Component } from "antd/lib";` are valid import paths for antd components. Do not flag imports from "antd/lib" as errors.

Applied to files:

  • frontend/javascripts/admin/job/job_list_view.tsx
🧬 Code graph analysis (4)
frontend/javascripts/admin/tasktype/task_type_list_view.tsx (1)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
app/models/job/Job.scala (1)
app/models/job/JobCommand.scala (1)
  • JobCommand (5-22)
frontend/javascripts/admin/job/job_list_view.tsx (4)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
  • getReadableURLPart (747-751)
frontend/javascripts/types/api_types.ts (1)
  • APIJob (822-836)
frontend/javascripts/libs/format_utils.ts (2)
  • formatWkLibsNdBBox (540-553)
  • formatCreditsString (555-559)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
app/models/job/JobService.scala (2)
app/models/job/Job.scala (5)
  • JobCompactInfo (67-92)
  • JobCompactInfo (94-96)
  • id (38-38)
  • effectiveState (51-51)
  • effectiveState (85-85)
app/models/job/JobResultLinks.scala (1)
  • constructResultLink (24-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: backend-tests
  • GitHub Check: frontend-tests
  • GitHub Check: build-smoketest-push
🔇 Additional comments (12)
frontend/javascripts/admin/tasktype/task_type_list_view.tsx (2)

17-17: LGTM! Clean import of the new FormattedId component.

The import statement is correct and aligns with the project-wide refactoring to unify ID rendering across admin list views.


157-157: FormattedId component lacks keyboard accessibility.

The FormattedId component at frontend/javascripts/components/formatted_id.tsx uses a <div onClick={...}> without keyboard accessibility support. It's missing:

  • role="button" to identify the interactive element
  • tabIndex={0} to make it keyboard-focusable
  • onKeyDown handler to trigger copy on Enter/Space keys

This prevents keyboard users from copying IDs and violates WCAG Level A accessibility requirements. The fix should be applied to the FormattedId component to affect all usages across admin list views.

⛔ Skipped due to learnings
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8824
File: frontend/javascripts/viewer/view/context_menu.tsx:1033-1039
Timestamp: 2025-09-08T13:33:47.097Z
Learning: In frontend/javascripts/viewer/view/context_menu.tsx, the segmentIdLabel intentionally shows the unmapped segment ID (maybeUnmappedSegmentId) in the "within Segment ${id}" text when proofreading is active, as this can be helpful for users. The mapped segment ID (segmentIdAtPosition) is only used as a fallback when the unmapped ID is not available.
frontend/javascripts/dashboard/explorative_annotations_view.tsx (1)

621-635: Clean refactor to use the unified FormattedId component.

The ID column now uses the shared FormattedId component, which provides consistent copy-to-clipboard behavior and tooltip across the application. The width increase to 120 appropriately accommodates the component's layout.

frontend/javascripts/admin/job/job_list_view.tsx (4)

141-150: LGTM - clean async initialization pattern.

Extracting the async initialize function allows proper awaiting of fetchData before loading persisted state. Error handling is already present in fetchData via the getJobs call.


168-178: LGTM - dataset link construction handles both new and legacy formats.

The function correctly prefers the new datasetId-based path while falling back to legacy organization-based paths for older jobs.


329-333: LGTM - Voxelytics workflow link for superusers implemented correctly.

The new renderWorkflowLink helper and conditional column visibility align with the PR objective to show workflow links to superusers.

Also applies to: 543-545


498-542: LGTM - new columns and filter properly use the updated job data structure.

The filter, "Cost in Credits" column, and "Date" column correctly reference the new job.args and job.created fields. The formatCreditsString utility handles credit display formatting appropriately.

app/models/job/JobService.scala (2)

171-196: LGTM - publicWrites correctly constructs JobCompactInfo for single-job responses.

The method properly sanitizes sensitive tokens from args, constructs the result link, and handles credit cost negation (as noted in past reviews). The inline construction (rather than calling enrich) is appropriate here since publicWrites operates on a full Job object and directly computes all fields.


161-161: Field access updated to use renamed args field.

The changes from commandArgs to args are consistent with the field rename in the Job case class.

Also applies to: 208-208

app/models/job/Job.scala (3)

67-96: LGTM - JobCompactInfo provides a clean compact representation for list queries.

The case class properly extends JobResultLinks to access link construction logic, and the enrich method defers expensive computations and sanitizes sensitive tokens. The effectiveState accessor correctly returns state since the query already applies COALESCE(manualState, state).


154-205: LGTM - findAllCompact provides efficient bulk job retrieval.

The query joins necessary tables to avoid N+1 queries, uses LEFT JOIN for optional credit transactions, and the parsing correctly handles command/state validation. The credit cost sign convention is properly documented.


33-38: LGTM - consistent type updates for Instant and field rename to args.

The migration from Long to Instant for timestamps is properly applied throughout parsing and computation. The duration calculation correctly uses Instant subtraction which returns FiniteDuration.

Also applies to: 49-49, 125-127, 286-286

Copy link
Member

@philippotto philippotto left a comment

Choose a reason for hiding this comment

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

cool stuff! the type clean up is especially welcome. however, I suggest to merge this only after #8873 is merged, because there will be quite a few conflicts and your PR is smaller in comparison (and therefore easier to doublecheck again).

jobs
.map(transformBackendJobToAPIJob)
// Newest jobs should be first
.sort((a: APIJob, b: APIJob) => a.createdAt > b.createdAt)
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't need to happen anymore? I see that there is one other place where the jobs are sorted, but i didn't check whether that covers all usages.

Copy link
Member Author

Choose a reason for hiding this comment

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

The server should sort them by creation already. I’ll double-check that it works as expected.

creditCost: job.creditCost,
modelId: job.commandArgs.model_id,
...job,
args: _.mapKeys(job.args, (_value, key) => _.camelCase(key)),
Copy link
Member

Choose a reason for hiding this comment

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

for some backend types, we have CI checks that they match the TS types. I think it would be cool to have that for the job stuff, too. this would require some dummy jobs in the test db. do you think this is easy to do @fm3 ? see writeTypeCheckingFile in the e2e tests.

Copy link
Member Author

Choose a reason for hiding this comment

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

I’m afraid, since the backend does not actually validate the schema in the args field in the database (it’s just a json object there), that this test would not help us much. It would validate that the frontend types match what’s in the test database, but that doesn’t mean that it would match what the production code actually reads or writes. So I don’t think it’s worth adding the test at this point. What do you think?

Comment on lines +806 to +807
// To simplify the frontend code, this is just the union,
// listing all possible frontend-relevant arguments.
Copy link
Member

Choose a reason for hiding this comment

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

TS supports discriminated unions, so it would be possible to separate the properties depending on job command. that would be cleaner and more type safe, but if we are unlucky, it could make some usages more complicated.
but maybe you could add some comments that clarify which props belong to which jobs? then, refactoring it at a later point would be easier for the next reader.

Copy link
Member Author

Choose a reason for hiding this comment

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

Since they overlap heavily, mayte this flat union isn’t such a bad option. Everything has to be considered optional in this way, which may make for resilient rendering code. This also allows some older jobs to be rendered, even when their exact parameter set changes for new job instances.

@philippotto
Copy link
Member

@philippotto when reviewing the frontend, could you also have a quick look at #9095? I’m not super familiar with react or antd, so that one might have better solutions for some of the problems. If you think so, I’m happy to incorporate those here.

yes, #9095 makes use of react-query which is an elegant solution in my opinion. I'd use that 🙈 we could merge the other pr first, you undo your changes here and then pull (or pull directly and resolve potentially more conflicts).

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Backend looks good. Thanks for implementing the feedback so fast 🎉

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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/javascripts/admin/job/job_list_view.tsx (2)

135-158: Async initialization + loading state fix the missing spinner but lack error handling.

Extracting initialize and awaiting fetchData is the right way to make the initial <Spin> work reliably. To avoid a stuck spinner or silent failures on network errors, consider guarding with try/finally and surfacing an error toast:

-  async function initialize() {
-    await fetchData();
-    const { searchQuery } = persistence.load();
-    setSearchQuery(searchQuery || "");
-    setIsLoading(false);
-  }
+  async function initialize() {
+    try {
+      await fetchData();
+    } catch (e) {
+      console.error("Failed to load jobs", e);
+      Toast.error("Failed to load jobs. Please try again.");
+    } finally {
+      const { searchQuery } = persistence.load();
+      setSearchQuery(searchQuery || "");
+      setIsLoading(false);
+    }
+  }

180-326: Description rendering matches the new command/args schema; a couple of small robustness nits.

The switch from type + top-level fields to command + job.args.* looks thorough and consistent across all branches. Two minor edge cases worth tightening:

  1. EXPORT_TIFF with missing layerName:
    If annotationLayerName and layerName are both absent, the description renders layer undefined. A small fallback improves UX:
-    } else if (job.command === APIJobCommand.EXPORT_TIFF && linkToDataset != null) {
+    } else if (job.command === APIJobCommand.EXPORT_TIFF && linkToDataset != null) {
       const labelToAnnotationOrDataset =
         job.args.annotationId != null ? (
           <Link to={`/annotations/${job.args.annotationId}`}>
             annotation of dataset {job.args.datasetName}
           </Link>
         ) : (
           <Link to={linkToDataset}>dataset {job.args.datasetName}</Link>
         );
+      const effectiveLayerName = layerName ?? "unknown layer";
       return (
         <span>
-          Tiff export of layer {layerName} from {labelToAnnotationOrDataset} (Bounding Box{" "}
+          Tiff export of layer {effectiveLayerName} from {labelToAnnotationOrDataset} (Bounding Box{" "}
           {job.args.ndBoundingBox
             ? formatWkLibsNdBBox(job.args.ndBoundingBox)
             : job.args.boundingBox}
           )
         </span>
       );
  1. Training jobs with an empty trainingAnnotations array:
    numberOfTrainingAnnotations is computed defensively, but getShowTrainingDataLink(job.args.trainingAnnotations) will still try to access trainingAnnotations[0] when the array is empty, producing /annotations/undefined. You can harden the helper to treat [] like “no training data”:
 export const getShowTrainingDataLink = (
   trainingAnnotations: {
     annotationId: string;
   }[],
 ) => {
-  return trainingAnnotations == null ? null : trainingAnnotations.length > 1 ? (
+  if (trainingAnnotations == null || trainingAnnotations.length === 0) {
+    return null;
+  }
+  return trainingAnnotations.length > 1 ? (

Overall, the mapping from commands to human-readable descriptions looks solid.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8edfac8 and 64b4cbb.

📒 Files selected for processing (2)
  • app/models/job/Job.scala (7 hunks)
  • frontend/javascripts/admin/job/job_list_view.tsx (12 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
📚 Learning: 2025-01-13T09:06:15.202Z
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.

Applied to files:

  • frontend/javascripts/admin/job/job_list_view.tsx
  • app/models/job/Job.scala
📚 Learning: 2025-11-03T17:50:57.587Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9025
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:10-10
Timestamp: 2025-11-03T17:50:57.587Z
Learning: In the webknossos codebase, both `import { Component } from "antd";` and `import { Component } from "antd/lib";` are valid import paths for antd components. Do not flag imports from "antd/lib" as errors.

Applied to files:

  • frontend/javascripts/admin/job/job_list_view.tsx
📚 Learning: 2025-08-04T11:49:30.012Z
Learnt from: frcroth
Repo: scalableminds/webknossos PR: 8821
File: app/models/dataset/Dataset.scala:864-866
Timestamp: 2025-08-04T11:49:30.012Z
Learning: In WebKnossos Scala codebase, when querying database tables with Slick, explicit column listing in SELECT statements is preferred over SELECT * to ensure columns are returned in the exact order expected by case class mappings. This prevents parsing failures when the physical column order in the production database doesn't match the schema definition order.

Applied to files:

  • app/models/job/Job.scala
🧬 Code graph analysis (2)
frontend/javascripts/admin/job/job_list_view.tsx (4)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
  • getReadableURLPart (747-751)
frontend/javascripts/types/api_types.ts (1)
  • APIJob (822-836)
frontend/javascripts/libs/format_utils.ts (2)
  • formatWkLibsNdBBox (540-553)
  • formatCreditsString (555-559)
frontend/javascripts/components/formatted_id.tsx (1)
  • FormattedId (7-27)
app/models/job/Job.scala (5)
app/models/job/JobState.scala (1)
  • JobState (5-8)
app/models/job/JobResultLinks.scala (1)
  • constructResultLink (24-56)
app/utils/sql/SimpleSQLDAO.scala (1)
  • run (28-48)
app/models/job/JobCommand.scala (1)
  • JobCommand (5-22)
util/src/main/scala/com/scalableminds/util/tools/JsonHelper.scala (1)
  • JsonHelper (16-112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: frontend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (11)
app/models/job/Job.scala (6)

33-34: LGTM: Type safety improvement.

The change from Option[Long] to Option[Instant] for timestamp fields improves type safety and makes the intent clearer. This aligns well with the Instant utility class used throughout the codebase.


67-96: LGTM: Well-designed compact data structure with security consideration.

JobCompactInfo is well-structured for efficient job listing. The enrich method correctly removes sensitive tokens (webknossos_token and user_auth_token) from args before exposure, which is an important security measure.


125-126: LGTM: Consistent timestamp parsing.

The use of Instant.fromSql aligns correctly with the type change from Option[Long] to Option[Instant] for the started and ended fields.


154-205: LGTM: Efficient compact job listing with proper access control.

The findAllCompact method uses a single JOIN query to fetch enriched job data, which is more efficient than N+1 queries. The inline comment on line 202 clearly documents the credit cost sign convention. The 14-tuple result type is complex but necessary given the comprehensive data requirements.

The access control using listAccessQ with the q"j." prefix is correctly implemented, and the parsing logic properly handles type conversions and validation.


277-290: LGTM: Field rename consistency.

The change from j.commandArgs to j.args on line 286 correctly aligns with the field rename in the Job case class.


45-49: The Instant subtraction operator is correctly implemented and the code is valid.

The Instant class explicitly defines a - operator at line 32 of util/src/main/scala/com/scalableminds/util/time/Instant.scala:

def -(other: Instant): FiniteDuration = (epochMillis - other.epochMillis) milliseconds

This operator correctly calculates the duration between two Instant values. The since method (line 59) is a separate utility that calculates duration from a past instant to the present (now), not a replacement for the operator. The Job code correctly applies the - operator to subtract two Instant values.

frontend/javascripts/admin/job/job_list_view.tsx (5)

18-31: New imports for FormattedId and APIJobCommand are consistent with the refactor.

Using FormattedId for IDs and APIJobCommand for command checks aligns this view with the new API surface and shared ID-rendering behavior.


168-177: Dataset link construction with job.args looks correct and backward compatible.

Preferring datasetId + getReadableURLPart and falling back to organizationId + datasetDirectoryName/datasetName keeps old jobs working while generating nice URLs for new ones. No issues spotted here.


329-333: Voxelytics workflow link and column gating look correct.

renderWorkflowLink only renders a link when voxelyticsWorkflowHash is present, and the column itself is shown only to superusers. That matches the stated requirement and avoids leaking workflow links to regular users.

Also applies to: 543-545


375-455: Action rendering now consistently uses APIJobCommand; behavior aligns with descriptions.

The conditional branches for actions (view/download/result text) mirror the command-based descriptions and use job.resultLink/job.returnValue appropriately. The previous issue with a raw "find_largest_segment_id" string is gone; everything is on APIJobCommand now. No functional problems spotted.


498-542: Search, ID rendering, owner, cost, and date columns are aligned with the new API surface.

  • Filtering via Utils.filterWithSearchQueryAND on job.args.datasetName matches the args-based shape.
  • Job Id column now uses FormattedId, which satisfies the “single-click copy” requirement.
  • Owner column uses ownerLastName for sorting and renders last name, first name, and email clearly.
  • Cost in Credits correctly formats non-null creditCost via formatCreditsString and falls back to "-".
  • Date column uses FormattedDate on job.created with descending default sort; assuming created is epoch-millis, this should behave as expected.

This whole table configuration looks consistent and well-wired.

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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
frontend/javascripts/admin/job/job_list_view.tsx (3)

135-141: React-query integration and loading spinner behavior look correct

Using useQuery with data: jobs, isLoading and wrapping the Table in <Spin spinning={isLoading}> should reliably show a loading indicator during the initial fetch, addressing the previous “No Data” vs. loading issue. Optional: if you ever want to show a spinner during background refetches as well, you could consider isFetching instead of (or in addition to) isLoading.

Also applies to: 486-493


30-30: Command-based description/action rendering is consistent with the new API shape

The switch to APIJobCommand plus job.args.* throughout getLinkToDataset, renderDescription, and renderActions is cohesive and matches the backend’s command/args model. Link generation via job.args.datasetId (with a legacy fallback using organizationId + dataset name/directory) and the use of ndBoundingBox vs. boundingBox look correct.

One small UX nit: in the actions block, the "View Segmentation" tooltip is reused for several commands (including COMPUTE_MESH_FILE and MATERIALIZE_VOLUME_ANNOTATION) where a more generic label like “View Result” or per-command wording might be clearer.

Also applies to: 158-167, 170-317, 366-368, 380-391, 403-411, 424-425, 443-443


19-19: Unified Job Id formatting and owner display look good

Using <FormattedId id={id} /> for the Job Id column is a nice way to centralize ID rendering (copyability, truncation, etc.). The Owner column’s sort by ownerLastName and rendering of LastName, FirstName with the email beneath is clear and should be easy to scan, assuming these fields are always present from the API.

Also applies to: 506-506, 513-519

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64b4cbb and 6b08d83.

📒 Files selected for processing (1)
  • frontend/javascripts/admin/job/job_list_view.tsx (11 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
📚 Learning: 2025-01-13T09:06:15.202Z
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.

Applied to files:

  • frontend/javascripts/admin/job/job_list_view.tsx
📚 Learning: 2025-11-03T17:50:57.587Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9025
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:10-10
Timestamp: 2025-11-03T17:50:57.587Z
Learning: In the webknossos codebase, both `import { Component } from "antd";` and `import { Component } from "antd/lib";` are valid import paths for antd components. Do not flag imports from "antd/lib" as errors.

Applied to files:

  • frontend/javascripts/admin/job/job_list_view.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (2)
frontend/javascripts/admin/job/job_list_view.tsx (2)

319-323: Workflow link for superusers is straightforward and scoped correctly

renderWorkflowLink and the conditional “Voxelytics” column for superusers cleanly surface the /workflows/${job.voxelyticsWorkflowHash} link without affecting regular users. This matches the PR objective of exposing workflow reports only to superusers.

Also applies to: 533-535


488-492: Dataset-name search now aligns with args-based jobs

Feeding dataSource through Utils.filterWithSearchQueryAND using job.args.datasetName matches the new argument-centric job shape and keeps the search focused on dataset names. As long as non-dataset jobs are not expected to be searchable here, this looks good.

@fm3 fm3 requested a review from philippotto November 26, 2025 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Job list view and voxelytics workflow list view should show “loading” instead of “no data” when appropriate

4 participants