{ "custom": { "api_region_name": "eu", "default_file_config": { "ETag": "?", "LastModified": "?", "Size": 0, "StorageClass": "?" }, "dummy": [], "file_not_found": true, "file_options": [], "loading": false, "selected_file": "", "selected_file_config": { "ETag": "?", "Filename": ".svg", "Key": ".svg", "LastModified": "?", "Size": 0, "StorageClass": "?" }, "stage_config": { "account_id": "006306898152", "api_call_role": "arn:aws:iam::609617486056:role/RMESDScadaS3ManagementAPIcallRole-prod-eu-west-1", "endpoint": "https://eu-west-1.scada-s3-management.scada.eurme.amazon.dev/", "lambda_name": "RMESDScadaS3ManagementFlaskLambda-prod", "region": "eu-west-1", "repo_bucket": "ignition-image-repo", "s3_region": "eu-west-1", "source_bucket": "ignition-image-source" } }, "events": { "system": { "onStartup": { "config": { "script": "\tself.custom.selected_file \u003d self.params.selected_file" }, "scope": "G", "type": "script" } } }, "params": { "bucket": "", "enables": { "delete": true, "download": true, "file": true, "object_key": false, "upload": true }, "files": [], "prefix": "", "selected_file": "", "suffix": ".svg", "upload_file_types": [ "svg" ], "whid": "" }, "propConfig": { "custom.api_region_name": { "binding": { "config": { "path": "session.custom.aws.prefix" }, "type": "property" }, "persistent": true }, "custom.default_file_config": { "persistent": true }, "custom.default_file_config.Filename": { "binding": { "config": { "expression": "{view.params.selected_file}+{view.params.suffix}" }, "type": "expr" } }, "custom.default_file_config.Key": { "binding": { "config": { "expression": "{view.params.prefix}+{view.params.selected_file}+{view.params.suffix}" }, "type": "expr" } }, "custom.dummy": { "persistent": true }, "custom.file_not_found": { "binding": { "config": { "expression": "{view.custom.selected_file_config.Size}\u003d0" }, "type": "expr" }, "persistent": true }, "custom.file_options": { "binding": { "config": { "path": "view.params.files" }, "transforms": [ { "code": "\tprefix \u003d self.params.prefix\n\tsuffix \u003d self.params.suffix\n\treturn [{\u0027value\u0027: x.Filename, \n\t\t\u0027label\u0027: x.Filename} \n\t\tfor x in value]", "type": "script" } ], "type": "property" }, "persistent": true }, "custom.loading": { "persistent": true }, "custom.selected_file": { "persistent": true }, "custom.selected_file_config": { "binding": { "config": { "expression": "{view.custom.selected_file}+toStr({view.custom.file_options})" }, "transforms": [ { "code": "\tdef_file_config \u003d self.custom.default_file_config\n\treturn next((x for x in self.params.files if x.Filename \u003d\u003d self.custom.selected_file), def_file_config)", "type": "script" } ], "type": "expr" }, "persistent": true }, "custom.stage_config": { "binding": { "config": { "expression": "{view.custom.api_region_name}" }, "transforms": [ { "code": "\treturn AWS.s3.STAGE_CONFIG[\u0027prod\u0027][value]", "type": "script" } ], "type": "expr" }, "persistent": true }, "params.bucket": { "paramDirection": "input", "persistent": true }, "params.enables": { "paramDirection": "input", "persistent": true }, "params.files": { "paramDirection": "input", "persistent": true }, "params.prefix": { "paramDirection": "input", "persistent": true }, "params.selected_file": { "onChange": { "enabled": null, "script": "\tif not missedEvents:\n\t\tselection \u003d self.getChild(\"root\").getChild(\"FlexContainer\").getChild(\"Table\").props.selection\n\t\tif currentValue.value:\n\t\t\tif not currentValue.value.endswith(self.params.suffix):\n\t\t\t\tself.custom.selected_file \u003d currentValue.value + self.params.suffix\n\t\t\telse:\n\t\t\t\tself.custom.selected_file \u003d currentValue.value\n\t\t\t# now select the correct row in table that matches selection\n\t\t\tfilename \u003d self.custom.selected_file\n\t\t\tselected_row, selected_data \u003d None, []\n\t\t\tfor index, row in enumerate(self.params.files):\n\t\t\t\tif row.Filename \u003d\u003d filename:\n\t\t\t\t\tselected_row \u003d index\n\t\t\t\t\tselected_data.append(row)\n\t\t\t\t\tbreak\n\t\t\tselection.data \u003d selected_data\n\t\t\tselection.selectedRow \u003d selected_row\n\t\telse:\n\t\t\t# file is none, clear out file selection\n\t\t\t# I know, why are we setting to -1, then None? it\u0027s an ignition persp table bug\n\t\t\t# as of 8.1.20. It works...\n\t\t\tselection.selectedRow \u003d -1\n\t\t\tselection.selectedColumn \u003d -1\n\t\t\tselection.selectedRow \u003d None\n\t\t\tselection.selectedColumn \u003d None\n\t" }, "paramDirection": "input", "persistent": true }, "params.suffix": { "paramDirection": "input", "persistent": true }, "params.upload_file_types": { "paramDirection": "input", "persistent": true }, "params.whid": { "paramDirection": "input", "persistent": true } }, "props": { "defaultSize": { "height": 600, "width": 550 } }, "root": { "children": [ { "children": [ { "children": [ { "meta": { "name": "Label" }, "position": { "basis": "125px", "shrink": 0 }, "props": { "style": { "classes": "Framework/Card/Label Text/RightAlign_with_Padding", "paddingLeft": "5px" }, "text": "Select File:" }, "type": "ia.display.label" }, { "events": { "component": { "onActionPerformed": { "config": { "script": "\t# when user selects new file, send message to update selected image on parent view\n\tif self.props.value:\n\t\tsuffix \u003d self.view.params.suffix\n\t\tselected_image \u003d self.props.value.replace(suffix, \u0027\u0027)\n\t\tpayload \u003d {\u0027image\u0027: selected_image}\n\t\tsystem.perspective.sendMessage(\u0027update_selected_image\u0027, payload, scope\u003d\u0027session\u0027)\n\t\t" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Dropdown", "tooltip": { "location": "bottom", "style": { "whiteSpace": "pre" }, "text": "Click here to select a different \nstage folder to manage" } }, "position": { "basis": "175px", "grow": 1, "shrink": 0 }, "propConfig": { "props.enabled": { "binding": { "config": { "expression": "{view.params.enables.file}\u0026\u0026try(len({view.params.files})\u003e0,false)" }, "type": "expr" } }, "props.options": { "binding": { "config": { "path": "view.custom.file_options" }, "type": "property" } }, "props.value": { "binding": { "config": { "bidirectional": true, "path": "view.custom.selected_file" }, "type": "property" } } }, "props": { "style": { "margin": "2px", "marginRight": "5px" } }, "type": "ia.input.dropdown" }, { "events": { "component": { "onFileReceived": { "config": { "script": "\tself.upload_file(event\u003devent)\n" }, "scope": "G", "type": "script" } } }, "meta": { "name": "FileUpload", "tooltip": { "enabled": true } }, "position": { "basis": "26px", "shrink": 0 }, "propConfig": { "meta.tooltip.text": { "binding": { "config": { "expression": "stringFormat(\u0027Upload new version of %s\u0027,{view.custom.selected_file})" }, "type": "expr" } }, "meta.visible": { "binding": { "config": { "expression": "{view.params.enables.upload}\u0026\u0026!{view.custom.file_not_found}" }, "type": "expr" } }, "props.supportedFileTypes": { "binding": { "config": { "path": "view.params.upload_file_types" }, "type": "property" } } }, "props": { "fileSizeLimit": 20, "fileUploadIcon": { "style": { "borderStyle": "none", "classes": "Input/Button/Secondary_minimal", "margin": "-5px" } }, "maxUploads": 1 }, "scripts": { "customMethods": [ { "name": "update_bindings", "params": [], "script": "\t\"\"\"\n\t\tAfter data saved to S3, refresh session and view bindings\n\t\"\"\"\n\t\n\tself.view.custom.loading \u003d False\n\t# reset file upload component to default state\n\tself.clearUploads()\n\t# send message to update files param on parent view\n\tbucket \u003d self.view.params.bucket\n\tsystem.perspective.sendMessage(\u0027update_file_binding\u0027, {\u0027bucket\u0027: bucket}, scope\u003d\u0027session\u0027)\n\t" }, { "name": "show_success_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"success\", \n\t\t\"File Uploaded\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t" }, { "name": "show_warning_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"warning\", \n\t\t\"File NOT Uploaded\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t" }, { "name": "show_error_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"error\", \n\t\t\"File Upload Error!\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t\t" }, { "name": "upload_file", "params": [ "event\u003dNone" ], "script": "\tfrom AWS.s3 import S3Manager\n\tfrom pprint import pformat\n\tsystem.perspective.print(\u0027FileUpload component upload_file custom method reached...\u0027)\n\tself.view.custom.loading \u003d True\n\ttry:\n\t\tapi_stage \u003d \u0027prod\u0027\n\t\tusername \u003d self.session.props.auth.user.userName\n\t\tapi_region_name \u003d self.view.custom.api_region_name\n\t\tbucket \u003d self.view.params.bucket\n\t\tobj_key \u003d self.view.custom.selected_file_config.Key\n\t\tregion \u003d self.view.custom.stage_config.s3_region\n\t\texisting_filename \u003d self.view.custom.selected_file_config.Filename\n\t\tfilename \u003d event.file.name\n\t\t# verify that uploaded file name matches existing file name. Throw error if not.\n\t\tif filename !\u003d existing_filename:\n\t\t\tmsg \u003d \u0027File uploaded does not have the same name as target object key. Please check spelling and that you selected the correct file to upload. Target filename: %s. Received: %s\u0027 % (\n\t\t\t\t\texisting_filename, filename)\n\t\t\tself.show_error_dialog(msg)\n\t\t\tself.view.custom.loading \u003d False\n\t\t\tself.clearUploads()\n\t\t\treturn\n\t\tsystem.perspective.print(\u0027obj_key to upload: %s\u0027 % obj_key)\n\t\tobj_data \u003d event.file.getString()\n\t\tsystem.perspective.print(\u0027obj_data length: %s\u0027 % len(obj_data))\n\n\t\ts3m \u003d S3Manager(api_stage, api_region_name, username)\n\t\ttry:\n\t\t\tresp \u003d s3m.upload(\n\t\t\t\tobj_data\u003dobj_data,\n\t\t\t\tobj_key\u003dobj_key,\n\t\t\t\tbucket\u003dbucket,\n\t\t\t\tregion\u003dregion\n\t\t\t)\n\t\t\tresp_code \u003d resp.get(\u0027code\u0027, None)\n\t\t\tif (resp_code and resp_code !\u003d 200) or (not resp_code and \u0027message\u0027 in resp):\n\t\t\t\t# this means the API encountered an error, annunciate the error here\n\t\t\t\tmsg \u003d \u0027API encountered error uploading %s to %s bucket. \\nResponse: %s\u0027 % (obj_key, bucket, pformat(resp))\n\t\t\t\tsystem.perspective.print(msg)\n\t\t\t\tself.view.custom.loading \u003d False\n\t\t\t\tself.show_error_dialog(msg)\n\t\t\t\tself.clearUploads()\n\t\t\t\treturn\n\t\t\tmsg \u003d \u0027Successfully uploaded %s object in %s bucket!\\nResponse: %s\u0027 % (obj_key, bucket, pformat(resp))\n\t\t\tsystem.perspective.print(msg)\n\t\t\tself.show_success_dialog(msg)\n\t\t\tself.update_bindings()\n\t\texcept:\n\t\t\timport traceback\n\t\t\tmsg \u003d \u0027Error uploading %s object in %s bucket: %s\u0027 % (obj_key, bucket, traceback.format_exc())\n\t\t\tsystem.perspective.print(msg)\n\t\t\tself.view.custom.loading \u003d False\n\t\t\tself.show_error_dialog(msg)\n\t\t\tself.clearUploads()\n\texcept:\n\t\timport traceback\n\t\tmsg \u003d \u0027General Error uploading %s object in %s bucket: %s\u0027 % (obj_key, bucket, traceback.format_exc())\n\t\tsystem.perspective.print(msg)\n\t\tself.view.custom.loading \u003d False\n\t\tself.show_error_dialog(msg)\n\t\tself.clearUploads()\n\t\t" } ], "extensionFunctions": null, "messageHandlers": [] }, "type": "ia.input.fileupload" }, { "events": { "component": { "onActionPerformed": { "config": { "script": "\tfrom AWS.s3 import S3Manager\n\tfrom pprint import pformat\n\timport json\n\t\n\tapi_stage \u003d \u0027prod\u0027\n\tusername \u003d self.session.props.auth.user.userName\n\tapi_region_name \u003d self.view.custom.api_region_name\n\tbucket \u003d self.view.params.bucket\n\tobj_key \u003d self.view.custom.selected_file_config.Key\n\tregion \u003d self.view.custom.stage_config.s3_region\n\tfilename \u003d self.view.custom.selected_file_config.Filename\n\t\n\ts3m \u003d S3Manager(api_stage, api_region_name, username)\n\tdata \u003d None\n\ttry:\n\t\tresp \u003d s3m.download(bucket\u003dbucket, obj_key\u003dobj_key, region\u003dregion)\n\t\ttry:\n\t\t\tresp_code \u003d resp.get(\u0027code\u0027, None)\n\t\texcept AttributeError:\n\t\t\tresp_code \u003d None\n\t\tif (resp_code and resp_code !\u003d 200) or (not resp_code and \u0027message\u0027 in resp):\n\t\t\t# this means the API encountered an error, annunciate the error here\n\t\t\tmsg \u003d \u0027API encountered error downloading %s on %s bucket. \\nResponse: %s\u0027 % (obj_key, bucket, pformat(sanitize_tree(resp)))\n\t\t\tsystem.perspective.print(msg)\n\t\t\tself.show_error_dialog(msg)\n\t\t\treturn\n\t\tif isinstance(resp, dict) or isinstance(resp, list):\n\t\t\tdata \u003d json.dumps(resp, indent\u003d2)\n\t\telse:\n\t\t\tdata \u003d resp\n\texcept:\n\t\timport traceback\n\t\tmsg \u003d \u0027error downloading %s obj: %s\u0027 % (obj_key, traceback.format_exc())\n\t\tsystem.perspective.print(msg)\n\t\tself.show_error_dialog(msg)\n\tif data:\n\t\tsystem.perspective.download(filename, data)\n\telse:\n\t\tsystem.perspective.print(\u0027no data for %s obj!\u0027 % obj_key)\n\t\tself.show_error_dialog(msg)\n\t" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Download Button", "tooltip": { "enabled": true } }, "position": { "basis": "26px", "shrink": 0 }, "propConfig": { "meta.tooltip.text": { "binding": { "config": { "expression": "stringFormat(\u0027Download copy of %s\u0027,{view.custom.selected_file})" }, "type": "expr" } }, "props.enabled": { "binding": { "config": { "expression": "{view.params.enables.download}\u0026\u0026!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "image": { "icon": { "path": "material/cloud_download" } }, "primary": false, "style": { "classes": "Input/Button/Secondary_minimal", "margin": "2px" }, "text": "" }, "scripts": { "customMethods": [ { "name": "show_error_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"error\", \n\t\t\"File Download Error!\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t\t\t" } ], "extensionFunctions": null, "messageHandlers": [] }, "type": "ia.input.button" }, { "events": { "component": { "onActionPerformed": { "config": { "script": "\tself.show_confirm_dialog()" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Delete Button", "tooltip": { "enabled": true } }, "position": { "basis": "26px", "shrink": 0 }, "propConfig": { "meta.tooltip.text": { "binding": { "config": { "expression": "stringFormat(\u0027Delete record of %s file from %s bucket\u0027,\r\n\t{view.custom.selected_file},{view.params.bucket})" }, "type": "expr" } }, "props.enabled": { "binding": { "config": { "expression": "{view.params.enables.delete}\u0026\u0026!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "image": { "icon": { "path": "material/delete_forever" } }, "primary": false, "style": { "classes": "Input/Button/Secondary_minimal", "margin": "2px" }, "text": "" }, "scripts": { "customMethods": [ { "name": "show_success_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"success\", \n\t\t\"File Deleted\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t\t" }, { "name": "show_warning_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"warning\", \n\t\t\"File NOT Deleted\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t\t" }, { "name": "show_error_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"error\", \n\t\t\"File Delete Error!\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t\t" }, { "name": "show_confirm_dialog", "params": [ "payload\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add payload of data to pass to the popup\n\tmsg \u003d (\u0027Are you sure you want to delete %s file from %s S3 bucket? THIS OPERATION CANNOT BE UNDONE!\u0027) % (\n\t\tself.view.custom.selected_file, self.view.params.bucket)\n\tpayload \u003d {\u0027bucket\u0027: self.view.params.bucket}\t\t\n\tAlerts.showAlert(\n\t\t\"info\", \n\t\t\"Delete from S3?\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"Continue\", \n\t\t\"Cancel\", \n\t\t\"delete_forever\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"confirm_delete_file\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\tpayload\n\t)\n\t\t\t" }, { "name": "update_bindings", "params": [], "script": "\t\"\"\"\n\t\tAfter deleted from S3, refresh session and view bindings\n\t\"\"\"\n\tself.view.custom.loading \u003d False\n\t# send message to update files param on parent view\n\tbucket \u003d self.view.params.bucket\n\tsystem.perspective.sendMessage(\u0027update_file_binding\u0027, {\u0027bucket\u0027: bucket}, scope\u003d\u0027session\u0027)\n\t" }, { "name": "delete_file", "params": [], "script": "\t\"\"\"\n\t\tCall AWS.s3.S3Manager.delete() method with user selections\n\t\"\"\"\n\tfrom AWS.s3 import S3Manager\n\tfrom pprint import pformat\n\tfrom helper.helper import sanitize_tree\n\t\n\tapi_region_name \u003d self.view.custom.api_region_name\n\tusername \u003d self.session.props.auth.user.userName\n\tself.view.custom.loading \u003d True\n\n\ts3m \u003d S3Manager(\u0027prod\u0027, api_region_name, username)\n\n\tbucket \u003d self.view.params.bucket\n\tobj_key \u003d self.view.custom.selected_file_config.Key\n\toperation \u003d \u0027delete\u0027\n\tparams \u003d {\u0027obj_key\u0027: obj_key, \u0027bucket\u0027: bucket}\n\ttry:\n\t\tresp \u003d getattr(s3m, operation)(**params)\n\t\tresp_code \u003d resp.get(\u0027code\u0027, None)\n\t\tif (resp_code and resp_code !\u003d 200) or (not resp_code and \u0027message\u0027 in resp):\n\t\t\t# this means the API encountered an error, annunciate the error here\n\t\t\tmsg \u003d \u0027API encountered error deleting %s on %s bucket. \\nResponse: %s\u0027 % (obj_key, bucket, pformat(sanitize_tree(resp)))\n\t\t\tsystem.perspective.print(msg)\n\t\t\tself.view.custom.loading \u003d False\n\t\t\tself.show_error_dialog(msg)\n\t\t\treturn\n\t\tmsg \u003d pformat(sanitize_tree(resp))\n\t\tsystem.perspective.print(msg)\n\t\tself.show_success_dialog(msg)\n\t\tself.update_bindings()\n\texcept:\n\t\timport traceback\n\t\tmsg \u003d \u0027Error executing %s operation! \\nError: %s\u0027 % (\n\t\t\t\toperation, traceback.format_exc())\n\t\tsystem.perspective.print(msg)\n\t\tself.view.custom.loading \u003d False\n\t\tself.show_error_dialog(msg)\n\t" } ], "extensionFunctions": null, "messageHandlers": [ { "messageType": "confirm_delete_file", "pageScope": false, "script": "\tsystem.perspective.closePopup(\u0027alertDialog\u0027)\n\tif payload is not None:\n\t\tbucket_requested \u003d payload.get(\u0027bucket\u0027, None)\n\t\tif bucket_requested and bucket_requested \u003d\u003d self.view.params.bucket:\n\t\t\t# call the delete custom method\n\t\t\tself.delete_file()", "sessionScope": true, "viewScope": true } ] }, "type": "ia.input.button" } ], "meta": { "name": "FlexContainer File Selection" }, "position": { "shrink": 0 }, "props": { "style": { "classes": "Framework/Cards/Row" } }, "type": "ia.container.flex" }, { "children": [ { "meta": { "name": "Label" }, "position": { "basis": "125px", "shrink": 0 }, "props": { "style": { "classes": "Framework/Card/Label Text/RightAlign_with_Padding", "paddingLeft": "5px" }, "text": "Object Key (uri):" }, "type": "ia.display.label" }, { "meta": { "name": "label_LeftAlign" }, "position": { "basis": "200px", "grow": 1 }, "propConfig": { "props.params.text": { "binding": { "config": { "path": "view.custom.selected_file_config.Key" }, "type": "property" } } }, "props": { "path": "Objects/Templates/Labels/label_LeftAlign", "style": { "classes": "Framework/Card/Value" } }, "type": "ia.display.view" } ], "meta": { "name": "FlexContainer Object Key" }, "position": { "shrink": 0 }, "propConfig": { "position.display": { "binding": { "config": { "expression": "!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "style": { "classes": "Framework/Cards/Row" } }, "type": "ia.container.flex" }, { "children": [ { "meta": { "name": "Label" }, "position": { "basis": "125px", "shrink": 0 }, "props": { "style": { "classes": "Framework/Card/Label Text/RightAlign_with_Padding", "paddingLeft": "5px" }, "text": "Last Modified:" }, "type": "ia.display.label" }, { "meta": { "name": "label_LeftAlign" }, "position": { "basis": "200px", "grow": 1 }, "propConfig": { "props.params.text": { "binding": { "config": { "path": "view.custom.selected_file_config.LastModified" }, "type": "property" } } }, "props": { "path": "Objects/Templates/Labels/label_LeftAlign", "style": { "classes": "Framework/Card/Value" } }, "type": "ia.display.view" } ], "meta": { "name": "FlexContainer Last Modified" }, "position": { "shrink": 0 }, "propConfig": { "position.display": { "binding": { "config": { "expression": "!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "style": { "classes": "Framework/Cards/Row" } }, "type": "ia.container.flex" }, { "children": [ { "children": [ { "meta": { "name": "Label" }, "position": { "basis": "125px", "shrink": 0 }, "props": { "style": { "classes": "Framework/Card/Label Text/RightAlign_with_Padding", "paddingLeft": "5px" }, "text": "File Size (KB):" }, "type": "ia.display.label" }, { "meta": { "name": "label_LeftAlign" }, "position": { "basis": "200px", "grow": 1 }, "propConfig": { "props.params.text": { "binding": { "config": { "expression": "round({view.custom.selected_file_config.Size}/1024.0,2)" }, "type": "expr" } } }, "props": { "path": "Objects/Templates/Labels/label_LeftAlign", "style": { "classes": "Framework/Card/Value" } }, "type": "ia.display.view" } ], "meta": { "name": "FlexContainer File Size" }, "position": { "basis": "50%", "grow": 1 }, "type": "ia.container.flex" }, { "children": [ { "meta": { "name": "Label" }, "position": { "basis": "125px", "shrink": 0 }, "props": { "style": { "classes": "Framework/Card/Label Text/RightAlign_with_Padding", "paddingLeft": "5px" }, "text": "Storage Class:" }, "type": "ia.display.label" }, { "meta": { "name": "label_LeftAlign" }, "position": { "basis": "200px", "grow": 1 }, "propConfig": { "props.params.text": { "binding": { "config": { "path": "view.custom.selected_file_config.StorageClass" }, "type": "property" } } }, "props": { "path": "Objects/Templates/Labels/label_LeftAlign", "style": { "classes": "Framework/Card/Value" } }, "type": "ia.display.view" } ], "meta": { "name": "FlexContainer Storage Class" }, "position": { "basis": "50%", "grow": 1 }, "type": "ia.container.flex" } ], "meta": { "name": "FlexContainer File Detail" }, "position": { "shrink": 0 }, "propConfig": { "position.display": { "binding": { "config": { "expression": "!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "style": { "classes": "Framework/Cards/Row" } }, "type": "ia.container.flex" }, { "children": [ { "events": { "component": { "onActionPerformed": { "config": { "script": "\t# build out the stage, site, flow-view, and copy_option from the object-key in row\n\tobj_key \u003d self.view.custom.selected_file_config.Key\n\tfilename \u003d self.view.custom.selected_file_config.Filename\n\tsuffix \u003d self.view.params.suffix\n\tpath \u003d obj_key.split(\u0027/\u0027)\n\tsite \u003d path[1]\n\tview \u003d filename.replace(suffix,\u0027\u0027)\n\tbucket \u003d self.view.params.bucket\n\t# build out query_params from row values\n\t# view, site, and bucket are multi-select dropdowns so need to be cast as lists\n\tnull \u003d None\n\tquery_params \u003d {\n\t\t\"copy_option\": null,\n\t\t\"destination_bucket\": bucket,\n\t\t\"destination_site\": site,\n\t\t\"destination_view\": view,\n\t\t\"end_time\": null,\n\t\t\"error_occurred\": null,\n\t\t\"operation\": null,\n\t\t\"source_bucket\": null,\n\t\t\"source_site\": null,\n\t\t\"source_view\": null,\n\t\t\"start_time\": null,\n\t\t\"username\": \"\"\n\t}\n\t# Open audit log viewer\n\tview_path \u003d \u0027PopUp-Views/S3/Audit/Log_Viewer\u0027\n\tparams \u003d {\u0027query_params\u0027: query_params}\n\tsystem.perspective.openPopup(\u0027Audit Log Viewer\u0027, view_path, \n\t\t\t\t\t\t\t\tparams, \u0027SCADA S3 Audit Logs\u0027)" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Audit Logs Button", "tooltip": { "enabled": true, "location": "bottom", "text": "View Audit Logs for this file" } }, "position": { "shrink": 0 }, "propConfig": { "props.enabled": { "binding": { "config": { "expression": "!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "image": { "icon": { "path": "material/table_view" } }, "primary": false, "style": { "margin": "2px", "paddingLeft": "4px", "paddingRight": "4px" }, "text": "Audit Logs" }, "type": "ia.input.button" }, { "events": { "component": { "onActionPerformed": { "config": { "script": "\t# build out the stage, site, flow-view, and copy_option from the object-key in row\n\tobj_key \u003d self.view.custom.selected_file_config.Key\n\tfilename \u003d self.view.custom.selected_file_config.Filename\n\tsuffix \u003d self.view.params.suffix\n\tpath \u003d obj_key.split(\u0027/\u0027)\n\tsite \u003d path[1]\n\tview \u003d filename.replace(suffix,\u0027\u0027)\n\tbucket \u003d self.view.params.bucket\n\t# build out query_params from row values\n\tquery_params \u003d {\n\t\t\"view\": view,\n\t\t\"object_key\": obj_key,\n\t\t\"site\": site,\n\t\t\"bucket\": bucket\n\t}\n\t# Open version history log viewer\n\tview_path \u003d \u0027PopUp-Views/S3/Versions/Log_Viewer\u0027\n\tparams \u003d {\u0027query_params\u0027: query_params}\n\tsystem.perspective.openPopup(\u0027Version Log Viewer\u0027, view_path, \n\t\t\t\t\t\t\t\tparams, \u0027SCADA S3 Version History Log Viewer\u0027)\n\t\t" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Version History Button", "tooltip": { "text": "View Version History for this file" } }, "position": { "shrink": 0 }, "propConfig": { "props.enabled": { "binding": { "config": { "expression": "!{view.custom.file_not_found}" }, "type": "expr" } } }, "props": { "image": { "icon": { "path": "material/history" } }, "primary": false, "style": { "margin": "2px", "paddingLeft": "4px", "paddingRight": "4px" }, "text": "Version History" }, "type": "ia.input.button" }, { "meta": { "name": "Label" }, "position": { "basis": "75px", "shrink": 0 }, "props": { "style": { "classes": "Framework/Card/Label Text/RightAlign_with_Padding", "paddingLeft": "5px" }, "text": "Upload New File:" }, "type": "ia.display.label" }, { "events": { "component": { "onFileReceived": { "config": { "script": "\tself.upload_file(event\u003devent)\n" }, "scope": "G", "type": "script" } } }, "meta": { "name": "FileUpload", "tooltip": { "enabled": true } }, "position": { "basis": "25%", "shrink": 0 }, "propConfig": { "meta.tooltip.text": { "binding": { "config": { "expression": "stringFormat(\u0027Upload new file to %s S3 bucket\u0027,{view.params.bucket})" }, "type": "expr" } }, "position.display": { "binding": { "config": { "path": "view.params.enables.upload" }, "type": "property" } }, "props.supportedFileTypes": { "binding": { "config": { "path": "view.params.upload_file_types" }, "type": "property" } } }, "props": { "fileSizeLimit": 100, "fileUploadIcon": { "style": { "classes": "" } }, "maxUploads": 1, "style": { "backgroundColor": "var(--neutral-30)", "borderStyle": "none", "classes": "FadeInFast, background, background-none", "cursor": "pointer", "margin": "2px", "max-height": "40px", "overflow": "visible" } }, "scripts": { "customMethods": [ { "name": "update_bindings", "params": [], "script": "\t\"\"\"\n\t\tAfter data saved to S3, refresh session and view bindings\n\t\"\"\"\n\t\n\tself.view.custom.loading \u003d False\n\t# reset file upload component to default state\n\tself.clearUploads()\n\t# send message to update files param on parent view\n\tbucket \u003d self.view.params.bucket\n\tsystem.perspective.sendMessage(\u0027update_file_binding\u0027, {\u0027bucket\u0027: bucket}, scope\u003d\u0027session\u0027)\n\t" }, { "name": "show_success_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"success\", \n\t\t\"File Uploaded\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t" }, { "name": "show_warning_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"warning\", \n\t\t\"File NOT Uploaded\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t" }, { "name": "show_error_dialog", "params": [ "msg\u003dNone" ], "script": "\t# ~~ 13 PARAMETERS ~~\n\t# state\t\t\t\t\t(default \u003d info) empty string uses generic gray styling\n\t# title \t\t\t\t(default \u003d Alert Title) empty string sets the title visibility to false\n\t# message \t\t\t\t(default \u003d Alert message goes here.)\n\t# show close button\t\t(default \u003d true) boolean\n\t# btn text primary\t\t(default \u003d \"Primary\")\n\t# btn text secondary\t(default \u003d \"Secondary\")\n\t# btn icon primary \t\t(default \u003d chevron_right) do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon secondary \t(default \u003d \"\") do not include \u0027material/\u0027 in the path, just the icon name\n\t# btn icon alignment\t(default \u003d \"right\") left or right\n\t# btn primary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn secondary action\t(default \u003d \"\") add message handlers on this button to enable other script actions\n\t# btn close action\t\t(default \u003d \"\") add message handlers on this icon to enable other script actions\n\t# payload\t\t\t\t(default \u003d {}) add a payload here to return to the target message handler\n\t\n\tAlerts.showAlert(\n\t\t\"error\", \n\t\t\"File Upload Error!\", \n\t\tmsg, \n\t\t\"true\",\n\t\t\"OK\", \n\t\t\"CLOSE\", \n\t\t\"\", \n\t\t\"\", \n\t\t\"left\", \n\t\t\"closePopup\", \n\t\t\"closePopup\", \n\t\t\"closePopup\",\n\t\t{}\n\t)\n\t\t\t\t\t" }, { "name": "upload_file", "params": [ "event\u003dNone" ], "script": "\tfrom AWS.s3 import S3Manager\n\tfrom pprint import pformat\n\t\n\tself.view.custom.loading \u003d True\n\ttry:\n\t\tapi_stage \u003d \u0027prod\u0027\n\t\tusername \u003d self.session.props.auth.user.userName\n\t\tapi_region_name \u003d self.view.custom.api_region_name\n\t\tbucket \u003d self.view.params.bucket\n\t\tdefault_obj_key \u003d self.view.custom.default_file_config.Key\n\t\tdefault_filename \u003d self.view.custom.default_file_config.Filename\n\t\tregion \u003d self.view.custom.stage_config.s3_region\n\t\tfilename \u003d event.file.name\n\t\t# check if file already exists in S3 site folder, throw error if so\n\t\tif any(x.Filename \u003d\u003d filename for x in self.view.params.files):\n\t\t\tmsg \u003d \u0027%s file already exists in the site folder. Please use the upload button next to the file select dropdown or select a new file to upload\u0027 % filename\n\t\t\tself.show_error_dialog(msg)\n\t\t\tself.view.custom.loading \u003d False\n\t\t\tself.clearUploads()\n\t\t\treturn\n\t\tobj_key \u003d default_obj_key.replace(default_filename, filename)\n\t\tobj_data \u003d event.file.getString()\n\t\t\n\t\ts3m \u003d S3Manager(api_stage, api_region_name, username)\n\t\ttry:\n\t\t\tresp \u003d s3m.upload(\n\t\t\t\tobj_data\u003dobj_data,\n\t\t\t\tobj_key\u003dobj_key,\n\t\t\t\tbucket\u003dbucket,\n\t\t\t\tregion\u003dregion\n\t\t\t)\n\t\t\tresp_code \u003d resp.get(\u0027code\u0027, None)\n\t\t\tif (resp_code and resp_code !\u003d 200) or (not resp_code and \u0027message\u0027 in resp):\n\t\t\t\t# this means the API encountered an error, annunciate the error here\n\t\t\t\tmsg \u003d \u0027API encountered error uploading %s to %s bucket. \\nResponse: %s\u0027 % (obj_key, bucket, pformat(resp))\n\t\t\t\tsystem.perspective.print(msg)\n\t\t\t\tself.view.custom.loading \u003d False\n\t\t\t\tself.show_error_dialog(msg)\n\t\t\t\tself.clearUploads()\n\t\t\t\treturn\n\t\t\tmsg \u003d \u0027Successfully uploaded %s object in %s bucket!\\nResponse: %s\u0027 % (obj_key, bucket, pformat(resp))\n\t\t\tsystem.perspective.print(msg)\n\t\t\tself.show_success_dialog(msg)\n\t\t\tself.update_bindings()\n\t\texcept:\n\t\t\timport traceback\n\t\t\tmsg \u003d \u0027Error uploading %s object in %s bucket: %s\u0027 % (obj_key, bucket, traceback.format_exc())\n\t\t\tsystem.perspective.print(msg)\n\t\t\tself.view.custom.loading \u003d False\n\t\t\tself.show_error_dialog(msg)\n\t\t\tself.clearUploads()\n\texcept:\n\t\timport traceback\n\t\tmsg \u003d \u0027General Error uploading %s object in %s bucket: %s\u0027 % (obj_key, bucket, traceback.format_exc())\n\t\tsystem.perspective.print(msg)\n\t\tself.view.custom.loading \u003d False\n\t\tself.show_error_dialog(msg)\n\t\tself.clearUploads()\n\t\t" } ], "extensionFunctions": null, "messageHandlers": [] }, "type": "ia.input.fileupload" } ], "meta": { "name": "FlexContainer Log Buttons" }, "position": { "shrink": 0 }, "props": { "justify": "center" }, "type": "ia.container.flex" }, { "children": [ { "children": [ { "meta": { "name": "Title" }, "position": { "basis": "100%" }, "propConfig": { "props.text": { "binding": { "config": { "expression": "stringFormat(\u0027%s Object%s\u0027, len({view.params.files}), \r\n\tif(len({view.params.files})\u003d1,\u0027\u0027,\u0027s\u0027))" }, "type": "expr" } } }, "props": { "style": { "classes": "Title/Text", "fontSize": 14, "overflow": "visible" } }, "type": "ia.display.label" } ], "meta": { "name": "Title" }, "position": { "basis": "50%" }, "props": { "style": { "fontSize": 1, "marginLeft": 10, "overflow": "visible" } }, "type": "ia.container.flex" }, { "children": [ { "meta": { "name": "FilterCheck", "tooltip": { "enabled": true, "location": "top-left", "text": "Enable Table Search" } }, "position": { "basis": "108px" }, "propConfig": { "position.display": { "binding": { "config": { "expression": "LEN({..../Table.props.data})\u003e0" }, "type": "expr" } } }, "props": { "checkedIcon": { "style": { "fontSize": 16 } }, "indeterminateIcon": { "style": { "fontSize": 16 } }, "style": { "fontSize": 12 }, "text": "Search?", "textPosition": "left", "uncheckedIcon": { "style": { "fontSize": 16 } } }, "type": "ia.input.checkbox" }, { "meta": { "name": "Spacer1" }, "position": { "basis": "1px" }, "props": { "style": { "classes": "General/Divider" } }, "type": "ia.container.flex" }, { "events": { "dom": { "onClick": { "config": { "script": "\tself.view.custom.selected_file \u003d \u0027\u0027\n\ttable \u003d self.parent.parent.parent.getChild(\"Table\")\n\t# ignition perspective has a bug with table where the only way to \n\t# actually de-select and remove the row highlight is to set the\n\t# row and column to -1 and THEN None\n\t# this will automatically clear the selection.data array\n\ttable.props.selection.selectedRow \u003d -1\n\ttable.props.selection.selectedColumn \u003d -1\n\ttable.props.selection.selectedRow \u003d None\n\ttable.props.selection.selectedColumn \u003d None" }, "scope": "G", "type": "script" } } }, "meta": { "name": "ClearSelectionButton", "tooltip": { "enabled": true, "location": "top-left", "text": "Clear Selection" } }, "position": { "basis": "31px" }, "propConfig": { "position.display": { "binding": { "config": { "expression": "len({..../Table.props.selection.data})\u003e0" }, "type": "expr" } } }, "props": { "path": "material/clear", "style": { "classes": "General/Button" } }, "type": "ia.display.icon" }, { "meta": { "name": "Spacer2" }, "position": { "basis": "1px" }, "propConfig": { "position.display": { "binding": { "config": { "path": "../ClearSelectionButton.position.display" }, "type": "property" } } }, "props": { "style": { "classes": "General/Divider" } }, "type": "ia.container.flex" }, { "events": { "dom": { "onClick": { "config": { "draggable": false, "id": "ColumnSelection", "modal": true, "overlayDismiss": true, "position": { "relativeLocation": "bottom-left" }, "positionType": "relative", "resizable": true, "showCloseIcon": true, "type": "toggle", "viewParams": { "Columns": "{/root/FlexContainer/TableHeader/TableActions/ColumnSelectionButton.custom.Columns}" }, "viewPath": "Objects/PowerTable/ColumnSelection", "viewportBound": false }, "scope": "C", "type": "popup" } } }, "meta": { "name": "ColumnSelectionButton", "tooltip": { "enabled": true, "location": "top-left", "text": "+/- Columns" } }, "position": { "basis": "29px" }, "propConfig": { "custom.Columns": { "binding": { "config": { "path": "..../Table.props.columns" }, "transforms": [ { "code": "\tcolumns \u003d {}\n\tif len(value) \u003e 0:\n\t\tfor column in value:\n\t\t\t#field \u003d column.field\n\t\t\tfield \u003d column.header.title\n\t\t\tif field \u003d\u003d \u0027\u0027:\n\t\t\t\tfield \u003d \u0027None\u0027\n\t\t\tcolumns[field] \u003d column.visible\n\treturn columns", "type": "script" } ], "type": "property" } }, "position.display": { "binding": { "config": { "expression": "LEN({..../Table.props.data})\u003e0" }, "type": "expr" } } }, "props": { "path": "material/view_column", "style": { "classes": "General/Button", "fontSize": 12, "marginBottom": 5, "marginTop": 5 } }, "type": "ia.display.icon" }, { "meta": { "name": "Spacer4" }, "position": { "basis": "1px" }, "propConfig": { "position.display": { "binding": { "config": { "expression": "LEN({..../Table.props.data})\u003e0" }, "type": "expr" } } }, "props": { "style": { "classes": "General/Divider" } }, "type": "ia.container.flex" }, { "events": { "dom": { "onClick": { "config": { "script": "\tself.view.custom.filters.selection_active \u003d not self.view.custom.filters.selection_active" }, "scope": "G", "type": "script" } } }, "meta": { "name": "FilterButton", "tooltip": { "enabled": true, "location": "top-left", "text": "Filter Table" } }, "position": { "basis": "29px", "display": false }, "props": { "path": "material/filter_list", "style": { "classes": "General/Button", "fontSize": 12, "marginBottom": 5, "marginTop": 5 } }, "type": "ia.display.icon" }, { "meta": { "name": "Spacer3" }, "position": { "basis": "1px" }, "props": { "style": { "classes": "General/Divider" } }, "type": "ia.container.flex" }, { "events": { "dom": { "onClick": { "config": { "script": "\n\tcsv_headers \u003d []\n\tcsv_data \u003d []\n\tsystem.perspective.print(\u0027DOWNLOADING TABLE DATA\u0027)\n\tsource_data \u003d self.parent.parent.parent.getChild(\"Table\").props.data\n\theaders \u003d source_data[0].keys()\n\t\n\tif \u0027style\u0027 in headers and \u0027value\u0027 in headers and len(headers) \u003d\u003d 2:\n\t\tdata \u003d [row[\u0027value\u0027] for row in source_data]\n\telse:\n\t\tdata \u003d source_data\n\t\t\n\tfor record in data:\n\t\tif len(csv_headers) \u003d\u003d 0:\n\t\t\tcsv_headers \u003d record.keys()\n\t\t\tcsv_headers.sort()\n\t\t\tcsv_headers \u003d [str(i) for i in csv_headers]\n\t\tcsv_row \u003d []\n\t\tfor index in range(len(record)):\n\t\t\tcsv_row.append(str(record[csv_headers[index]]))\n\t\tcsv_data.append(csv_row)\n\t\n\ttry:\n\t\tcsv_dataset \u003d system.dataset.toDataSet(csv_headers, csv_data)\n\texcept Exception, e:\n\t\tsystem.perspective.print(str(e))\n\tcsv_export \u003d system.dataset.toCSV(csv_dataset)\n\tfilename \u003d \u0027{0}.csv\u0027.format(str(system.date.now()).replace(\u0027 \u0027, \u0027_\u0027))\n\tsystem.perspective.download(filename, csv_export)\n\t\n\tsystem.perspective.print(\u0027DONE DOWNLOADING TABLE DATA\u0027)" }, "scope": "G", "type": "script" } } }, "meta": { "name": "SettingsButton", "tooltip": { "enabled": true, "location": "top-left", "text": "Download Table Contents" } }, "position": { "basis": "30px" }, "propConfig": { "position.display": { "binding": { "config": { "expression": "LEN({..../Table.props.data})\u003e0" }, "type": "expr" } } }, "props": { "path": "material/cloud_download", "style": { "classes": "General/Button", "marginRight": 10 } }, "type": "ia.display.icon" } ], "meta": { "name": "TableActions", "tooltip": { "location": "top-right" } }, "position": { "grow": 1 }, "props": { "justify": "flex-end" }, "type": "ia.container.flex" } ], "meta": { "name": "TableHeader" }, "position": { "shrink": 0 }, "props": { "justify": "space-between", "style": { "borderBottomStyle": "solid", "borderBottomWidth": 1, "cursor": "pointer" } }, "type": "ia.container.flex" }, { "events": { "component": { "onSelectionChange": { "config": { "script": "\t# validate the selection data is not null\n\tif self.props.selection.data:\n\t\tfilename \u003d self.props.selection.data[0].Filename\n\t\tself.view.custom.selected_file \u003d filename\n\t\t# send message to update selected file on parent container\n\t\tsuffix \u003d self.view.params.suffix\n\t\tpayload \u003d {\u0027image\u0027: filename.replace(suffix, \u0027\u0027)}\n\t\tsystem.perspective.sendMessage(\u0027update_selected_image\u0027, payload, scope\u003d\u0027session\u0027)\n\t" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Table" }, "position": { "basis": "370px", "shrink": 0 }, "propConfig": { "props.data": { "binding": { "config": { "path": "view.params.files" }, "type": "property" } }, "props.filter.enabled": { "binding": { "config": { "path": "../TableHeader/TableActions/FilterCheck.props.selected" }, "type": "property" }, "onChange": { "enabled": null, "script": "\tif not getattr(currentValue, \u0027value\u0027, None):\n\t\t# clear filter text when filter is disabled\n\t\tself.props.filter.text \u003d \u0027\u0027\n\t\t" } } }, "props": { "columns": [ { "align": "center", "boolean": "checkbox", "dateFormat": "MM/DD/YYYY", "editable": false, "field": "Filename", "filter": { "boolean": { "condition": "" }, "date": { "condition": "", "value": "" }, "enabled": false, "number": { "condition": "", "value": "" }, "string": { "condition": "", "value": "" }, "visible": "on-hover" }, "footer": { "align": "center", "justify": "left", "style": { "classes": "" }, "title": "" }, "header": { "align": "center", "justify": "center", "style": { "classes": "" }, "title": "Filename" }, "justify": "center", "number": "value", "numberFormat": "0,0.##", "progressBar": { "bar": { "color": "", "style": { "classes": "" } }, "max": 100, "min": 0, "track": { "color": "", "style": { "classes": "" } }, "value": { "enabled": true, "format": "0,0.##", "justify": "center", "style": { "classes": "" } } }, "render": "auto", "resizable": true, "sort": "none", "sortable": true, "strictWidth": false, "style": { "classes": "" }, "toggleSwitch": { "color": { "selected": "", "unselected": "" } }, "viewParams": {}, "viewPath": "", "visible": true, "width": "" }, { "align": "center", "boolean": "checkbox", "dateFormat": "MM/DD/YYYY", "editable": false, "field": "Size", "filter": { "boolean": { "condition": "" }, "date": { "condition": "", "value": "" }, "enabled": false, "number": { "condition": "", "value": "" }, "string": { "condition": "", "value": "" }, "visible": "on-hover" }, "footer": { "align": "center", "justify": "left", "style": { "classes": "" }, "title": "" }, "header": { "align": "center", "justify": "center", "style": { "classes": "" }, "title": "Size (bytes)" }, "justify": "center", "number": "value", "numberFormat": "0,0.##", "progressBar": { "bar": { "color": "", "style": { "classes": "" } }, "max": 100, "min": 0, "track": { "color": "", "style": { "classes": "" } }, "value": { "enabled": true, "format": "0,0.##", "justify": "center", "style": { "classes": "" } } }, "render": "auto", "resizable": true, "sort": "none", "sortable": true, "strictWidth": false, "style": { "classes": "" }, "toggleSwitch": { "color": { "selected": "", "unselected": "" } }, "viewParams": {}, "viewPath": "", "visible": true, "width": "" }, { "align": "center", "boolean": "checkbox", "dateFormat": "MM/DD/YYYY", "editable": false, "field": "LastModified", "filter": { "boolean": { "condition": "" }, "date": { "condition": "", "value": "" }, "enabled": false, "number": { "condition": "", "value": "" }, "string": { "condition": "", "value": "" }, "visible": "on-hover" }, "footer": { "align": "center", "justify": "left", "style": { "classes": "" }, "title": "" }, "header": { "align": "center", "justify": "center", "style": { "classes": "" }, "title": "Last Updated" }, "justify": "center", "number": "value", "numberFormat": "0,0.##", "progressBar": { "bar": { "color": "", "style": { "classes": "" } }, "max": 100, "min": 0, "track": { "color": "", "style": { "classes": "" } }, "value": { "enabled": true, "format": "0,0.##", "justify": "center", "style": { "classes": "" } } }, "render": "auto", "resizable": true, "sort": "none", "sortable": true, "strictWidth": false, "style": { "classes": "" }, "toggleSwitch": { "color": { "selected": "", "unselected": "" } }, "viewParams": {}, "viewPath": "", "visible": true, "width": "" }, { "align": "center", "boolean": "checkbox", "dateFormat": "MM/DD/YYYY", "editable": false, "field": "Key", "filter": { "boolean": { "condition": "" }, "date": { "condition": "", "value": "" }, "enabled": false, "number": { "condition": "", "value": "" }, "string": { "condition": "", "value": "" }, "visible": "on-hover" }, "footer": { "align": "center", "justify": "left", "style": { "classes": "" }, "title": "" }, "header": { "align": "center", "justify": "center", "style": { "classes": "" }, "title": "Key" }, "justify": "center", "number": "value", "numberFormat": "0,0.##", "progressBar": { "bar": { "color": "", "style": { "classes": "" } }, "max": 100, "min": 0, "track": { "color": "", "style": { "classes": "" } }, "value": { "enabled": true, "format": "0,0.##", "justify": "center", "style": { "classes": "" } } }, "render": "auto", "resizable": true, "sort": "none", "sortable": true, "strictWidth": false, "style": { "classes": "" }, "toggleSwitch": { "color": { "selected": "", "unselected": "" } }, "viewParams": {}, "viewPath": "", "visible": true, "width": "" }, { "align": "center", "boolean": "checkbox", "dateFormat": "MM/DD/YYYY", "editable": false, "field": "ETag", "filter": { "boolean": { "condition": "" }, "date": { "condition": "", "value": "" }, "enabled": false, "number": { "condition": "", "value": "" }, "string": { "condition": "", "value": "" }, "visible": "on-hover" }, "footer": { "align": "center", "justify": "left", "style": { "classes": "" }, "title": "" }, "header": { "align": "center", "justify": "center", "style": { "classes": "" }, "title": "ETag" }, "justify": "center", "number": "value", "numberFormat": "0,0.##", "progressBar": { "bar": { "color": "", "style": { "classes": "" } }, "max": 100, "min": 0, "track": { "color": "", "style": { "classes": "" } }, "value": { "enabled": true, "format": "0,0.##", "justify": "center", "style": { "classes": "" } } }, "render": "auto", "resizable": true, "sort": "none", "sortable": true, "strictWidth": false, "style": { "classes": "" }, "toggleSwitch": { "color": { "selected": "", "unselected": "" } }, "viewParams": {}, "viewPath": "", "visible": false, "width": "" }, { "align": "center", "boolean": "checkbox", "dateFormat": "MM/DD/YYYY", "editable": false, "field": "StorageClass", "filter": { "boolean": { "condition": "" }, "date": { "condition": "", "value": "" }, "enabled": false, "number": { "condition": "", "value": "" }, "string": { "condition": "", "value": "" }, "visible": "on-hover" }, "footer": { "align": "center", "justify": "left", "style": { "classes": "" }, "title": "" }, "header": { "align": "center", "justify": "center", "style": { "classes": "" }, "title": "Storage Class" }, "justify": "center", "number": "value", "numberFormat": "0,0.##", "progressBar": { "bar": { "color": "", "style": { "classes": "" } }, "max": 100, "min": 0, "track": { "color": "", "style": { "classes": "" } }, "value": { "enabled": true, "format": "0,0.##", "justify": "center", "style": { "classes": "" } } }, "render": "auto", "resizable": true, "sort": "none", "sortable": true, "strictWidth": false, "style": { "classes": "" }, "toggleSwitch": { "color": { "selected": "", "unselected": "" } }, "viewParams": {}, "viewPath": "", "visible": false, "width": "" } ], "filter": {}, "style": { "margin": "10px" } }, "type": "ia.display.table" } ], "meta": { "name": "FlexContainer" }, "position": { "basis": "100%", "grow": 1 }, "props": { "direction": "column" }, "type": "ia.container.flex" } ], "meta": { "name": "root" }, "scripts": { "customMethods": [], "extensionFunctions": null, "messageHandlers": [ { "messageType": "activate-filter", "pageScope": true, "script": "\t# implement your handler here\n\tfilter_position \u003d payload[\u0027id\u0027]\n\tsystem.perspective.print(filter_position)\n\tadd \u003d True\n\tfor filter in self.view.custom.filters.active:\n\t\tif filter.id \u003d\u003d filter_position:\n\t\t\tadd \u003d False\n\tif add:\n\t\tfor filter in self.view.custom.filters.deactive:\n\t\t\tif filter.id \u003d\u003d filter_position:\t\t\t\t\n\t\t\t\tself.view.custom.filters.active.append(filter)", "sessionScope": true, "viewScope": false }, { "messageType": "deactivate-filter", "pageScope": true, "script": "\tfilter_position \u003d payload[\u0027id\u0027]\n\tsystem.perspective.print(filter_position)\n\t\n\tif filter_position \u003d\u003d -1 :\n\t\tself.view.custom.filters.active \u003d []\n\telse:\n\t\tfor index, filter in enumerate(self.view.custom.filters.active):\n\t\t\tif filter.id \u003d\u003d filter_position:\n\t\t\t\tsystem.perspective.print(filter.id)\n\t\t\t\tself.view.custom.filters.active.pop(index)\n\n#\tfor filter in self.view.custom.filter_menu_data:\n#\t\tif filter.filter_id \u003d\u003d filter_position:\n#\t\t\tsystem.perspective.print(filter.filter_id)\n#\t\t\tfilter.active \u003d False\n#\t\t\tbreak", "sessionScope": true, "viewScope": false }, { "messageType": "column-visibility", "pageScope": true, "script": "\t# implement your handler here\n\ttable_columns \u003d self.getChild(\"FlexContainer\").getChild(\"Table\").props.columns\n\tfor table_column in table_columns:\n\t\t#if payload.keys()[0] \u003d\u003d table_column[\u0027field\u0027]:\n\t\tif payload.keys()[0] \u003d\u003d table_column[\u0027header\u0027][\u0027title\u0027]:\n\t\t\ttable_column.visible \u003d payload.values()[0]", "sessionScope": false, "viewScope": false } ] }, "type": "ia.container.flex" } }