diff --git a/euphro_tools/hooks.py b/euphro_tools/hooks.py index 6046769bf..93eb5aa0b 100644 --- a/euphro_tools/hooks.py +++ b/euphro_tools/hooks.py @@ -50,3 +50,19 @@ def initialize_run_directory(project_name: str, run_name: str): response.status_code, response.text, ) + + +def rename_run_directory(project_name: str, run_name: str, new_run_name: str): + base_url = os.environ["EUPHROSYNE_TOOLS_API_URL"] + response = _make_request( + f"{base_url}/data/{project_name}/runs/{run_name}/rename/{new_run_name}" + ) + if response is not None and not response.ok: + logger.error( + "Could not update run directory name from %s to %s of project %s. %s: %s", + run_name, + new_run_name, + project_name, + response.status_code, + response.text, + ) diff --git a/euphro_tools/tests/test_hooks.py b/euphro_tools/tests/test_hooks.py index 03b26f786..3fe2308a0 100644 --- a/euphro_tools/tests/test_hooks.py +++ b/euphro_tools/tests/test_hooks.py @@ -7,6 +7,7 @@ _make_request, initialize_project_directory, initialize_run_directory, + rename_run_directory, ) @@ -32,6 +33,17 @@ def test_initialize_run_directory(requests_mock: MagicMock, monkeypatch: MonkeyP ) +@patch("euphro_tools.hooks.requests") +def test_rename_run_directory(requests_mock: MagicMock, monkeypatch: MonkeyPatch): + monkeypatch.setenv("EUPHROSYNE_TOOLS_API_URL", "http://euphro.tools") + rename_run_directory("project", "run", "newname") + + requests_mock.post.assert_called_once() + assert requests_mock.post.call_args[0] == ( + "http://euphro.tools/data/project/runs/run/rename/newname", + ) + + @patch("euphro_tools.hooks.requests") @patch("euphro_tools.hooks.EuphroToolsAPIToken") def test_make_request_has_auth_header( diff --git a/lab/admin/run.py b/lab/admin/run.py index 26be632fd..d64b4f218 100644 --- a/lab/admin/run.py +++ b/lab/admin/run.py @@ -9,7 +9,7 @@ from django.http.request import HttpRequest from django.utils.translation import gettext_lazy as _ -from euphro_tools.hooks import initialize_run_directory +from euphro_tools.hooks import initialize_run_directory, rename_run_directory from ..forms import RunDetailsForm from ..models import Project, Run @@ -143,7 +143,7 @@ def get_readonly_fields( readonly_fields = (*super().get_readonly_fields(request, obj), "status") if obj: # Prevent changing label after creation (because of data folder sync). - readonly_fields += ("project", "label") + readonly_fields += ("project",) if not is_lab_admin(request.user): readonly_fields += ("start_date", "end_date") @@ -229,7 +229,9 @@ def changelist_view( def save_model(self, request: Any, obj: Run, form: ModelForm, change: bool) -> None: super().save_model(request, obj, form, change) if not change: - initialize_run_directory(obj.label, obj.project.name) + initialize_run_directory(obj.project.name, obj.label) + elif "label" in form.changed_data: + rename_run_directory(obj.project.name, form.initial["label"], obj.label) def _get_project(self, request, object_id=None) -> Optional[Project]: if object_id: diff --git a/lab/tests/admin/test_run_admin.py b/lab/tests/admin/test_run_admin.py index 21b8f9da3..534b2121d 100644 --- a/lab/tests/admin/test_run_admin.py +++ b/lab/tests/admin/test_run_admin.py @@ -76,13 +76,6 @@ def test_project_is_readonly_when_change(self): request.user = self.lab_admin_user assert "project" in self.run_admin.get_readonly_fields(request, self.run) - def test_label_is_readonly_when_change(self): - request = RequestFactory().get( - reverse("admin:lab_run_change", args=[self.run.id]) - ) - request.user = self.lab_admin_user - assert "label" in self.run_admin.get_readonly_fields(request, self.run) - def test_dates_are_readonly_when_change_as_non_admin(self): request = RequestFactory().get( reverse("admin:lab_run_change", args=[self.run.id]) @@ -188,7 +181,48 @@ def test_add_run_calls_init_directroy_hook(self, init_run_dir_mock): form=MagicMock(), change=False, ) - init_run_dir_mock.assert_called_once_with(run.label, run.project.name) + init_run_dir_mock.assert_called_once_with(run.project.name, run.label) + + @patch("lab.admin.run.rename_run_directory") + def test_change_run_label_calls_hook(self, rename_run_dir_mock): + run = factories.RunFactory() + run.project.members.add(self.staff_user) + request = self.request_factory.post( + reverse( + "admin:lab_run_change", + args=[run.id], + ), + data={ + "label": "new-run-name", + "particle_type": "", + "energy_in_keV_Proton": "", + "energy_in_keV_Alpha particle": "", + "energy_in_keV_Deuton": "", + "beamline": "Microbeam", + "filters_for_detector_LE0": "", + "filters_for_detector_HE1": "", + "filters_for_detector_HE2": "", + "filters_for_detector_HE3": "", + "filters_for_detector_HE4": "", + "detector_IBIL_other": "", + "detector_FORS_other": "", + "detector_ERDA": "", + "detector_NRA": "", + "Run_run_object_groups-TOTAL_FORMS": "0", + "Run_run_object_groups-INITIAL_FORMS": "0", + }, + ) + request.user = self.staff_user + with patch.object(run, "save"): + run_admin = RunAdmin(Run, admin_site=AdminSite()) + form_class = run_admin.get_form(request, obj=run, change=True) + form = form_class(request.POST, request.FILES, instance=run) + # Clean form to populate run instance + form.is_valid() + run_admin.save_model(request, run, form=form, change=True) + rename_run_dir_mock.assert_called_once_with( + run.project.name, form.initial["label"], "new-run-name" + ) class TestRunAdminViewAsAdmin(TestCase):