diff --git a/tests/data/Valid.wml b/tests/data/Valid.wml index 1e6e435..06c2c1a 100644 --- a/tests/data/Valid.wml +++ b/tests/data/Valid.wml @@ -199,25 +199,25 @@ "id": "67188218-7988-45b2-9cdf-a95897f40eb4", "orientation": 0, "ports": { - "port1": { - "id": "port1", + "in_port": { + "id": "in_port", "position": { "x": 100, "y": 0 }, "properties": { - "custom": "property" + "value": "in" }, "type": "top" }, - "port2": { - "id": "port2", + "out_port": { + "id": "out_port", "position": { "x": 100, "y": 80 }, "properties": { - "custom": "property" + "value": "out" }, "type": "bottom" } @@ -502,22 +502,22 @@ "orientation": 0, "type": "BashOperator", "ports": { - "port1": { - "id": "port1", + "in_port": { + "id": "in_port", "type": "top", "properties": { - "custom": "property" + "value": "in" }, "position": { "x": 100, "y": 0 } }, - "port2": { - "id": "port2", + "out_port": { + "id": "out_port", "type": "bottom", "properties": { - "custom": "property" + "value": "out" }, "position": { "x": 100, diff --git a/tests/test_dag_imports.py b/tests/test_dag_imports.py index 6321aa2..0bcc84e 100644 --- a/tests/test_dag_imports.py +++ b/tests/test_dag_imports.py @@ -38,7 +38,7 @@ def tearDown(self): class TestDagToDagHandler(Fixture): def test_valid_pyfile(self): dag_file_handler = DagFileHandler(self.testfile, self.conf) - dags = dag_file_handler.dags + dag = dag_file_handler.dag - assert len(dags) == 1 - # assert dags[0].dag_id == "ValidDag" + # assert len(dags) == 1 + assert dag.dag_id == "ValidDag" diff --git a/tox.ini b/tox.ini index 09f4780..9f9c4f7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] skipsdist = True -envlist = py36, py37, lint +envlist = py36, py37, py38, lint [testenv] whitelist_externals = poetry diff --git a/windmill/http/api/endpoints.py b/windmill/http/api/endpoints.py index ab498d8..9c02941 100644 --- a/windmill/http/api/endpoints.py +++ b/windmill/http/api/endpoints.py @@ -9,7 +9,7 @@ from ...config.project_config import ProjectConfig from ...exceptions import DagHandlerValidationError -from ...models.dags.dag_handler import DagHandler +from ...models.dags.dag_handler import DagHandler, DagFileHandler from ...models.operators.operator_index import get_operator_index from ...models.schemas.app_schemas import OperatorSchema, MinimalWmlSchema @@ -49,15 +49,48 @@ def get_wmls(name=None): logging.info(f"GET /v1/wml/{name}") if name: - f_path = os.path.join(app.config["project_conf"].wml_dir, name) - if os.path.exists(f_path): - with open(f_path, "r") as f: - data = json.load(f) - return jsonify(data), 200 - else: - return f"File {f_path} not found", 404 + if os.path.splitext(name)[1] == ".wml": + f_path = os.path.join(app.config["project_conf"].wml_dir, name) + if os.path.exists(f_path): + try: + with open(f_path, "r") as f: + data = json.load(f) + return jsonify(data), 200 + except TypeError as e: + logging.exception(f"Unable to parse WML file {f_path}") + return ( + f"Unable to parse WML file {f_path} - internal state corrupted", + 400, + ) + except Exception as e: + logging.exception(f"Unable to parse WML file {f_path}") + return f"Internal error parsing WML", 400 + else: + return f"File {f_path} not found", 404 + elif os.path.splitext(name)[1] == ".py": + try: + dh = DagFileHandler(name, app.config["project_conf"]) + return jsonify(dh.wml), 200 + except DagHandlerValidationError as e: + logging.exception(f"Unable to parse python file {name}") + return f"Unable to parse python file {name}: {e}", 400 + except FileNotFoundError: + return f"File {name} not found", 404 + except Exception as e: + logging.exception(f"Unknown error parsing Python file {name}") + return f"Internal error parsing Python", 500 else: - return jsonify(os.listdir(app.config["project_conf"].wml_dir)), 200 + return ( + jsonify( + [ + f + for f in os.listdir(app.config["project_conf"].wml_dir) + + os.listdir(app.config["project_conf"].dags_dir) + if f.endswith(".wml") or f.endswith(".py") + ] + ), + 200, + ) @app.route("/v1/wml/", methods=["POST"]) diff --git a/windmill/http/app/package-lock.json b/windmill/http/app/package-lock.json index d05f180..ac5029e 100644 --- a/windmill/http/app/package-lock.json +++ b/windmill/http/app/package-lock.json @@ -1040,6 +1040,12 @@ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", "dev": true }, + "@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "dev": true + }, "@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", @@ -1053,12 +1059,12 @@ "dev": true }, "@mrblenny/react-flow-chart": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@mrblenny/react-flow-chart/-/react-flow-chart-0.0.13.tgz", - "integrity": "sha512-vK1HDX16yA+p3fA2sypoKIlQdxh3v32/ne2JVPp/riWrjHROJhd/RgRo4cKK+1+U34XvGLQZ1N+/j8f0Y7WgKw==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@mrblenny/react-flow-chart/-/react-flow-chart-0.0.14.tgz", + "integrity": "sha512-3bFjlmlYuqHpCRCPoA59jok2Vhe59ZKT5g9lb6U5IM+Zk2fIsKmXp8LEcliW0TrHtNMtZw5Gm3/rScrg/DwAFQ==", "requires": { "pathfinding": "^0.4.18", - "react-draggable": "^4.3.1", + "react-draggable": "^4.4.3", "react-resize-observer": "^1.1.1", "react-zoom-pan-pinch": "^1.6.1", "uuid": "^3.3.2" @@ -1131,9 +1137,9 @@ } }, "@types/lodash": { - "version": "4.14.150", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz", - "integrity": "sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w==", + "version": "4.14.157", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.157.tgz", + "integrity": "sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==", "dev": true }, "@types/prop-types": { @@ -1148,18 +1154,18 @@ "dev": true }, "@types/react": { - "version": "16.9.34", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz", - "integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==", + "version": "16.9.41", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.41.tgz", + "integrity": "sha512-6cFei7F7L4wwuM+IND/Q2cV1koQUvJ8iSV+Gwn0c3kvABZ691g7sp3hfEQHOUBJtccl1gPi+EyNjMIl9nGA0ug==", "requires": { "@types/prop-types": "*", "csstype": "^2.2.0" } }, "@types/react-dom": { - "version": "16.9.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.7.tgz", - "integrity": "sha512-GHTYhM8/OwUCf254WO5xqR/aqD3gC9kSTLpopWGpQLpnw23jk44RvMHsyUSEplvRJZdHxhJGMMLF0kCPYHPhQA==", + "version": "16.9.8", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", + "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==", "requires": { "@types/react": "*" } @@ -2409,14 +2415,22 @@ } }, "css-to-react-native": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz", - "integrity": "sha512-VOFaeZA053BqvvvqIA8c9n0+9vFppVBAHCp6JgFTtTMU3Mzi+XnelJ9XC9ul3BqFzZyQ5N+H0SnwsWT2Ebchxw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", + "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", "dev": true, "requires": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^3.3.0" + "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + } } }, "css-tree": { @@ -2561,9 +2575,9 @@ } }, "csstype": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", - "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.11.tgz", + "integrity": "sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw==" }, "dashdash": { "version": "1.14.1", @@ -4137,6 +4151,15 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + } + }, "hsl-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", @@ -4574,12 +4597,6 @@ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "dev": true }, - "is-what": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.8.0.tgz", - "integrity": "sha512-UKeBoQfV8bjlM4pmx1FLDHdxslW/1mTksEs8ReVsilPmUv5cORd4+2/wFcviI3cUjrLybxCjzc8DnodAzJ/Wrg==", - "dev": true - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -4907,21 +4924,6 @@ "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", "dev": true }, - "memoize-one": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", - "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==", - "dev": true - }, - "merge-anything": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-2.4.4.tgz", - "integrity": "sha512-l5XlriUDJKQT12bH+rVhAHjwIuXWdAIecGwsYjv2LJo+dA1AeRTmeQS+3QBpO6lEthBMDi2IUMpLC1yyRvGlwQ==", - "dev": true, - "requires": { - "is-what": "^3.3.1" - } - }, "merge-source-map": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", @@ -6278,9 +6280,9 @@ } }, "react-draggable": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.3.1.tgz", - "integrity": "sha512-m8QeV+eIi7LhD5mXoLqDzLbokc6Ncwa0T34fF6uJzWSs4vc4fdZI/XGqHYoEn91T8S6qO+BSXslONh7Jz9VPQQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz", + "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==", "requires": { "classnames": "^2.2.5", "prop-types": "^15.6.0" @@ -6295,9 +6297,9 @@ } }, "react-is": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", - "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-onclickoutside": { "version": "6.9.0", @@ -6752,6 +6754,12 @@ "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=", "dev": true }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "dev": true + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -7208,23 +7216,20 @@ "integrity": "sha1-IrD6OkE4WzO+PzMVUbu4N/oM164=" }, "styled-components": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.4.1.tgz", - "integrity": "sha512-RNqj14kYzw++6Sr38n7197xG33ipEOktGElty4I70IKzQF1jzaD1U4xQ+Ny/i03UUhHlC5NWEO+d8olRCDji6g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.1.1.tgz", + "integrity": "sha512-1ps8ZAYu2Husx+Vz8D+MvXwEwvMwFv+hqqUwhNlDN5ybg6A+3xyW1ECrAgywhvXapNfXiz79jJyU0x22z0FFTg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@emotion/is-prop-valid": "^0.8.1", - "@emotion/unitless": "^0.7.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^0.8.8", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", "babel-plugin-styled-components": ">= 1", - "css-to-react-native": "^2.2.2", - "memoize-one": "^5.0.0", - "merge-anything": "^2.2.4", - "prop-types": "^15.5.4", - "react-is": "^16.6.0", - "stylis": "^3.5.0", - "stylis-rule-sheet": "^0.0.10", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", "supports-color": "^5.5.0" } }, @@ -7252,18 +7257,6 @@ } } }, - "stylis": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", - "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==", - "dev": true - }, - "stylis-rule-sheet": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", - "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -7466,9 +7459,9 @@ "dev": true }, "typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", + "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", "dev": true }, "uncss": { diff --git a/windmill/http/app/package.json b/windmill/http/app/package.json index 7af296a..08da3f7 100644 --- a/windmill/http/app/package.json +++ b/windmill/http/app/package.json @@ -14,17 +14,17 @@ "author": "", "license": "ISC", "devDependencies": { - "@types/lodash": "^4.14.150", + "@types/lodash": "^4.14.157", "@types/react-onclickoutside": "^6.7.3", "lodash": "^4.17.15", "parcel-bundler": "^1.12.4", - "styled-components": "^4.4.1", - "typescript": "^3.8.3" + "styled-components": "^5.1.1", + "typescript": "^3.9.6" }, "dependencies": { - "@mrblenny/react-flow-chart": "0.0.13", - "@types/react": "^16.9.34", - "@types/react-dom": "^16.9.7", + "@mrblenny/react-flow-chart": "0.0.14", + "@types/react": "^16.9.41", + "@types/react-dom": "^16.9.8", "@types/react-resizable": "^1.7.2", "@types/react-textarea-autosize": "^4.3.5", "@types/react-tooltip": "^3.11.0", @@ -35,6 +35,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-icons": "^3.10.0", + "react-is": "^16.13.1", "react-onclickoutside": "^6.9.0", "react-textarea-autosize": "^7.1.2", "react-tooltip": "^3.11.6" diff --git a/windmill/http/app/src/App.tsx b/windmill/http/app/src/App.tsx index 8b17ebd..480b8ce 100644 --- a/windmill/http/app/src/App.tsx +++ b/windmill/http/app/src/App.tsx @@ -215,10 +215,10 @@ export class App extends React.Component<{}, IAppState> { // name: "Help", // callback: () => this} /> // }, - { - name: "View", - callback: () => this} />, - }, + // { + // name: "View", + // callback: () => this} />, + // }, ], }; diff --git a/windmill/http/app/src/misc/defaultChartState.ts b/windmill/http/app/src/misc/defaultChartState.ts index 8fbbc57..13d4e59 100644 --- a/windmill/http/app/src/misc/defaultChartState.ts +++ b/windmill/http/app/src/misc/defaultChartState.ts @@ -2,9 +2,10 @@ import { IAppState } from ".."; export const defaultChart: IAppState = { offset: { - x: -1000, - y: -1000 + x: 0, + y: 0, }, + scale: 1, nodes: {}, links: {}, selected: {}, diff --git a/windmill/models/dags/dag_handler.py b/windmill/models/dags/dag_handler.py index 0e82872..6c3dd34 100644 --- a/windmill/models/dags/dag_handler.py +++ b/windmill/models/dags/dag_handler.py @@ -172,7 +172,7 @@ def snake_name(self): [str]: task_name """ if not self._snake_name: - name = underscore(self.task_id) + name = underscore(self.task_id.replace(" ", "_")) name = re.sub("[^0-9a-zA-Z_]", "", name) name = re.sub("^[^a-zA-Z_]+", "", name) @@ -314,8 +314,8 @@ def load_from_links(links, task_name_mappings): return Links(graph, task_name_mappings) @staticmethod - def graph_to_efficient_representation(graph): - """Greedy algorithm to find an efficient representation of a graph + def graph_to_efficient_representation(graph): # r to ignore Wd1401 + r"""Greedy algorithm to find an efficient representation of a graph A Graph that looks like: @@ -444,10 +444,10 @@ def snake_name(self): Returns: [str]: dag_name """ - return underscore(self.dag_id) + return underscore(self.dag_id.replace(" ", "_")) @classmethod - def fix_task_names(self, tasks: List[TaskHandler]): + def fix_task_names(self, dag_id, tasks: List[TaskHandler]): """Task IDs are enforced as unique (as of AF 2.0) but when converting to python vars there's potential for clashes. @@ -455,6 +455,7 @@ def fix_task_names(self, tasks: List[TaskHandler]): an error if duplicate task names are Arguments: + dag_id {str} -- Name of the dag tasks {List[TaskHandler]} -- Task handlers to dedupe Raises: @@ -464,7 +465,7 @@ def fix_task_names(self, tasks: List[TaskHandler]): if len(task_ids) != len(set(task_ids)): raise DagHandlerValidationError("DAGs cannot have duplicate task IDs") - snake_names = [] + snake_names = [dag_id] for task in tasks: if task.snake_name in snake_names: basename = task.snake_name @@ -503,7 +504,7 @@ def load_from_wml(cls, wml_dict): tasks = [ TaskHandler.load_from_node(node) for node in wml_dict["nodes"].values() ] - tasks = cls.fix_task_names(tasks) + tasks = cls.fix_task_names(dag_params["dag_id"], tasks) task_name_mappings = {t.node_id: t.snake_name for t in tasks} links = Links.load_from_links(wml_dict["links"].values(), task_name_mappings) @@ -522,9 +523,15 @@ def to_wml(self) -> Dict: parameter["value"] = self.params[field]["value"] dag_dict["name"] = self.dag_id - links = self.links.to_app_schema() - coords = self.links.to_coords() - nodes = {t.node_id: t.to_app_schema(**coords[t.node_id]) for t in self.tasks} + if len(self.links.graph): + links = self.links.to_app_schema() + coords = self.links.to_coords() + nodes = { + t.node_id: t.to_app_schema(**coords[t.node_id]) for t in self.tasks + } + else: + links = {} + nodes = {t.node_id: t.to_app_schema() for t in self.tasks} wml = { "filename": self.filename, @@ -547,7 +554,7 @@ def load_from_dag(cls, dag: DAG): # TODO: Remove values if mastered in dag (e.g. start_date, default args) tasks = [TaskHandler.load_from_task(task) for task in dag.task_dict.values()] - tasks = cls.fix_task_names(tasks) + tasks = cls.fix_task_names(dag_params["dag_id"], tasks) task_id_to_node_id = {task.task_id: task.node_id for task in tasks} task_name_mappings = {t.node_id: t.snake_name for t in tasks} @@ -591,5 +598,14 @@ def __init__(self, pyfile: str, config: ProjectConfig): self.mod = import_dag_from_project(pyfile, config) @property - def dags(self): - return {k: v for k, v in self.mod.__dict__.items() if type(v) == DAG} + def dag(self): + dags = {k: v for k, v in self.mod.__dict__.items() if type(v) == DAG} + if len(dags) != 1: + raise DagHandlerValidationError( + f"Importing from Python only supports files with exactly one dag object. Found the following dags: {','.join(dags.keys())}" + ) + return dags.popitem()[1] + + @property + def wml(self): + return DagHandler.load_from_dag(self.dag).to_wml() diff --git a/windmill/models/schemas/app_schemas.py b/windmill/models/schemas/app_schemas.py index 2e3646d..e3e60e8 100644 --- a/windmill/models/schemas/app_schemas.py +++ b/windmill/models/schemas/app_schemas.py @@ -85,19 +85,19 @@ class NodeSchema(Schema): keys=fields.Str(), values=fields.Nested(PortSchema), default={ - "port1": { - "id": "port1", + "in_port": { + "id": "in_port", "position": {"x": int(GraphConstants.NODE_WIDTH / 2), "y": 0}, - "properties": {"custom": "property"}, + "properties": {"value": "in"}, "type": "top", }, - "port2": { - "id": "port2", + "out_port": { + "id": "out_port", "position": { "x": int(GraphConstants.NODE_WIDTH / 2), "y": GraphConstants.NODE_HEIGHT, }, - "properties": {"custom": "property"}, + "properties": {"value": "out"}, "type": "bottom", }, },