flowtask 5.8.4__cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
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/__init__.py +93 -0
- flowtask/__main__.py +38 -0
- flowtask/bots/__init__.py +6 -0
- flowtask/bots/check.py +93 -0
- flowtask/bots/codebot.py +51 -0
- flowtask/components/ASPX.py +148 -0
- flowtask/components/AddDataset.py +352 -0
- flowtask/components/Amazon.py +523 -0
- flowtask/components/AutoTask.py +314 -0
- flowtask/components/Azure.py +80 -0
- flowtask/components/AzureUsers.py +106 -0
- flowtask/components/BaseAction.py +91 -0
- flowtask/components/BaseLoop.py +198 -0
- flowtask/components/BestBuy.py +800 -0
- flowtask/components/CSVToGCS.py +120 -0
- flowtask/components/CompanyScraper/__init__.py +1 -0
- flowtask/components/CompanyScraper/parsers/__init__.py +6 -0
- flowtask/components/CompanyScraper/parsers/base.py +102 -0
- flowtask/components/CompanyScraper/parsers/explorium.py +192 -0
- flowtask/components/CompanyScraper/parsers/leadiq.py +206 -0
- flowtask/components/CompanyScraper/parsers/rocket.py +133 -0
- flowtask/components/CompanyScraper/parsers/siccode.py +109 -0
- flowtask/components/CompanyScraper/parsers/visualvisitor.py +130 -0
- flowtask/components/CompanyScraper/parsers/zoominfo.py +118 -0
- flowtask/components/CompanyScraper/scrapper.py +1054 -0
- flowtask/components/CopyTo.py +177 -0
- flowtask/components/CopyToBigQuery.py +243 -0
- flowtask/components/CopyToMongoDB.py +291 -0
- flowtask/components/CopyToPg.py +609 -0
- flowtask/components/CopyToRethink.py +207 -0
- flowtask/components/CreateGCSBucket.py +102 -0
- flowtask/components/CreateReport/CreateReport.py +228 -0
- flowtask/components/CreateReport/__init__.py +9 -0
- flowtask/components/CreateReport/charts/__init__.py +15 -0
- flowtask/components/CreateReport/charts/bar.py +51 -0
- flowtask/components/CreateReport/charts/base.py +66 -0
- flowtask/components/CreateReport/charts/pie.py +64 -0
- flowtask/components/CreateReport/utils.py +9 -0
- flowtask/components/CustomerSatisfaction.py +196 -0
- flowtask/components/DataInput.py +200 -0
- flowtask/components/DateList.py +255 -0
- flowtask/components/DbClient.py +163 -0
- flowtask/components/DialPad.py +146 -0
- flowtask/components/DocumentDBQuery.py +200 -0
- flowtask/components/DownloadFrom.py +371 -0
- flowtask/components/DownloadFromD2L.py +113 -0
- flowtask/components/DownloadFromFTP.py +181 -0
- flowtask/components/DownloadFromIMAP.py +315 -0
- flowtask/components/DownloadFromS3.py +198 -0
- flowtask/components/DownloadFromSFTP.py +265 -0
- flowtask/components/DownloadFromSharepoint.py +110 -0
- flowtask/components/DownloadFromSmartSheet.py +114 -0
- flowtask/components/DownloadS3File.py +229 -0
- flowtask/components/Dummy.py +59 -0
- flowtask/components/DuplicatePhoto.py +411 -0
- flowtask/components/EmployeeEvaluation.py +237 -0
- flowtask/components/ExecuteSQL.py +323 -0
- flowtask/components/ExtractHTML.py +178 -0
- flowtask/components/FileBase.py +178 -0
- flowtask/components/FileCopy.py +181 -0
- flowtask/components/FileDelete.py +82 -0
- flowtask/components/FileExists.py +146 -0
- flowtask/components/FileIteratorDelete.py +112 -0
- flowtask/components/FileList.py +194 -0
- flowtask/components/FileOpen.py +75 -0
- flowtask/components/FileRead.py +120 -0
- flowtask/components/FileRename.py +106 -0
- flowtask/components/FilterIf.py +284 -0
- flowtask/components/FilterRows/FilterRows.py +200 -0
- flowtask/components/FilterRows/__init__.py +10 -0
- flowtask/components/FilterRows/functions.py +4 -0
- flowtask/components/GCSToBigQuery.py +103 -0
- flowtask/components/GoogleA4.py +150 -0
- flowtask/components/GoogleGeoCoding.py +344 -0
- flowtask/components/GooglePlaces.py +315 -0
- flowtask/components/GoogleSearch.py +539 -0
- flowtask/components/HTTPClient.py +268 -0
- flowtask/components/ICIMS.py +146 -0
- flowtask/components/IF.py +179 -0
- flowtask/components/IcimsFolderCopy.py +173 -0
- flowtask/components/ImageFeatures/__init__.py +5 -0
- flowtask/components/ImageFeatures/process.py +233 -0
- flowtask/components/IteratorBase.py +251 -0
- flowtask/components/LangchainLoader/__init__.py +5 -0
- flowtask/components/LangchainLoader/loader.py +194 -0
- flowtask/components/LangchainLoader/loaders/__init__.py +22 -0
- flowtask/components/LangchainLoader/loaders/abstract.py +362 -0
- flowtask/components/LangchainLoader/loaders/basepdf.py +50 -0
- flowtask/components/LangchainLoader/loaders/docx.py +91 -0
- flowtask/components/LangchainLoader/loaders/html.py +119 -0
- flowtask/components/LangchainLoader/loaders/pdfblocks.py +146 -0
- flowtask/components/LangchainLoader/loaders/pdfmark.py +79 -0
- flowtask/components/LangchainLoader/loaders/pdftables.py +135 -0
- flowtask/components/LangchainLoader/loaders/qa.py +67 -0
- flowtask/components/LangchainLoader/loaders/txt.py +55 -0
- flowtask/components/LeadIQ.py +650 -0
- flowtask/components/Loop.py +253 -0
- flowtask/components/Lowes.py +334 -0
- flowtask/components/MS365Usage.py +156 -0
- flowtask/components/MSTeamsMessages.py +320 -0
- flowtask/components/MarketClustering.py +1051 -0
- flowtask/components/MergeFiles.py +362 -0
- flowtask/components/MilvusOutput.py +87 -0
- flowtask/components/NearByStores.py +175 -0
- flowtask/components/NetworkNinja/__init__.py +6 -0
- flowtask/components/NetworkNinja/models/__init__.py +52 -0
- flowtask/components/NetworkNinja/models/abstract.py +177 -0
- flowtask/components/NetworkNinja/models/account.py +39 -0
- flowtask/components/NetworkNinja/models/client.py +19 -0
- flowtask/components/NetworkNinja/models/district.py +14 -0
- flowtask/components/NetworkNinja/models/events.py +101 -0
- flowtask/components/NetworkNinja/models/forms.py +499 -0
- flowtask/components/NetworkNinja/models/market.py +16 -0
- flowtask/components/NetworkNinja/models/organization.py +34 -0
- flowtask/components/NetworkNinja/models/photos.py +125 -0
- flowtask/components/NetworkNinja/models/project.py +44 -0
- flowtask/components/NetworkNinja/models/region.py +28 -0
- flowtask/components/NetworkNinja/models/store.py +203 -0
- flowtask/components/NetworkNinja/models/user.py +151 -0
- flowtask/components/NetworkNinja/router.py +854 -0
- flowtask/components/Odoo.py +175 -0
- flowtask/components/OdooInjector.py +192 -0
- flowtask/components/OpenFromXML.py +126 -0
- flowtask/components/OpenWeather.py +41 -0
- flowtask/components/OpenWithBase.py +616 -0
- flowtask/components/OpenWithPandas.py +715 -0
- flowtask/components/PGPDecrypt.py +199 -0
- flowtask/components/PandasIterator.py +187 -0
- flowtask/components/PandasToFile.py +189 -0
- flowtask/components/Paradox.py +339 -0
- flowtask/components/ParamIterator.py +117 -0
- flowtask/components/ParseHTML.py +84 -0
- flowtask/components/PlacerStores.py +249 -0
- flowtask/components/Pokemon.py +507 -0
- flowtask/components/PositiveBot.py +62 -0
- flowtask/components/PowerPointSlide.py +400 -0
- flowtask/components/PrintMessage.py +127 -0
- flowtask/components/ProductCompetitors/__init__.py +5 -0
- flowtask/components/ProductCompetitors/parsers/__init__.py +7 -0
- flowtask/components/ProductCompetitors/parsers/base.py +72 -0
- flowtask/components/ProductCompetitors/parsers/bestbuy.py +86 -0
- flowtask/components/ProductCompetitors/parsers/lowes.py +103 -0
- flowtask/components/ProductCompetitors/scrapper.py +155 -0
- flowtask/components/ProductCompliant.py +169 -0
- flowtask/components/ProductInfo/__init__.py +1 -0
- flowtask/components/ProductInfo/parsers/__init__.py +5 -0
- flowtask/components/ProductInfo/parsers/base.py +83 -0
- flowtask/components/ProductInfo/parsers/brother.py +97 -0
- flowtask/components/ProductInfo/parsers/canon.py +167 -0
- flowtask/components/ProductInfo/parsers/epson.py +118 -0
- flowtask/components/ProductInfo/parsers/hp.py +131 -0
- flowtask/components/ProductInfo/parsers/samsung.py +97 -0
- flowtask/components/ProductInfo/scraper.py +319 -0
- flowtask/components/ProductPricing.py +118 -0
- flowtask/components/QS.py +261 -0
- flowtask/components/QSBase.py +201 -0
- flowtask/components/QueryIterator.py +273 -0
- flowtask/components/QueryToInsert.py +327 -0
- flowtask/components/QueryToPandas.py +432 -0
- flowtask/components/RESTClient.py +195 -0
- flowtask/components/RethinkDBQuery.py +189 -0
- flowtask/components/Rsync.py +74 -0
- flowtask/components/RunSSH.py +59 -0
- flowtask/components/RunShell.py +71 -0
- flowtask/components/SalesForce.py +20 -0
- flowtask/components/SaveImageBank/__init__.py +257 -0
- flowtask/components/SchedulingVisits.py +592 -0
- flowtask/components/ScrapPage.py +216 -0
- flowtask/components/ScrapSearch.py +79 -0
- flowtask/components/SendNotify.py +257 -0
- flowtask/components/SentimentAnalysis.py +694 -0
- flowtask/components/ServiceScrapper/__init__.py +5 -0
- flowtask/components/ServiceScrapper/parsers/__init__.py +1 -0
- flowtask/components/ServiceScrapper/parsers/base.py +94 -0
- flowtask/components/ServiceScrapper/parsers/costco.py +93 -0
- flowtask/components/ServiceScrapper/scrapper.py +199 -0
- flowtask/components/SetVariables.py +156 -0
- flowtask/components/SubTask.py +182 -0
- flowtask/components/SuiteCRM.py +48 -0
- flowtask/components/Switch.py +175 -0
- flowtask/components/TableBase.py +148 -0
- flowtask/components/TableDelete.py +312 -0
- flowtask/components/TableInput.py +143 -0
- flowtask/components/TableOutput/TableOutput.py +384 -0
- flowtask/components/TableOutput/__init__.py +3 -0
- flowtask/components/TableSchema.py +534 -0
- flowtask/components/Target.py +223 -0
- flowtask/components/ThumbnailGenerator.py +156 -0
- flowtask/components/ToPandas.py +67 -0
- flowtask/components/TransformRows/TransformRows.py +507 -0
- flowtask/components/TransformRows/__init__.py +9 -0
- flowtask/components/TransformRows/functions.py +559 -0
- flowtask/components/TransposeRows.py +176 -0
- flowtask/components/UPCDatabase.py +86 -0
- flowtask/components/UnGzip.py +171 -0
- flowtask/components/Uncompress.py +172 -0
- flowtask/components/UniqueRows.py +126 -0
- flowtask/components/Unzip.py +107 -0
- flowtask/components/UpdateOperationalVars.py +147 -0
- flowtask/components/UploadTo.py +299 -0
- flowtask/components/UploadToS3.py +136 -0
- flowtask/components/UploadToSFTP.py +160 -0
- flowtask/components/UploadToSharepoint.py +205 -0
- flowtask/components/UserFunc.py +122 -0
- flowtask/components/VivaTracker.py +140 -0
- flowtask/components/WSDLClient.py +123 -0
- flowtask/components/Wait.py +18 -0
- flowtask/components/Walmart.py +199 -0
- flowtask/components/Workplace.py +134 -0
- flowtask/components/XMLToPandas.py +267 -0
- flowtask/components/Zammad/__init__.py +41 -0
- flowtask/components/Zammad/models.py +0 -0
- flowtask/components/ZoomInfoScraper.py +409 -0
- flowtask/components/__init__.py +104 -0
- flowtask/components/abstract.py +18 -0
- flowtask/components/flow.py +530 -0
- flowtask/components/google.py +335 -0
- flowtask/components/group.py +221 -0
- flowtask/components/py.typed +0 -0
- flowtask/components/reviewscrap.py +132 -0
- flowtask/components/tAutoincrement.py +117 -0
- flowtask/components/tConcat.py +109 -0
- flowtask/components/tExplode.py +119 -0
- flowtask/components/tFilter.py +184 -0
- flowtask/components/tGroup.py +236 -0
- flowtask/components/tJoin.py +270 -0
- flowtask/components/tMap/__init__.py +9 -0
- flowtask/components/tMap/functions.py +54 -0
- flowtask/components/tMap/tMap.py +450 -0
- flowtask/components/tMelt.py +112 -0
- flowtask/components/tMerge.py +114 -0
- flowtask/components/tOrder.py +93 -0
- flowtask/components/tPandas.py +94 -0
- flowtask/components/tPivot.py +71 -0
- flowtask/components/tPluckCols.py +76 -0
- flowtask/components/tUnnest.py +82 -0
- flowtask/components/user.py +401 -0
- flowtask/conf.py +457 -0
- flowtask/download.py +102 -0
- flowtask/events/__init__.py +11 -0
- flowtask/events/events/__init__.py +20 -0
- flowtask/events/events/abstract.py +95 -0
- flowtask/events/events/alerts/__init__.py +362 -0
- flowtask/events/events/alerts/colfunctions.py +131 -0
- flowtask/events/events/alerts/functions.py +158 -0
- flowtask/events/events/dummy.py +12 -0
- flowtask/events/events/exec.py +124 -0
- flowtask/events/events/file/__init__.py +7 -0
- flowtask/events/events/file/base.py +51 -0
- flowtask/events/events/file/copy.py +23 -0
- flowtask/events/events/file/delete.py +16 -0
- flowtask/events/events/interfaces/__init__.py +9 -0
- flowtask/events/events/interfaces/client.py +67 -0
- flowtask/events/events/interfaces/credentials.py +28 -0
- flowtask/events/events/interfaces/notifications.py +58 -0
- flowtask/events/events/jira.py +122 -0
- flowtask/events/events/log.py +26 -0
- flowtask/events/events/logerr.py +52 -0
- flowtask/events/events/notify.py +59 -0
- flowtask/events/events/notify_event.py +160 -0
- flowtask/events/events/publish.py +54 -0
- flowtask/events/events/sendfile.py +104 -0
- flowtask/events/events/task.py +97 -0
- flowtask/events/events/teams.py +98 -0
- flowtask/events/events/webhook.py +58 -0
- flowtask/events/manager.py +287 -0
- flowtask/exceptions.c +39393 -0
- flowtask/exceptions.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/extensions/__init__.py +3 -0
- flowtask/extensions/abstract.py +82 -0
- flowtask/extensions/logging/__init__.py +65 -0
- flowtask/hooks/__init__.py +9 -0
- flowtask/hooks/actions/__init__.py +22 -0
- flowtask/hooks/actions/abstract.py +66 -0
- flowtask/hooks/actions/dummy.py +23 -0
- flowtask/hooks/actions/jira.py +74 -0
- flowtask/hooks/actions/rest.py +320 -0
- flowtask/hooks/actions/sampledata.py +37 -0
- flowtask/hooks/actions/sensor.py +23 -0
- flowtask/hooks/actions/task.py +9 -0
- flowtask/hooks/actions/ticket.py +37 -0
- flowtask/hooks/actions/zammad.py +55 -0
- flowtask/hooks/hook.py +62 -0
- flowtask/hooks/models.py +17 -0
- flowtask/hooks/service.py +187 -0
- flowtask/hooks/step.py +91 -0
- flowtask/hooks/types/__init__.py +23 -0
- flowtask/hooks/types/base.py +129 -0
- flowtask/hooks/types/brokers/__init__.py +11 -0
- flowtask/hooks/types/brokers/base.py +54 -0
- flowtask/hooks/types/brokers/mqtt.py +35 -0
- flowtask/hooks/types/brokers/rabbitmq.py +82 -0
- flowtask/hooks/types/brokers/redis.py +83 -0
- flowtask/hooks/types/brokers/sqs.py +44 -0
- flowtask/hooks/types/fs.py +232 -0
- flowtask/hooks/types/http.py +49 -0
- flowtask/hooks/types/imap.py +200 -0
- flowtask/hooks/types/jira.py +279 -0
- flowtask/hooks/types/mail.py +205 -0
- flowtask/hooks/types/postgres.py +98 -0
- flowtask/hooks/types/responses/__init__.py +8 -0
- flowtask/hooks/types/responses/base.py +5 -0
- flowtask/hooks/types/sharepoint.py +288 -0
- flowtask/hooks/types/ssh.py +141 -0
- flowtask/hooks/types/tagged.py +59 -0
- flowtask/hooks/types/upload.py +85 -0
- flowtask/hooks/types/watch.py +71 -0
- flowtask/hooks/types/web.py +36 -0
- flowtask/interfaces/AzureClient.py +137 -0
- flowtask/interfaces/AzureGraph.py +839 -0
- flowtask/interfaces/Boto3Client.py +326 -0
- flowtask/interfaces/DropboxClient.py +173 -0
- flowtask/interfaces/ExcelHandler.py +94 -0
- flowtask/interfaces/FTPClient.py +131 -0
- flowtask/interfaces/GoogleCalendar.py +201 -0
- flowtask/interfaces/GoogleClient.py +133 -0
- flowtask/interfaces/GoogleDrive.py +127 -0
- flowtask/interfaces/GoogleGCS.py +89 -0
- flowtask/interfaces/GoogleGeocoding.py +93 -0
- flowtask/interfaces/GoogleLang.py +114 -0
- flowtask/interfaces/GooglePub.py +61 -0
- flowtask/interfaces/GoogleSheet.py +68 -0
- flowtask/interfaces/IMAPClient.py +137 -0
- flowtask/interfaces/O365Calendar.py +113 -0
- flowtask/interfaces/O365Client.py +220 -0
- flowtask/interfaces/OneDrive.py +284 -0
- flowtask/interfaces/Outlook.py +155 -0
- flowtask/interfaces/ParrotBot.py +130 -0
- flowtask/interfaces/SSHClient.py +378 -0
- flowtask/interfaces/Sharepoint.py +496 -0
- flowtask/interfaces/__init__.py +36 -0
- flowtask/interfaces/azureauth.py +119 -0
- flowtask/interfaces/cache.py +201 -0
- flowtask/interfaces/client.py +82 -0
- flowtask/interfaces/compress.py +525 -0
- flowtask/interfaces/credentials.py +124 -0
- flowtask/interfaces/d2l.py +239 -0
- flowtask/interfaces/databases/__init__.py +5 -0
- flowtask/interfaces/databases/db.py +223 -0
- flowtask/interfaces/databases/documentdb.py +55 -0
- flowtask/interfaces/databases/rethink.py +39 -0
- flowtask/interfaces/dataframes/__init__.py +11 -0
- flowtask/interfaces/dataframes/abstract.py +21 -0
- flowtask/interfaces/dataframes/arrow.py +71 -0
- flowtask/interfaces/dataframes/dt.py +69 -0
- flowtask/interfaces/dataframes/pandas.py +167 -0
- flowtask/interfaces/dataframes/polars.py +60 -0
- flowtask/interfaces/db.py +263 -0
- flowtask/interfaces/env.py +46 -0
- flowtask/interfaces/func.py +137 -0
- flowtask/interfaces/http.py +1780 -0
- flowtask/interfaces/locale.py +40 -0
- flowtask/interfaces/log.py +75 -0
- flowtask/interfaces/mask.py +143 -0
- flowtask/interfaces/notification.py +154 -0
- flowtask/interfaces/playwright.py +339 -0
- flowtask/interfaces/powerpoint.py +368 -0
- flowtask/interfaces/py.typed +0 -0
- flowtask/interfaces/qs.py +376 -0
- flowtask/interfaces/result.py +87 -0
- flowtask/interfaces/selenium_service.py +779 -0
- flowtask/interfaces/smartsheet.py +154 -0
- flowtask/interfaces/stat.py +39 -0
- flowtask/interfaces/task.py +96 -0
- flowtask/interfaces/template.py +118 -0
- flowtask/interfaces/vectorstores/__init__.py +1 -0
- flowtask/interfaces/vectorstores/abstract.py +133 -0
- flowtask/interfaces/vectorstores/milvus.py +669 -0
- flowtask/interfaces/zammad.py +107 -0
- flowtask/models.py +193 -0
- flowtask/parsers/__init__.py +15 -0
- flowtask/parsers/_yaml.c +11978 -0
- flowtask/parsers/_yaml.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/argparser.py +235 -0
- flowtask/parsers/base.c +15155 -0
- flowtask/parsers/base.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/json.c +11968 -0
- flowtask/parsers/json.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/maps.py +49 -0
- flowtask/parsers/toml.c +11968 -0
- flowtask/parsers/toml.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/plugins/__init__.py +16 -0
- flowtask/plugins/components/__init__.py +0 -0
- flowtask/plugins/handler/__init__.py +45 -0
- flowtask/plugins/importer.py +31 -0
- flowtask/plugins/sources/__init__.py +0 -0
- flowtask/runner.py +283 -0
- flowtask/scheduler/__init__.py +9 -0
- flowtask/scheduler/functions.py +493 -0
- flowtask/scheduler/handlers/__init__.py +8 -0
- flowtask/scheduler/handlers/manager.py +504 -0
- flowtask/scheduler/handlers/models.py +58 -0
- flowtask/scheduler/handlers/service.py +72 -0
- flowtask/scheduler/notifications.py +65 -0
- flowtask/scheduler/scheduler.py +993 -0
- flowtask/services/__init__.py +0 -0
- flowtask/services/bots/__init__.py +0 -0
- flowtask/services/bots/telegram.py +264 -0
- flowtask/services/files/__init__.py +11 -0
- flowtask/services/files/manager.py +522 -0
- flowtask/services/files/model.py +37 -0
- flowtask/services/files/service.py +767 -0
- flowtask/services/jira/__init__.py +3 -0
- flowtask/services/jira/jira_actions.py +191 -0
- flowtask/services/tasks/__init__.py +13 -0
- flowtask/services/tasks/launcher.py +213 -0
- flowtask/services/tasks/manager.py +323 -0
- flowtask/services/tasks/service.py +275 -0
- flowtask/services/tasks/task_manager.py +376 -0
- flowtask/services/tasks/tasks.py +155 -0
- flowtask/storages/__init__.py +16 -0
- flowtask/storages/exceptions.py +12 -0
- flowtask/storages/files/__init__.py +8 -0
- flowtask/storages/files/abstract.py +29 -0
- flowtask/storages/files/filesystem.py +66 -0
- flowtask/storages/tasks/__init__.py +19 -0
- flowtask/storages/tasks/abstract.py +26 -0
- flowtask/storages/tasks/database.py +33 -0
- flowtask/storages/tasks/filesystem.py +108 -0
- flowtask/storages/tasks/github.py +119 -0
- flowtask/storages/tasks/memory.py +45 -0
- flowtask/storages/tasks/row.py +25 -0
- flowtask/tasks/__init__.py +0 -0
- flowtask/tasks/abstract.py +526 -0
- flowtask/tasks/command.py +118 -0
- flowtask/tasks/pile.py +486 -0
- flowtask/tasks/py.typed +0 -0
- flowtask/tasks/task.py +778 -0
- flowtask/template/__init__.py +161 -0
- flowtask/tests.py +257 -0
- flowtask/types/__init__.py +8 -0
- flowtask/types/typedefs.c +11347 -0
- flowtask/types/typedefs.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/__init__.py +24 -0
- flowtask/utils/constants.py +117 -0
- flowtask/utils/encoders.py +21 -0
- flowtask/utils/executor.py +112 -0
- flowtask/utils/functions.cpp +14280 -0
- flowtask/utils/functions.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/json.cpp +13349 -0
- flowtask/utils/json.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/mail.py +63 -0
- flowtask/utils/parseqs.c +13324 -0
- flowtask/utils/parserqs.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/stats.py +308 -0
- flowtask/utils/transformations.py +74 -0
- flowtask/utils/uv.py +12 -0
- flowtask/utils/validators.py +97 -0
- flowtask/version.py +11 -0
- flowtask-5.8.4.dist-info/LICENSE +201 -0
- flowtask-5.8.4.dist-info/METADATA +209 -0
- flowtask-5.8.4.dist-info/RECORD +470 -0
- flowtask-5.8.4.dist-info/WHEEL +6 -0
- flowtask-5.8.4.dist-info/entry_points.txt +3 -0
- flowtask-5.8.4.dist-info/top_level.txt +2 -0
- plugins/components/CreateQR.py +39 -0
- plugins/components/TestComponent.py +28 -0
- plugins/components/Use1.py +13 -0
- plugins/components/Workplace.py +117 -0
- plugins/components/__init__.py +3 -0
- plugins/sources/__init__.py +0 -0
- plugins/sources/get_populartimes.py +78 -0
- plugins/sources/google.py +150 -0
- plugins/sources/hubspot.py +679 -0
- plugins/sources/icims.py +679 -0
- plugins/sources/mobileinsight.py +501 -0
- plugins/sources/newrelic.py +262 -0
- plugins/sources/uap.py +268 -0
- plugins/sources/venu.py +244 -0
- plugins/sources/vocinity.py +314 -0
@@ -0,0 +1,526 @@
|
|
1
|
+
from typing import Union
|
2
|
+
import os
|
3
|
+
import time
|
4
|
+
import asyncio
|
5
|
+
import uuid
|
6
|
+
import random
|
7
|
+
import locale
|
8
|
+
import traceback
|
9
|
+
from collections.abc import Callable, Awaitable
|
10
|
+
from abc import ABC, abstractmethod
|
11
|
+
import jsonschema
|
12
|
+
# asyncdb
|
13
|
+
from asyncdb import AsyncDB
|
14
|
+
from asyncdb.drivers.base import BaseDriver
|
15
|
+
from asyncdb.exceptions import ProviderError
|
16
|
+
from asyncdb.meta.record import Record
|
17
|
+
# and navconfig
|
18
|
+
from navconfig import config, DEBUG
|
19
|
+
from navconfig.logging import logging
|
20
|
+
from ..exceptions import TaskError, TaskParseError
|
21
|
+
# Flowtask Dependencies
|
22
|
+
from ..conf import (
|
23
|
+
default_dsn,
|
24
|
+
SYSTEM_LOCALE,
|
25
|
+
TASK_STORAGES,
|
26
|
+
FILE_STORAGES
|
27
|
+
)
|
28
|
+
from ..utils.stats import TaskMonitor
|
29
|
+
from ..models import TaskState
|
30
|
+
from ..events.manager import EventManager
|
31
|
+
from ..events import LogEvent, LogError
|
32
|
+
|
33
|
+
class AbstractTask(ABC):
|
34
|
+
"""
|
35
|
+
AbstractTask.
|
36
|
+
|
37
|
+
Base class for all Dataintegration tasks.
|
38
|
+
"""
|
39
|
+
|
40
|
+
_logger: logging.Logger = None
|
41
|
+
|
42
|
+
# pre-init and post-end functions
|
43
|
+
pre_init: Awaitable[asyncio.Task] = None
|
44
|
+
post_end: Awaitable[asyncio.Task] = None
|
45
|
+
_event_list: list = [
|
46
|
+
"start",
|
47
|
+
"running",
|
48
|
+
"exception",
|
49
|
+
"data_not_found",
|
50
|
+
"data_error",
|
51
|
+
"file_not_found",
|
52
|
+
"file_empty",
|
53
|
+
]
|
54
|
+
|
55
|
+
def __init__(
|
56
|
+
self,
|
57
|
+
task_id: str = None,
|
58
|
+
task: str = None,
|
59
|
+
program: str = None,
|
60
|
+
loop: asyncio.AbstractEventLoop = None,
|
61
|
+
parser: Callable = None,
|
62
|
+
userid: Union[int, str] = None,
|
63
|
+
**kwargs,
|
64
|
+
):
|
65
|
+
self._state = TaskState.PENDING
|
66
|
+
self.enable_stat: bool = kwargs.pop("enable_stat", True)
|
67
|
+
self._final_stats: dict = None
|
68
|
+
# Task Definition:
|
69
|
+
self._task_ = None
|
70
|
+
self._taskname = task
|
71
|
+
self._taskdef = None
|
72
|
+
self._env = config
|
73
|
+
self._attrs = {}
|
74
|
+
self._variables: dict = {}
|
75
|
+
# program definition
|
76
|
+
self._program = program
|
77
|
+
if not self._program:
|
78
|
+
self._program = "navigator"
|
79
|
+
self._schema = program
|
80
|
+
self._kwargs = {}
|
81
|
+
self._args = {}
|
82
|
+
self._conditions = {}
|
83
|
+
self._argparser = None
|
84
|
+
self._options = None
|
85
|
+
self._parameters: list = []
|
86
|
+
self._arguments: list = []
|
87
|
+
# User running the task:
|
88
|
+
self._userid = userid
|
89
|
+
# configure logging
|
90
|
+
self.logger = logging.getLogger("FlowTask.Task")
|
91
|
+
# re-use task Stat object from parent (subtasks)
|
92
|
+
self.stat = kwargs.get('stat', None)
|
93
|
+
if parser:
|
94
|
+
self._argparser = parser
|
95
|
+
self._options = parser.options
|
96
|
+
self._taskdef: Record = None
|
97
|
+
# define if results are returned or not (when run on scheduler)
|
98
|
+
try:
|
99
|
+
self._ignore_results: bool = bool(kwargs["ignore_results"])
|
100
|
+
del kwargs["ignore_results"]
|
101
|
+
except KeyError:
|
102
|
+
self._ignore_results: bool = False
|
103
|
+
if parser:
|
104
|
+
if "ignore-results" in parser.attributes:
|
105
|
+
self._ignore_results: bool = bool(
|
106
|
+
parser.attributes["ignore-results"]
|
107
|
+
)
|
108
|
+
# disable notifications
|
109
|
+
self._no_notify = kwargs.pop("disable_notifications", False)
|
110
|
+
if self._no_notify is True:
|
111
|
+
self.enable_stat = False
|
112
|
+
if loop:
|
113
|
+
self._loop = loop
|
114
|
+
else:
|
115
|
+
try:
|
116
|
+
self._loop = asyncio.get_running_loop()
|
117
|
+
except RuntimeError:
|
118
|
+
try:
|
119
|
+
self._loop = asyncio.new_event_loop()
|
120
|
+
asyncio.set_event_loop(self._loop)
|
121
|
+
except RuntimeError:
|
122
|
+
raise RuntimeError(
|
123
|
+
"Task: No event loop running."
|
124
|
+
)
|
125
|
+
# Task ID:
|
126
|
+
self.task_id = (
|
127
|
+
task_id
|
128
|
+
if task_id
|
129
|
+
else uuid.uuid1(node=random.getrandbits(48) | 0x010000000000)
|
130
|
+
)
|
131
|
+
# DEBUG
|
132
|
+
self._debug = kwargs.pop('debug', DEBUG)
|
133
|
+
info = {
|
134
|
+
"task_id": task_id,
|
135
|
+
"task": self._taskname,
|
136
|
+
"program": self._program,
|
137
|
+
"debug": self._debug,
|
138
|
+
"started": time.time(),
|
139
|
+
}
|
140
|
+
self.logger.info(f"::: TASK: {info!r} :::")
|
141
|
+
if self._debug:
|
142
|
+
self.logger.setLevel(logging.DEBUG)
|
143
|
+
else:
|
144
|
+
self.logger.setLevel(logging.INFO)
|
145
|
+
# defining Locale
|
146
|
+
try:
|
147
|
+
locale.setlocale(locale.LC_ALL, SYSTEM_LOCALE)
|
148
|
+
except locale.Error as e:
|
149
|
+
self.logger.error(e)
|
150
|
+
# initialize the event system
|
151
|
+
self._events = EventManager(name="task")
|
152
|
+
self.task_events()
|
153
|
+
# On Finish Events:
|
154
|
+
log = LogEvent(use_notice=True)
|
155
|
+
errlog = LogError()
|
156
|
+
self._events.addEvent(done=[log])
|
157
|
+
self._events.addEvent(completed=[log])
|
158
|
+
self._events.addEvent(on_error=[log, errlog])
|
159
|
+
# TaskStorage
|
160
|
+
### default Filesystem Storage:
|
161
|
+
self._storage = kwargs.pop("storage", "default")
|
162
|
+
try:
|
163
|
+
self.taskstore = TASK_STORAGES[self._storage]
|
164
|
+
except KeyError as exc:
|
165
|
+
raise RuntimeError(
|
166
|
+
f"Invalid Task Storage {self._storage}"
|
167
|
+
) from exc
|
168
|
+
# File Storage:
|
169
|
+
# Site where Files are stored during Task execution
|
170
|
+
fstore = kwargs.pop("filestore", "default")
|
171
|
+
try:
|
172
|
+
self._filestore = FILE_STORAGES[fstore]
|
173
|
+
self._filestore.set_program(self._program)
|
174
|
+
except KeyError as exc:
|
175
|
+
raise RuntimeError(f"Invalid File Store {fstore}") from exc
|
176
|
+
# params
|
177
|
+
self._params = {}
|
178
|
+
if "params" in kwargs:
|
179
|
+
self._params = {**kwargs["params"]}
|
180
|
+
del kwargs["params"]
|
181
|
+
# also, work with arguments
|
182
|
+
# command-line arguments
|
183
|
+
self._arguments = []
|
184
|
+
if parser:
|
185
|
+
self._arguments = self._options.arguments
|
186
|
+
try:
|
187
|
+
args = kwargs["arguments"]
|
188
|
+
del kwargs["arguments"]
|
189
|
+
if isinstance(args, list):
|
190
|
+
self._arguments = self._arguments + args
|
191
|
+
except KeyError:
|
192
|
+
pass
|
193
|
+
if parser:
|
194
|
+
try:
|
195
|
+
self._args = self._options.args
|
196
|
+
except (KeyError, ValueError, TypeError):
|
197
|
+
pass
|
198
|
+
elif "args" in kwargs:
|
199
|
+
self._args = kwargs["args"]
|
200
|
+
# processed parameters
|
201
|
+
try:
|
202
|
+
self._parameters = self._options.parameters
|
203
|
+
except AttributeError:
|
204
|
+
pass
|
205
|
+
if kwargs:
|
206
|
+
# remain args go to kwargs:
|
207
|
+
self._kwargs = {**kwargs}
|
208
|
+
|
209
|
+
def get_variables(self):
|
210
|
+
return self._variables
|
211
|
+
|
212
|
+
# Context Methods:
|
213
|
+
async def __aenter__(self) -> "AbstractTask":
|
214
|
+
"""Magic Context Methods"""
|
215
|
+
if callable(self.pre_init):
|
216
|
+
# this is a function called before start.
|
217
|
+
await self.pre_init() # pylint: disable=E1102
|
218
|
+
return self
|
219
|
+
|
220
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
221
|
+
# clean up anything you need to clean up
|
222
|
+
try:
|
223
|
+
await self.close()
|
224
|
+
finally:
|
225
|
+
# this is a function called when Task Ends.
|
226
|
+
if callable(self.post_end):
|
227
|
+
await self.post_end() # pylint: disable=E1102
|
228
|
+
|
229
|
+
def task_events(self):
|
230
|
+
"""Initialize Event manager with a default list of events."""
|
231
|
+
log = LogEvent()
|
232
|
+
event_list = {}
|
233
|
+
for event in self._event_list:
|
234
|
+
event_list[event] = [log] # Create a new list for each event
|
235
|
+
self._events.addEvent(**event_list)
|
236
|
+
|
237
|
+
def event_defaults(self, function: Callable):
|
238
|
+
for event in self._event_list:
|
239
|
+
evt = getattr(self._events, event)
|
240
|
+
evt.add(function)
|
241
|
+
|
242
|
+
def set_timezone(self, timezone: str = "UTC") -> None:
|
243
|
+
os.environ["TZ"] = timezone
|
244
|
+
time.tzset()
|
245
|
+
|
246
|
+
async def start(self) -> bool:
|
247
|
+
self._state = TaskState.STARTED
|
248
|
+
self._events.start(
|
249
|
+
message=f":: Starting Task: {self._program}.{self._taskname}",
|
250
|
+
task=self,
|
251
|
+
status="start",
|
252
|
+
disable_notification=self._no_notify,
|
253
|
+
)
|
254
|
+
if not self.stat:
|
255
|
+
if self.enable_stat is True:
|
256
|
+
# create the stat component:
|
257
|
+
try:
|
258
|
+
self.stat = TaskMonitor(
|
259
|
+
name=self._taskname,
|
260
|
+
program=self._program,
|
261
|
+
task_id=self.task_id,
|
262
|
+
userid=self._userid,
|
263
|
+
)
|
264
|
+
await self.stat.start()
|
265
|
+
except Exception as err:
|
266
|
+
raise TaskError(f"Task: Error on TaskMonitor: {err}") from err
|
267
|
+
try:
|
268
|
+
# getting Task information
|
269
|
+
await self.get_task()
|
270
|
+
except Exception as err:
|
271
|
+
self.logger.exception(err)
|
272
|
+
self._state = TaskState.EXCEPTION
|
273
|
+
self._events.exception(
|
274
|
+
message=f"Task Error: {self._taskname}: {err!r}",
|
275
|
+
task=self,
|
276
|
+
status="exception",
|
277
|
+
)
|
278
|
+
return False
|
279
|
+
return True
|
280
|
+
|
281
|
+
@abstractmethod
|
282
|
+
async def run(self) -> bool:
|
283
|
+
pass
|
284
|
+
|
285
|
+
@property
|
286
|
+
def taskname(self):
|
287
|
+
return self._taskname
|
288
|
+
|
289
|
+
@property
|
290
|
+
def id(self):
|
291
|
+
return self.task_id
|
292
|
+
|
293
|
+
def getState(self):
|
294
|
+
return self._state
|
295
|
+
|
296
|
+
def getProgram(self):
|
297
|
+
return self._program
|
298
|
+
|
299
|
+
def schema(self):
|
300
|
+
return self._schema
|
301
|
+
|
302
|
+
@property
|
303
|
+
def stats(self) -> TaskMonitor:
|
304
|
+
"""stats.
|
305
|
+
Return a TaskMonitor object with all collected stats.
|
306
|
+
Returns:
|
307
|
+
TaskMonitor: stat object.
|
308
|
+
"""
|
309
|
+
return self.stat
|
310
|
+
|
311
|
+
def setStat(self, stat):
|
312
|
+
self.stat = stat
|
313
|
+
|
314
|
+
def get_stats(self):
|
315
|
+
return self._final_stats
|
316
|
+
|
317
|
+
async def get_taskrow(self, table: str, conn: BaseDriver) -> Record:
|
318
|
+
definition = None
|
319
|
+
# TODO: add column "storage" and "datastore"
|
320
|
+
t = """
|
321
|
+
SELECT task_id, url, url_response, task_function, task_path,
|
322
|
+
task_definition, attributes, params, is_coroutine, executor,
|
323
|
+
program_slug, storage FROM {table} WHERE enabled = true AND task='{task}';
|
324
|
+
"""
|
325
|
+
task = t.format(table=table, task=self._taskname)
|
326
|
+
self.logger.debug(
|
327
|
+
f":: Task Query: {task}"
|
328
|
+
)
|
329
|
+
try:
|
330
|
+
result, error = await conn.queryrow(task)
|
331
|
+
if error:
|
332
|
+
return None
|
333
|
+
if result:
|
334
|
+
definition = Record.from_dict(dict(result))
|
335
|
+
return definition
|
336
|
+
except ProviderError as exc:
|
337
|
+
if "does not exist" in str(exc):
|
338
|
+
# Table doesn't exist
|
339
|
+
self.logger.warning(
|
340
|
+
f"Task: Table {table} doesn't exist"
|
341
|
+
)
|
342
|
+
else:
|
343
|
+
self.logger.error(str(exc))
|
344
|
+
except Exception as err: # pylint: disable=W0718
|
345
|
+
self.logger.exception(
|
346
|
+
str(err),
|
347
|
+
stack_info=False
|
348
|
+
)
|
349
|
+
return None
|
350
|
+
|
351
|
+
def retry(self):
|
352
|
+
try:
|
353
|
+
return self._taskdef.get('attributes', {}).get('retry', False)
|
354
|
+
except (KeyError, AttributeError, TypeError):
|
355
|
+
return False
|
356
|
+
|
357
|
+
async def get_task(self):
|
358
|
+
try:
|
359
|
+
db = AsyncDB("pg", dsn=default_dsn, loop=self._loop)
|
360
|
+
async with await db.connection() as conn:
|
361
|
+
# first, check if a Tasks table exists on tenant:
|
362
|
+
sql = f"""SELECT EXISTS (
|
363
|
+
SELECT FROM pg_catalog.pg_class c
|
364
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
365
|
+
WHERE n.nspname = '{self._program}'
|
366
|
+
AND c.relname = 'tasks'
|
367
|
+
AND c.relkind = 'r');"""
|
368
|
+
try:
|
369
|
+
row, error = await conn.queryrow(sql)
|
370
|
+
if error:
|
371
|
+
self.logger.error(
|
372
|
+
f"{error}"
|
373
|
+
)
|
374
|
+
if row and row["exists"]:
|
375
|
+
# its a database-defined task
|
376
|
+
table = f"{self._program}.tasks"
|
377
|
+
self._schema = self._program
|
378
|
+
taskdef = await self.get_taskrow(table, conn)
|
379
|
+
if not taskdef:
|
380
|
+
# fallback to navigator.tasks:
|
381
|
+
table = "navigator.tasks"
|
382
|
+
self._schema = "navigator"
|
383
|
+
else:
|
384
|
+
self._taskdef = taskdef
|
385
|
+
if self._storage == "row":
|
386
|
+
### getting Task directly from taskdef
|
387
|
+
self.taskstore.set_definition(taskdef)
|
388
|
+
return True
|
389
|
+
else:
|
390
|
+
# fallback to navigator.tasks:
|
391
|
+
table = "navigator.tasks"
|
392
|
+
self._schema = "navigator"
|
393
|
+
# getting task definition (a row in table "tasks")
|
394
|
+
taskdef = await self.get_taskrow(table, conn)
|
395
|
+
if taskdef is not None:
|
396
|
+
self._taskdef = taskdef
|
397
|
+
if self._storage == "row":
|
398
|
+
### getting Task directly from task definition
|
399
|
+
self.taskstore.set_definition(taskdef)
|
400
|
+
elif self._taskdef.storage != "default":
|
401
|
+
# Changing the default storage for Task.
|
402
|
+
self._storage = self._taskdef.storage
|
403
|
+
self.taskstore = TASK_STORAGES[self._storage]
|
404
|
+
return True
|
405
|
+
else:
|
406
|
+
self._schema = None
|
407
|
+
self.logger.warning(
|
408
|
+
f"Task '{self._taskname}' Not found in Database."
|
409
|
+
)
|
410
|
+
return False
|
411
|
+
except Exception as err:
|
412
|
+
print(err)
|
413
|
+
return False
|
414
|
+
except Exception as err:
|
415
|
+
dump = traceback.format_exc()
|
416
|
+
self._state = TaskState.EXCEPTION
|
417
|
+
self._events.exception(
|
418
|
+
message=f"Error on Task definition: {err!s}",
|
419
|
+
cls=err,
|
420
|
+
trace=dump,
|
421
|
+
task=self,
|
422
|
+
status="exception",
|
423
|
+
)
|
424
|
+
return False
|
425
|
+
|
426
|
+
def check_syntax(self, task):
|
427
|
+
"""
|
428
|
+
check_syntax.
|
429
|
+
|
430
|
+
Validates the syntax of a JSON task based on a predefined schema.
|
431
|
+
Ensures that the task has all required fields and correct data structures.
|
432
|
+
|
433
|
+
Parameters:
|
434
|
+
----------
|
435
|
+
task : dict
|
436
|
+
The task definition to validate.
|
437
|
+
|
438
|
+
Returns:
|
439
|
+
-------
|
440
|
+
bool
|
441
|
+
Returns True if the task is valid; raises TaskParseError otherwise.
|
442
|
+
|
443
|
+
Raises:
|
444
|
+
------
|
445
|
+
TaskParseError
|
446
|
+
If the task does not conform to the expected schema, an error is raised
|
447
|
+
with details about what failed.
|
448
|
+
"""
|
449
|
+
schema = {
|
450
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
451
|
+
"type": "object",
|
452
|
+
"properties": {
|
453
|
+
"name": {"type": "string"},
|
454
|
+
"description": {"type": "string"},
|
455
|
+
"timezone": {"type": "string"},
|
456
|
+
"comments": {"type": "string"},
|
457
|
+
"events": {
|
458
|
+
"type": "object",
|
459
|
+
"properties": {
|
460
|
+
"publish": {"type": "boolean"},
|
461
|
+
},
|
462
|
+
"patternProperties": {
|
463
|
+
"^[A-Za-z0-9_]+$": {
|
464
|
+
"anyOf": [
|
465
|
+
{"type": "boolean"},
|
466
|
+
{
|
467
|
+
"type": "array",
|
468
|
+
"items": {
|
469
|
+
"type": "object",
|
470
|
+
"additionalProperties": True
|
471
|
+
}
|
472
|
+
}
|
473
|
+
]
|
474
|
+
}
|
475
|
+
},
|
476
|
+
"additionalProperties": True
|
477
|
+
},
|
478
|
+
"steps": {
|
479
|
+
"type": "array",
|
480
|
+
"items": {
|
481
|
+
"type": "object",
|
482
|
+
"minProperties": 1,
|
483
|
+
"maxProperties": 1,
|
484
|
+
"patternProperties": {
|
485
|
+
"^[A-Za-z0-9_]+$": {
|
486
|
+
"type": "object",
|
487
|
+
"additionalProperties": True,
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
}
|
492
|
+
},
|
493
|
+
"required": ["name", "steps"],
|
494
|
+
"additionalProperties": False,
|
495
|
+
}
|
496
|
+
try:
|
497
|
+
jsonschema.validate(instance=task, schema=schema)
|
498
|
+
return True
|
499
|
+
except jsonschema.ValidationError as err:
|
500
|
+
self._state = TaskState.ERROR
|
501
|
+
self._events.exception(
|
502
|
+
message=f"Error on Task Parse: {err!s}",
|
503
|
+
cls=err,
|
504
|
+
task=self,
|
505
|
+
status="TaskError",
|
506
|
+
)
|
507
|
+
raise TaskParseError(
|
508
|
+
f"Task: Error parsing {self._taskname}: {err!s}"
|
509
|
+
) from err
|
510
|
+
except TaskParseError:
|
511
|
+
raise
|
512
|
+
except Exception as err:
|
513
|
+
self._state = TaskState.EXCEPTION
|
514
|
+
self._events.exception(
|
515
|
+
message=f"Exception on Task: {err!s}",
|
516
|
+
cls=err,
|
517
|
+
task=self,
|
518
|
+
status="TaskError",
|
519
|
+
)
|
520
|
+
raise TaskParseError(
|
521
|
+
f"Task: Unknown parsing Error on {self._taskname}: {err!s}"
|
522
|
+
) from err
|
523
|
+
|
524
|
+
async def close(self):
|
525
|
+
self.set_timezone("UTC") # forcing UTC at Task End.
|
526
|
+
# TODO: closing Memcached-related connections
|
@@ -0,0 +1,118 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
import asyncio
|
3
|
+
from pathlib import Path
|
4
|
+
import traceback
|
5
|
+
import subprocess
|
6
|
+
from ..models import TaskState
|
7
|
+
from ..exceptions import TaskNotFound
|
8
|
+
from .abstract import AbstractTask
|
9
|
+
|
10
|
+
|
11
|
+
class Command(AbstractTask):
|
12
|
+
"""
|
13
|
+
Command.
|
14
|
+
|
15
|
+
Execution of Operating System commands.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(
|
19
|
+
self,
|
20
|
+
task_id: str = None,
|
21
|
+
task: str = None,
|
22
|
+
program: str = None,
|
23
|
+
loop: asyncio.AbstractEventLoop = None,
|
24
|
+
parser: Callable = None,
|
25
|
+
**kwargs,
|
26
|
+
) -> None:
|
27
|
+
super(Command, self).__init__(
|
28
|
+
task_id=task_id,
|
29
|
+
task=task,
|
30
|
+
program=program,
|
31
|
+
loop=loop,
|
32
|
+
parser=parser,
|
33
|
+
**kwargs,
|
34
|
+
)
|
35
|
+
self._task = task
|
36
|
+
# can we mix args and params
|
37
|
+
try:
|
38
|
+
self._parameters = self._parameters + self._arguments
|
39
|
+
except ValueError:
|
40
|
+
pass
|
41
|
+
|
42
|
+
async def start(self) -> bool:
|
43
|
+
await super(Command, self).start()
|
44
|
+
if self._taskdef:
|
45
|
+
# we can replace the task definition from database:
|
46
|
+
self._task = self._taskdef.task_path
|
47
|
+
if self._taskdef.params:
|
48
|
+
# add it to existing params:
|
49
|
+
self._parameters = self._parameters + self._taskdef.params
|
50
|
+
self._logger.debug(f"Command: new parameters: {self._parameters}")
|
51
|
+
command = Path(self._task)
|
52
|
+
if not command.exists():
|
53
|
+
self._state = TaskState.STOPPED
|
54
|
+
self._events.onTaskFailure(
|
55
|
+
error=f"Command: missing or doesn't exists: {self._task}", task=self
|
56
|
+
)
|
57
|
+
raise TaskNotFound(f"Command: missing or doesn't exists: {self._task}")
|
58
|
+
return True
|
59
|
+
|
60
|
+
async def run(self) -> bool:
|
61
|
+
self._state = TaskState.RUNNING
|
62
|
+
self._logger.debug(
|
63
|
+
f"Running Command {self._task!s} with params: {self._parameters!r}"
|
64
|
+
)
|
65
|
+
try:
|
66
|
+
result = self.run_exec(self._task, self._parameters)
|
67
|
+
if not result:
|
68
|
+
self._state = TaskState.DONE_WITH_NODATA
|
69
|
+
else:
|
70
|
+
self._state = TaskState.DONE
|
71
|
+
self._events.onTaskDone(
|
72
|
+
message=f":: Command Ended: {self._taskname}", task=self
|
73
|
+
)
|
74
|
+
return result
|
75
|
+
except Exception as err:
|
76
|
+
self._state = TaskState.FAILED
|
77
|
+
trace = traceback.format_exc()
|
78
|
+
self._events.onTaskFailure(cls=err, trace=trace, task=self)
|
79
|
+
return False
|
80
|
+
|
81
|
+
async def close(self) -> None:
|
82
|
+
pass
|
83
|
+
|
84
|
+
def run_exec(self, path, args, shell: bool = False):
|
85
|
+
result = None
|
86
|
+
ex = [path]
|
87
|
+
if args:
|
88
|
+
ex = ex + args
|
89
|
+
try:
|
90
|
+
cp = subprocess.run(
|
91
|
+
ex,
|
92
|
+
capture_output=True,
|
93
|
+
shell=shell,
|
94
|
+
check=True,
|
95
|
+
universal_newlines=True,
|
96
|
+
)
|
97
|
+
try:
|
98
|
+
data = cp.stdout.decode()
|
99
|
+
err = cp.stderr.decode()
|
100
|
+
except (UnicodeDecodeError, AttributeError):
|
101
|
+
data = cp.stdout
|
102
|
+
err = cp.stderr
|
103
|
+
if err:
|
104
|
+
result = err
|
105
|
+
else:
|
106
|
+
result = data
|
107
|
+
return result
|
108
|
+
except subprocess.CalledProcessError as err:
|
109
|
+
self._state = TaskState.FAILED
|
110
|
+
self._events.onTaskFailure(
|
111
|
+
message=f"SubProcess Call Error {err!s}",
|
112
|
+
trace=traceback.format_exc(),
|
113
|
+
cls=err,
|
114
|
+
task=self,
|
115
|
+
)
|
116
|
+
except Exception as err:
|
117
|
+
self._logger.exception(err)
|
118
|
+
raise
|