flowtask 5.2.12__tar.gz → 5.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flowtask-5.2.12 → flowtask-5.3.3}/Makefile +4 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/PKG-INFO +12 -11
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/__init__.py +6 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/__main__.py +5 -6
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/AddDataset.py +89 -111
- flowtask-5.3.3/flowtask/components/AutoTask.py +226 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Azure.py +12 -17
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/AzureUsers.py +20 -25
- flowtask-5.3.3/flowtask/components/BaseAction.py +72 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Boto3Client.py +26 -43
- flowtask-5.3.3/flowtask/components/CopyTo.py +57 -0
- flowtask-5.3.3/flowtask/components/CopyToPg.py +369 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/CreateReport.py +43 -77
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/charts/__init__.py +4 -4
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/charts/bar.py +11 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/charts/base.py +11 -16
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/charts/pie.py +11 -18
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/utils.py +1 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DataInput.py +32 -49
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DateList.py +53 -64
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DbClient.py +43 -65
- flowtask-5.3.3/flowtask/components/DialPad.py +80 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFrom.py +133 -131
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFromFTP.py +38 -60
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFromIMAP.py +94 -112
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFromS3.py +61 -88
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFromSFTP.py +60 -95
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFromSharepoint.py +16 -30
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DownloadFromSmartSheet.py +28 -42
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/DropboxClient.py +1 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Dummy.py +4 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Excel365.py +2 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/ExecuteSQL.py +65 -122
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FTPClient.py +42 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileBase.py +42 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileCopy.py +29 -45
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileDelete.py +5 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileExists.py +32 -49
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileIteratorDelete.py +11 -16
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileList.py +20 -26
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FileOpen.py +11 -13
- flowtask-5.3.3/flowtask/components/FileRename.py +68 -0
- flowtask-5.3.3/flowtask/components/FilterRows/FilterRows.py +150 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FilterRows/functions.py +29 -14
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/GoogleA4.py +25 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/GoogleClient.py +1 -1
- flowtask-5.3.3/flowtask/components/GoogleGeoCoding.py +137 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/HTTPClient.py +205 -354
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/ICIMS.py +15 -32
- flowtask-5.3.3/flowtask/components/IMAPClient.py +145 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/IteratorBase.py +47 -38
- flowtask-5.3.3/flowtask/components/MS365Usage.py +87 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/MergeFiles.py +73 -82
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/O365Client.py +24 -36
- flowtask-5.3.3/flowtask/components/Odoo.py +117 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/OneDrive.py +2 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/OpenFromXML.py +19 -31
- flowtask-5.3.3/flowtask/components/OpenWeather.py +41 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/OpenWithBase.py +188 -207
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/OpenWithPandas.py +157 -189
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/PGPDecrypt.py +31 -50
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/PandasIterator.py +27 -41
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/PandasToFile.py +38 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/ParamIterator.py +7 -11
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/PrintMessage.py +19 -28
- flowtask-5.3.3/flowtask/components/QS.py +213 -0
- flowtask-5.3.3/flowtask/components/QSBase.py +185 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/QueryIterator.py +56 -105
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/QueryToInsert.py +133 -91
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/QueryToPandas.py +133 -214
- flowtask-5.3.3/flowtask/components/RESTClient.py +142 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Rsync.py +27 -35
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/RunSSH.py +12 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/RunShell.py +12 -13
- flowtask-5.3.3/flowtask/components/SalesForce.py +20 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/SendNotify.py +37 -62
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/SetVariables.py +30 -24
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Sharepoint.py +48 -28
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/SubTask.py +29 -32
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/SuiteCRM.py +7 -13
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableBase.py +43 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableDelete.py +69 -116
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableInput.py +21 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableOutput/TableOutput.py +66 -107
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableOutput/__init__.py +1 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableOutput/mysql.py +33 -48
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableOutput/postgres.py +26 -45
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableOutput/sa.py +28 -42
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TableSchema.py +115 -128
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TransformRows/TransformRows.py +69 -92
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TransformRows/functions.py +570 -296
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TransposeRows.py +27 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UPCDatabase.py +24 -29
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Uncompress.py +27 -49
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UniqueRows.py +30 -40
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UpdateOperationalVars.py +17 -23
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UploadTo.py +68 -101
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UploadToS3.py +38 -55
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UploadToSFTP.py +34 -53
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/UserFunc.py +17 -36
- flowtask-5.3.3/flowtask/components/VivaTracker.py +123 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/WSDLClient.py +26 -32
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/Wait.py +1 -1
- flowtask-5.3.3/flowtask/components/Workplace.py +134 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/XMLToPandas.py +56 -69
- flowtask-5.3.3/flowtask/components/Zammad/__init__.py +22 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/__init__.py +18 -20
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/abstract.py +204 -108
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/azureauth.py +14 -18
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/group.py +44 -74
- flowtask-5.3.3/flowtask/components/interfaces/ASPX.py +136 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/interfaces/AzureClient.py +31 -31
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/interfaces/SSHClient.py +109 -170
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/interfaces/__init__.py +4 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/interfaces/client.py +34 -36
- flowtask-5.3.3/flowtask/components/interfaces/dataframes/__init__.py +11 -0
- flowtask-5.3.3/flowtask/components/interfaces/dataframes/abstract.py +34 -0
- flowtask-5.3.3/flowtask/components/interfaces/dataframes/arrow.py +74 -0
- flowtask-5.3.3/flowtask/components/interfaces/dataframes/dt.py +72 -0
- flowtask-5.3.3/flowtask/components/interfaces/dataframes/pandas.py +109 -0
- flowtask-5.3.3/flowtask/components/interfaces/dataframes/polars.py +63 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/interfaces/db.py +52 -59
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/interfaces/http.py +244 -333
- flowtask-5.3.3/flowtask/components/interfaces/zammad.py +80 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/support/__init__.py +7 -7
- flowtask-5.3.3/flowtask/components/support/db.py +87 -0
- flowtask-5.3.3/flowtask/components/support/func.py +113 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/support/locale.py +9 -8
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/support/log.py +25 -30
- flowtask-5.3.3/flowtask/components/support/qs.py +232 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/support/result.py +10 -17
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/support/stat.py +4 -8
- flowtask-5.3.3/flowtask/components/support/template.py +82 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tConcat.py +21 -34
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tExplode.py +24 -41
- flowtask-5.3.3/flowtask/components/tFilter.py +154 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tGroup.py +15 -19
- flowtask-5.3.3/flowtask/components/tJoin.py +199 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tMap/functions.py +9 -6
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tMap/tMap.py +96 -121
- flowtask-5.3.3/flowtask/components/tMelt.py +65 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tPluckCols.py +10 -15
- flowtask-5.3.3/flowtask/components/tUnnest.py +76 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/user.py +83 -142
- flowtask-5.3.3/flowtask/conf.py +305 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/download.py +18 -24
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/__init__.py +4 -4
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/__init__.py +13 -13
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/abstract.py +12 -20
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/alerts/__init__.py +16 -38
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/alerts/colfunctions.py +6 -15
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/alerts/functions.py +6 -8
- flowtask-5.3.3/flowtask/events/events/dummy.py +12 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/exec.py +22 -29
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/file/__init__.py +2 -2
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/file/base.py +18 -24
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/file/copy.py +2 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/file/delete.py +2 -6
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/interfaces/__init__.py +3 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/interfaces/client.py +15 -31
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/interfaces/credentials.py +9 -18
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/interfaces/notifications.py +13 -19
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/log.py +7 -8
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/logerr.py +10 -16
- flowtask-5.3.3/flowtask/events/events/notify.py +59 -0
- flowtask-5.3.3/flowtask/events/events/notify_event.py +160 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/publish.py +9 -11
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/sendfile.py +21 -42
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/task.py +18 -26
- flowtask-5.3.3/flowtask/events/events/teams.py +66 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/events/webhook.py +18 -19
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/events/manager.py +89 -96
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/exceptions.c +30 -41
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/extensions/__init__.py +1 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/extensions/abstract.py +5 -6
- flowtask-5.3.3/flowtask/extensions/logging/__init__.py +65 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/__init__.py +5 -5
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/abstract.py +9 -9
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/dummy.py +3 -4
- flowtask-5.3.3/flowtask/hooks/actions/jira.py +45 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/rest.py +80 -101
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/task.py +1 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/ticket.py +7 -10
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/actions/zammad.py +18 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/__init__.py +4 -4
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/cache.py +33 -77
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/client.py +18 -39
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/env.py +2 -5
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/log.py +19 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/mask.py +6 -6
- flowtask-5.3.3/flowtask/hooks/interfaces/masks.py +69 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/message.py +47 -84
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/task.py +19 -32
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/interfaces/templates.py +8 -12
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/service.py +46 -45
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/__init__.py +7 -7
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/base.py +9 -19
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/fs.py +25 -38
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/http.py +5 -8
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/imap.py +39 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/responses/__init__.py +1 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/responses/base.py +0 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/ssh.py +15 -25
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/tagged.py +17 -22
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/upload.py +4 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/watch.py +13 -18
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/types/web.py +5 -10
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/models.py +67 -90
- flowtask-5.3.3/flowtask/parsers/__init__.py +15 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/parsers/_yaml.c +88 -99
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/parsers/argparser.py +46 -56
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/parsers/base.c +95 -106
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/parsers/json.c +88 -99
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/parsers/maps.py +18 -17
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/parsers/toml.c +88 -99
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/plugins/__init__.py +3 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/plugins/handler/__init__.py +9 -9
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/plugins/importer.py +4 -3
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/runner.py +40 -62
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/scheduler/__init__.py +1 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/scheduler/functions.py +95 -139
- flowtask-5.3.3/flowtask/scheduler/handlers/__init__.py +8 -0
- flowtask-5.3.3/flowtask/scheduler/handlers/manager.py +497 -0
- flowtask-5.3.3/flowtask/scheduler/handlers/models.py +58 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/scheduler/handlers/service.py +15 -24
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/scheduler/notifications.py +21 -23
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/scheduler/scheduler.py +289 -341
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/services/tasks/__init__.py +1 -7
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/services/tasks/launcher.py +54 -97
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/services/tasks/manager.py +34 -28
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/services/tasks/service.py +48 -110
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/services/tasks/task_manager.py +77 -100
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/services/tasks/tasks.py +39 -72
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/abstract.py +91 -147
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/command.py +15 -26
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/files/__init__.py +1 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/files/abstract.py +6 -9
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/files/filesystem.py +14 -11
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/pile.py +97 -58
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/storages/__init__.py +6 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/storages/abstract.py +3 -11
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/storages/database.py +7 -8
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/storages/filesystem.py +34 -52
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/storages/github.py +22 -26
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/storages/row.py +3 -7
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tasks/task.py +157 -265
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/template/__init__.py +36 -39
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/tests.py +31 -19
- flowtask-5.3.3/flowtask/types/__init__.py +8 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/types/typedefs.c +38 -48
- flowtask-5.3.3/flowtask/utils/__init__.py +24 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/constants.py +10 -8
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/encoders.py +2 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/executor.py +31 -29
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/functions.cpp +61 -70
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/json.cpp +99 -110
- flowtask-5.3.3/flowtask/utils/mail.py +63 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/parseqs.c +125 -135
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/stats.py +27 -41
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/uv.py +3 -1
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/utils/validators.py +11 -3
- flowtask-5.3.3/flowtask/version.py +11 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask.egg-info/PKG-INFO +12 -11
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask.egg-info/SOURCES.txt +23 -2
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask.egg-info/entry_points.txt +0 -1
- flowtask-5.3.3/flowtask.egg-info/requires.txt +34 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/pyproject.toml +1 -1
- flowtask-5.3.3/resources/__init__.py +0 -0
- flowtask-5.3.3/resources/image.py +269 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/setup.py +19 -22
- flowtask-5.2.12/flowtask/components/CopyTo.py +0 -8
- flowtask-5.2.12/flowtask/components/CopyToPg.py +0 -501
- flowtask-5.2.12/flowtask/components/FileRename.py +0 -84
- flowtask-5.2.12/flowtask/components/FilterRows/FilterRows.py +0 -139
- flowtask-5.2.12/flowtask/components/IMAPClient.py +0 -179
- flowtask-5.2.12/flowtask/components/QSBase.py +0 -244
- flowtask-5.2.12/flowtask/components/RESTBase.py +0 -63
- flowtask-5.2.12/flowtask/components/RESTClient.py +0 -182
- flowtask-5.2.12/flowtask/components/SalesForce.py +0 -25
- flowtask-5.2.12/flowtask/components/Workplace.py +0 -139
- flowtask-5.2.12/flowtask/components/support/db.py +0 -105
- flowtask-5.2.12/flowtask/components/support/func.py +0 -106
- flowtask-5.2.12/flowtask/components/support/template.py +0 -34
- flowtask-5.2.12/flowtask/components/tFilter.py +0 -112
- flowtask-5.2.12/flowtask/components/tJoin.py +0 -178
- flowtask-5.2.12/flowtask/components/tMelt.py +0 -84
- flowtask-5.2.12/flowtask/conf.py +0 -316
- flowtask-5.2.12/flowtask/events/events/dummy.py +0 -12
- flowtask-5.2.12/flowtask/events/events/notify.py +0 -67
- flowtask-5.2.12/flowtask/events/events/notify_event.py +0 -127
- flowtask-5.2.12/flowtask/events/events/teams.py +0 -78
- flowtask-5.2.12/flowtask/extensions/logging/__init__.py +0 -82
- flowtask-5.2.12/flowtask/hooks/actions/jira.py +0 -59
- flowtask-5.2.12/flowtask/hooks/interfaces/masks.py +0 -42
- flowtask-5.2.12/flowtask/parsers/__init__.py +0 -11
- flowtask-5.2.12/flowtask/scheduler/handlers/__init__.py +0 -8
- flowtask-5.2.12/flowtask/scheduler/handlers/manager.py +0 -436
- flowtask-5.2.12/flowtask/types/__init__.py +0 -9
- flowtask-5.2.12/flowtask/utils/__init__.py +0 -30
- flowtask-5.2.12/flowtask/utils/_functions.py +0 -552
- flowtask-5.2.12/flowtask/utils/mail.py +0 -49
- flowtask-5.2.12/flowtask/version.py +0 -9
- flowtask-5.2.12/flowtask.egg-info/requires.txt +0 -37
- {flowtask-5.2.12 → flowtask-5.3.3}/CONTRIBUTING.md +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/INSTALL +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/LICENSE +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/MANIFEST.in +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/README.md +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/CreateReport/__init__.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/FilterRows/__init__.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/TransformRows/__init__.py +0 -0
- /flowtask-5.2.12/flowtask/components/py.typed → /flowtask-5.3.3/flowtask/components/Zammad/models.py +0 -0
- /flowtask-5.2.12/flowtask/plugins/components/__init__.py → /flowtask-5.3.3/flowtask/components/py.typed +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/components/tMap/__init__.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask/hooks/__init__.py +0 -0
- {flowtask-5.2.12/flowtask/plugins/sources → flowtask-5.3.3/flowtask/plugins/components}/__init__.py +0 -0
- {flowtask-5.2.12/flowtask/services → flowtask-5.3.3/flowtask/plugins/sources}/__init__.py +0 -0
- {flowtask-5.2.12/flowtask/tasks → flowtask-5.3.3/flowtask/services}/__init__.py +0 -0
- {flowtask-5.2.12/resources → flowtask-5.3.3/flowtask/tasks}/__init__.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask.egg-info/dependency_links.txt +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/flowtask.egg-info/top_level.txt +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/plugins/__init__.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/resources/auth.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/resources/functions.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/resources/users/__init__.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/resources/users/models.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/resources/version.py +0 -0
- {flowtask-5.2.12 → flowtask-5.3.3}/setup.cfg +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
venv:
|
|
2
|
-
python3.
|
|
2
|
+
python3.11 -m venv .venv
|
|
3
3
|
echo 'run `source .venv/bin/activate` to start develop FlowTask'
|
|
4
4
|
|
|
5
5
|
install:
|
|
@@ -11,14 +11,15 @@ install:
|
|
|
11
11
|
pip install --upgrade navigator-auth
|
|
12
12
|
pip install --upgrade async-notify[all]
|
|
13
13
|
pip install --upgrade querysource
|
|
14
|
-
# dev
|
|
15
|
-
python -m pip install -Ur docs/requirements-dev.txt
|
|
16
14
|
# Install extra resources
|
|
17
15
|
pip install --upgrade qworker
|
|
18
16
|
# install FlowTask
|
|
19
17
|
pip install -e .
|
|
20
18
|
echo 'start develop FlowTask'
|
|
21
19
|
|
|
20
|
+
develop:
|
|
21
|
+
pip install -Ur docs/requirements-dev.txt
|
|
22
|
+
|
|
22
23
|
compile:
|
|
23
24
|
python setup.py build_ext --inplace
|
|
24
25
|
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flowtask
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.3.3
|
|
4
4
|
Summary: Framework for running Tasks and from CLI and API for orchestation. Component-based Task builder/Runner for non-programmers.
|
|
5
5
|
Home-page: https://github.com/phenobarbital/flowtask
|
|
6
6
|
Author: Jesus Lara
|
|
7
|
-
Author-email:
|
|
8
|
-
License: Apache
|
|
7
|
+
Author-email: "Jesus Lara G." <jesuslarag@gmail.com>
|
|
8
|
+
License: Apache-2.0
|
|
9
9
|
Project-URL: Source, https://github.com/phenobarbital/flowtask
|
|
10
10
|
Project-URL: Funding, https://paypal.me/phenobarbital
|
|
11
11
|
Project-URL: Say Thanks!, https://saythanks.io/to/phenobarbital
|
|
12
|
-
Keywords: DataIntegration
|
|
12
|
+
Keywords: DataIntegration,Task,Orchestation,Task-Runner,Pipelines,Data-Pipelines
|
|
13
13
|
Platform: *nix
|
|
14
14
|
Classifier: Development Status :: 4 - Beta
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Classifier: Development Status :: 4 - Beta
|
|
22
|
+
Classifier: Environment :: Web Environment
|
|
23
|
+
Classifier: Framework :: AsyncIO
|
|
16
24
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
26
|
Classifier: Topic :: Software Development :: Build Tools
|
|
19
|
-
Classifier: Topic :: Utilities
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
23
27
|
Classifier: License :: OSI Approved :: BSD License
|
|
24
|
-
Classifier: Framework :: AsyncIO
|
|
25
28
|
Requires-Python: >=3.9.16
|
|
26
29
|
Description-Content-Type: text/markdown
|
|
27
30
|
License-File: LICENSE
|
|
@@ -112,5 +115,3 @@ Please have a look at the Contribution Guide
|
|
|
112
115
|
### License ###
|
|
113
116
|
|
|
114
117
|
Navigator is licensed under Apache 2.0 License. See the LICENSE file for more details.
|
|
115
|
-
|
|
116
|
-
|
|
@@ -5,9 +5,14 @@ Navigator Data-Integration.
|
|
|
5
5
|
Tool for execution of Tasks.
|
|
6
6
|
"""
|
|
7
7
|
from .version import (
|
|
8
|
-
__title__,
|
|
8
|
+
__title__,
|
|
9
|
+
__description__,
|
|
10
|
+
__version__,
|
|
11
|
+
__author__,
|
|
12
|
+
__author_email__,
|
|
9
13
|
)
|
|
10
14
|
|
|
15
|
+
|
|
11
16
|
def version():
|
|
12
17
|
"""version.
|
|
13
18
|
Returns:
|
|
@@ -24,16 +24,15 @@ def main():
|
|
|
24
24
|
loop = asyncio.get_event_loop()
|
|
25
25
|
loop.slow_callback_duration = 0.5 # Set threshold to 0.5 seconds
|
|
26
26
|
# try:
|
|
27
|
-
result = loop.run_until_complete(
|
|
28
|
-
task(loop)
|
|
29
|
-
)
|
|
27
|
+
result = loop.run_until_complete(task(loop))
|
|
30
28
|
if result:
|
|
31
|
-
cPrint(
|
|
29
|
+
cPrint(" === RESULT === ", level="DEBUG")
|
|
32
30
|
print(result.result)
|
|
33
|
-
cPrint(
|
|
31
|
+
cPrint("== Task stats === ", level="INFO")
|
|
34
32
|
print(result.stats)
|
|
35
33
|
# finally:
|
|
36
34
|
# loop.close()
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
39
38
|
main()
|
|
@@ -2,15 +2,9 @@ import asyncio
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
import numpy as np
|
|
4
4
|
import pandas as pd
|
|
5
|
-
from asyncdb.exceptions import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from flowtask.exceptions import (
|
|
9
|
-
ComponentError,
|
|
10
|
-
DataNotFound,
|
|
11
|
-
TaskError
|
|
12
|
-
)
|
|
13
|
-
from flowtask.utils import cPrint
|
|
5
|
+
from asyncdb.exceptions import NoDataFound
|
|
6
|
+
from ..exceptions import ComponentError, DataNotFound, TaskError
|
|
7
|
+
from ..utils import cPrint
|
|
14
8
|
from .abstract import DtComponent
|
|
15
9
|
|
|
16
10
|
|
|
@@ -19,25 +13,24 @@ class AddDataset(DtComponent):
|
|
|
19
13
|
|
|
20
14
|
Join two Datasets based on some criteria.
|
|
21
15
|
"""
|
|
16
|
+
|
|
22
17
|
df1 = None
|
|
23
18
|
df2 = None
|
|
24
19
|
|
|
25
20
|
def __init__(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
self,
|
|
22
|
+
loop: asyncio.AbstractEventLoop = None,
|
|
23
|
+
job: Callable = None,
|
|
24
|
+
stat: Callable = None,
|
|
25
|
+
**kwargs,
|
|
31
26
|
) -> None:
|
|
32
27
|
"""Init Method."""
|
|
33
|
-
self.type: str =
|
|
28
|
+
self.type: str = "left"
|
|
34
29
|
self._dtypes: dict = {}
|
|
35
30
|
self.infer_types: bool = False
|
|
36
31
|
self.to_string: bool = True
|
|
37
32
|
self.as_objects: bool = False
|
|
38
|
-
super(AddDataset, self).__init__(
|
|
39
|
-
loop=loop, job=job, stat=stat, **kwargs
|
|
40
|
-
)
|
|
33
|
+
super(AddDataset, self).__init__(loop=loop, job=job, stat=stat, **kwargs)
|
|
41
34
|
|
|
42
35
|
async def start(self, **kwargs):
|
|
43
36
|
"""Obtain Pandas Dataframe."""
|
|
@@ -49,22 +42,18 @@ class AddDataset(DtComponent):
|
|
|
49
42
|
else:
|
|
50
43
|
self.df1 = self.previous.output()
|
|
51
44
|
except IndexError as ex:
|
|
52
|
-
raise ComponentError(
|
|
53
|
-
"Missing LEFT Dataframe"
|
|
54
|
-
) from ex
|
|
45
|
+
raise ComponentError("Missing LEFT Dataframe") from ex
|
|
55
46
|
### check info for creating the second dataset:
|
|
56
47
|
self.df2 = None
|
|
57
|
-
if not hasattr(self,
|
|
58
|
-
raise TaskError(
|
|
59
|
-
|
|
60
|
-
)
|
|
61
|
-
if not hasattr(self, 'dataset'):
|
|
48
|
+
if not hasattr(self, "fields"):
|
|
49
|
+
raise TaskError("Wrong Task configuration: need *fields* declaration.")
|
|
50
|
+
if not hasattr(self, "dataset"):
|
|
62
51
|
raise TaskError(
|
|
63
52
|
"Wrong Task configuration: need *dataset* name declaration."
|
|
64
53
|
)
|
|
65
|
-
if not hasattr(self,
|
|
66
|
-
self.datasource =
|
|
67
|
-
if self.datasource in (
|
|
54
|
+
if not hasattr(self, "datasource"):
|
|
55
|
+
self.datasource = "datasets"
|
|
56
|
+
if self.datasource in ("datasets", "vision"):
|
|
68
57
|
# using current datasets on pg database
|
|
69
58
|
self.connection = self.pg_connection()
|
|
70
59
|
else:
|
|
@@ -75,25 +64,21 @@ class AddDataset(DtComponent):
|
|
|
75
64
|
async def run(self):
|
|
76
65
|
args = {}
|
|
77
66
|
if self.df1.empty:
|
|
78
|
-
raise DataNotFound(
|
|
79
|
-
"Data Was Not Found on Dataframe 1"
|
|
80
|
-
)
|
|
67
|
+
raise DataNotFound("Data Was Not Found on Dataframe 1")
|
|
81
68
|
## getting second dataset:
|
|
82
69
|
try:
|
|
83
70
|
### TODO: instrumentation for getting dataset from different sources
|
|
84
71
|
async with await self.connection.connection() as conn:
|
|
85
|
-
fields =
|
|
86
|
-
if hasattr(self,
|
|
87
|
-
join =
|
|
72
|
+
fields = ", ".join(self.fields)
|
|
73
|
+
if hasattr(self, "distinct"):
|
|
74
|
+
join = ", ".join(self.join)
|
|
88
75
|
query = f"SELECT DISTINCT ON ({join}) {fields} FROM {self.datasource}.{self.dataset}"
|
|
89
76
|
else:
|
|
90
77
|
query = f"SELECT {fields} FROM {self.datasource}.{self.dataset}"
|
|
91
|
-
self._logger.info(f
|
|
78
|
+
self._logger.info(f"DATASET QUERY: {query}")
|
|
92
79
|
result, error = await conn.query(query)
|
|
93
80
|
if error or not result:
|
|
94
|
-
raise DataNotFound(
|
|
95
|
-
"Empty Dataset"
|
|
96
|
-
)
|
|
81
|
+
raise DataNotFound("Empty Dataset")
|
|
97
82
|
## converting on Dataframe:
|
|
98
83
|
self.df2 = await self.get_dataframe(result, infer_types=True)
|
|
99
84
|
except (DataNotFound, NoDataFound) as exc:
|
|
@@ -101,44 +86,42 @@ class AddDataset(DtComponent):
|
|
|
101
86
|
raise DataNotFound(str(exc)) from exc
|
|
102
87
|
finally:
|
|
103
88
|
self.connection = None
|
|
104
|
-
if self.type ==
|
|
89
|
+
if self.type == "left" and (self.df2 is None or self.df2.empty):
|
|
105
90
|
self._result = self.df1
|
|
106
91
|
return True
|
|
107
92
|
elif self.df2 is None or self.df2.empty:
|
|
108
|
-
raise DataNotFound(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if hasattr(self, 'no_copy'):
|
|
112
|
-
args['copy'] = self.no_copy
|
|
93
|
+
raise DataNotFound("Data Was Not Found on Dataframe 2")
|
|
94
|
+
if hasattr(self, "no_copy"):
|
|
95
|
+
args["copy"] = self.no_copy
|
|
113
96
|
if not self.type:
|
|
114
|
-
self.type =
|
|
115
|
-
elif self.type ==
|
|
116
|
-
args[
|
|
117
|
-
if hasattr(self,
|
|
97
|
+
self.type = "left"
|
|
98
|
+
elif self.type == "inner":
|
|
99
|
+
args["left_index"] = True
|
|
100
|
+
if hasattr(self, "args") and isinstance(self.args, dict):
|
|
118
101
|
args = {**args, **self.args}
|
|
119
|
-
if hasattr(self,
|
|
102
|
+
if hasattr(self, "operator"):
|
|
120
103
|
operator = self.operator
|
|
121
104
|
else:
|
|
122
|
-
operator =
|
|
123
|
-
if hasattr(self,
|
|
124
|
-
args[
|
|
105
|
+
operator = "and"
|
|
106
|
+
if hasattr(self, "join"):
|
|
107
|
+
args["on"] = self.join
|
|
125
108
|
else:
|
|
126
|
-
args[
|
|
109
|
+
args["left_index"] = True
|
|
127
110
|
# making a Join between 2 dataframes
|
|
128
111
|
try:
|
|
129
|
-
if operator ==
|
|
112
|
+
if operator == "and":
|
|
130
113
|
df = pd.merge(
|
|
131
114
|
self.df1,
|
|
132
115
|
self.df2,
|
|
133
116
|
how=self.type,
|
|
134
|
-
suffixes=(
|
|
135
|
-
**args
|
|
117
|
+
suffixes=("_left", "_right"),
|
|
118
|
+
**args,
|
|
136
119
|
)
|
|
137
120
|
else:
|
|
138
|
-
if hasattr(self,
|
|
139
|
-
args[
|
|
121
|
+
if hasattr(self, "join"):
|
|
122
|
+
args["left_on"] = self.join
|
|
140
123
|
else:
|
|
141
|
-
args[
|
|
124
|
+
args["left_index"] = True
|
|
142
125
|
ndf = self.df1
|
|
143
126
|
sdf = self.df2.copy()
|
|
144
127
|
merge = []
|
|
@@ -148,15 +131,21 @@ class AddDataset(DtComponent):
|
|
|
148
131
|
sdf,
|
|
149
132
|
right_on=key,
|
|
150
133
|
how=self.type,
|
|
151
|
-
suffixes=(
|
|
152
|
-
**args
|
|
134
|
+
suffixes=("_left", None),
|
|
135
|
+
**args,
|
|
153
136
|
)
|
|
154
137
|
ndf = d[d[key].isnull()]
|
|
155
|
-
ndf.drop(
|
|
156
|
-
|
|
138
|
+
ndf.drop(
|
|
139
|
+
ndf.columns[ndf.columns.str.contains("_left")],
|
|
140
|
+
axis=1,
|
|
141
|
+
inplace=True,
|
|
142
|
+
)
|
|
157
143
|
ddf = d[d[key].notnull()]
|
|
158
|
-
ddf.drop(
|
|
159
|
-
|
|
144
|
+
ddf.drop(
|
|
145
|
+
ddf.columns[ddf.columns.str.contains("_left")],
|
|
146
|
+
axis=1,
|
|
147
|
+
inplace=True,
|
|
148
|
+
)
|
|
160
149
|
merge.append(ddf)
|
|
161
150
|
# merge the last (not matched) rows
|
|
162
151
|
merge.append(ndf)
|
|
@@ -164,25 +153,19 @@ class AddDataset(DtComponent):
|
|
|
164
153
|
df.reset_index(drop=True)
|
|
165
154
|
df.is_copy = None
|
|
166
155
|
except (ValueError, KeyError) as err:
|
|
167
|
-
raise ComponentError(
|
|
168
|
-
f'Cannot Join with missing Column: {err!s}'
|
|
169
|
-
) from err
|
|
156
|
+
raise ComponentError(f"Cannot Join with missing Column: {err!s}") from err
|
|
170
157
|
except Exception as err:
|
|
171
|
-
raise ComponentError(
|
|
172
|
-
f"Unknown JOIN error {err!s}"
|
|
173
|
-
) from err
|
|
158
|
+
raise ComponentError(f"Unknown JOIN error {err!s}") from err
|
|
174
159
|
numrows = len(df.index)
|
|
175
160
|
if numrows == 0:
|
|
176
|
-
raise DataNotFound(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
self.
|
|
180
|
-
print('ON END> ', numrows)
|
|
181
|
-
self.add_metric('JOINED_ROWS', numrows)
|
|
161
|
+
raise DataNotFound("Cannot make any JOIN, returns zero coincidences")
|
|
162
|
+
self._variables[f"{self.TaskName}_NUMROWS"] = numrows
|
|
163
|
+
print("ON END> ", numrows)
|
|
164
|
+
self.add_metric("JOINED_ROWS", numrows)
|
|
182
165
|
if self._debug is True:
|
|
183
|
-
print(
|
|
166
|
+
print("::: Printing Column Information === ")
|
|
184
167
|
for column, t in df.dtypes.items():
|
|
185
|
-
print(column,
|
|
168
|
+
print(column, "->", t, "->", df[column].iloc[0])
|
|
186
169
|
# helping some transformations
|
|
187
170
|
df.is_copy = None
|
|
188
171
|
self._result = df
|
|
@@ -198,63 +181,58 @@ class AddDataset(DtComponent):
|
|
|
198
181
|
result = [dict(row) for row in result]
|
|
199
182
|
try:
|
|
200
183
|
if self.as_objects is True:
|
|
201
|
-
df = pd.DataFrame(
|
|
202
|
-
result,
|
|
203
|
-
dtype=object
|
|
204
|
-
)
|
|
184
|
+
df = pd.DataFrame(result, dtype=object)
|
|
205
185
|
else:
|
|
206
|
-
df = pd.DataFrame(
|
|
207
|
-
result,
|
|
208
|
-
**self._dtypes
|
|
209
|
-
)
|
|
186
|
+
df = pd.DataFrame(result, **self._dtypes)
|
|
210
187
|
except Exception as err: # pylint: disable=W0718
|
|
211
188
|
self._logger.exception(err, stack_info=True)
|
|
212
189
|
# Attempt to infer better dtypes for object columns.
|
|
213
190
|
if hasattr(self, "infer_types") or infer_types is True:
|
|
214
191
|
df.infer_objects()
|
|
215
|
-
df = df.convert_dtypes(
|
|
216
|
-
convert_string=self.to_string
|
|
217
|
-
)
|
|
192
|
+
df = df.convert_dtypes(convert_string=self.to_string)
|
|
218
193
|
if self._debug is True:
|
|
219
|
-
cPrint(
|
|
194
|
+
cPrint("Data Types:")
|
|
220
195
|
print(df.dtypes)
|
|
221
196
|
if hasattr(self, "drop_empty"):
|
|
222
|
-
df.dropna(axis=1, how=
|
|
223
|
-
df.dropna(axis=0, how=
|
|
224
|
-
if hasattr(self,
|
|
225
|
-
df.dropna(subset=self.dropna, how=
|
|
226
|
-
if
|
|
227
|
-
|
|
228
|
-
|
|
197
|
+
df.dropna(axis=1, how="all", inplace=True)
|
|
198
|
+
df.dropna(axis=0, how="all", inplace=True)
|
|
199
|
+
if hasattr(self, "dropna"):
|
|
200
|
+
df.dropna(subset=self.dropna, how="all", inplace=True)
|
|
201
|
+
if (
|
|
202
|
+
hasattr(self, "clean_strings")
|
|
203
|
+
and getattr(self, "clean_strings", False) is True
|
|
204
|
+
):
|
|
205
|
+
u = df.select_dtypes(include=["object", "string"])
|
|
206
|
+
df[u.columns] = u.fillna("")
|
|
229
207
|
return df
|
|
230
208
|
|
|
231
209
|
def set_datatypes(self):
|
|
232
|
-
if hasattr(self,
|
|
210
|
+
if hasattr(self, "datatypes"):
|
|
233
211
|
dtypes = {}
|
|
234
212
|
for field, dtype in self.datatypes.items():
|
|
235
|
-
if dtype ==
|
|
213
|
+
if dtype == "uint8":
|
|
236
214
|
dtypes[field] = np.uint8
|
|
237
|
-
elif dtype ==
|
|
215
|
+
elif dtype == "uint16":
|
|
238
216
|
dtypes[field] = np.uint16
|
|
239
|
-
elif dtype ==
|
|
217
|
+
elif dtype == "uint32":
|
|
240
218
|
dtypes[field] = np.uint32
|
|
241
|
-
elif dtype ==
|
|
219
|
+
elif dtype == "int8":
|
|
242
220
|
dtypes[field] = np.int8
|
|
243
|
-
elif dtype ==
|
|
221
|
+
elif dtype == "int16":
|
|
244
222
|
dtypes[field] = np.int16
|
|
245
|
-
elif dtype ==
|
|
223
|
+
elif dtype == "int32":
|
|
246
224
|
dtypes[field] = np.int32
|
|
247
|
-
elif dtype ==
|
|
225
|
+
elif dtype == "float":
|
|
248
226
|
dtypes[field] = float
|
|
249
|
-
elif dtype ==
|
|
227
|
+
elif dtype == "float32":
|
|
250
228
|
dtypes[field] = float
|
|
251
|
-
elif dtype in (
|
|
229
|
+
elif dtype in ("string", "varchar", "str"):
|
|
252
230
|
dtypes[field] = str
|
|
253
231
|
else:
|
|
254
232
|
# invalid datatype
|
|
255
233
|
self._logger.warning(
|
|
256
|
-
f
|
|
234
|
+
f"Invalid DataType value: {field} for field {dtype}"
|
|
257
235
|
)
|
|
258
236
|
continue
|
|
259
237
|
if dtypes:
|
|
260
|
-
self._dtypes[
|
|
238
|
+
self._dtypes["dtype"] = dtypes
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import re
|
|
3
|
+
from urllib.parse import urljoin
|
|
4
|
+
import numpy as np
|
|
5
|
+
import orjson
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from flowtask.components.abstract import DtComponent
|
|
8
|
+
from flowtask.components.interfaces.http import HTTPService
|
|
9
|
+
from flowtask.exceptions import ComponentError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def to_snake_case(s):
|
|
13
|
+
"""Converts a string to snake_case format.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
name: The string to convert.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
The converted string in snake_case format.
|
|
20
|
+
"""
|
|
21
|
+
# Remove unwanted characters
|
|
22
|
+
s = re.sub(r"[^a-zA-Z0-9_\s]", "", s)
|
|
23
|
+
|
|
24
|
+
# Insert underscores before uppercase letters
|
|
25
|
+
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", s)
|
|
26
|
+
|
|
27
|
+
# Replace remaining uppercase letters with underscores
|
|
28
|
+
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AutoTask(DtComponent, HTTPService):
|
|
32
|
+
_credentials: dict = {"API_INTEGRATION_CODE": str, "USERNAME": str, "SECRET": str}
|
|
33
|
+
|
|
34
|
+
async def start(self, **kwargs):
|
|
35
|
+
self.headers = None
|
|
36
|
+
self._proxies = None
|
|
37
|
+
self.auth = ""
|
|
38
|
+
self.auth_type = ""
|
|
39
|
+
self.download = None
|
|
40
|
+
self.timeout = 180
|
|
41
|
+
self.accept = "application/json"
|
|
42
|
+
self.query_json = self.mask_replacement_recursively(self.query_json)
|
|
43
|
+
self.processing_credentials()
|
|
44
|
+
|
|
45
|
+
self._base_url = (
|
|
46
|
+
f"https://{self.zone}.autotask.net/atservicesrest/v1.0/{self.entity}/"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self.headers = {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
"ApiIntegrationCode": self.credentials["API_INTEGRATION_CODE"],
|
|
52
|
+
"UserName": self.credentials["USERNAME"],
|
|
53
|
+
"Secret": self.credentials["SECRET"],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
self.ids_chunks = []
|
|
57
|
+
if self.previous:
|
|
58
|
+
self.data = self.input
|
|
59
|
+
|
|
60
|
+
self.ids_chunks = self.filter_ids(
|
|
61
|
+
id_field=self.id_column_name,
|
|
62
|
+
items=self.data,
|
|
63
|
+
chunk_size=500,
|
|
64
|
+
)
|
|
65
|
+
elif getattr(self, "ids", None):
|
|
66
|
+
self._logger.info("Dropping specified Filters. Using ids instead.")
|
|
67
|
+
self.ids_chunks = [self.ids]
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
async def run(self):
|
|
72
|
+
if not self.ids_chunks:
|
|
73
|
+
# Use the Filter specified in the task
|
|
74
|
+
df_items = await self.get_dataframe_from_entity(
|
|
75
|
+
payload=orjson.dumps(self.query_json),
|
|
76
|
+
id_column_name=self.id_column_name,
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
# Use the ids from the previous component or from the ids argument
|
|
80
|
+
df_items = pd.DataFrame()
|
|
81
|
+
for ids_chunk in self.ids_chunks:
|
|
82
|
+
self.query_json.update(
|
|
83
|
+
{
|
|
84
|
+
"Filter": [
|
|
85
|
+
{"op": "in", "field": "id", "value": ids_chunk.tolist()}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
items = await self.get_dataframe_from_entity(
|
|
91
|
+
payload=orjson.dumps(self.query_json),
|
|
92
|
+
id_column_name=self.id_column_name,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
df_items = pd.concat([df_items, items], ignore_index=True)
|
|
96
|
+
|
|
97
|
+
if not df_items.empty:
|
|
98
|
+
for field in self.picklist_fields:
|
|
99
|
+
df_picklist_values = await self.get_picklist_values(field)
|
|
100
|
+
|
|
101
|
+
snake_case_field_name = to_snake_case(field)
|
|
102
|
+
|
|
103
|
+
df_items = df_items.astype({snake_case_field_name: "str"})
|
|
104
|
+
|
|
105
|
+
df_items = df_items.merge(
|
|
106
|
+
df_picklist_values,
|
|
107
|
+
how="left",
|
|
108
|
+
on=snake_case_field_name,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
self._result = df_items
|
|
112
|
+
return self._result
|
|
113
|
+
|
|
114
|
+
async def close(self):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
def processing_credentials(self):
|
|
118
|
+
if self.credentials:
|
|
119
|
+
for value, dtype in self._credentials.items():
|
|
120
|
+
try:
|
|
121
|
+
if type(self.credentials[value]) == dtype:
|
|
122
|
+
default = getattr(self, value, self.credentials[value])
|
|
123
|
+
val = self.get_env_value(
|
|
124
|
+
self.credentials[value], default=default
|
|
125
|
+
)
|
|
126
|
+
self.credentials[value] = val
|
|
127
|
+
except (TypeError, KeyError) as ex:
|
|
128
|
+
self._logger.error(f"{__name__}: Wrong or missing Credentials")
|
|
129
|
+
raise ComponentError(
|
|
130
|
+
f"{__name__}: Wrong or missing Credentials"
|
|
131
|
+
) from ex
|
|
132
|
+
|
|
133
|
+
def filter_ids(self, id_field: str, items: pd.DataFrame, chunk_size):
|
|
134
|
+
data = items[id_field].dropna().unique().astype(int)
|
|
135
|
+
|
|
136
|
+
if data.size > 0:
|
|
137
|
+
split_n = math.ceil(data.size / chunk_size)
|
|
138
|
+
|
|
139
|
+
# Split into chunks of n items
|
|
140
|
+
return np.array_split(data, split_n) # Convert to NumPy array and split
|
|
141
|
+
|
|
142
|
+
return [data]
|
|
143
|
+
|
|
144
|
+
def get_autotask_url(self, resource):
|
|
145
|
+
return urljoin(self._base_url, resource)
|
|
146
|
+
|
|
147
|
+
async def get_dataframe_from_entity(self, payload, id_column_name):
|
|
148
|
+
args = {
|
|
149
|
+
"url": self.get_autotask_url("query"),
|
|
150
|
+
"method": "post",
|
|
151
|
+
"data": payload,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
results = []
|
|
155
|
+
while True:
|
|
156
|
+
result, error = await self.session(**args)
|
|
157
|
+
|
|
158
|
+
if error:
|
|
159
|
+
self._logger.error(f"{__name__}: Error getting {self.entity}")
|
|
160
|
+
raise ComponentError(f"{__name__}: Error getting {self.entity}") from error
|
|
161
|
+
|
|
162
|
+
results.extend(result.get("items", []))
|
|
163
|
+
|
|
164
|
+
args.update({"url": result["pageDetails"].get("nextPageUrl", None)})
|
|
165
|
+
|
|
166
|
+
if not args["url"]:
|
|
167
|
+
break
|
|
168
|
+
|
|
169
|
+
df_results = await self.create_dataframe(results)
|
|
170
|
+
|
|
171
|
+
if not df_results.empty:
|
|
172
|
+
for field in self.user_defined_fields:
|
|
173
|
+
df_results[field] = df_results["userDefinedFields"].apply(
|
|
174
|
+
self.get_udf_value, args=[field]
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
df_results = (
|
|
178
|
+
df_results.drop("userDefinedFields", axis=1, errors="ignore")
|
|
179
|
+
.fillna(value=self.fillna_values)
|
|
180
|
+
.astype(self.map_field_type)
|
|
181
|
+
.rename(columns=lambda x: to_snake_case(x))
|
|
182
|
+
.rename(columns={"id": id_column_name})
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return df_results
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def get_udf_value(udfs, field_name):
|
|
189
|
+
"""
|
|
190
|
+
Extracts the value of a specific user-defined field from a item dictionary.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
item (dict): A dictionary representing a item.
|
|
194
|
+
field_name (str): The name of the user-defined field to extract.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
str: The value of the specified user-defined field, or None if not found.
|
|
198
|
+
"""
|
|
199
|
+
for udf in udfs:
|
|
200
|
+
if udf["name"] == field_name:
|
|
201
|
+
return udf["value"]
|
|
202
|
+
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
async def get_picklist_values(self, field_name: str) -> pd.DataFrame:
|
|
206
|
+
result, error = await self.session(
|
|
207
|
+
url=self.get_autotask_url("entityInformation/fields"),
|
|
208
|
+
method="get",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if error:
|
|
212
|
+
self._logger.error(f"{__name__}: Error getting {self.entity}")
|
|
213
|
+
raise ComponentError(f"{__name__}: Error getting {self.entity}") from error
|
|
214
|
+
|
|
215
|
+
for field in result["fields"]:
|
|
216
|
+
if field["name"] == field_name:
|
|
217
|
+
df = await self.create_dataframe(field["picklistValues"])
|
|
218
|
+
df = df[["label", "value"]]
|
|
219
|
+
snake_case_field_name = to_snake_case(field_name)
|
|
220
|
+
|
|
221
|
+
return df.rename(
|
|
222
|
+
columns={
|
|
223
|
+
"label": f"{snake_case_field_name}_label",
|
|
224
|
+
"value": snake_case_field_name,
|
|
225
|
+
}
|
|
226
|
+
)
|