-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path1c_PythonExampleEngine.py
More file actions
208 lines (165 loc) · 8.66 KB
/
1c_PythonExampleEngine.py
File metadata and controls
208 lines (165 loc) · 8.66 KB
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
"""
AyxPlugin (required) has-a IncomingInterface (optional).
Although defining IncomingInterface is optional, the interface methods are needed if an upstream tool exists.
"""
import AlteryxPythonSDK as Sdk
import xml.etree.ElementTree as Et
import csv
import os
class AyxPlugin:
"""
Implements the plugin interface methods, to be utilized by the Alteryx engine to communicate with this plugin.
Prefixed with "pi", the Alteryx engine will expect the below five interface methods to be defined.
"""
def __init__(self, n_tool_id: int, alteryx_engine: object, output_anchor_mgr: object):
"""
Constructor is called whenever the Alteryx engine wants to instantiate an instance of this plugin.
:param n_tool_id: The assigned unique identification for a tool instance.
:param alteryx_engine: Provides an interface into the Alteryx engine.
:param output_anchor_mgr: A helper that wraps the outgoing connections for a plugin.
"""
# Default properties
self.n_tool_id = n_tool_id
self.alteryx_engine = alteryx_engine
self.output_anchor_mgr = output_anchor_mgr
# Custom properties
self.is_initialized = True
self.output_anchor = None
self.output_text = None
self.n_columns = None
self.n_rows = None
def pi_init(self, str_xml: str):
"""
As this tool is not taking any values from the User/upstream, it will be used only to initialize the ouput anchor.
Called when the Alteryx engine is ready to provide the tool configuration from the GUI.
:param str_xml: The raw XML from the GUI.
"""
self.output_anchor = self.output_anchor_mgr.get_output_anchor('Output')
# Getting the user-entered selections from the GUI.
self.output_text = [Et.fromstring(str_xml).find('FText').text if 'FText' in str_xml else None]
if self.output_text[0] is None:
self.output_text =['infolab']
self.display_error_msg('Not valid text supplied, defaulting to "infolab"', 'warning')
self.n_columns = int(Et.fromstring(str_xml).find('NColumns').text) if 'NColumns' in str_xml else None
temp_n_rows = Et.fromstring(str_xml).find('NRows').text
try:
self.n_rows = int(temp_n_rows)
except ValueError:
self.n_rows = 1
self.display_error_msg('Number of rows is not an integer! Defaulting to a single row.','warning')
except TypeError:
self.n_rows = 1
self.display_error_msg('Invalid number of rows! Defaulting to a single row.','warning')
# Limit the number of rows to 10000
if self.n_rows > 10000:
self.n_rows = 10000
self.display_error_msg('Maximum number of rows reached, capped at 10000','warning')
pass
def pi_add_incoming_connection(self, str_type: str, str_name: str) -> object:
"""
The IncomingInterface objects are instantiated here, one object per incoming connection, however since
this tool does not accept an incoming connection, instantiation is not needed and "ii" methods won't be called.
Called when the Alteryx engine is attempting to add an incoming data connection.
:param str_type: The name of the input connection anchor, defined in the Config.xml file.
:param str_name: The name of the wire, defined by the workflow author.
:return: self.
"""
return self
def pi_add_outgoing_connection(self, str_name: str) -> bool:
"""
Called when the Alteryx engine is attempting to add an outgoing data connection.
:param str_name: The name of the output connection anchor, defined in the Config.xml file.
:return: True signifies that the connection is accepted.
"""
return True
def pi_push_all_records(self, n_record_limit: int) -> bool:
"""
Handles pushing records out to downstream tool(s).
Called when a tool has no incoming data connection.
:param n_record_limit: Set it to <0 for no limit, 0 for no records, and >0 to specify the number of records.
:return: False if there are issues with the input data or if the workflow isn't being ran, otherwise True.
"""
record_info_out = self.build_record_info_out() # Building out the outgoing record layout.
self.output_anchor.init(record_info_out) # Lets the downstream tools know of the outgoing record metadata.
record_creator = record_info_out.construct_record_creator() # Creating a new record_creator for the new data.
for record in range(self.n_rows):
for field in enumerate(self.output_text*self.n_columns):
record_info_out[field[0]].set_from_string(record_creator, field[1] + '_r'+str(record)+'c'+str(field[0]))
out_record = record_creator.finalize_record()
self.output_anchor.push_record(out_record, False) # False: completed connections will automatically close.
record_creator.reset() # Resets the variable length data to 0 bytes (default) to prevent unexpected results.
self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.info, self.xmsg(
str(self.n_rows)+' records were processed, and '+str(self.n_columns)+ ' fields were created.'))
self.output_anchor.close() # Close outgoing connections.
return True
def pi_close(self, b_has_errors: bool):
"""
Called after all records have been processed.
:param b_has_errors: Set to true to not do the final processing.
"""
self.output_anchor.assert_close() # Checks whether connections were properly closed.
def build_record_info_out(self):
"""
A non-interface helper for pi_push_all_records() responsible for creating the outgoing record layout.
:param file_reader: The name for csv file reader.
:return: The outgoing record layout, otherwise nothing.
"""
record_info_out = Sdk.RecordInfo(self.alteryx_engine) # A fresh record info object for outgoing records.
#We are returning a single column and a single row.
for i in range(self.n_columns):
record_info_out.add_field('NewText_'+str(i), Sdk.FieldType.string, 254)
return record_info_out
def display_error_msg(self, msg_string: str, msg_type: str):
"""
A non-interface method, that is responsible for displaying the relevant error message in Designer.
:param msg_string: The custom error message.
"""
self.is_initialized = False
if msg_type == 'info':
self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.info, self.xmsg(msg_string))
elif msg_type == 'warning':
self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.warning, self.xmsg(msg_string))
elif msg_type == 'error':
self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.error, self.xmsg(msg_string))
def xmsg(self, msg_string: str):
"""
A non-interface, non-operational placeholder for the eventual localization of predefined user-facing strings.
:param msg_string: The user-facing string.
:return: msg_string
"""
return msg_string
class IncomingInterface:
"""
This optional class is returned by pi_add_incoming_connection, and it implements the incoming interface methods, to
be utilized by the Alteryx engine to communicate with a plugin when processing an incoming connection.
Prefixed with "ii", the Alteryx engine will expect the below four interface methods to be defined.
"""
def __init__(self, parent: object):
"""
Constructor for IncomingInterface.
:param parent: AyxPlugin
"""
pass
def ii_init(self, record_info_in: object) -> bool:
"""
Called to report changes of the incoming connection's record metadata to the Alteryx engine.
:param record_info_in: A RecordInfo object for the incoming connection's fields.
"""
pass
def ii_push_record(self, in_record: object) -> bool:
"""
Called when an input record is being sent to the plugin.
:param in_record: The data for the incoming record.
"""
pass
def ii_update_progress(self, d_percent: float):
"""
Called by the upstream tool to report what percentage of records have been pushed.
:param d_percent: Value between 0.0 and 1.0.
"""
pass
def ii_close(self):
"""
Called when the incoming connection has finished passing all of its records.
"""
pass