From b9952181011a8d5044e56712b3e3024da84f06aa Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 16 Oct 2023 15:15:39 +0200 Subject: [PATCH 01/35] started command --- dds_cli/__main__.py | 29 ++++++++++++++++++++ dds_cli/project_status.py | 58 +++++++++++++++++++++++++++++++++++++++ dds_cli/utils.py | 2 ++ 3 files changed, 89 insertions(+) diff --git a/dds_cli/__main__.py b/dds_cli/__main__.py index 86fa6fb7..82eed57f 100644 --- a/dds_cli/__main__.py +++ b/dds_cli/__main__.py @@ -1243,6 +1243,35 @@ def delete_project(click_ctx, project: str): sys.exit(1) +# -- dds project status extend -- # +@project_status.command(name="extend", no_args_is_help=True) +# Options +@project_option(required=True) +@click.pass_obj +def extend_deadline(click_ctx, project: str): + """Extend a project deadline by an specified number of days. + + This operation has the same effect as expiring and re-releasing a project + Therefore it consumes one of the possible expiring times. + """ + try: + with dds_cli.project_status.ProjectStatusManager( + project=project, + no_prompt=click_ctx.get("NO_PROMPT", False), + token_path=click_ctx.get("TOKEN_PATH"), + ) as updater: + updater.extend_deadline() + except ( + dds_cli.exceptions.APIError, + dds_cli.exceptions.AuthenticationError, + dds_cli.exceptions.DDSCLIException, + dds_cli.exceptions.ApiResponseError, + dds_cli.exceptions.ApiRequestError, + ) as err: + LOG.error(err) + sys.exit(1) + + # -- dds project status busy -- # @project_status.command(name="busy", no_args_is_help=False) # Flags diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index c06e4b42..a00004a2 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -148,6 +148,64 @@ def update_status(self, new_status, deadline=None, is_aborted=False, no_mail=Fal dds_cli.utils.console.print(f"Project {response_json.get('message')}") + def extend_deadline(self): + extra_params = {"send_email": False} + + # make a first call to fetch the project status and default deadline + response_json, _ = dds_cli.utils.perform_request( + endpoint=DDSEndpoint.UPDATE_PROJ_STATUS, + headers=self.token, + method="patch", + params={"project": self.project}, + json=extra_params, + ) + + dds_cli.utils.console.print( + "You are about to extend the deadline of the following project:\n\n" + ) + table = self.generate_project_table(project_info=response_json.get("project_info")) + dds_cli.utils.console.print(table) + + default_unit_days = response_json.get("default_unit_days") + current_deadline = response_json.get("project_status").get("current_deadline") + print_info = f"\n\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" + print_info += ( + f"Default deadline extension: [b][green]{default_unit_days}[/green][/b] days\n" + ) + dds_cli.utils.console.print(print_info) + + answered = False + while not answered: + prompt_question = f"Enter the number of days you want to extend the project, the number of days has to be equal or same as [b][green]{response_json.get('default_unit_days')}[/green][/b].\n" + prompt_question += f"Or leave it empty to apply the default [b][green]{response_json.get('default_unit_days')} days [/green][/b]" + extend_deadline = rich.prompt.Prompt.ask(prompt_question) + try: + if extend_deadline == "" or int(extend_deadline) < response_json.get( + "default_unit_days" + ): + break + else: + dds_cli.utils.console.print( + "\n[b][red]The number of days has to be lower than the default deadline extension number[/b][/red]\n" + ) + except: + dds_cli.utils.console.print( + "\n[b][red]Remember to write the number of days using numbers (dont use letters)[/b][/red]\n" + ) + continue + extend_deadline = int(extend_deadline or default_unit_days) + + print_info = f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " + print_info += ( + f"\nThis will extend the deadline by [b][blue]{extend_deadline} days[/b][/blue]." + ) + print_info += "\nYou can only extend the data availability a maximun of [b][blue]3 times[/b][/blue], this consumes one of those times." + if not rich.prompt.Confirm.ask(print_info): + LOG.info("Probably for the best. Exiting.") + sys.exit(0) + + # TODO second call with confirmed flag + class ProjectBusyStatusManager(base.DDSBaseClass): """Project Busy Status manager class.""" diff --git a/dds_cli/utils.py b/dds_cli/utils.py index c04e487d..1b004537 100644 --- a/dds_cli/utils.py +++ b/dds_cli/utils.py @@ -165,6 +165,8 @@ def perform_request( request_method = requests.post elif method == "delete": request_method = requests.delete + elif method == "patch": + request_method = requests.patch def transform_paths(json_input): """Make paths serializable.""" From 89c7144b698a26a4d042d15606c445436fbec242 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 16 Oct 2023 15:20:53 +0200 Subject: [PATCH 02/35] second call --- dds_cli/project_status.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index a00004a2..8abc99dd 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -204,7 +204,15 @@ def extend_deadline(self): LOG.info("Probably for the best. Exiting.") sys.exit(0) - # TODO second call with confirmed flag + extra_params = {**extra_params, "confirmed": True, "new_deadline_in": extend_deadline} + response_json, _ = dds_cli.utils.perform_request( + endpoint=DDSEndpoint.UPDATE_PROJ_STATUS, + headers=self.token, + method="patch", + params={"project": self.project}, + json=extra_params, + ) + dds_cli.utils.console.print(f"Project {response_json.get('message')}") class ProjectBusyStatusManager(base.DDSBaseClass): From 0f58043c64db3aa81ee4fb9ee84b11c8aeed04b8 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 14:58:51 +0200 Subject: [PATCH 03/35] added tests --- dds_cli/project_status.py | 72 +++++++------ tests/test_project_status.py | 199 +++++++++++++++++++++++++++++++++-- 2 files changed, 234 insertions(+), 37 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 8abc99dd..2bb039be 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -149,9 +149,10 @@ def update_status(self, new_status, deadline=None, is_aborted=False, no_mail=Fal dds_cli.utils.console.print(f"Project {response_json.get('message')}") def extend_deadline(self): + # Define initial parameters extra_params = {"send_email": False} - # make a first call to fetch the project status and default deadline + # Fetch project status and default deadline response_json, _ = dds_cli.utils.perform_request( endpoint=DDSEndpoint.UPDATE_PROJ_STATUS, headers=self.token, @@ -159,52 +160,61 @@ def extend_deadline(self): params={"project": self.project}, json=extra_params, ) + # Extract default unit days and current deadline + default_unit_days = response_json.get("default_unit_days") + current_deadline = response_json.get("project_status").get("current_deadline") - dds_cli.utils.console.print( - "You are about to extend the deadline of the following project:\n\n" + # information about the project status and information + print_info = ( + f"\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" + f"Default deadline extension: [b][green]{default_unit_days}[/green][/b] days\n" ) table = self.generate_project_table(project_info=response_json.get("project_info")) dds_cli.utils.console.print(table) + dds_cli.utils.console.print(print_info) - default_unit_days = response_json.get("default_unit_days") - current_deadline = response_json.get("project_status").get("current_deadline") - print_info = f"\n\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" - print_info += ( - f"Default deadline extension: [b][green]{default_unit_days}[/green][/b] days\n" + prompt_question = ( + f"Enter the number of days you want to extend the project, " + f"the number of days has to be equal or same as [b][green]{default_unit_days}[/green][/b].\n" + f"Or leave it empty to apply the default [b][green]{default_unit_days} days [/green][/b]" ) - dds_cli.utils.console.print(print_info) - answered = False - while not answered: - prompt_question = f"Enter the number of days you want to extend the project, the number of days has to be equal or same as [b][green]{response_json.get('default_unit_days')}[/green][/b].\n" - prompt_question += f"Or leave it empty to apply the default [b][green]{response_json.get('default_unit_days')} days [/green][/b]" - extend_deadline = rich.prompt.Prompt.ask(prompt_question) - try: - if extend_deadline == "" or int(extend_deadline) < response_json.get( - "default_unit_days" - ): - break - else: - dds_cli.utils.console.print( - "\n[b][red]The number of days has to be lower than the default deadline extension number[/b][/red]\n" - ) - except: + dds_cli.utils.console.print(prompt_question) + extend_deadline = rich.prompt.Prompt.ask("-") + if not extend_deadline: + # Set extend_deadline to default + extend_deadline = default_unit_days + try: + # the input was an string --> convert to integer + extend_deadline = int(extend_deadline) + if extend_deadline > default_unit_days: dds_cli.utils.console.print( - "\n[b][red]Remember to write the number of days using numbers (dont use letters)[/b][/red]\n" + "\n[b][red]The number of days has to be lower than the default deadline extension number[/b][/red]\n" ) - continue - extend_deadline = int(extend_deadline or default_unit_days) + LOG.info("Exiting the function, try again") + sys.exit(0) + + except ValueError: + dds_cli.utils.console.print( + "\n[b][red]Remember to write the number of days using numbers (dont use letters)[/b][/red]\n" + ) + LOG.info("Exiting the function, try again") + sys.exit(0) - print_info = f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " - print_info += ( + prompt_question = ( + f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " f"\nThis will extend the deadline by [b][blue]{extend_deadline} days[/b][/blue]." + "\nYou can only extend the data availability a maximum of [b][blue]3 times[/b][/blue], this consumes one of those times." ) - print_info += "\nYou can only extend the data availability a maximun of [b][blue]3 times[/b][/blue], this consumes one of those times." - if not rich.prompt.Confirm.ask(print_info): + + dds_cli.utils.console.print(prompt_question) + if not rich.prompt.Confirm.ask("-"): LOG.info("Probably for the best. Exiting.") sys.exit(0) + # Update parameters for the request extra_params = {**extra_params, "confirmed": True, "new_deadline_in": extend_deadline} + response_json, _ = dds_cli.utils.perform_request( endpoint=DDSEndpoint.UPDATE_PROJ_STATUS, headers=self.token, diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 0d76cc5d..02668bae 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -6,6 +6,7 @@ from _pytest.capture import CaptureFixture import logging from dds_cli.exceptions import ApiResponseError, DDSCLIException +import datetime import typing @@ -34,7 +35,32 @@ "message": f"{project_name} updated to status Available. An e-mail notification has been sent." } + +deadline = str(datetime.datetime.now() + datetime.timedelta(days=1)) +default_unit_days = 30 +returned_response_extend_deadline_fetch_information = { + "project_info": returned_response_get_info, + "default_unit_days": default_unit_days, + "warning": "Operation must be confirmed before proceding.", + "project_status": {"current_deadline": deadline, "current_status": "Available"}, +} +returned_response_extend_deadline_fetch_information.get("project_info").update( + {"Status": "Available"} +) + +returned_response_extend_deadline_ok: typing.Dict = { + "message": f"Project {project_name} has been given a new deadline. An e-mail notification has not been sent." +} + + ######### +def check_table_proj_info(table_output): + assert "┏━━━━━" in table_output.out # A table has generated + assert f"{returned_response_get_info['Project ID']}" in table_output.out + assert f"{returned_response_get_info['Created by']}" in table_output.out + assert f"{returned_response_get_info['Status']}" in table_output.out + assert f"{returned_response_get_info['Last updated']}" in table_output.out + assert f"{returned_response_get_info['Size']}" in table_output.out def perform_archive_delete_operation(new_status, confirmed, mock, json_project_info=None): @@ -75,12 +101,26 @@ def check_output_project_info(new_status, captured_output, caplog_tuples=None): assert f"{project_name}" in captured_output.out assert f"{new_status}" - assert "┏━━━━━" in captured_output.out # A table has generated - assert f"{returned_response_get_info['Project ID']}" in captured_output.out - assert f"{returned_response_get_info['Created by']}" in captured_output.out - assert f"{returned_response_get_info['Status']}" in captured_output.out - assert f"{returned_response_get_info['Last updated']}" in captured_output.out - assert f"{returned_response_get_info['Size']}" in captured_output.out + check_table_proj_info(table_output=captured_output) + # if not confirmed operation + if caplog_tuples: + assert ( + "dds_cli.project_status", + logging.INFO, + "Probably for the best. Exiting.", + ) in caplog_tuples + + +def check_output_extend_deadline(captured_output, caplog_tuples=None): + assert "Current deadline:" in captured_output.out + assert "Default deadline extension:" in captured_output.out + + assert "Enter the number of days you want to extend the project" in captured_output.out + assert "the number of days has to be equal or same as" in captured_output.out + assert f"{default_unit_days}" in captured_output.out + assert "Or leave it empty to apply the default" in captured_output.out + + check_table_proj_info(table_output=captured_output) # if not confirmed operation if caplog_tuples: @@ -296,3 +336,150 @@ def test_update_extra_params(capsys: CaptureFixture, monkeypatch, caplog: LogCap status_mngr.update_status(new_status="Archived", is_aborted=True, deadline=10) assert returned_response_archived_ok["message"] in capsys.readouterr().out + + +def test_extend_deadline_no_confirmed( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + # The user decided to not accept the extension + + confirmed = False + caplog.set_level(logging.INFO) + + # Create mocker + with Mocker() as mock: + # set confirmation object to false + monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) + # number of days to extend deadline + monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: 2) + + # Create first mocked request - not confirmed + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + status_code=200, + json=returned_response_extend_deadline_fetch_information, + ) + + # capture system exit on not accepting operation + with pytest.raises(SystemExit): + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + check_output_extend_deadline( + captured_output=capsys.readouterr(), caplog_tuples=caplog.record_tuples + ) + + +def test_extend_deadline_too_many_number_of_days( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + # Check that when using more days than the default it fails + + confirmed = False + caplog.set_level(logging.INFO) + + # Create mocker + with Mocker() as mock: + # set confirmation object to false + monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) + # number of days to extend deadline + monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: default_unit_days + 10) + + # Create first mocked request - not confirmed + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + status_code=200, + json=returned_response_extend_deadline_fetch_information, + ) + + # capture system exit on not accepting operation + with pytest.raises(SystemExit): + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + captured_output = capsys.readouterr() + assert ( + "The number of days has to be lower than the default deadline extension number" + in captured_output.out + ) + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + + +def test_extend_deadline_wrong_number_of_days( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + # Bad number of days used + + confirmed = False + caplog.set_level(logging.INFO) + + # Create mocker + with Mocker() as mock: + # set confirmation object to false + monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) + # number of days to extend deadline + monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: "5 days") + + # Create first mocked request - not confirmed + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + status_code=200, + json=returned_response_extend_deadline_fetch_information, + ) + + # capture system exit on not accepting operation + with pytest.raises(SystemExit): + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + captured_output = capsys.readouterr() + assert "Remember to write the number of days using numbers" in captured_output.out + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + + +def test_extend_deadline_confirmed_ok( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + # test that the operation is performed - ok + + confirmed = True + + # Create mocker + with Mocker() as mock: + # set confirmation object to false + monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) + # number of days to extend deadline + days_to_extend = 2 + monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: days_to_extend) + + # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + [ + {"status_code": 200, "json": returned_response_extend_deadline_fetch_information}, + {"status_code": 200, "json": returned_response_extend_deadline_ok}, + ], + ) + + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + captured_output = capsys.readouterr() + assert returned_response_extend_deadline_ok["message"] in captured_output.out + assert "This will extend the deadline by" in captured_output.out + assert f"{days_to_extend}" in captured_output.out + assert "You can only extend the data availability a maximum of" in captured_output.out + + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) From 363f15396d2be938bd541703e12324e68b4905d1 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:00:38 +0200 Subject: [PATCH 04/35] docstring --- dds_cli/project_status.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 2bb039be..ebb49079 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -149,6 +149,9 @@ def update_status(self, new_status, deadline=None, is_aborted=False, no_mail=Fal dds_cli.utils.console.print(f"Project {response_json.get('message')}") def extend_deadline(self): + """ + Extend the project deadline + """ # Define initial parameters extra_params = {"send_email": False} From 2e254002186caedf02ee907cc2a2b763bd4b775b Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:11:01 +0200 Subject: [PATCH 05/35] pylint --- dds_cli/project_status.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index ebb49079..42a4111d 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -178,8 +178,10 @@ def extend_deadline(self): prompt_question = ( f"Enter the number of days you want to extend the project, " - f"the number of days has to be equal or same as [b][green]{default_unit_days}[/green][/b].\n" - f"Or leave it empty to apply the default [b][green]{default_unit_days} days [/green][/b]" + f"the number of days has to be equal or same as " + f"[b][green]{default_unit_days}[/green][/b].\n" + f"Or leave it empty to apply the default " + f"[b][green]{default_unit_days} days [/green][/b]" ) dds_cli.utils.console.print(prompt_question) @@ -192,14 +194,16 @@ def extend_deadline(self): extend_deadline = int(extend_deadline) if extend_deadline > default_unit_days: dds_cli.utils.console.print( - "\n[b][red]The number of days has to be lower than the default deadline extension number[/b][/red]\n" + "\n[b][red]The number of days has to be lower than " + "the default deadline extension number[/b][/red]\n" ) LOG.info("Exiting the function, try again") sys.exit(0) except ValueError: dds_cli.utils.console.print( - "\n[b][red]Remember to write the number of days using numbers (dont use letters)[/b][/red]\n" + "\n[b][red]Remember to write the number of days " + "using numbers (dont use letters)[/b][/red]\n" ) LOG.info("Exiting the function, try again") sys.exit(0) @@ -207,7 +211,8 @@ def extend_deadline(self): prompt_question = ( f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " f"\nThis will extend the deadline by [b][blue]{extend_deadline} days[/b][/blue]." - "\nYou can only extend the data availability a maximum of [b][blue]3 times[/b][/blue], this consumes one of those times." + "\nYou can only extend the data availability a maximum of " + "[b][blue]3 times[/b][/blue], this consumes one of those times." ) dds_cli.utils.console.print(prompt_question) From 4a12d23ca89760cb853e8a2cfa22fc7e192dcb00 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:18:29 +0200 Subject: [PATCH 06/35] tests! --- tests/test_project_status.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 02668bae..4cd24132 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -49,7 +49,7 @@ ) returned_response_extend_deadline_ok: typing.Dict = { - "message": f"Project {project_name} has been given a new deadline. An e-mail notification has not been sent." + "message": f"Project {project_name} has been given a new deadline." } @@ -115,11 +115,6 @@ def check_output_extend_deadline(captured_output, caplog_tuples=None): assert "Current deadline:" in captured_output.out assert "Default deadline extension:" in captured_output.out - assert "Enter the number of days you want to extend the project" in captured_output.out - assert "the number of days has to be equal or same as" in captured_output.out - assert f"{default_unit_days}" in captured_output.out - assert "Or leave it empty to apply the default" in captured_output.out - check_table_proj_info(table_output=captured_output) # if not confirmed operation @@ -341,7 +336,7 @@ def test_update_extra_params(capsys: CaptureFixture, monkeypatch, caplog: LogCap def test_extend_deadline_no_confirmed( capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture ): - # The user decided to not accept the extension + """The user decided to not accept the extension""" confirmed = False caplog.set_level(logging.INFO) @@ -376,7 +371,7 @@ def test_extend_deadline_no_confirmed( def test_extend_deadline_too_many_number_of_days( capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture ): - # Check that when using more days than the default it fails + """Check that when using more days than the default it fails""" confirmed = False caplog.set_level(logging.INFO) @@ -414,7 +409,7 @@ def test_extend_deadline_too_many_number_of_days( def test_extend_deadline_wrong_number_of_days( capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture ): - # Bad number of days used + """Bad number of days used""" confirmed = False caplog.set_level(logging.INFO) @@ -449,7 +444,7 @@ def test_extend_deadline_wrong_number_of_days( def test_extend_deadline_confirmed_ok( capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture ): - # test that the operation is performed - ok + """test that the operation is performed - ok""" confirmed = True From 9b27bdafc5b5c9de8b2cf1315bb77628637cae7c Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:27:55 +0200 Subject: [PATCH 07/35] missing test --- tests/test_project_status.py | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 4cd24132..999d2a03 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -478,3 +478,41 @@ def test_extend_deadline_confirmed_ok( assert "You can only extend the data availability a maximum of" in captured_output.out check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + + +def test_extend_deadline_confirmed_ok_default_days( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + """test that the operation is performed with default days to extend""" + + confirmed = True + + # Create mocker + with Mocker() as mock: + # set confirmation object to false + monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) + # number of days to extend deadline + monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: "") + + # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + [ + {"status_code": 200, "json": returned_response_extend_deadline_fetch_information}, + {"status_code": 200, "json": returned_response_extend_deadline_ok}, + ], + ) + + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + captured_output = capsys.readouterr() + assert returned_response_extend_deadline_ok["message"] in captured_output.out + assert "This will extend the deadline by" in captured_output.out + assert f"{default_unit_days}" in captured_output.out + assert "You can only extend the data availability a maximum of" in captured_output.out + + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) From 75038331759aa2de0283049f96fc6dec7cad90e0 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:48:08 +0200 Subject: [PATCH 08/35] missing test --- tests/test_project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 999d2a03..7d6c8f87 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -492,7 +492,7 @@ def test_extend_deadline_confirmed_ok_default_days( # set confirmation object to false monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) # number of days to extend deadline - monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: "") + monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: None) # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) mock.patch( From baaa98f51a1c0ef75791c5a8c2293dcfca8c27b0 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 17 Oct 2023 15:56:52 +0200 Subject: [PATCH 09/35] sprintlog --- SPRINTLOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SPRINTLOG.md b/SPRINTLOG.md index dc9977dd..fa47dc03 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -302,3 +302,7 @@ _Nothing merged in CLI during this sprint_ - GitHub Actions to generate the documentation fixed ([#1473])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1473) - Print project information and ask user for confirmation when deleting or archiving projects ([#1401])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1401) + +# 2023-10-16 - 2023-10-27 + +- Added a new command to match with the new extend deadline endpoint ([#661])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) From ed779e8f791e0a91c4ad8dc2ab1259b341c2132f Mon Sep 17 00:00:00 2001 From: rv0lt Date: Wed, 18 Oct 2023 10:59:34 +0200 Subject: [PATCH 10/35] prettier --- dds_cli/project_status.py | 26 ++++++++++++++++++++++++-- tests/test_project_status.py | 6 +----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 42a4111d..c3b352ec 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -163,11 +163,31 @@ def extend_deadline(self): params={"project": self.project}, json=extra_params, ) + + # Structure of the response: + # { + # 'default_unit_days': 30, + # 'project_info': { + # 'Created by': 'First Unit User', + # 'Description': 'This is a test project. You will be able to upload to but NOT download from this project. Create a new project to test the entire system. ', + # 'Last updated': 'Wed, 18 Oct 2023 08:40:43 GMT', + # 'PI': 'support@example.com', + # 'Project ID': 'project_1', + # 'Size': 0, + # 'Status': 'Available', + # 'Title': 'First Project' + # }, + # 'project_status': { + # 'current_deadline': 'Sat, 04 Nov 2023 23:59:59 GMT', + # 'current_status': 'Available'}, + # 'warning': 'Operation must be confirmed before proceding.' + # } + # Extract default unit days and current deadline default_unit_days = response_json.get("default_unit_days") current_deadline = response_json.get("project_status").get("current_deadline") - # information about the project status and information + # print information about the project status and table with the project info print_info = ( f"\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" f"Default deadline extension: [b][green]{default_unit_days}[/green][/b] days\n" @@ -176,6 +196,7 @@ def extend_deadline(self): dds_cli.utils.console.print(table) dds_cli.utils.console.print(print_info) + # First question, number of days to extend the deadline prompt_question = ( f"Enter the number of days you want to extend the project, " f"the number of days has to be equal or same as " @@ -208,6 +229,7 @@ def extend_deadline(self): LOG.info("Exiting the function, try again") sys.exit(0) + # Second question, confirm operation prompt_question = ( f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " f"\nThis will extend the deadline by [b][blue]{extend_deadline} days[/b][/blue]." @@ -220,7 +242,7 @@ def extend_deadline(self): LOG.info("Probably for the best. Exiting.") sys.exit(0) - # Update parameters for the request + # Update parameters for the second request extra_params = {**extra_params, "confirmed": True, "new_deadline_in": extend_deadline} response_json, _ = dds_cli.utils.perform_request( diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 7d6c8f87..c72c1fd8 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -44,10 +44,6 @@ "warning": "Operation must be confirmed before proceding.", "project_status": {"current_deadline": deadline, "current_status": "Available"}, } -returned_response_extend_deadline_fetch_information.get("project_info").update( - {"Status": "Available"} -) - returned_response_extend_deadline_ok: typing.Dict = { "message": f"Project {project_name} has been given a new deadline." } @@ -483,7 +479,7 @@ def test_extend_deadline_confirmed_ok( def test_extend_deadline_confirmed_ok_default_days( capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture ): - """test that the operation is performed with default days to extend""" + """test that the operation is performed when the default days to extend is used""" confirmed = True From 67db1943c076db3783aac712fdb371c177942958 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Wed, 18 Oct 2023 11:08:10 +0200 Subject: [PATCH 11/35] better coments --- tests/test_project_status.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index c72c1fd8..17972816 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -446,10 +446,10 @@ def test_extend_deadline_confirmed_ok( # Create mocker with Mocker() as mock: - # set confirmation object to false + # set confirmation object to true monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) # number of days to extend deadline - days_to_extend = 2 + days_to_extend = default_unit_days - 1 monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: days_to_extend) # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) @@ -485,9 +485,9 @@ def test_extend_deadline_confirmed_ok_default_days( # Create mocker with Mocker() as mock: - # set confirmation object to false + # set confirmation object to true monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) - # number of days to extend deadline + # number of days to extend deadline is None - user didnt wrote anything monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: None) # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) From b8cc80b77d4e7de770f85d6bf8faeedc655d2d2d Mon Sep 17 00:00:00 2001 From: rv0lt Date: Wed, 18 Oct 2023 11:10:20 +0200 Subject: [PATCH 12/35] line too long --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index c3b352ec..4ad03fb8 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -169,7 +169,7 @@ def extend_deadline(self): # 'default_unit_days': 30, # 'project_info': { # 'Created by': 'First Unit User', - # 'Description': 'This is a test project. You will be able to upload to but NOT download from this project. Create a new project to test the entire system. ', + # 'Description': 'This is a test project', # 'Last updated': 'Wed, 18 Oct 2023 08:40:43 GMT', # 'PI': 'support@example.com', # 'Project ID': 'project_1', From 916710269cc412245042b8495c073229385de917 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Thu, 19 Oct 2023 15:16:10 +0200 Subject: [PATCH 13/35] prettier --- SPRINTLOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPRINTLOG.md b/SPRINTLOG.md index 30b24504..39887f0a 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -306,4 +306,4 @@ _Nothing merged in CLI during this sprint_ # 2023-10-16 - 2023-10-27 - Added a new command to match with the new extend deadline endpoint ([#661])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) -- Change "Checksum verification successful. File integrity verified." logging level from INFO to DEBUG in order to not print for all files ([#662])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) \ No newline at end of file +- Change "Checksum verification successful. File integrity verified." logging level from INFO to DEBUG in order to not print for all files ([#662])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) From f5829a0cfe20887929f6dd5857bd5240965e53f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:17:45 +0200 Subject: [PATCH 14/35] Update SPRINTLOG.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- SPRINTLOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SPRINTLOG.md b/SPRINTLOG.md index 39887f0a..cc8ec221 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -305,5 +305,5 @@ _Nothing merged in CLI during this sprint_ # 2023-10-16 - 2023-10-27 -- Added a new command to match with the new extend deadline endpoint ([#661])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) -- Change "Checksum verification successful. File integrity verified." logging level from INFO to DEBUG in order to not print for all files ([#662])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) +- Change "Checksum verification successful. File integrity verified." logging level from INFO to DEBUG in order to not print for all files ([#662])(https://github.com/ScilifelabDataCentre/dds_cli/pull/662) +- New command `dds project status extend` to allow extension of project deadline ([#661])(https://github.com/ScilifelabDataCentre/dds_cli/pull/661) From fd469d726b730549ee1a482949ce901e070a79d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:18:32 +0200 Subject: [PATCH 15/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 4ad03fb8..78839aa2 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -149,9 +149,7 @@ def update_status(self, new_status, deadline=None, is_aborted=False, no_mail=Fal dds_cli.utils.console.print(f"Project {response_json.get('message')}") def extend_deadline(self): - """ - Extend the project deadline - """ + """Extend the project deadline.""" # Define initial parameters extra_params = {"send_email": False} From 86039bd711c52d8db6b7dec733c76e11f4917ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:19:04 +0200 Subject: [PATCH 16/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 78839aa2..f48b2d35 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -212,20 +212,13 @@ def extend_deadline(self): # the input was an string --> convert to integer extend_deadline = int(extend_deadline) if extend_deadline > default_unit_days: - dds_cli.utils.console.print( - "\n[b][red]The number of days has to be lower than " - "the default deadline extension number[/b][/red]\n" + raise DDSCLIException( + "\n[b][red]The number of days has to be lower than or equal to your unit's default: {default_unit_days}[/b][/red]\n" ) - LOG.info("Exiting the function, try again") - sys.exit(0) - except ValueError: - dds_cli.utils.console.print( - "\n[b][red]Remember to write the number of days " - "using numbers (dont use letters)[/b][/red]\n" + raise DDSCLIException( + "\n[b][red]Invalid value. Remember to enter a digit (not letters) when being asked for the number of days.[/b][/red]\n" ) - LOG.info("Exiting the function, try again") - sys.exit(0) # Second question, confirm operation prompt_question = ( From 68d317c34fabed079498d5f901bd138c1c93b6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:21:46 +0200 Subject: [PATCH 17/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index f48b2d35..23c27b31 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -243,7 +243,11 @@ def extend_deadline(self): params={"project": self.project}, json=extra_params, ) - dds_cli.utils.console.print(f"Project {response_json.get('message')}") + message = response_json.get("message") + if not message: + raise DDSCLIException("No message returned from API. Cannot verify extension of project deadline.") + + LOG.info(message) class ProjectBusyStatusManager(base.DDSBaseClass): From f83a1cfe480223039f17672a7f5fad757440e059 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 20 Oct 2023 14:29:01 +0200 Subject: [PATCH 18/35] feedback --- dds_cli/__main__.py | 13 +++++--- dds_cli/project_status.py | 57 ++++++++++++++++++------------------ tests/test_project_status.py | 33 ++++++++++----------- 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/dds_cli/__main__.py b/dds_cli/__main__.py index 82eed57f..9c7f65f8 100644 --- a/dds_cli/__main__.py +++ b/dds_cli/__main__.py @@ -1247,12 +1247,17 @@ def delete_project(click_ctx, project: str): @project_status.command(name="extend", no_args_is_help=True) # Options @project_option(required=True) +@click.option( + "--new_deadline", + required=False, + type=int, + help="Number of days to extend the deadline.", +) @click.pass_obj -def extend_deadline(click_ctx, project: str): +def extend_deadline(click_ctx, project: str, new_deadline: int): """Extend a project deadline by an specified number of days. - This operation has the same effect as expiring and re-releasing a project - Therefore it consumes one of the possible expiring times. + It consumes one of allowed times to renew data access. """ try: with dds_cli.project_status.ProjectStatusManager( @@ -1260,7 +1265,7 @@ def extend_deadline(click_ctx, project: str): no_prompt=click_ctx.get("NO_PROMPT", False), token_path=click_ctx.get("TOKEN_PATH"), ) as updater: - updater.extend_deadline() + updater.extend_deadline(new_deadline=new_deadline) except ( dds_cli.exceptions.APIError, dds_cli.exceptions.AuthenticationError, diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 23c27b31..bc4840c3 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -148,7 +148,7 @@ def update_status(self, new_status, deadline=None, is_aborted=False, no_mail=Fal dds_cli.utils.console.print(f"Project {response_json.get('message')}") - def extend_deadline(self): + def extend_deadline(self, new_deadline=None): """Extend the project deadline.""" # Define initial parameters extra_params = {"send_email": False} @@ -184,7 +184,7 @@ def extend_deadline(self): # Extract default unit days and current deadline default_unit_days = response_json.get("default_unit_days") current_deadline = response_json.get("project_status").get("current_deadline") - + project_id = response_json.get("project_info").get("Project ID") # print information about the project status and table with the project info print_info = ( f"\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" @@ -194,47 +194,46 @@ def extend_deadline(self): dds_cli.utils.console.print(table) dds_cli.utils.console.print(print_info) - # First question, number of days to extend the deadline - prompt_question = ( - f"Enter the number of days you want to extend the project, " - f"the number of days has to be equal or same as " - f"[b][green]{default_unit_days}[/green][/b].\n" - f"Or leave it empty to apply the default " - f"[b][green]{default_unit_days} days [/green][/b]" - ) - - dds_cli.utils.console.print(prompt_question) - extend_deadline = rich.prompt.Prompt.ask("-") - if not extend_deadline: - # Set extend_deadline to default - extend_deadline = default_unit_days + # If it wasnt provided during the command click, ask the user for the new deadline + if not new_deadline: + # Question number of days to extend the deadline + prompt_question = ( + f"How many days would you like to extend the project deadline with? " + f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])." + ) + new_deadline = rich.prompt.Prompt.ask(prompt_question) + if not new_deadline: + # Set new_deadline to default + new_deadline = default_unit_days try: # the input was an string --> convert to integer - extend_deadline = int(extend_deadline) - if extend_deadline > default_unit_days: - raise DDSCLIException( - "\n[b][red]The number of days has to be lower than or equal to your unit's default: {default_unit_days}[/b][/red]\n" + new_deadline = int(new_deadline) + if new_deadline > default_unit_days: + raise exceptions.DDSCLIException( + f"\n[b][red]The number of days has to be lower than or equal to your unit's default: {default_unit_days}[/b][/red]\n" ) except ValueError: - raise DDSCLIException( + raise exceptions.DDSCLIException( "\n[b][red]Invalid value. Remember to enter a digit (not letters) when being asked for the number of days.[/b][/red]\n" ) - # Second question, confirm operation + # Confirm operation question + from dateutil.parser import parse + + new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) prompt_question = ( + f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " - f"\nThis will extend the deadline by [b][blue]{extend_deadline} days[/b][/blue]." "\nYou can only extend the data availability a maximum of " "[b][blue]3 times[/b][/blue], this consumes one of those times." ) - dds_cli.utils.console.print(prompt_question) - if not rich.prompt.Confirm.ask("-"): + if not rich.prompt.Confirm.ask(prompt_question): LOG.info("Probably for the best. Exiting.") sys.exit(0) # Update parameters for the second request - extra_params = {**extra_params, "confirmed": True, "new_deadline_in": extend_deadline} + extra_params = {**extra_params, "confirmed": True, "new_deadline_in": new_deadline} response_json, _ = dds_cli.utils.perform_request( endpoint=DDSEndpoint.UPDATE_PROJ_STATUS, @@ -245,8 +244,10 @@ def extend_deadline(self): ) message = response_json.get("message") if not message: - raise DDSCLIException("No message returned from API. Cannot verify extension of project deadline.") - + raise exceptions.DDSCLIException( + "No message returned from API. Cannot verify extension of project deadline." + ) + LOG.info(message) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 17972816..e031edd0 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -387,7 +387,7 @@ def test_extend_deadline_too_many_number_of_days( ) # capture system exit on not accepting operation - with pytest.raises(SystemExit): + with pytest.raises(DDSCLIException) as err: with project_status.ProjectStatusManager( project=project_name, no_prompt=True, authenticate=False ) as status_mngr: @@ -395,11 +395,8 @@ def test_extend_deadline_too_many_number_of_days( status_mngr.extend_deadline() captured_output = capsys.readouterr() - assert ( - "The number of days has to be lower than the default deadline extension number" - in captured_output.out - ) check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + assert "The number of days has to be lower" in str(err.value) def test_extend_deadline_wrong_number_of_days( @@ -425,7 +422,7 @@ def test_extend_deadline_wrong_number_of_days( ) # capture system exit on not accepting operation - with pytest.raises(SystemExit): + with pytest.raises(DDSCLIException) as err: with project_status.ProjectStatusManager( project=project_name, no_prompt=True, authenticate=False ) as status_mngr: @@ -433,8 +430,8 @@ def test_extend_deadline_wrong_number_of_days( status_mngr.extend_deadline() captured_output = capsys.readouterr() - assert "Remember to write the number of days using numbers" in captured_output.out check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + assert "Remember to enter a digit (not letters)" in str(err.value) def test_extend_deadline_confirmed_ok( @@ -443,6 +440,7 @@ def test_extend_deadline_confirmed_ok( """test that the operation is performed - ok""" confirmed = True + caplog.set_level(logging.INFO) # Create mocker with Mocker() as mock: @@ -468,11 +466,11 @@ def test_extend_deadline_confirmed_ok( status_mngr.extend_deadline() captured_output = capsys.readouterr() - assert returned_response_extend_deadline_ok["message"] in captured_output.out - assert "This will extend the deadline by" in captured_output.out - assert f"{days_to_extend}" in captured_output.out - assert "You can only extend the data availability a maximum of" in captured_output.out - + assert ( + "dds_cli.project_status", + logging.INFO, + returned_response_extend_deadline_ok["message"], + ) in caplog.record_tuples check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) @@ -482,7 +480,7 @@ def test_extend_deadline_confirmed_ok_default_days( """test that the operation is performed when the default days to extend is used""" confirmed = True - + caplog.set_level(logging.INFO) # Create mocker with Mocker() as mock: # set confirmation object to true @@ -506,9 +504,10 @@ def test_extend_deadline_confirmed_ok_default_days( status_mngr.extend_deadline() captured_output = capsys.readouterr() - assert returned_response_extend_deadline_ok["message"] in captured_output.out - assert "This will extend the deadline by" in captured_output.out - assert f"{default_unit_days}" in captured_output.out - assert "You can only extend the data availability a maximum of" in captured_output.out + assert ( + "dds_cli.project_status", + logging.INFO, + returned_response_extend_deadline_ok["message"], + ) in caplog.record_tuples check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) From 06949d59f8dc3d6aaab6b38ad439c462ab0c31f8 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 20 Oct 2023 14:32:51 +0200 Subject: [PATCH 19/35] pylint --- dds_cli/project_status.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index bc4840c3..0a1e841e 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -3,6 +3,7 @@ import logging import typing import sys +from dateutil.parser import parse # Installed import pytz @@ -210,16 +211,16 @@ def extend_deadline(self, new_deadline=None): new_deadline = int(new_deadline) if new_deadline > default_unit_days: raise exceptions.DDSCLIException( - f"\n[b][red]The number of days has to be lower than or equal to your unit's default: {default_unit_days}[/b][/red]\n" + f"\n[b][red]The number of days has to be lower than or equal " + "to your unit's default: {default_unit_days}[/b][/red]\n" ) except ValueError: raise exceptions.DDSCLIException( - "\n[b][red]Invalid value. Remember to enter a digit (not letters) when being asked for the number of days.[/b][/red]\n" + "\n[b][red]Invalid value. Remember to enter a digit (not letters)" + "when being asked for the number of days.[/b][/red]\n" ) # Confirm operation question - from dateutil.parser import parse - new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) prompt_question = ( f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" From f047fb0e6b8a5e758219d06902738813f2649ac6 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 20 Oct 2023 14:36:20 +0200 Subject: [PATCH 20/35] pylint --- dds_cli/project_status.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 0a1e841e..7e4a39f9 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -199,7 +199,7 @@ def extend_deadline(self, new_deadline=None): if not new_deadline: # Question number of days to extend the deadline prompt_question = ( - f"How many days would you like to extend the project deadline with? " + "How many days would you like to extend the project deadline with? " f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])." ) new_deadline = rich.prompt.Prompt.ask(prompt_question) @@ -211,20 +211,20 @@ def extend_deadline(self, new_deadline=None): new_deadline = int(new_deadline) if new_deadline > default_unit_days: raise exceptions.DDSCLIException( - f"\n[b][red]The number of days has to be lower than or equal " - "to your unit's default: {default_unit_days}[/b][/red]\n" + "\n[b][red]The number of days has to be lower than or equal " + f"to your unit's default: {default_unit_days}[/b][/red]\n" ) - except ValueError: + except ValueError as e: raise exceptions.DDSCLIException( "\n[b][red]Invalid value. Remember to enter a digit (not letters)" "when being asked for the number of days.[/b][/red]\n" - ) + ) from e # Confirm operation question new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) prompt_question = ( f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" - f"\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " + "\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " "\nYou can only extend the data availability a maximum of " "[b][blue]3 times[/b][/blue], this consumes one of those times." ) From 3f514d3bde2d38b4b3c8e55dd695f235e3e1565d Mon Sep 17 00:00:00 2001 From: rv0lt Date: Fri, 20 Oct 2023 14:39:46 +0200 Subject: [PATCH 21/35] pylint --- dds_cli/project_status.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 7e4a39f9..c19c70c1 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -214,11 +214,11 @@ def extend_deadline(self, new_deadline=None): "\n[b][red]The number of days has to be lower than or equal " f"to your unit's default: {default_unit_days}[/b][/red]\n" ) - except ValueError as e: + except ValueError as error: raise exceptions.DDSCLIException( "\n[b][red]Invalid value. Remember to enter a digit (not letters)" "when being asked for the number of days.[/b][/red]\n" - ) from e + ) from error # Confirm operation question new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) From c1206f7568b20ea62d5c5ddc6ef47878ddc859d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:19:39 +0200 Subject: [PATCH 22/35] Update dds_cli/__main__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/__main__.py b/dds_cli/__main__.py index 9c7f65f8..fafc964d 100644 --- a/dds_cli/__main__.py +++ b/dds_cli/__main__.py @@ -1248,7 +1248,7 @@ def delete_project(click_ctx, project: str): # Options @project_option(required=True) @click.option( - "--new_deadline", + "--new-deadline", required=False, type=int, help="Number of days to extend the deadline.", From 42cd8bddfd8710ae87a607a3f13251cb39039b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:20:10 +0200 Subject: [PATCH 23/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index c19c70c1..f6d3f7e5 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -202,10 +202,7 @@ def extend_deadline(self, new_deadline=None): "How many days would you like to extend the project deadline with? " f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])." ) - new_deadline = rich.prompt.Prompt.ask(prompt_question) - if not new_deadline: - # Set new_deadline to default - new_deadline = default_unit_days + new_deadline = rich.prompt.Prompt.ask(prompt_question, default=default_unit_days) try: # the input was an string --> convert to integer new_deadline = int(new_deadline) From ddb2db733e708e9d1a99cde362c1e58b11908e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:20:23 +0200 Subject: [PATCH 24/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index f6d3f7e5..d0ed492b 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -200,7 +200,7 @@ def extend_deadline(self, new_deadline=None): # Question number of days to extend the deadline prompt_question = ( "How many days would you like to extend the project deadline with? " - f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])." + f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])" ) new_deadline = rich.prompt.Prompt.ask(prompt_question, default=default_unit_days) try: From 12ade91bfba56afa75d7df14f729b4654c6b5cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:20:38 +0200 Subject: [PATCH 25/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index d0ed492b..e8391f0d 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -221,7 +221,7 @@ def extend_deadline(self, new_deadline=None): new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) prompt_question = ( f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" - "\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation?. " + "\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation? " "\nYou can only extend the data availability a maximum of " "[b][blue]3 times[/b][/blue], this consumes one of those times." ) From 693155a82b3caf0a116d8fb2979c88b680395782 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 11:17:08 +0200 Subject: [PATCH 26/35] feedback --- dds_cli/project_status.py | 32 +++-- tests/test_project_status.py | 219 ++++++++++++----------------------- 2 files changed, 91 insertions(+), 160 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index e8391f0d..c0ab716c 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -179,13 +179,22 @@ def extend_deadline(self, new_deadline=None): # 'project_status': { # 'current_deadline': 'Sat, 04 Nov 2023 23:59:59 GMT', # 'current_status': 'Available'}, - # 'warning': 'Operation must be confirmed before proceding.' # } - # Extract default unit days and current deadline + # Check that the returned information was ok + keys = ["project_info", "project_status", "default_unit_days"] + dds_cli.utils.get_required_in_response(keys=keys, response=response_json) default_unit_days = response_json.get("default_unit_days") - current_deadline = response_json.get("project_status").get("current_deadline") - project_id = response_json.get("project_info").get("Project ID") + + # Check and extract the required information for the operation + current_deadline = dds_cli.utils.get_required_in_response( + keys=["current_deadline"], response=response_json.get("project_status") + ) + project_id = dds_cli.utils.get_required_in_response( + keys=["Project ID"], response=response_json.get("project_info") + ) + current_deadline = current_deadline[0] + project_id = project_id[0] # print information about the project status and table with the project info print_info = ( f"\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" @@ -202,20 +211,7 @@ def extend_deadline(self, new_deadline=None): "How many days would you like to extend the project deadline with? " f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])" ) - new_deadline = rich.prompt.Prompt.ask(prompt_question, default=default_unit_days) - try: - # the input was an string --> convert to integer - new_deadline = int(new_deadline) - if new_deadline > default_unit_days: - raise exceptions.DDSCLIException( - "\n[b][red]The number of days has to be lower than or equal " - f"to your unit's default: {default_unit_days}[/b][/red]\n" - ) - except ValueError as error: - raise exceptions.DDSCLIException( - "\n[b][red]Invalid value. Remember to enter a digit (not letters)" - "when being asked for the number of days.[/b][/red]\n" - ) from error + new_deadline = rich.prompt.IntPrompt.ask(prompt_question, default=default_unit_days) # Confirm operation question new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index e031edd0..53a39150 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -1,5 +1,6 @@ import pytest from requests_mock.mocker import Mocker +import unittest from dds_cli import DDSEndpoint from dds_cli import project_status from _pytest.logging import LogCaptureFixture @@ -341,173 +342,107 @@ def test_extend_deadline_no_confirmed( with Mocker() as mock: # set confirmation object to false monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) - # number of days to extend deadline - monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: 2) + # Set number of days to extend deadline - Bc of the default value in the function we need to create a mock answer + with unittest.mock.patch("rich.prompt.IntPrompt.ask") as deadline: + deadline.return_value = 2 - # Create first mocked request - not confirmed - mock.patch( - DDSEndpoint.UPDATE_PROJ_STATUS, - status_code=200, - json=returned_response_extend_deadline_fetch_information, - ) + # Create first mocked request - not confirmed + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + status_code=200, + json=returned_response_extend_deadline_fetch_information, + ) - # capture system exit on not accepting operation - with pytest.raises(SystemExit): - with project_status.ProjectStatusManager( - project=project_name, no_prompt=True, authenticate=False - ) as status_mngr: - status_mngr.token = {} # required, otherwise none - status_mngr.extend_deadline() + # capture system exit on not accepting operation + with pytest.raises(SystemExit): + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() - check_output_extend_deadline( - captured_output=capsys.readouterr(), caplog_tuples=caplog.record_tuples - ) + check_output_extend_deadline( + captured_output=capsys.readouterr(), caplog_tuples=caplog.record_tuples + ) -def test_extend_deadline_too_many_number_of_days( - capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture -): - """Check that when using more days than the default it fails""" +def test_no_msg_returned_request(capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture): + """Error - no message returned from request""" - confirmed = False + confirmed = True caplog.set_level(logging.INFO) # Create mocker with Mocker() as mock: - # set confirmation object to false + # set confirmation object to true monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) - # number of days to extend deadline - monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: default_unit_days + 10) - - # Create first mocked request - not confirmed - mock.patch( - DDSEndpoint.UPDATE_PROJ_STATUS, - status_code=200, - json=returned_response_extend_deadline_fetch_information, - ) - - # capture system exit on not accepting operation - with pytest.raises(DDSCLIException) as err: - with project_status.ProjectStatusManager( - project=project_name, no_prompt=True, authenticate=False - ) as status_mngr: - status_mngr.token = {} # required, otherwise none - status_mngr.extend_deadline() - - captured_output = capsys.readouterr() - check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) - assert "The number of days has to be lower" in str(err.value) + # Set number of days to extend deadline - Bc of the default value in the function we need to create a mock answer + with unittest.mock.patch("rich.prompt.IntPrompt.ask") as deadline: + deadline.return_value = 1 + + # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + [ + { + "status_code": 200, + "json": returned_response_extend_deadline_fetch_information, + }, + {"status_code": 200, "json": {}}, # empty response + ], + ) + + # capture system exit on not accepting operation + with pytest.raises(DDSCLIException) as err: + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + captured_output = capsys.readouterr() + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + assert "No message returned from API." in str(err.value) -def test_extend_deadline_wrong_number_of_days( +def test_extend_deadline_confirmed_ok( capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture ): - """Bad number of days used""" + """test that the operation is performed - ok""" - confirmed = False + confirmed = True caplog.set_level(logging.INFO) # Create mocker with Mocker() as mock: - # set confirmation object to false + # set confirmation object to true monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) - # number of days to extend deadline - monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: "5 days") - - # Create first mocked request - not confirmed - mock.patch( - DDSEndpoint.UPDATE_PROJ_STATUS, - status_code=200, - json=returned_response_extend_deadline_fetch_information, - ) + # Set number of days to extend deadline - Bc of the default value in the function we need to create a mock answer + with unittest.mock.patch("rich.prompt.IntPrompt.ask") as deadline: + deadline.return_value = default_unit_days - 1 + + # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + [ + { + "status_code": 200, + "json": returned_response_extend_deadline_fetch_information, + }, + {"status_code": 200, "json": returned_response_extend_deadline_ok}, + ], + ) - # capture system exit on not accepting operation - with pytest.raises(DDSCLIException) as err: with project_status.ProjectStatusManager( project=project_name, no_prompt=True, authenticate=False ) as status_mngr: status_mngr.token = {} # required, otherwise none status_mngr.extend_deadline() - captured_output = capsys.readouterr() - check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) - assert "Remember to enter a digit (not letters)" in str(err.value) - - -def test_extend_deadline_confirmed_ok( - capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture -): - """test that the operation is performed - ok""" - - confirmed = True - caplog.set_level(logging.INFO) - - # Create mocker - with Mocker() as mock: - # set confirmation object to true - monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) - # number of days to extend deadline - days_to_extend = default_unit_days - 1 - monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: days_to_extend) - - # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) - mock.patch( - DDSEndpoint.UPDATE_PROJ_STATUS, - [ - {"status_code": 200, "json": returned_response_extend_deadline_fetch_information}, - {"status_code": 200, "json": returned_response_extend_deadline_ok}, - ], - ) - - with project_status.ProjectStatusManager( - project=project_name, no_prompt=True, authenticate=False - ) as status_mngr: - status_mngr.token = {} # required, otherwise none - status_mngr.extend_deadline() - - captured_output = capsys.readouterr() - assert ( - "dds_cli.project_status", - logging.INFO, - returned_response_extend_deadline_ok["message"], - ) in caplog.record_tuples - check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) - - -def test_extend_deadline_confirmed_ok_default_days( - capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture -): - """test that the operation is performed when the default days to extend is used""" - - confirmed = True - caplog.set_level(logging.INFO) - # Create mocker - with Mocker() as mock: - # set confirmation object to true - monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) - # number of days to extend deadline is None - user didnt wrote anything - monkeypatch.setattr("rich.prompt.Prompt.ask", lambda question: None) - - # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) - mock.patch( - DDSEndpoint.UPDATE_PROJ_STATUS, - [ - {"status_code": 200, "json": returned_response_extend_deadline_fetch_information}, - {"status_code": 200, "json": returned_response_extend_deadline_ok}, - ], - ) - - with project_status.ProjectStatusManager( - project=project_name, no_prompt=True, authenticate=False - ) as status_mngr: - status_mngr.token = {} # required, otherwise none - status_mngr.extend_deadline() - - captured_output = capsys.readouterr() - assert ( - "dds_cli.project_status", - logging.INFO, - returned_response_extend_deadline_ok["message"], - ) in caplog.record_tuples - - check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + captured_output = capsys.readouterr() + assert ( + "dds_cli.project_status", + logging.INFO, + returned_response_extend_deadline_ok["message"], + ) in caplog.record_tuples + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) From 59e42f7b444745ea3b6286eab3ef9d5eaffaabb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:50:04 +0200 Subject: [PATCH 27/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index c0ab716c..614c7302 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -183,18 +183,16 @@ def extend_deadline(self, new_deadline=None): # Check that the returned information was ok keys = ["project_info", "project_status", "default_unit_days"] - dds_cli.utils.get_required_in_response(keys=keys, response=response_json) - default_unit_days = response_json.get("default_unit_days") + project_info, project_status, default_unit_days, *_ = dds_cli.utils.get_required_in_response(keys=keys, response=response_json) # Check and extract the required information for the operation - current_deadline = dds_cli.utils.get_required_in_response( - keys=["current_deadline"], response=response_json.get("project_status") + current_deadline, *_ = dds_cli.utils.get_required_in_response( + keys=["current_deadline"], response=project_status ) - project_id = dds_cli.utils.get_required_in_response( - keys=["Project ID"], response=response_json.get("project_info") + project_id, *_ = dds_cli.utils.get_required_in_response( + keys=["Project ID"], response=project_info ) - current_deadline = current_deadline[0] - project_id = project_id[0] + # print information about the project status and table with the project info print_info = ( f"\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" From ff58777414fe0899623a8b4b4bfd6577a9666ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:50:21 +0200 Subject: [PATCH 28/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 614c7302..ae4a4f83 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -198,7 +198,7 @@ def extend_deadline(self, new_deadline=None): f"\nCurrent deadline: [b][green]{current_deadline}[/green][/b]\n" f"Default deadline extension: [b][green]{default_unit_days}[/green][/b] days\n" ) - table = self.generate_project_table(project_info=response_json.get("project_info")) + table = self.generate_project_table(project_info=project_info) dds_cli.utils.console.print(table) dds_cli.utils.console.print(print_info) From d81c2517f05b5f82a9549f5a4fd88d8094a8b5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:50:35 +0200 Subject: [PATCH 29/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index ae4a4f83..5561365c 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -207,7 +207,7 @@ def extend_deadline(self, new_deadline=None): # Question number of days to extend the deadline prompt_question = ( "How many days would you like to extend the project deadline with? " - f"Leave empty in order to choose the default ([b][green]{default_unit_days}[/green][/b])" + f"Leave empty in order to choose the default" ) new_deadline = rich.prompt.IntPrompt.ask(prompt_question, default=default_unit_days) From 61835abd99e529c562b0e3a0b03cad33aef8bad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:51:04 +0200 Subject: [PATCH 30/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 5561365c..04369d72 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -212,7 +212,7 @@ def extend_deadline(self, new_deadline=None): new_deadline = rich.prompt.IntPrompt.ask(prompt_question, default=default_unit_days) # Confirm operation question - new_deadline_date = str(parse(current_deadline) + datetime.timedelta(days=new_deadline)) + new_deadline_date = parse(current_deadline) + datetime.timedelta(days=new_deadline) prompt_question = ( f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" "\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation? " From 2f10d1f39b7e903b08b1345ff38cb819df9023a0 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 14:03:17 +0200 Subject: [PATCH 31/35] feedback --- dds_cli/project_status.py | 20 +++++++++- tests/test_project_status.py | 76 ++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 04369d72..399620e7 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -183,9 +183,24 @@ def extend_deadline(self, new_deadline=None): # Check that the returned information was ok keys = ["project_info", "project_status", "default_unit_days"] - project_info, project_status, default_unit_days, *_ = dds_cli.utils.get_required_in_response(keys=keys, response=response_json) + ( + project_info, + project_status, + default_unit_days, + *_, + ) = dds_cli.utils.get_required_in_response(keys=keys, response=response_json) # Check and extract the required information for the operation + current_status, *_ = dds_cli.utils.get_required_in_response( + keys=["current_status"], response=project_status + ) + + # if the project is still in progress it won't have a current_deadline parameter + if not current_status == "Available": + raise exceptions.DDSCLIException( + "You can only extend the deadline for a project that has the status 'Available'." + ) + current_deadline, *_ = dds_cli.utils.get_required_in_response( keys=["current_deadline"], response=project_status ) @@ -207,12 +222,13 @@ def extend_deadline(self, new_deadline=None): # Question number of days to extend the deadline prompt_question = ( "How many days would you like to extend the project deadline with? " - f"Leave empty in order to choose the default" + "Leave empty in order to choose the default" ) new_deadline = rich.prompt.IntPrompt.ask(prompt_question, default=default_unit_days) # Confirm operation question new_deadline_date = parse(current_deadline) + datetime.timedelta(days=new_deadline) + new_deadline_date = new_deadline_date.strftime("%d/%m/%Y, %H:%M:%S") prompt_question = ( f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" "\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation? " diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 53a39150..46dead02 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -45,6 +45,11 @@ "warning": "Operation must be confirmed before proceding.", "project_status": {"current_deadline": deadline, "current_status": "Available"}, } +returned_response_extend_deadline_fetch_information_in_progress = { + **returned_response_extend_deadline_fetch_information, + "project_status": {"current_status": "In progress"}, +} + returned_response_extend_deadline_ok: typing.Dict = { "message": f"Project {project_name} has been given a new deadline." } @@ -366,33 +371,29 @@ def test_extend_deadline_no_confirmed( ) -def test_no_msg_returned_request(capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture): - """Error - no message returned from request""" +def test_extend_deadline_no_available( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + """If the project is not in status available the operation should fail""" - confirmed = True + confirmed = False caplog.set_level(logging.INFO) # Create mocker with Mocker() as mock: - # set confirmation object to true + # set confirmation object to false monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) # Set number of days to extend deadline - Bc of the default value in the function we need to create a mock answer with unittest.mock.patch("rich.prompt.IntPrompt.ask") as deadline: - deadline.return_value = 1 + deadline.return_value = 2 - # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) + # Create first mocked request - not confirmed mock.patch( DDSEndpoint.UPDATE_PROJ_STATUS, - [ - { - "status_code": 200, - "json": returned_response_extend_deadline_fetch_information, - }, - {"status_code": 200, "json": {}}, # empty response - ], + status_code=200, + json=returned_response_extend_deadline_fetch_information_in_progress, ) - # capture system exit on not accepting operation with pytest.raises(DDSCLIException) as err: with project_status.ProjectStatusManager( project=project_name, no_prompt=True, authenticate=False @@ -400,9 +401,10 @@ def test_no_msg_returned_request(capsys: CaptureFixture, monkeypatch, caplog: Lo status_mngr.token = {} # required, otherwise none status_mngr.extend_deadline() - captured_output = capsys.readouterr() - check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) - assert "No message returned from API." in str(err.value) + assert ( + "You can only extend the deadline for a project that has the status 'Available'." + in str(err.value) + ) def test_extend_deadline_confirmed_ok( @@ -446,3 +448,43 @@ def test_extend_deadline_confirmed_ok( returned_response_extend_deadline_ok["message"], ) in caplog.record_tuples check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + + +def test_extend_deadline_no_msg_returned_request( + capsys: CaptureFixture, monkeypatch, caplog: LogCaptureFixture +): + """Error - no message returned from request""" + + confirmed = True + caplog.set_level(logging.INFO) + + # Create mocker + with Mocker() as mock: + # set confirmation object to true + monkeypatch.setattr("rich.prompt.Confirm.ask", lambda question: confirmed) + # Set number of days to extend deadline - Bc of the default value in the function we need to create a mock answer + with unittest.mock.patch("rich.prompt.IntPrompt.ask") as deadline: + deadline.return_value = 1 + + # Mock a dyanic request, the second call should return a different response thatn the first one (operation is confirmed) + mock.patch( + DDSEndpoint.UPDATE_PROJ_STATUS, + [ + { + "status_code": 200, + "json": returned_response_extend_deadline_fetch_information, + }, + {"status_code": 200, "json": {}}, # empty response + ], + ) + + with pytest.raises(DDSCLIException) as err: + with project_status.ProjectStatusManager( + project=project_name, no_prompt=True, authenticate=False + ) as status_mngr: + status_mngr.token = {} # required, otherwise none + status_mngr.extend_deadline() + + captured_output = capsys.readouterr() + check_output_extend_deadline(captured_output=captured_output, caplog_tuples=None) + assert "No message returned from API." in str(err.value) From 8a20679ab0435f5c350c0bc1c31dd534b990a5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:10:27 +0200 Subject: [PATCH 32/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index 399620e7..e2ad6f31 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -161,6 +161,7 @@ def extend_deadline(self, new_deadline=None): method="patch", params={"project": self.project}, json=extra_params, + error_message="Failed to extend project deadline" ) # Structure of the response: From ea57cef783fb2dfa385b5ca6d3ffbb7727700467 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Mon, 23 Oct 2023 17:11:43 +0200 Subject: [PATCH 33/35] black --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index e2ad6f31..eca71ec4 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -161,7 +161,7 @@ def extend_deadline(self, new_deadline=None): method="patch", params={"project": self.project}, json=extra_params, - error_message="Failed to extend project deadline" + error_message="Failed to extend project deadline", ) # Structure of the response: From a47159bbac3ae058fa49adf86e55b4714a2d5ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Revuelta?= <46089290+rv0lt@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:07:24 +0200 Subject: [PATCH 34/35] Update dds_cli/project_status.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com> --- dds_cli/project_status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index eca71ec4..b3e2199d 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -250,6 +250,7 @@ def extend_deadline(self, new_deadline=None): method="patch", params={"project": self.project}, json=extra_params, + error_message="Failed to extend project deadline", ) message = response_json.get("message") if not message: From eaa7e4ace17208b4cc4feb9eb7432e5e4eeb9ca8 Mon Sep 17 00:00:00 2001 From: rv0lt Date: Tue, 24 Oct 2023 11:09:47 +0200 Subject: [PATCH 35/35] format date --- dds_cli/project_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dds_cli/project_status.py b/dds_cli/project_status.py index b3e2199d..253a61e2 100644 --- a/dds_cli/project_status.py +++ b/dds_cli/project_status.py @@ -229,7 +229,7 @@ def extend_deadline(self, new_deadline=None): # Confirm operation question new_deadline_date = parse(current_deadline) + datetime.timedelta(days=new_deadline) - new_deadline_date = new_deadline_date.strftime("%d/%m/%Y, %H:%M:%S") + new_deadline_date = new_deadline_date.strftime("%a,%d %b %Y %H:%M:%S") prompt_question = ( f"\nThe new deadline for project {project_id} will be: [b][blue]{new_deadline_date}[/b][/blue]" "\n\n[b][blue]Are you sure [/b][/blue]you want to perform this operation? "