-
Notifications
You must be signed in to change notification settings - Fork 13.7k
/
example_params_ui_tutorial.py
246 lines (239 loc) · 11 KB
/
example_params_ui_tutorial.py
1
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""DAG demonstrating various options for a trigger form generated by DAG params.
The DAG attribute `params` is used to define a default dictionary of parameters which are usually passed
to the DAG and which are used to render a trigger form.
"""
from __future__ import annotations
import datetime
import json
from pathlib import Path
from airflow.decorators import task
from airflow.models.dag import DAG
from airflow.models.param import Param, ParamsDict
with DAG(
dag_id=Path(__file__).stem,
dag_display_name="Params UI tutorial",
description=__doc__.partition(".")[0],
doc_md=__doc__,
schedule=None,
start_date=datetime.datetime(2022, 3, 4),
catchup=False,
tags=["example", "params", "ui"],
params={
# Let's start simple: Standard dict values are detected from type and offered as entry form fields.
# Detected types are numbers, text, boolean, lists and dicts.
# Note that such auto-detected parameters are treated as optional (not required to contain a value)
"x": 3,
"text": "Hello World!",
"flag": False,
"a_simple_list": ["one", "two", "three", "actually one value is made per line"],
# But of course you might want to have it nicer! Let's add some description to parameters.
# Note if you can add any Markdown formatting to the description, you need to use the description_md
# attribute.
"most_loved_number": Param(
42,
type="integer",
title="Your favorite number",
description_md="Everybody should have a **favorite** number. Not only _math teachers_. "
"If you can not think of any at the moment please think of the 42 which is very famous because"
"of the book [The Hitchhiker's Guide to the Galaxy]"
"(https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#"
"The_Answer_to_the_Ultimate_Question_of_Life,_the_Universe,_and_Everything_is_42).",
),
# If you want to have a selection list box then you can use the enum feature of JSON schema
"pick_one": Param(
"value 42",
type="string",
title="Select one Value",
description="You can use JSON schema enum's to generate drop down selection boxes.",
enum=[f"value {i}" for i in range(16, 64)],
),
# You can also label the selected values via values_display attribute
"pick_with_label": Param(
3,
type="number",
title="Select one Number",
description="With drop down selections you can also have nice display labels for the values.",
enum=[*range(1, 10)],
values_display={
1: "One",
2: "Two",
3: "Three",
4: "Four - is like you take three and get one for free!",
5: "Five",
6: "Six",
7: "Seven",
8: "Eight",
9: "Nine",
},
),
# If you want to have a list box with proposals but not enforcing a fixed list
# then you can use the examples feature of JSON schema
"proposals": Param(
"some value",
type="string",
title="Field with proposals",
description="You can use JSON schema examples's to generate drop down selection boxes "
"but allow also to enter custom values. Try typing an 'a' and see options.",
examples=(
"Alpha,Bravo,Charlie,Delta,Echo,Foxtrot,Golf,Hotel,India,Juliett,Kilo,Lima,Mike,November,Oscar,Papa,"
"Quebec,Romeo,Sierra,Tango,Uniform,Victor,Whiskey,X-ray,Yankee,Zulu"
).split(","),
),
# If you want to select multiple items from a fixed list JSON schema des not allow to use enum
# In this case the type "array" is being used together with "examples" as pick list
"multi_select": Param(
["two", "three"],
"Select from the list of options.",
type="array",
title="Multi Select",
examples=["one", "two", "three", "four", "five"],
),
# A multiple options selection can also be combined with values_display
"multi_select_with_label": Param(
["2", "3"],
"Select from the list of options. See that options can have nicer text and still technical values"
"are propagated as values during trigger to the DAG.",
type="array",
title="Multi Select with Labels",
examples=["1", "2", "3", "4", "5"],
values_display={
"1": "One box of choccolate",
"2": "Two bananas",
"3": "Three apples",
# Note: Value display mapping does not need to be complete.s
},
),
# An array of numbers
"array_of_numbers": Param(
[1, 2, 3],
"Only integers are accepted in this array",
type="array",
title="Array of numbers",
items={"type": "number"},
),
# Boolean as proper parameter with description
"bool": Param(
True,
type="boolean",
title="Please confirm",
description="A On/Off selection with a proper description.",
),
# Dates and Times are also supported
"date_time": Param(
f"{datetime.date.today()}T{datetime.time(hour=12, minute=17, second=00)}+00:00",
type="string",
format="date-time",
title="Date-Time Picker",
description="Please select a date and time, use the button on the left for a pup-up calendar.",
),
"date": Param(
f"{datetime.date.today()}",
type="string",
format="date",
title="Date Picker",
description="Please select a date, use the button on the left for a pup-up calendar. "
"See that here are no times!",
),
"time": Param(
f"{datetime.time(hour=12, minute=13, second=14)}",
type=["string", "null"],
format="time",
title="Time Picker",
description="Please select a time, use the button on the left for a pup-up tool.",
),
# Fields can be required or not. If the defined fields are typed they are getting required by default
# (else they would not pass JSON schema validation) - to make typed fields optional you must
# permit the optional "null" type.
# You can omit a default value if the DAG is triggered manually
"required_field": Param(
# In this example we have no default value
# Form will enforce a value supplied by users to be able to trigger
type="string",
title="Required text field",
description="This field is required. You can not submit without having text in here.",
),
"optional_field": Param(
"optional text, you can trigger also w/o text",
type=["null", "string"],
title="Optional text field",
description_md="This field is optional. As field content is JSON schema validated you must "
"allow the `null` type.",
),
# You can arrange the entry fields in sections so that you can have a better overview for the user
# Therefore you can add the "section" attribute.
# The benefit of the Params class definition is that the full scope of JSON schema validation
# can be leveraged for form fields and they will be validated before DAG submission.
"checked_text": Param(
"length-checked-field",
type="string",
title="Text field with length check",
description_md="""This field is required. And you need to provide something between 10 and 30
characters. See the JSON
[schema description (string)](https://json-schema.org/understanding-json-schema/reference/string.html)
for more details""",
minLength=10,
maxLength=20,
section="JSON Schema validation options",
),
"checked_number": Param(
100,
type="number",
title="Number field with value check",
description_md="""This field is required. You need to provide any number between 64 and 128.
See the JSON
[schema description (numbers)](https://json-schema.org/understanding-json-schema/reference/numeric.html)
for more details""",
minimum=64,
maximum=128,
section="JSON Schema validation options",
),
# Some further cool stuff as advanced options are also possible
# You can have the user entering a dict object as a JSON with validation
"object": Param(
{"key": "value"},
type=["object", "null"],
title="JSON entry field",
section="Special advanced stuff with form fields",
),
"array_of_objects": Param(
[{"name": "account_name", "country": "country_name"}],
description_md="Array with complex objects and validation rules. "
"See [JSON Schema validation options in specs]"
"(https://json-schema.org/understanding-json-schema/reference/array.html#items).",
type="array",
title="JSON array field",
items={
"type": "object",
"properties": {"name": {"type": "string"}, "country_name": {"type": "string"}},
"required": ["name"],
},
section="Special advanced stuff with form fields",
),
# If you want to have static parameters which are always passed and not editable by the user
# then you can use the JSON schema option of passing constant values. These parameters
# will not be displayed but passed to the DAG
"hidden_secret_field": Param("constant value", const="constant value"),
},
) as dag:
@task(task_display_name="Show used parameters")
def show_params(**kwargs) -> None:
params: ParamsDict = kwargs["params"]
print(f"This DAG was triggered with the following parameters:\n\n{json.dumps(params, indent=4)}\n")
show_params()