Repository: NVIDIA/NVFlare

Issue:
Make task name configurable in AnalysisController
https://github.com/NVIDIA/NVFlare/blob/c0dc52fd301d572787eac13a81febb1b2217bb2e/examples/federated_analysis/configs/fed_analysis/custom/analysis_controller.py#L32


Discussion:

Resolving patch:
diff --git a/examples/federated_analysis/configs/fed_analysis/custom/analysis_constants.py b/examples/federated_analysis/configs/fed_analysis/custom/analysis_constants.py
new file mode 100644
--- /dev/null
+++ b/examples/federated_analysis/configs/fed_analysis/custom/analysis_constants.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2022, NVIDIA CORPORATION.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class SupportedTasks(object):
+    HISTOGRAM = "histogram"
diff --git a/examples/federated_analysis/configs/fed_analysis/custom/analysis_controller.py b/examples/federated_analysis/configs/fed_analysis/custom/analysis_controller.py
--- a/examples/federated_analysis/configs/fed_analysis/custom/analysis_controller.py
+++ b/examples/federated_analysis/configs/fed_analysis/custom/analysis_controller.py
@@ -29,16 +29,18 @@
 
 
 class AnalysisController(Controller):
-    def __init__(self, min_clients: int = 1):
+    def __init__(self, min_clients: int = 1, task_name_histogram: str = SupportedTasks.HISTOGRAM):
         """Controller for federated analysis.
 
         Args:
             min_clients: how many statistics to gather before computing the global statisitcs.
+            task_name_histogram: task name for histogram computation.
         """
         super().__init__()
         self.histograms = dict()
         self._min_clients = min_clients
         self.run_dir = None
+        self.task_name_histogram = task_name_histogram
 
     def start_controller(self, fl_ctx: FLContext):
         self.run_dir = os.path.join(fl_ctx.get_prop(FLContextKey.APP_ROOT), "..")
@@ -60,7 +62,7 @@ def control_flow(self, abort_signal: Signal, fl_ctx: FLContext):
         self.log_info(fl_ctx, "Analysis control flow started.")
         if abort_signal.triggered:
             return
-        task = Task(name=SupportedTasks.HISTOGRAM, data=Shareable(), result_received_cb=self._process_result_histogram)
+        task = Task(name=self.task_name_histogram, data=Shareable(), result_received_cb=self._process_result_histogram)
         self.broadcast_and_wait(
             task=task,
             min_responses=self._min_clients,
diff --git a/examples/federated_analysis/configs/fed_analysis/custom/analysis_executor.py b/examples/federated_analysis/configs/fed_analysis/custom/analysis_executor.py
--- a/examples/federated_analysis/configs/fed_analysis/custom/analysis_executor.py
+++ b/examples/federated_analysis/configs/fed_analysis/custom/analysis_executor.py
@@ -16,6 +16,7 @@
 import os
 
 import numpy as np
+from analysis_constants import SupportedTasks
 from monai.data import ITKReader, load_decathlon_datalist
 from monai.transforms import LoadImage
 
@@ -27,10 +28,6 @@
 from nvflare.apis.signal import Signal
 
 
-class SupportedTasks(object):
-    HISTOGRAM = "histogram"
-
-
 class AnalysisExecutor(Executor):
     def __init__(
         self,
@@ -40,6 +37,7 @@ def __init__(
         n_bins: int = 256,
         range_min: float = 0.0,
         range_max: float = 255.0,  # assumes BMP/PNG/JPEGSs as input
+        supported_task: str = SupportedTasks.HISTOGRAM,
     ):
         """Executor for federated analysis.
 
@@ -49,8 +47,8 @@ def __init__(
             min_images: minimum number of images needed to compute histogram (if less images are included in the computation, no statistics will be sent to the server).
             n_bins: number of bins for the histogram.
             range_min: minimum intensity value to include in the histogram.
-            range_max: maximum intensity value to include in the histogram
-
+            range_max: maximum intensity value to include in the histogram.
+            supported_task: task name for histogram computation.
         Returns:
             a Shareable with the computed statistics after `execute()`
         """
@@ -63,6 +61,8 @@ def __init__(
         self.range_min = range_min
         self.range_max = range_max
 
+        self.supported_task = supported_task
+
         self.data_list = None
 
         self._min_images = min_images
@@ -93,7 +93,7 @@ def execute(self, task_name: str, shareable: Shareable, fl_ctx: FLContext, abort
                 self.log_error(fl_ctx, f"Reading data list for client {client_name} failed!")
                 return make_reply(ReturnCode.ERROR)
 
-            if task_name == SupportedTasks.HISTOGRAM:
+            if task_name == self.supported_task:
                 result_dict = self._compute_histo(fl_ctx, abort_signal)
                 if abort_signal.triggered:
                     return make_reply(ReturnCode.TASK_ABORTED)


Test patch: