Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions flow360/component/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,7 @@ def _run(
use_geometry_AI: bool,
raise_on_error: bool,
tags: List[str],
draft_only: bool,
**kwargs,
):
"""
Expand Down Expand Up @@ -1277,11 +1278,13 @@ def _run(
Option to raise if submission error occurs (default is False)
tags: List[str], optional
A list of tags to add to the target asset.
draft_only: bool, optional
Whether to only create and submit a draft and not run the simulation.

Returns
-------
AssetOrResource
The destination asset
The destination asset or the draft if `draft_only` is True.

Raises
------
Expand Down Expand Up @@ -1349,6 +1352,15 @@ def _run(

draft.update_simulation_params(params)

if draft_only:
# pylint: disable=import-outside-toplevel
import click

log.info("Draft submitted, copy the link to browser to view the draft:")
# Not using log.info to avoid the link being wrapped and thus not clickable.
click.secho(draft.web_url, fg="blue", underline=True)
return draft

try:
destination_id = draft.run_up_to_target_asset(
target,
Expand Down Expand Up @@ -1398,6 +1410,7 @@ def generate_surface_mesh(
use_geometry_AI: bool = False, # pylint: disable=invalid-name
raise_on_error: bool = False,
tags: List[str] = None,
draft_only: bool = False,
**kwargs,
):
"""
Expand All @@ -1421,11 +1434,18 @@ def generate_surface_mesh(
Option to raise if submission error occurs (default is False)
tags: List[str], optional
A list of tags to add to the generated surface mesh.
draft_only: bool, optional
Whether to only create and submit a draft and not generate the surface mesh.

Raises
------
Flow360ValueError
If the root item type is not Geometry.

Returns
-------
SurfaceMeshV2 | Draft
The surface mesh asset or the draft if `draft_only` is True.
"""
self._check_initialized()
if self.metadata.root_item_type is not RootType.GEOMETRY:
Expand All @@ -1444,6 +1464,7 @@ def generate_surface_mesh(
use_geometry_AI=use_geometry_AI,
raise_on_error=raise_on_error,
tags=tags,
draft_only=draft_only,
**kwargs,
)
return surface_mesh
Expand All @@ -1459,6 +1480,7 @@ def generate_volume_mesh(
use_geometry_AI: bool = False, # pylint: disable=invalid-name
raise_on_error: bool = False,
tags: List[str] = None,
draft_only: bool = False,
**kwargs,
):
"""
Expand All @@ -1482,11 +1504,18 @@ def generate_volume_mesh(
Option to raise if submission error occurs (default is False)
tags: List[str], optional
A list of tags to add to the generated volume mesh.
draft_only: bool, optional
Whether to only create and submit a draft and not generate the volume mesh.

Raises
------
Flow360ValueError
If the root item type is not Geometry.

Returns
-------
VolumeMeshV2 | Draft
The volume mesh asset or the draft if `draft_only` is True.
"""
self._check_initialized()
if (
Expand All @@ -1496,7 +1525,7 @@ def generate_volume_mesh(
raise Flow360ValueError(
"Volume mesher can only be run by projects with a geometry or surface mesh root asset"
)
volume_mesh = self._run(
volume_mesh_or_draft = self._run(
params=params,
target=VolumeMeshV2,
draft_name=name,
Expand All @@ -1508,8 +1537,13 @@ def generate_volume_mesh(
use_geometry_AI=use_geometry_AI,
raise_on_error=raise_on_error,
tags=tags,
draft_only=draft_only,
**kwargs,
)
if draft_only:
draft = volume_mesh_or_draft
return draft
volume_mesh = volume_mesh_or_draft
return volume_mesh

@pd.validate_call(config={"arbitrary_types_allowed": True})
Expand All @@ -1525,6 +1559,7 @@ def run_case(
use_geometry_AI: bool = False, # pylint: disable=invalid-name
raise_on_error: bool = False,
tags: List[str] = None,
draft_only: bool = False,
**kwargs,
):
"""
Expand Down Expand Up @@ -1552,9 +1587,16 @@ def run_case(
Option to raise if submission error occurs (default is False)
tags: List[str], optional
A list of tags to add to the case.
draft_only: bool, optional
Whether to only create and submit a draft and not run the case.

Returns
-------
Case | Draft
The case asset or the draft if `draft_only` is True.
"""
self._check_initialized()
case = self._run(
case_or_draft = self._run(
params=params,
target=Case,
draft_name=name,
Expand All @@ -1566,8 +1608,14 @@ def run_case(
use_geometry_AI=use_geometry_AI,
raise_on_error=raise_on_error,
tags=tags,
draft_only=draft_only,
**kwargs,
)

if draft_only:
draft = case_or_draft
return draft
case = case_or_draft
report_template = get_default_report_summary_template()
report_template.create_in_cloud(
name=f"{name}-summary",
Expand Down
2 changes: 1 addition & 1 deletion flow360/component/resource_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def _from_meta(cls, meta):
"This is abstract method. Needs to be implemented by specialised class."
)

def get_info(self, force=False) -> AssetMetaBaseModel:
def get_info(self, force=False):
"""
returns metadata info for resource
"""
Expand Down
39 changes: 31 additions & 8 deletions flow360/component/simulation/web/draft.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

import ast
import json
from functools import cached_property
from typing import Literal, Union

from pydantic import BaseModel, ConfigDict, Field

from flow360.cloud.flow360_requests import (
DraftCreateRequest,
DraftRunRequest,
Expand All @@ -14,16 +17,25 @@
)
from flow360.cloud.rest_api import RestApi
from flow360.component.interfaces import DraftInterface
from flow360.component.resource_base import (
AssetMetaBaseModel,
Flow360Resource,
ResourceDraft,
)
from flow360.component.resource_base import Flow360Resource, ResourceDraft
from flow360.component.utils import formatting_validation_errors, validate_type
from flow360.environment import Env
from flow360.exceptions import Flow360RuntimeError, Flow360WebError
from flow360.log import log


class DraftMetaModel(BaseModel):
"""Draft metadata deserializer"""

type: Literal["Draft"] = "Draft"
name: str
id: str
project_id: str = Field(alias="projectId")
solver_version: str = Field(alias="solverVersion")

model_config = ConfigDict(extra="ignore")


class DraftDraft(ResourceDraft):
"""
Draft Draft component
Expand Down Expand Up @@ -71,14 +83,14 @@ class Draft(Flow360Resource):
def __init__(self, draft_id: IDStringType):
super().__init__(
interface=DraftInterface,
meta_class=AssetMetaBaseModel, # We do not have dedicated meta class for Draft
meta_class=DraftMetaModel, # We do not have dedicated meta class for Draft
id=draft_id,
)

@classmethod
# pylint: disable=protected-access
def _from_meta(cls, meta: AssetMetaBaseModel):
validate_type(meta, "meta", AssetMetaBaseModel)
def _from_meta(cls, meta: DraftMetaModel):
validate_type(meta, "meta", DraftMetaModel)
resource = cls(draft_id=meta.id)
return resource

Expand Down Expand Up @@ -185,3 +197,14 @@ def run_up_to_target_asset(
"An unexpected error has occurred. Please contact customer support."
) from None
raise RuntimeError("Submission not successful.")

@cached_property
def project_id(self) -> str:
"""Get the project ID of the draft"""
return self.info.project_id

@property
def web_url(self) -> str:
"""Get the web URL of the draft"""

return Env.current.web_url + f"/workbench/{self.project_id}?id={self.id}&type=Draft"
Loading