diff --git a/haupt/haupt/apis/methods/entity_stages.py b/haupt/haupt/apis/methods/entity_stages.py index 54d24e8ba1..854b5b8253 100644 --- a/haupt/haupt/apis/methods/entity_stages.py +++ b/haupt/haupt/apis/methods/entity_stages.py @@ -1,5 +1,8 @@ from typing import Optional +from django.conf import settings + +from haupt.common.authentication.base import is_normal_user from haupt.db.managers.stages import new_stage from haupt.db.managers.versions import add_version_contributors from polyaxon.schemas import V1StageCondition @@ -13,12 +16,21 @@ def create_stage(view, serializer, event_type: Optional[str] = None): condition = None if validated_data.get("condition"): condition = V1StageCondition.get_condition(**validated_data.get("condition")) - if condition: - new_stage( - entity=view.version, - owner_id=view._owner_id, - user=view.request.user, - condition=condition, - event_type=event_type, - ) - add_version_contributors(view.version, users=[view.request.user]) + if not condition: + return + if settings.HAS_ORG_MANAGEMENT and is_normal_user(view.request.user): + status_meta_info = { + "user": { + "username": view.request.user.username, + "email": view.request.user.email, + }, + } + condition.meta_info = status_meta_info + new_stage( + entity=view.version, + owner_id=view._owner_id, + user=view.request.user, + condition=condition, + event_type=event_type, + ) + add_version_contributors(view.version, users=[view.request.user]) diff --git a/haupt/haupt/apis/methods/project_resources.py b/haupt/haupt/apis/methods/project_resources.py index c5cd370fe3..94bb411218 100644 --- a/haupt/haupt/apis/methods/project_resources.py +++ b/haupt/haupt/apis/methods/project_resources.py @@ -6,6 +6,7 @@ from haupt.apis.serializers.base.tags import TagsMixin from haupt.common import auditor +from haupt.common.authentication.base import is_normal_user from haupt.common.content_types import ContentTypes from haupt.common.events.registry.archive import RUN_ARCHIVED_ACTOR, RUN_RESTORED_ACTOR from haupt.common.events.registry.run import ( @@ -63,6 +64,11 @@ def stop_runs(view, request, actor, *args, **kwargs): reason="EventHandler", message="User requested to stop the run.", ) + if settings.HAS_ORG_MANAGEMENT and is_normal_user(actor): + status_meta_info = { + "user": {"username": actor.username, "email": actor.email}, + } + condition.meta_info = status_meta_info bulk_new_run_status(queryset, condition) # For Audit for run in runs: @@ -87,6 +93,11 @@ def stop_runs(view, request, actor, *args, **kwargs): reason="EventHandler", message="User requested to stop the run.", ) + if settings.HAS_ORG_MANAGEMENT and is_normal_user(actor): + status_meta_info = { + "user": {"username": actor.username, "email": actor.email}, + } + condition.meta_info = status_meta_info bulk_new_run_status(runs, condition) # For Audit for run in runs: @@ -120,6 +131,11 @@ def skip_runs(view, request, actor, *args, **kwargs): reason="EventHandler", message="User requested to skip the run.", ) + if settings.HAS_ORG_MANAGEMENT and is_normal_user(actor): + status_meta_info = { + "user": {"username": actor.username, "email": actor.email}, + } + condition.meta_info = status_meta_info bulk_new_run_status(queryset, condition) # For Audit for run in runs: diff --git a/haupt/haupt/apis/methods/runs.py b/haupt/haupt/apis/methods/runs.py index 8420666d5b..7aa55ff455 100644 --- a/haupt/haupt/apis/methods/runs.py +++ b/haupt/haupt/apis/methods/runs.py @@ -7,6 +7,7 @@ from django.conf import settings from django.db.models import Q +from haupt.common.authentication.base import is_normal_user from haupt.db.defs import Models from haupt.db.managers.live_state import archive_run as base_archive_run from haupt.db.managers.live_state import restore_run as base_restore_run @@ -57,21 +58,54 @@ def create_status(view, serializer): condition = None if validated_data.get("condition"): condition = V1StatusCondition.get_condition(**validated_data.get("condition")) - if condition: - new_run_status( - run=view.run, condition=condition, force=validated_data.get("force", False) - ) + if not condition: + return + if settings.HAS_ORG_MANAGEMENT and is_normal_user(view.request.user): + status_meta_info = { + "user": { + "username": view.request.user.username, + "email": view.request.user.email, + }, + } + condition.meta_info = status_meta_info + new_run_status( + run=view.run, condition=condition, force=validated_data.get("force", False) + ) def stop_run(view, request, *args, **kwargs): - if new_run_stopping_status(run=view.run, message="User requested to stop the run."): + status_meta_info = None + if settings.HAS_ORG_MANAGEMENT and is_normal_user(request.user): + status_meta_info = { + "user": { + "username": view.request.user.username, + "email": view.request.user.email, + }, + } + if new_run_stopping_status( + run=view.run, + message="User requested to stop the run.", + meta_info=status_meta_info, + ): add_run_contributors(view.run, users=[request.user]) view.audit(request, *args, **kwargs) return Response(status=status.HTTP_200_OK, data={}) def skip_run(view, request, *args, **kwargs): - if new_run_skipped_status(run=view.run, message="User requested to skip the run."): + status_meta_info = None + if settings.HAS_ORG_MANAGEMENT and is_normal_user(request.user): + status_meta_info = { + "user": { + "username": view.request.user.username, + "email": view.request.user.email, + }, + } + if new_run_skipped_status( + run=view.run, + message="User requested to skip the run.", + meta_info=status_meta_info, + ): add_run_contributors(view.run, users=[request.user]) view.audit(request, *args, **kwargs) return Response(status=status.HTTP_200_OK, data={}) diff --git a/haupt/haupt/db/managers/statuses.py b/haupt/haupt/db/managers/statuses.py index 4303cd0eb5..34481dcf84 100644 --- a/haupt/haupt/db/managers/statuses.py +++ b/haupt/haupt/db/managers/statuses.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any, Dict, List, Optional from clipped.utils.lists import to_list @@ -165,7 +165,9 @@ def new_run_status( ) -def new_run_stop_status(run, message): +def new_run_stop_status( + run: Models.Run, message: str, meta_info: Optional[Dict] = None +): # Update run status to show that its stopped message = f"Run is stopped; {message}" if message else "Run is stopped" condition = V1StatusCondition.get_condition( @@ -173,16 +175,19 @@ def new_run_stop_status(run, message): status="True", reason="StateManager", message=message, + meta_info=meta_info, ) new_run_status(run=run, condition=condition) -def new_run_stopping_status(run, message) -> bool: +def new_run_stopping_status( + run: Models.Run, message: str, meta_info: Optional[Dict] = None +) -> bool: if LifeCycle.is_done(run.status, progressing=True): return False if LifeCycle.is_safe_stoppable(run.status): - new_run_stop_status(run, message) + new_run_stop_status(run, message, meta_info=meta_info) return True message = f"Run is stopping; {message}" if message else "Run is stopping" condition = V1StatusCondition.get_condition( @@ -190,12 +195,15 @@ def new_run_stopping_status(run, message) -> bool: status="True", reason="StateManager", message=message, + meta_info=meta_info, ) new_run_status(run=run, condition=condition) return True -def new_run_skip_status(run, message): +def new_run_skip_status( + run: Models.Run, message: str, meta_info: Optional[Dict] = None +): # Update run status to show that its stopped message = f"Run is skipped; {message}" if message else "Run is skipped" condition = V1StatusCondition.get_condition( @@ -203,15 +211,18 @@ def new_run_skip_status(run, message): status="True", reason="StateManager", message=message, + meta_info=meta_info, ) new_run_status(run=run, condition=condition) -def new_run_skipped_status(run, message) -> bool: +def new_run_skipped_status( + run: Models.Run, message: str, meta_info: Optional[Dict] = None +) -> bool: if LifeCycle.is_done(run.status, progressing=True): return False if not LifeCycle.is_safe_stoppable(run.status): return False - new_run_skip_status(run, message) + new_run_skip_status(run, message, meta_info) return True diff --git a/haupt/haupt/orchestration/operations/service.py b/haupt/haupt/orchestration/operations/service.py index 3aa5b7fde9..9458a1b920 100644 --- a/haupt/haupt/orchestration/operations/service.py +++ b/haupt/haupt/orchestration/operations/service.py @@ -376,6 +376,7 @@ def resume_run( **kwargs, ): meta_info = kwargs.pop("meta_info", {}) or {} + status_meta_info = kwargs.pop("status_meta_info", None) recompile = meta_info.pop(META_RECOMPILE, False) if recompile: op_spec = V1Operation.read(content) @@ -406,14 +407,17 @@ def resume_run( run.tags = instance.tags run.params = instance.params run.save() + status_condition = V1StatusCondition.get_condition( + type=V1Statuses.RESUMING, + status=True, + reason="ResumeManager", + message=message, + ) + if status_meta_info: + status_condition.meta_info = status_meta_info new_run_status( run, - condition=V1StatusCondition.get_condition( - type=V1Statuses.RESUMING, - status=True, - reason="ResumeManager", - message=message, - ), + condition=status_condition, force=True, ) return run