Python으로 Report와 Flow 함께 만들기
계산과 보고서를 화면 대신 Python 코드로 작성하는 방법입니다. 코드로 만들어도 결과물은 화면에서 만든 것과 같아서, 저장한 뒤 PXFLOW Studio와 Report Workbench에서 그대로 열어 이어서 편집합니다.
pipelinexlab SDK 하나로 두 가지를 작성합니다.
| 작성하는 것 | 쓰는 객체 | 하는 일 | 저장되는 파일 | 열리는 화면 |
|---|---|---|---|---|
| 계산 | px.Flow | 값을 입력받아 계산하고 결과를 내보냅니다 | .pxflow | PXFLOW Studio |
| 보고서 | px.Report | 본문·값·표를 쓰고 계산 결과가 들어갈 자리를 정합니다 | .pxreport | Report Workbench |
두 객체를 같은 Python 파일에 나란히 써도 저장하면 .pxflow와 .pxreport 두 문서로 나뉩니다. 같은 계산을 여러 보고서에서 써야 하면 px.Flow를 하나만 만들고 Report마다 연결합니다. 계산은 그대로 두고 문서 내용만 다르게 씁니다.
pip install pipelinexlabfrom pipelinexlab import pxSDK 이름을 읽는 법
px는 PipelineXLab SDK의 하나뿐인 public 진입점입니다. SDK가 제공하는 객체·타입·선언 도구는 px.에서 찾고, 만든 객체가 소유하는 작업은 해당 변수에서 찾습니다.
| 먼저 쓰는 코드 | 받은 객체·handle | 그 객체에서 호출하는 메서드 |
|---|---|---|
flow = px.Flow(...) | Flow 객체 flow | .input(), .node(), .connect(), .result() |
report = px.Report(...) 또는 px.Report.open(...) | Report 객체 report | .section(), .connect_flow(), .save() |
summary = report.section(...) | Section handle summary | .text(), .value(), .table(), .attachment(), 결과 위치 메서드 |
connection = report.connect_flow(...) | Connected Flow handle connection | .bind(), .show() |
client = px.Client(...) | Client 객체 client | .run(), .run_many(), .run_batch() |
check = flow.node(...) | Node handle check | check.inputs.span, check.results.utilization |
px. 뒤에 오는 이름은 첫 글자만 보면 어떻게 쓰는지 알 수 있습니다.
| 첫 글자 | 이런 이름 | 그 줄에서 일어나는 일 | 쓰는 자리 |
|---|---|---|---|
| 대문자 | px.Flow, px.Report, px.Client, px.Results, px.Table, px.Artifact | 그 자리에서 하나가 생겨 변수에 담깁니다 | flow = px.Flow(...)로 받아서 이후 flow.node(...)처럼 그 변수로 이어 작업합니다 |
| 소문자 | px.input, px.result | 값에 단위와 설명이 붙습니다 | 함수 인자의 기본값 자리 |
소문자 앞에 @ | @px.function | 바로 아래 함수가 Flow의 Node에서 쓸 수 있는 계산으로 등록됩니다 | 함수 정의 바로 윗줄 |
소문자 앞에 @ | @px.component | 현재 V1 companion descriptor metadata를 등록합니다. 이 decorator만으로 production Flow Node나 설치 권한이 생기지 않습니다 | contract-only Component authoring 설명 |
대문자로 시작하는 이름은 단어마다 첫 글자를 대문자로 쓰고 띄어쓰기와 밑줄 없이 이어 붙입니다. px.Flow, px.Report, px.Client가 이 모양이고, 이 표기를 PascalCase라고 부릅니다.
함수 하나를 Node 여러 개에서 쓰기
@px.function을 붙인 함수는 계산 내용입니다. Flow에 놓이는 것은 Node이고, 함수는 그 Node가 가리키는 계산으로 남습니다. flow.node("node_key", 함수) 한 줄이 하는 일은 두 가지입니다.
- 이 Flow 안에 Node를 하나 만듭니다. Canvas에서 상자 하나로 보입니다.
- 그 Node가 어떤 함수를 쓸지 지정합니다.
같은 함수를 두 Node에서 씁니다. Node마다 입력값을 따로 받아 결과도 따로 냅니다.
uls_check = flow.node("uls_check", check_resistance) # Node 1개
sls_check = flow.node("sls_check", check_resistance) # Node 1개 더, 같은 함수를 씁니다2
함수는 하나이므로 계산 내용을 고치면 그 함수를 쓰는 Node에 모두 반영됩니다.
Project Definitions → Contract & usage — 화면 예시에서는 girder_resistance 함수 하나를 uls_check와 sls_check 두 Node가 사용합니다. 오른쪽 Used by가 그 Node 목록이고, 아래에 2 Nodes · shared Function으로 표시됩니다. 왼쪽 목록의 함수마다 붙은 2 Nodes, 3 Nodes, 0 Nodes가 그 함수를 쓰는 Node 개수입니다.
| 쓰는 코드 | 무엇을 정하나 | 몇 개 만드나 | |
|---|---|---|---|
| 계산 내용 (Function) | @px.function을 붙인 함수 | 어떤 값을 받아 무엇을 계산할지 | 계산 종류마다 하나 |
| Node | flow.node("node_key", 함수) | 그 계산을 쓸 자리와 그 자리의 이름 | 계산을 쓰는 자리마다 하나씩 |
화면에서 Node를 만들 때도 같은 두 가지를 지정합니다. 함수를 고르고 Node key를 정하면 flow.node(...) 한 줄이 그대로 만들어집니다.
Place Node — 왼쪽에서 함수 girder_resistance를 고르고 Node key에 girder_resistance_3을 입력하면 아래에 girder_resistance_3 = flow.node("girder_resistance_3", girder_resistance)가 표시됩니다. Place on canvas를 누르면 그 Node가 Canvas에 생깁니다.
px.Flow(...)가 만드는 것은 아직 Node가 없는 Flow 하나입니다. flow.node(...)를 부를 때마다 그 Flow 안에 Node가 하나씩 생깁니다. 즉 Flow가 담고 있는 것은 Node와 그 연결입니다.
Node를 이으면 값이 전달됩니다
flow.connect(앞, 뒤)는 앞의 결과값을 뒤 Node의 입력으로 보냅니다. 값이 이 순서로 흐르므로 계산 순서도 함께 정해집니다. 앞 Node가 먼저 계산되고, 그 결과가 뒤 Node의 입력으로 들어갑니다.
span = flow.input("span", float, unit="m", description="Clear span.")
check = flow.node("resistance_check", check_resistance)
summary = flow.node("summary", format_summary)
flow.connect(span, check.inputs.span) # ① 밖에서 받은 값 → check
flow.connect(check.results.utilization, summary.inputs.utilization) # ② check 결과 → summary
flow.result("utilization", check.results.utilization) # ③ check 결과 → Flow 밖2
3
4
5
6
7
8
| 코드 | 무엇이 전달되나 | Canvas에서 보이는 것 |
|---|---|---|
① flow.connect(span, check.inputs.span) | Flow가 밖에서 받은 값이 Node 입력으로 | Flow 입력에서 상자로 가는 선 |
② flow.connect(check.results.utilization, summary.inputs.utilization) | 앞 Node의 결과가 뒤 Node 입력으로 | 상자에서 상자로 가는 선 |
③ flow.result("utilization", check.results.utilization) | Node의 결과가 Flow 밖으로 공개 | 상자에서 Flow 결과로 가는 선 |
여기까지 이으면 Run 한 번으로 check → summary 순서로 계산되고, ③으로 공개한 utilization을 Report 위치나 Result 영역에서 확인합니다. 입력을 밖에서 받을 자리는 ①처럼 flow.input(...)으로 공개하고, 결과를 밖에 보여 줄 자리는 ③처럼 flow.result(...)로 공개합니다.
@px.function( # ① 계산 내용을 함수로 씁니다
key="check_resistance",
version="1.0.0",
description="Checks girder demand against resistance.",
)
def check_resistance(
span: float = px.input(unit="m", description="Clear span."),
demand: float = px.input(unit="kN.m", description="Design demand."),
) -> CheckResults:
return CheckResults(utilization=demand / (span * 100.0))
flow = px.Flow( # ② Node가 아직 없는 Flow 하나가 생깁니다
"girder_check",
label="Girder check",
description="Checks girder demand against resistance.",
)
check = flow.node("resistance_check", check_resistance) # ③ 이 Flow 안에 Node 하나가 생기고 ①을 씁니다2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
계산이 실제로 수행되는 시점은 ③으로 만든 Node가 Run될 때입니다. 같은 함수를 Flow 여러 개에서 쓰거나 한 Flow 안에서 Node 두 개가 쓸 때도 ①은 한 번만 씁니다.
@px.function을 붙인 함수와 그냥 Python 함수
@px.function을 붙인 함수는 Definitions 목록에 올라가고 Node로 배치할 수 있습니다. decorator를 붙이지 않은 일반 def는 계산의 한 조각을 나눠 둔 helper이며, @px.function 본문에서 호출해 씁니다.
그냥 def | @px.function을 붙인 함수 | |
|---|---|---|
| 쓰는 방법 | 다른 함수 본문에서 호출합니다 | flow.node(...)로 Node를 만들어 씁니다 |
| 화면에서 보이는 곳 | 호출하는 Node 안에서 함께 실행됩니다 | Definitions → Functions 목록. flow.node(...)로 배치한 뒤에는 Canvas의 Node |
| 입력과 결과 | 일반 Python 인자와 반환값 | px.input·px.result로 이름·단위·설명이 붙은 port |
| 저장되는 이름 | Python 함수 이름 | key — Python 함수 이름을 바꿔도 그대로 유지됩니다 |
| version | 파일에 있는 현재 코드를 그대로 씁니다 | version을 직접 정하고, 어떤 version으로 계산했는지 실행 이력에 남습니다 |
| 다른 Flow에서 재사용 | import해서 호출합니다 | Definitions에서 골라 Node로 배치합니다 |
def rectangular_area(width: float, depth: float) -> float: # 계산 한 조각을 나눈 helper
return width * depth
@px.function( # Node에서 쓸 수 있는 계산으로 등록
key="calculate_area",
version="1.0.0",
description="Calculates the area of a rectangular section.",
)
def calculate_area(
width: float = px.input(unit="m", description="Section width."),
depth: float = px.input(unit="m", description="Section depth."),
) -> AreaResults:
return AreaResults(area=rectangular_area(width, depth))
area = flow.node("area", calculate_area) # 여기서 Canvas에 Node가 생깁니다2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@px.function을 붙인 시점까지 Canvas는 그대로이고, Definitions → Functions 목록에 calculate_area 한 줄이 늘어납니다. Canvas에 상자가 생기는 시점은 flow.node(...)를 부른 마지막 줄입니다. rectangular_area는 그 Node가 계산할 때 안에서 함께 실행됩니다.
helper를 여러 개로 나눠 @px.function 하나로 묶어도 화면에는 그 @px.function 하나만 나타납니다. Definitions 목록과 Canvas Node, port 목록은 decorator를 붙인 함수의 px.input·px.result에서만 읽습니다. helper의 인자와 반환값은 계산 중간값으로 남고, helper 코드는 Definitions → Code에서 그 Function의 source를 열어 확인합니다.
helper 안의 중간값은 화면에 나오지 않습니다
@px.function이 화면에 드러나는 경계입니다. helper가 계산한 중간값은 Node의 port로 올라오지 않고, 실행이 실패하면 그 Node에 PX_FUNCTION_FAILED 진단 하나로 표시됩니다. 검토자에게 보여야 하는 값은 아래 기준대로 경계 밖으로 꺼내 놓습니다.
| 이런 값이면 | 이렇게 씁니다 | 화면에 나오는 것 |
|---|---|---|
| 검토자에게 보여야 하는 값 | 그 값을 px.result에 추가해 결과로 내보냅니다 | Node의 result port와 Report의 결과 위치 |
| 다른 계산에서도 쓰는 단계 | 그 단계를 별도 @px.function으로 나눕니다 | Canvas에 Node가 하나 더 생기고, 연결선으로 이어집니다 |
| 코드를 짧게 나누려는 목적 | 일반 def helper로 둡니다 | 호출하는 Node 안에서 함께 실행됩니다 |
이렇게 경계를 나누면 helper를 합치거나 쪼개도 port와 Node가 그대로 유지되므로, 그 Function을 쓰는 Node와 Report 연결은 영향을 받지 않습니다. Python 문장마다 Node를 만들면 Canvas가 코드 줄 수만큼 늘어납니다. 자세한 규칙은 일반 Python helper에서 확인합니다.
flow, report, summary, connection, client는 SDK namespace가 아니라 사용자가 정한 변수 이름입니다. 현재 Flow를 숨겨 두는 전역 명령 대신 strength_flow.node(...)처럼 소유 객체를 앞에 씁니다. Flow module은 discovery가 항상 같도록 public flow 객체 하나만 export합니다. 같은 파일에 그 Flow를 사용하는 Report 하나를 함께 둘 수 있고, 여러 Flow가 필요하면 Flow별 module로 나눠 import합니다. 각 문장이 바꾸는 문서는 소유 객체 이름으로 드러납니다.
from pipelinexlab import px
flow = px.Flow(
"girder_check",
label="Girder check",
description="Checks girder demand against resistance.",
)
report = px.Report("girder_report", title="Girder review")
check = flow.node("resistance_check", check_resistance)
summary = report.section("summary", title="Summary")
connection = report.connect_flow("strength_check", flow)
client = px.Client(workspace="engineering", project="bridge_project")
report.save()
run = client.run(connection)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
아래 코드는 successor V2의 contract-only 예시입니다. exact ProjectScope data-path isolation은 구현됐지만 production third-party Component activation과 install admission은 Project authorization과 execution-trust closure가 모두 닫힐 때까지 차단되고, current reader는 exact target을 read-only unsupported로 유지합니다. 미래 Canvas projection은 host-owned component.declarative@2이고 settings를 component-declared-settings@1로 선언당 최대 8개까지 받고 host control registry가 그립니다. package-owned rich UI는 RFC-016의 component-rich-surface-declaration@1이 소유하고, production third-party admission은 그대로 차단됩니다.
승인된 successor에서 Component는 제공 package의 public import 이름을 유지하고, flow.node(...)가 어느 Flow에 배치되는지를 나타냅니다.
from midas_civil.components import model_viewer
viewer = flow.node("model_viewer", model_viewer)2
3
시작 전에 준비하기
- Python 환경에
pipelinexlab을 설치하고 PipelineXLab에 로그인합니다. - Report와 Flow를 저장할 Project를 만들거나 접근 권한이 있는 기존 Project를 선택합니다.
- 아래 예제의
bridge_project는 현재 Project key로 바꿉니다. Project key는 Project settings에서 확인합니다. - 같은 파일에서 새 Flow와 Report를 만들면 저장 과정이 Flow를 먼저 검증·저장한 뒤 Report를 저장합니다. 기존 Flow를 연결할 때는 읽을 수 있는 저장된 Flow를 import합니다.
먼저 구분할 두 문서
| Python 객체 | 작성하는 내용 | 저장 문서 | 편집 화면 |
|---|---|---|---|
px.Flow | Report와 다른 화면에서 연결할 input, Node, Node 간 연결, result | .pxflow | PXFLOW Studio |
px.Report | section, text, 값, 표, result 위치, Connected Flow | .pxreport | Report Workbench |
저장된 Report의 실제 문서는 .pxreport입니다. Python SDK, Report Workbench와 AI는 서로 다른 복사본을 만들지 않고 같은 Report 문서에 동일한 편집 명령을 적용합니다.
작은 Project에서는 Flow와 Report를 같은 .py 파일에 이어서 작성할 수 있습니다. 파일을 나누더라도 report.connect_flow(...)가 두 문서의 관계 초안을 만들고, report.save()가 exact Flow revision을 pin해 .pxreport에 저장합니다.
처음부터 끝까지 작성하기
다음 예제는 계산 Flow를 만들고, Report의 값을 Flow input에 연결하고, 결과 위치를 만든 뒤 Report를 저장하고 connection 하나를 실행합니다.
from pipelinexlab import px
class CheckResults(px.Results):
utilization: float = px.result(
unit="1",
description="Demand divided by resistance.",
)
@px.function(
key="check_resistance",
version="1.0.0",
description="Checks girder demand against resistance.",
)
def check_resistance(
span: float = px.input(unit="m", description="Clear span."),
demand: float = px.input(unit="kN.m", description="Design demand."),
) -> CheckResults:
resistance = span * 100.0
return CheckResults(utilization=demand / resistance)
# 1. 계산 Flow
flow = px.Flow(
"girder_check",
label="Girder check",
description="Checks girder demand against resistance.",
)
span = flow.input("span", float, unit="m", description="Clear span.")
demand = flow.input(
"demand",
float,
unit="kN.m",
description="Design demand.",
)
check = flow.node("resistance_check", check_resistance)
flow.connect(span, check.inputs.span)
flow.connect(demand, check.inputs.demand)
flow.result("utilization", check.results.utilization)
# 2. Report 본문과 작성값
report = px.Report("girder_report", title="Girder review")
basis = report.section("design_basis", title="Design basis")
basis.text("purpose", "Review the governing girder check.")
report_span = basis.value("span", 12.0, unit="m")
report_demand = basis.value("demand", 860.0, unit="kN.m")
summary = report.section("check_summary", title="Check result")
utilization_slot = summary.result_slot(
"utilization",
label="Utilization",
)
# 3. Report와 Flow 연결
strength = report.connect_flow("strength_check", flow)
strength.bind("span_input", report_span, to=flow.inputs.span)
strength.bind("demand_input", report_demand, to=flow.inputs.demand)
strength.show(
"utilization_result",
flow.results.utilization,
at=utilization_slot,
)
# import/discovery에는 저장·실행 부작용을 두지 않음
if __name__ == "__main__":
# 4. 새 Flow를 먼저 검증·저장하고 .pxreport 저장
client = px.Client(workspace="engineering", project="bridge_project")
report_revision = report.save()
# 5. 저장한 connection 실행
run = client.run(strength)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
strength는 이 Report에서 girder_check Flow를 사용하는 한 connection입니다. bind(...)는 Report 값을 Flow input에 연결하고, show(...)는 Flow result가 표시될 위치를 연결합니다. 각 mapping의 첫 인자는 그 연결에 붙이는 고유한 이름입니다. label을 바꾸거나 순서가 달라져도 이 이름으로 같은 연결을 다시 찾습니다.
같은 파일의 새 flow를 연결하면 report.save()가 다음 순서로 두 문서를 저장합니다.
- Flow의 input, Node, 연결과 result를 검증합니다.
- 새
.pxflowrevision을 저장합니다. strength가 그 Flow revision을 가리키도록 확정합니다.- 새
.pxreportrevision을 저장합니다.
Flow 검증이나 저장이 실패하면 Report도 저장하지 않습니다. 여기까지는 문서 작성 과정이며 계산은 client.run(strength)에서 시작합니다. 반환된 Run은 Project Activity와 Result 상태를 확인할 때 사용합니다.
Flow만 따로 저장하거나 CI에서 검증할 때의 명령은 Flow materialize와 검증에서 확인합니다.
Flow만 실행하고 Report가 필요하지 않다면 저장한 Flow와 입력값을 바로 전달합니다.
run = client.run(flow, span=12.0, demand=860.0)파일을 나눠 작성하기
Project가 커지면 계산과 Report를 읽기 쉬운 파일로 나눕니다. 연결 방식은 달라지지 않습니다.
# flows/girder_check.py
from pipelinexlab import px
flow = px.Flow(
"girder_check",
label="Girder check",
description="Checks girder demand against resistance.",
)
# flow.input(...), flow.node(...), flow.connect(...), flow.result(...)2
3
4
5
6
7
8
9
# reports/girder_report.py
from pipelinexlab import px
from flows.girder_check import flow
report = px.Report("girder_report", title="Girder review")
strength = report.connect_flow("strength_check", flow)
# report content, strength.bind(...), strength.show(...)
if __name__ == "__main__":
client = px.Client(workspace="engineering", project="bridge_project")
report_revision = report.save()2
3
4
5
6
7
8
9
10
11
flow import는 연결할 정의를 준비합니다. report.save()가 저장을 시작하고, client.run(...)이 계산을 시작합니다.
화면과 Python 코드 대응
| Report Workbench 또는 Project 화면 | Python에서 먼저 받는 객체 | 호출 | 결과 |
|---|---|---|---|
| New Report | from pipelinexlab import px | report = px.Report(...) | 작성 중인 Report 객체 |
| Add section | Report 객체 report | summary = report.section(...) | Section handle summary |
| 본문 text 추가 | Section handle summary | summary.text(...) | text block 하나 |
| 작성값 추가 | Section handle basis | basis.value(...) | type과 단위가 정해진 값 |
| 표 추가 | Section handle basis | basis.table(...) | 각 행을 식별할 고유 key가 있는 Table |
| 결과 위치 추가 | Section handle summary | summary.result_slot(...) | 계산 결과가 표시되는 위치 |
| Connected Flows → Add Flow | Report 객체 report | strength = report.connect_flow(...) | Connected Flow handle strength |
| Use report input | Connected Flow handle strength | strength.bind(...) | Report 원본 → Flow input |
| Show result | Connected Flow handle strength | strength.show(...) | Flow result → Report 위치 |
| Save | Report 객체 report | report.save() | 새 .pxreport 저장본 |
| connection Run | Client 객체 client | client.run(strength) | Flow Run 기록 하나 |
| Run selected | Client 객체 client | client.run_many(connections) | connection별 독립 Run |
| Batch Run | Client 객체 client | client.run_batch(...) | 각 scalar case의 독립 Run |
| Project Activity | 반환된 Run handle | 상태·Result 조회 | 여러 Run의 진행 상태와 이력 |
Report를 저장한 뒤 어떤 내용이 기준인가요?
report.save()는 지금까지 작성한 내용을 검증하고 새 .pxreport 저장본을 만듭니다. 저장이 끝나면 이 저장본이 사용자가 다시 여는 현재 Report가 됩니다. Python 파일은 Report를 처음 만들거나 반복 편집을 자동화할 때 계속 사용할 수 있습니다.
Workbench나 AI가 먼저 새 저장본을 만들었다면, 이전 상태에서 시작한 Python 변경은 바로 덮어쓰지 않습니다. 현재 문서와 달라진 부분을 확인한 뒤 다시 적용하므로 Python 자동화와 화면 편집이 서로의 내용을 지우지 않습니다.
저장된 Report를 Python에서 이어서 편집할 때는 base revision을 명시적으로 엽니다.
report = px.Report.open(
"girder_report",
workspace="engineering",
project="bridge_project",
revision="sha256:report-revision-digest",
)2
3
4
5
6
report.save() 전에 Workbench나 AI가 새 revision을 저장했다면 stale-generation 진단과 변경 요약을 반환합니다. 현재 revision을 다시 열어 변경을 재적용하므로 오래된 script가 새 내용을 덮어쓰지 않습니다.
Flow의 계산 구조는 별도 .pxflow 저장본으로 유지됩니다. Report에는 Flow의 Node나 내부 연결을 복사하지 않고, Connected Flow가 사용할 정확한 Flow 저장본과 input/result 연결을 저장합니다.
여러 Flow 실행하기
Report connection 하나를 client.run(...)으로 실행하면 Flow Run 기록 하나가 생깁니다. 여러 connection을 client.run_many(...)에 전달하면 connection마다 독립 Run이 생기며 Runtime이 설정된 동시 실행 한도 안에서 병렬로 처리합니다.
같은 Flow도 서로 다른 connection key로 여러 번 연결할 수 있습니다. 각 connection은 입력 연결, 결과 위치, 갱신 필요 상태와 Run 이력을 따로 유지합니다.
strength_uls = report.connect_flow("strength_uls", flow)
strength_sls = report.connect_flow("strength_sls", flow)
report.save()
runs = client.run_many([strength_uls, strength_sls])2
3
4
5
Project Activity는 이 Run들을 한 화면에서 찾고 상태를 확인하는 표시 단위입니다. 각 Run의 실행, 취소, 재시도와 Result는 connection별로 유지됩니다.
Table 한 번 실행과 Batch 구분
List 또는 Table을 해당 형태의 Flow input 하나에 연결하면 전체 목록이나 표가 값 하나로 전달되어 Run 하나가 생깁니다. 같은 scalar 계산을 여러 case에 반복하려면 Batch를 명시합니다.
# 이 connection은 case가 제공할 span·demand를 saved input mapping에 연결하지 않습니다.
strength_cases = report.connect_flow("strength_cases", flow)
cases = [
{"case_key": "uls_01", "span": 12.0, "demand": 860.0},
{"case_key": "uls_02", "span": 12.0, "demand": 920.0},
]
if __name__ == "__main__":
report.save()
client = px.Client(workspace="engineering", project="bridge_project")
batch = client.run_batch(
strength_cases,
cases,
case_key="case_key",
inputs={
"span": flow.inputs.span,
"demand": flow.inputs.demand,
},
)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Batch는 각 case_key마다 독립 Run 하나를 만들고, Project에 설정된 동시 실행 한도 안에서 실행합니다. strength_cases의 span·demand port는 case input 전용으로 비워 둡니다. 위 예제는 결과를 child Run별로 보존합니다. Report result Table에 합치려면 section.result_table(...)과 stable result mapping을 저장한 뒤 result_mapping="mapping_key"를 명시합니다. stable case_key가 결과 행 identity를 결정합니다.
Flow 사이에 실행 순서가 있을 때
Flow B가 Flow A의 result를 input으로 사용하면 두 Report connection의 나열 순서에 기대지 않고 parent Flow에서 Subflow A와 B를 연결합니다.
pipeline = px.Flow(
"design_pipeline",
label="Design pipeline",
description="Runs section properties before the strength check.",
)
section = pipeline.subflow("section_properties", section_properties_flow)
strength_check = pipeline.subflow("strength_check", strength_check_flow)
pipeline.connect(
section.results.section,
strength_check.inputs.section,
)
pipeline.result(
"utilization",
strength_check.results.utilization,
)2
3
4
5
6
7
8
9
10
11
12
13
14
15
Report에는 완성된 parent Flow를 connection 하나로 추가합니다. PXFLOW Studio와 Runtime은 이 Flow 안의 연결을 실제 계산 dependency로 사용합니다.
작성 중인 보고서의 일부를 함께 수정하기
저장된 리비전의 일부를 읽을 때는 client.read_report(...)를, 아직 저장하지 않은 본문과 입력값을 기준으로 수정할 때는 report.capture_context(reads=..., writes=...)를 사용합니다. reads에는 참고한 블록을, writes에는 바꿀 블록을 넣습니다. 이 호출은 자동 저장하지 않습니다.
수정 후보는 report.apply_edits(edits, context=context, values=..., cells=...)로 함께 검증하고 초안에 반영합니다. 참고 내용이나 대상이 바뀌면 거절되므로 현재 내용을 확인한 뒤 재검토합니다. 적용한 초안을 영속 저장하는 단계는 기존 report.save()입니다.
미저장 이미지의 현재 내용을 고정하려면 첨부 handle의 attachment.capture()를 먼저 호출합니다. 그 뒤에는 원본 파일이 바뀌어도 캡처한 bytes로 저장합니다. 캡처만으로 앱 종료 후 복구나 장기 보관을 보장하지는 않습니다. 문맥 캡처는 이미지 bytes를 자동으로 읽지 않습니다.
호출 예제, 범위 상한과 충돌 조건은 부분 편집·이미지 API를 따릅니다. 이 편집 API는 모델 호출이나 AI 작업 실행 기능을 포함하지 않습니다.

