We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Yaml loader should be replaced by safe_loader or other more secure loader
To load yaml files from unknown source, we should avoid using yaml's loader.  A better way is to use either safe_loader or other mechanism.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nvflare/ha/overseer/utils.py`
Content:
```
1 # Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import os
16 import uuid
17 from datetime import datetime, timedelta
18 
19 import yaml
20 
21 OVERSEER_STORE = os.environ.get("OVERSEER_STORE")
22 
23 if OVERSEER_STORE == "REDIS":
24     from .redis_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp
25 elif OVERSEER_STORE == "SQL":
26     from .sql_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp
27 elif OVERSEER_STORE == "MEM":
28     from .mem_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp
29 else:
30     print("Using default STORE (MEM)")
31     from .mem_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp  # noqa
32 
33 
34 def load_privilege():
35     privilege_file = os.environ.get("AUTHZ_FILE", "privilege.yml")
36     try:
37         privilege = yaml.load(open(privilege_file, "tr"), Loader=yaml.Loader)
38     except:
39         privilege = dict()
40     return privilege
41 
42 
43 def update_sp_state(project, now, heartbeat_timeout=10):
44     valid_starting = now - timedelta(seconds=heartbeat_timeout)
45     # mark all late SP as offline and not primary
46     # print(f"{now=} {valid_starting=}")
47     for sp in get_all_sp(project):
48         if datetime.fromisoformat(sp["last_heartbeat"]) < valid_starting:
49             sp["state"] = "offline"
50             sp["primary"] = False
51         else:
52             sp["state"] = "online"
53         update_sp(sp)
54 
55 
56 def simple_PSP_policy(incoming_sp, now):
57     """Find the primary SP (PSP).
58 
59     If there is no PSP or current PSP timeout, choose one without heartbeat timeout.
60     """
61     project = incoming_sp["project"]
62     sp = get_sp_by(dict(project=project, sp_end_point=incoming_sp["sp_end_point"]))
63     if sp:
64         sp["last_heartbeat"] = now.isoformat()
65         update_sp(sp)
66     else:
67         update_sp(
68             dict(
69                 project=incoming_sp["project"],
70                 sp_end_point=incoming_sp["sp_end_point"],
71                 last_heartbeat=now.isoformat(),
72                 state="online",
73                 primary=False,
74             )
75         )
76 
77     psp = get_primary_sp(project)
78     if not psp:
79         psp = get_sp_by(dict(project=project, state="online"))
80         if psp:
81             print(f"{psp['sp_end_point']} online")
82             psp["primary"] = True
83             psp["service_session_id"] = str(uuid.uuid4())
84             update_sp(psp)
85 
86     return psp
87 
88 
89 def promote_sp(sp):
90     psp = get_sp_by(sp)
91     project = sp["project"]
92     sp_end_point = sp["sp_end_point"]
93     if psp and psp["state"] == "online":
94         current_psp = get_primary_sp(project)
95         if all(current_psp[k] == v for k, v in sp.items()):
96             return True, f"Same sp_end_point, no need to promote {sp_end_point}."
97         psp["primary"] = True
98         current_psp["primary"] = False
99         psp["service_session_id"] = str(uuid.uuid4())
100         print(f"{psp['sp_end_point']} promoted")
101         print(f"{current_psp['sp_end_point']} demoted")
102         update_sp(psp)
103         update_sp(current_psp)
104         return False, psp
105     else:
106         return True, f"Unable to promote {sp_end_point}, either offline or not registered."
107
```

Path: `nvflare/lighter/study.py`
Content:
```
1 # Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from __future__ import absolute_import
16 
17 import argparse
18 import json
19 import os
20 from datetime import datetime
21 
22 import yaml
23 
24 
25 def get_input(prompt, item_list, multiple=False):
26     while True:
27         answer = input(prompt)
28         result = None
29         if multiple:
30             try:
31                 if answer == "":
32                     print("None of the choices is selected.")
33                     result = []
34                 else:
35                     trimmed = set(answer.split(","))
36                     result = [item_list[int(i)] for i in trimmed]
37                     print(f"{result} selected after duplicate inputs removed.")
38             except BaseException:
39                 print("Input contains errors (non-integer or out of index range)")
40         else:
41             try:
42                 result = item_list[int(answer)]
43             except ValueError:
44                 print(f"Expect integer but got {answer.__class__.__name__}")
45             except IndexError:
46                 print("Number out of index range")
47         if result is not None:
48             break
49     return result
50 
51 
52 def get_date_input(prompt):
53     while True:
54         answer = input(prompt)
55         try:
56             result = datetime.strptime(answer, "%m/%d/%Y").date().isoformat()
57             break
58         except:
59             print(f"Expect MM/DD/YYYY but got {answer}")
60     return result
61 
62 
63 def main():
64     parser = argparse.ArgumentParser()
65     parser.add_argument("-p", "--project_file", type=str, default="project.yml", help="file to describe FL project")
66 
67     args = parser.parse_args()
68 
69     current_path = os.getcwd()
70 
71     # main project file
72     project_file = args.project_file
73     project_full_path = os.path.join(current_path, project_file)
74     if not os.path.exists(project_full_path):
75         print(f"{project_full_path} not found.  Running study requires that file.")
76         exit(0)
77 
78     project = yaml.load(open(project_full_path, "r"), Loader=yaml.Loader)
79     api_version = project.get("api_version")
80     if api_version not in [3]:
81         raise ValueError(f"API version expected 3 but found {api_version}")
82 
83     admin_list = list()
84     client_list = list()
85     for p in project.get("participants"):
86         if p.get("type") == "admin":
87             admin_list.append(p.get("name"))
88         elif p.get("type") == "client":
89             client_list.append(p.get("name"))
90 
91     admin_list_string = ", ".join([f"{i}:{v}" for i, v in enumerate(admin_list)])
92     client_list_string = ", ".join([f"{i}:{v}" for i, v in enumerate(client_list)])
93 
94     name = input("Please enter the name of this study: ")
95     description = input("and brief description: ")
96     contact = get_input(f"select one admin for contact {admin_list_string}: ", admin_list)
97 
98     participating_admins = get_input(
99         f"select participating_admins admins (separated by ',') {admin_list_string} ", admin_list, multiple=True
100     )
101     participating_clients = get_input(
102         f"select participating clients (separated by ',') {client_list_string} ", client_list, multiple=True
103     )
104     participating_clients_string = ", ".join([f"{i}:{v}" for i, v in enumerate(participating_clients)])
105     # reviewer_dict = dict()
106     # for admin in participating_admins:
107     #     reviewed_clients = get_input(
108     #         f"what clients will reviewer {admin} review {participating_clients_string} ",
109     #         participating_clients,
110     #         multiple=True,
111     #     )
112     #     reviewer_dict[admin] = reviewed_clients
113     start_date = get_date_input("input start date of this study (MM/DD/YYYY): ")
114     end_date = get_date_input("input end date of this study (MM/DD/YYYY): ")
115 
116     study_config = dict(
117         name=name,
118         description=description,
119         contact=contact,
120         participating_admins=participating_admins,
121         participating_clients=participating_clients,
122         # reviewers=reviewer_dict,
123         start_date=start_date,
124         end_date=end_date,
125     )
126     with open(f"{name}.json", "wt") as f:
127         f.write(json.dumps(study_config, indent=2))
128     print(f"study config file was generated at {name}.json")
129 
130 
131 if __name__ == "__main__":
132     main()
133
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/nvflare/ha/overseer/utils.py b/nvflare/ha/overseer/utils.py
--- a/nvflare/ha/overseer/utils.py
+++ b/nvflare/ha/overseer/utils.py
@@ -16,7 +16,7 @@
 import uuid
 from datetime import datetime, timedelta
 
-import yaml
+from nvflare.lighter.utils import load_yaml
 
 OVERSEER_STORE = os.environ.get("OVERSEER_STORE")
 
@@ -34,7 +34,7 @@
 def load_privilege():
     privilege_file = os.environ.get("AUTHZ_FILE", "privilege.yml")
     try:
-        privilege = yaml.load(open(privilege_file, "tr"), Loader=yaml.Loader)
+        privilege = load_yaml(privilege_file)
     except:
         privilege = dict()
     return privilege
diff --git a/nvflare/lighter/study.py b/nvflare/lighter/study.py
--- a/nvflare/lighter/study.py
+++ b/nvflare/lighter/study.py
@@ -19,7 +19,7 @@
 import os
 from datetime import datetime
 
-import yaml
+from nvflare.lighter.utils import load_yaml
 
 
 def get_input(prompt, item_list, multiple=False):
@@ -75,7 +75,7 @@
         print(f"{project_full_path} not found.  Running study requires that file.")
         exit(0)
 
-    project = yaml.load(open(project_full_path, "r"), Loader=yaml.Loader)
+    project = load_yaml(project_full_path)
     api_version = project.get("api_version")
     if api_version not in [3]:
         raise ValueError(f"API version expected 3 but found {api_version}")

{"golden_diff": "diff --git a/nvflare/ha/overseer/utils.py b/nvflare/ha/overseer/utils.py\n--- a/nvflare/ha/overseer/utils.py\n+++ b/nvflare/ha/overseer/utils.py\n@@ -16,7 +16,7 @@\n import uuid\n from datetime import datetime, timedelta\n \n-import yaml\n+from nvflare.lighter.utils import load_yaml\n \n OVERSEER_STORE = os.environ.get(\"OVERSEER_STORE\")\n \n@@ -34,7 +34,7 @@\n def load_privilege():\n     privilege_file = os.environ.get(\"AUTHZ_FILE\", \"privilege.yml\")\n     try:\n-        privilege = yaml.load(open(privilege_file, \"tr\"), Loader=yaml.Loader)\n+        privilege = load_yaml(privilege_file)\n     except:\n         privilege = dict()\n     return privilege\ndiff --git a/nvflare/lighter/study.py b/nvflare/lighter/study.py\n--- a/nvflare/lighter/study.py\n+++ b/nvflare/lighter/study.py\n@@ -19,7 +19,7 @@\n import os\n from datetime import datetime\n \n-import yaml\n+from nvflare.lighter.utils import load_yaml\n \n \n def get_input(prompt, item_list, multiple=False):\n@@ -75,7 +75,7 @@\n         print(f\"{project_full_path} not found.  Running study requires that file.\")\n         exit(0)\n \n-    project = yaml.load(open(project_full_path, \"r\"), Loader=yaml.Loader)\n+    project = load_yaml(project_full_path)\n     api_version = project.get(\"api_version\")\n     if api_version not in [3]:\n         raise ValueError(f\"API version expected 3 but found {api_version}\")\n", "issue": "Yaml loader should be replaced by safe_loader or other more secure loader\nTo load yaml files from unknown source, we should avoid using yaml's loader.  A better way is to use either safe_loader or other mechanism.\n", "before_files": [{"content": "# Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport uuid\nfrom datetime import datetime, timedelta\n\nimport yaml\n\nOVERSEER_STORE = os.environ.get(\"OVERSEER_STORE\")\n\nif OVERSEER_STORE == \"REDIS\":\n    from .redis_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp\nelif OVERSEER_STORE == \"SQL\":\n    from .sql_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp\nelif OVERSEER_STORE == \"MEM\":\n    from .mem_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp\nelse:\n    print(\"Using default STORE (MEM)\")\n    from .mem_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp  # noqa\n\n\ndef load_privilege():\n    privilege_file = os.environ.get(\"AUTHZ_FILE\", \"privilege.yml\")\n    try:\n        privilege = yaml.load(open(privilege_file, \"tr\"), Loader=yaml.Loader)\n    except:\n        privilege = dict()\n    return privilege\n\n\ndef update_sp_state(project, now, heartbeat_timeout=10):\n    valid_starting = now - timedelta(seconds=heartbeat_timeout)\n    # mark all late SP as offline and not primary\n    # print(f\"{now=} {valid_starting=}\")\n    for sp in get_all_sp(project):\n        if datetime.fromisoformat(sp[\"last_heartbeat\"]) < valid_starting:\n            sp[\"state\"] = \"offline\"\n            sp[\"primary\"] = False\n        else:\n            sp[\"state\"] = \"online\"\n        update_sp(sp)\n\n\ndef simple_PSP_policy(incoming_sp, now):\n    \"\"\"Find the primary SP (PSP).\n\n    If there is no PSP or current PSP timeout, choose one without heartbeat timeout.\n    \"\"\"\n    project = incoming_sp[\"project\"]\n    sp = get_sp_by(dict(project=project, sp_end_point=incoming_sp[\"sp_end_point\"]))\n    if sp:\n        sp[\"last_heartbeat\"] = now.isoformat()\n        update_sp(sp)\n    else:\n        update_sp(\n            dict(\n                project=incoming_sp[\"project\"],\n                sp_end_point=incoming_sp[\"sp_end_point\"],\n                last_heartbeat=now.isoformat(),\n                state=\"online\",\n                primary=False,\n            )\n        )\n\n    psp = get_primary_sp(project)\n    if not psp:\n        psp = get_sp_by(dict(project=project, state=\"online\"))\n        if psp:\n            print(f\"{psp['sp_end_point']} online\")\n            psp[\"primary\"] = True\n            psp[\"service_session_id\"] = str(uuid.uuid4())\n            update_sp(psp)\n\n    return psp\n\n\ndef promote_sp(sp):\n    psp = get_sp_by(sp)\n    project = sp[\"project\"]\n    sp_end_point = sp[\"sp_end_point\"]\n    if psp and psp[\"state\"] == \"online\":\n        current_psp = get_primary_sp(project)\n        if all(current_psp[k] == v for k, v in sp.items()):\n            return True, f\"Same sp_end_point, no need to promote {sp_end_point}.\"\n        psp[\"primary\"] = True\n        current_psp[\"primary\"] = False\n        psp[\"service_session_id\"] = str(uuid.uuid4())\n        print(f\"{psp['sp_end_point']} promoted\")\n        print(f\"{current_psp['sp_end_point']} demoted\")\n        update_sp(psp)\n        update_sp(current_psp)\n        return False, psp\n    else:\n        return True, f\"Unable to promote {sp_end_point}, either offline or not registered.\"\n", "path": "nvflare/ha/overseer/utils.py"}, {"content": "# Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\n\nimport argparse\nimport json\nimport os\nfrom datetime import datetime\n\nimport yaml\n\n\ndef get_input(prompt, item_list, multiple=False):\n    while True:\n        answer = input(prompt)\n        result = None\n        if multiple:\n            try:\n                if answer == \"\":\n                    print(\"None of the choices is selected.\")\n                    result = []\n                else:\n                    trimmed = set(answer.split(\",\"))\n                    result = [item_list[int(i)] for i in trimmed]\n                    print(f\"{result} selected after duplicate inputs removed.\")\n            except BaseException:\n                print(\"Input contains errors (non-integer or out of index range)\")\n        else:\n            try:\n                result = item_list[int(answer)]\n            except ValueError:\n                print(f\"Expect integer but got {answer.__class__.__name__}\")\n            except IndexError:\n                print(\"Number out of index range\")\n        if result is not None:\n            break\n    return result\n\n\ndef get_date_input(prompt):\n    while True:\n        answer = input(prompt)\n        try:\n            result = datetime.strptime(answer, \"%m/%d/%Y\").date().isoformat()\n            break\n        except:\n            print(f\"Expect MM/DD/YYYY but got {answer}\")\n    return result\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"-p\", \"--project_file\", type=str, default=\"project.yml\", help=\"file to describe FL project\")\n\n    args = parser.parse_args()\n\n    current_path = os.getcwd()\n\n    # main project file\n    project_file = args.project_file\n    project_full_path = os.path.join(current_path, project_file)\n    if not os.path.exists(project_full_path):\n        print(f\"{project_full_path} not found.  Running study requires that file.\")\n        exit(0)\n\n    project = yaml.load(open(project_full_path, \"r\"), Loader=yaml.Loader)\n    api_version = project.get(\"api_version\")\n    if api_version not in [3]:\n        raise ValueError(f\"API version expected 3 but found {api_version}\")\n\n    admin_list = list()\n    client_list = list()\n    for p in project.get(\"participants\"):\n        if p.get(\"type\") == \"admin\":\n            admin_list.append(p.get(\"name\"))\n        elif p.get(\"type\") == \"client\":\n            client_list.append(p.get(\"name\"))\n\n    admin_list_string = \", \".join([f\"{i}:{v}\" for i, v in enumerate(admin_list)])\n    client_list_string = \", \".join([f\"{i}:{v}\" for i, v in enumerate(client_list)])\n\n    name = input(\"Please enter the name of this study: \")\n    description = input(\"and brief description: \")\n    contact = get_input(f\"select one admin for contact {admin_list_string}: \", admin_list)\n\n    participating_admins = get_input(\n        f\"select participating_admins admins (separated by ',') {admin_list_string} \", admin_list, multiple=True\n    )\n    participating_clients = get_input(\n        f\"select participating clients (separated by ',') {client_list_string} \", client_list, multiple=True\n    )\n    participating_clients_string = \", \".join([f\"{i}:{v}\" for i, v in enumerate(participating_clients)])\n    # reviewer_dict = dict()\n    # for admin in participating_admins:\n    #     reviewed_clients = get_input(\n    #         f\"what clients will reviewer {admin} review {participating_clients_string} \",\n    #         participating_clients,\n    #         multiple=True,\n    #     )\n    #     reviewer_dict[admin] = reviewed_clients\n    start_date = get_date_input(\"input start date of this study (MM/DD/YYYY): \")\n    end_date = get_date_input(\"input end date of this study (MM/DD/YYYY): \")\n\n    study_config = dict(\n        name=name,\n        description=description,\n        contact=contact,\n        participating_admins=participating_admins,\n        participating_clients=participating_clients,\n        # reviewers=reviewer_dict,\n        start_date=start_date,\n        end_date=end_date,\n    )\n    with open(f\"{name}.json\", \"wt\") as f:\n        f.write(json.dumps(study_config, indent=2))\n    print(f\"study config file was generated at {name}.json\")\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "nvflare/lighter/study.py"}], "after_files": [{"content": "# Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport uuid\nfrom datetime import datetime, timedelta\n\nfrom nvflare.lighter.utils import load_yaml\n\nOVERSEER_STORE = os.environ.get(\"OVERSEER_STORE\")\n\nif OVERSEER_STORE == \"REDIS\":\n    from .redis_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp\nelif OVERSEER_STORE == \"SQL\":\n    from .sql_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp\nelif OVERSEER_STORE == \"MEM\":\n    from .mem_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp\nelse:\n    print(\"Using default STORE (MEM)\")\n    from .mem_store import do_refresh, get_all_sp, get_primary_sp, get_sp_by, update_sp  # noqa\n\n\ndef load_privilege():\n    privilege_file = os.environ.get(\"AUTHZ_FILE\", \"privilege.yml\")\n    try:\n        privilege = load_yaml(privilege_file)\n    except:\n        privilege = dict()\n    return privilege\n\n\ndef update_sp_state(project, now, heartbeat_timeout=10):\n    valid_starting = now - timedelta(seconds=heartbeat_timeout)\n    # mark all late SP as offline and not primary\n    # print(f\"{now=} {valid_starting=}\")\n    for sp in get_all_sp(project):\n        if datetime.fromisoformat(sp[\"last_heartbeat\"]) < valid_starting:\n            sp[\"state\"] = \"offline\"\n            sp[\"primary\"] = False\n        else:\n            sp[\"state\"] = \"online\"\n        update_sp(sp)\n\n\ndef simple_PSP_policy(incoming_sp, now):\n    \"\"\"Find the primary SP (PSP).\n\n    If there is no PSP or current PSP timeout, choose one without heartbeat timeout.\n    \"\"\"\n    project = incoming_sp[\"project\"]\n    sp = get_sp_by(dict(project=project, sp_end_point=incoming_sp[\"sp_end_point\"]))\n    if sp:\n        sp[\"last_heartbeat\"] = now.isoformat()\n        update_sp(sp)\n    else:\n        update_sp(\n            dict(\n                project=incoming_sp[\"project\"],\n                sp_end_point=incoming_sp[\"sp_end_point\"],\n                last_heartbeat=now.isoformat(),\n                state=\"online\",\n                primary=False,\n            )\n        )\n\n    psp = get_primary_sp(project)\n    if not psp:\n        psp = get_sp_by(dict(project=project, state=\"online\"))\n        if psp:\n            print(f\"{psp['sp_end_point']} online\")\n            psp[\"primary\"] = True\n            psp[\"service_session_id\"] = str(uuid.uuid4())\n            update_sp(psp)\n\n    return psp\n\n\ndef promote_sp(sp):\n    psp = get_sp_by(sp)\n    project = sp[\"project\"]\n    sp_end_point = sp[\"sp_end_point\"]\n    if psp and psp[\"state\"] == \"online\":\n        current_psp = get_primary_sp(project)\n        if all(current_psp[k] == v for k, v in sp.items()):\n            return True, f\"Same sp_end_point, no need to promote {sp_end_point}.\"\n        psp[\"primary\"] = True\n        current_psp[\"primary\"] = False\n        psp[\"service_session_id\"] = str(uuid.uuid4())\n        print(f\"{psp['sp_end_point']} promoted\")\n        print(f\"{current_psp['sp_end_point']} demoted\")\n        update_sp(psp)\n        update_sp(current_psp)\n        return False, psp\n    else:\n        return True, f\"Unable to promote {sp_end_point}, either offline or not registered.\"\n", "path": "nvflare/ha/overseer/utils.py"}, {"content": "# Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\n\nimport argparse\nimport json\nimport os\nfrom datetime import datetime\n\nfrom nvflare.lighter.utils import load_yaml\n\n\ndef get_input(prompt, item_list, multiple=False):\n    while True:\n        answer = input(prompt)\n        result = None\n        if multiple:\n            try:\n                if answer == \"\":\n                    print(\"None of the choices is selected.\")\n                    result = []\n                else:\n                    trimmed = set(answer.split(\",\"))\n                    result = [item_list[int(i)] for i in trimmed]\n                    print(f\"{result} selected after duplicate inputs removed.\")\n            except BaseException:\n                print(\"Input contains errors (non-integer or out of index range)\")\n        else:\n            try:\n                result = item_list[int(answer)]\n            except ValueError:\n                print(f\"Expect integer but got {answer.__class__.__name__}\")\n            except IndexError:\n                print(\"Number out of index range\")\n        if result is not None:\n            break\n    return result\n\n\ndef get_date_input(prompt):\n    while True:\n        answer = input(prompt)\n        try:\n            result = datetime.strptime(answer, \"%m/%d/%Y\").date().isoformat()\n            break\n        except:\n            print(f\"Expect MM/DD/YYYY but got {answer}\")\n    return result\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"-p\", \"--project_file\", type=str, default=\"project.yml\", help=\"file to describe FL project\")\n\n    args = parser.parse_args()\n\n    current_path = os.getcwd()\n\n    # main project file\n    project_file = args.project_file\n    project_full_path = os.path.join(current_path, project_file)\n    if not os.path.exists(project_full_path):\n        print(f\"{project_full_path} not found.  Running study requires that file.\")\n        exit(0)\n\n    project = load_yaml(project_full_path)\n    api_version = project.get(\"api_version\")\n    if api_version not in [3]:\n        raise ValueError(f\"API version expected 3 but found {api_version}\")\n\n    admin_list = list()\n    client_list = list()\n    for p in project.get(\"participants\"):\n        if p.get(\"type\") == \"admin\":\n            admin_list.append(p.get(\"name\"))\n        elif p.get(\"type\") == \"client\":\n            client_list.append(p.get(\"name\"))\n\n    admin_list_string = \", \".join([f\"{i}:{v}\" for i, v in enumerate(admin_list)])\n    client_list_string = \", \".join([f\"{i}:{v}\" for i, v in enumerate(client_list)])\n\n    name = input(\"Please enter the name of this study: \")\n    description = input(\"and brief description: \")\n    contact = get_input(f\"select one admin for contact {admin_list_string}: \", admin_list)\n\n    participating_admins = get_input(\n        f\"select participating_admins admins (separated by ',') {admin_list_string} \", admin_list, multiple=True\n    )\n    participating_clients = get_input(\n        f\"select participating clients (separated by ',') {client_list_string} \", client_list, multiple=True\n    )\n    participating_clients_string = \", \".join([f\"{i}:{v}\" for i, v in enumerate(participating_clients)])\n    # reviewer_dict = dict()\n    # for admin in participating_admins:\n    #     reviewed_clients = get_input(\n    #         f\"what clients will reviewer {admin} review {participating_clients_string} \",\n    #         participating_clients,\n    #         multiple=True,\n    #     )\n    #     reviewer_dict[admin] = reviewed_clients\n    start_date = get_date_input(\"input start date of this study (MM/DD/YYYY): \")\n    end_date = get_date_input(\"input end date of this study (MM/DD/YYYY): \")\n\n    study_config = dict(\n        name=name,\n        description=description,\n        contact=contact,\n        participating_admins=participating_admins,\n        participating_clients=participating_clients,\n        # reviewers=reviewer_dict,\n        start_date=start_date,\n        end_date=end_date,\n    )\n    with open(f\"{name}.json\", \"wt\") as f:\n        f.write(json.dumps(study_config, indent=2))\n    print(f\"study config file was generated at {name}.json\")\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "nvflare/lighter/study.py"}]}