flowtask 5.8.4__cp312-cp312-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-312-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-312-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/argparser.py +235 -0
- flowtask/parsers/base.c +15155 -0
- flowtask/parsers/base.cpython-312-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/json.c +11968 -0
- flowtask/parsers/json.cpython-312-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/maps.py +49 -0
- flowtask/parsers/toml.c +11968 -0
- flowtask/parsers/toml.cpython-312-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-312-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-312-x86_64-linux-gnu.so +0 -0
- flowtask/utils/json.cpp +13349 -0
- flowtask/utils/json.cpython-312-x86_64-linux-gnu.so +0 -0
- flowtask/utils/mail.py +63 -0
- flowtask/utils/parseqs.c +13324 -0
- flowtask/utils/parserqs.cpython-312-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,323 @@
|
|
1
|
+
"""
|
2
|
+
TaskManager.
|
3
|
+
|
4
|
+
Task Management: creation, deleting, listing, etc.
|
5
|
+
"""
|
6
|
+
import re
|
7
|
+
import uuid
|
8
|
+
import io
|
9
|
+
import traceback
|
10
|
+
from contextlib import redirect_stdout
|
11
|
+
import pandas as pd
|
12
|
+
from asyncdb.exceptions import NoDataFound
|
13
|
+
from navigator_session import get_session
|
14
|
+
from navigator.views import BaseView
|
15
|
+
from navigator.responses import JSONResponse
|
16
|
+
from ...models import TaskModel
|
17
|
+
# Getting Task
|
18
|
+
from ...tasks.task import Task
|
19
|
+
from ...storages import MemoryTaskStorage
|
20
|
+
from ...utils.stats import TaskMonitor
|
21
|
+
|
22
|
+
def is_uuid(s):
|
23
|
+
try:
|
24
|
+
uuid.UUID(s)
|
25
|
+
return True
|
26
|
+
except (TypeError, ValueError):
|
27
|
+
return False
|
28
|
+
|
29
|
+
|
30
|
+
class TaskManager(BaseView):
|
31
|
+
"""TaskManager.
|
32
|
+
|
33
|
+
description: API Endpoint for Task Management (creation of Tasks).
|
34
|
+
"""
|
35
|
+
def post_init(self, *args, **kwargs):
|
36
|
+
super().post_init(*args, **kwargs)
|
37
|
+
self.taskstorage = MemoryTaskStorage()
|
38
|
+
self.ansi_escape = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
39
|
+
|
40
|
+
model: TaskModel = TaskModel
|
41
|
+
_fields = [
|
42
|
+
"task",
|
43
|
+
"task_id",
|
44
|
+
"task_path",
|
45
|
+
"task_definition",
|
46
|
+
"url",
|
47
|
+
"attributes",
|
48
|
+
"params",
|
49
|
+
"enabled",
|
50
|
+
"last_done_time",
|
51
|
+
"last_exec_time",
|
52
|
+
"program_id",
|
53
|
+
"program_slug",
|
54
|
+
"task_state",
|
55
|
+
"traceback",
|
56
|
+
]
|
57
|
+
|
58
|
+
async def session(self):
|
59
|
+
session = None
|
60
|
+
try:
|
61
|
+
session = await get_session(self.request)
|
62
|
+
except (ValueError, RuntimeError) as err:
|
63
|
+
return self.critical(
|
64
|
+
reason="Error Decoding Session", request=self.request, exception=err
|
65
|
+
)
|
66
|
+
return session
|
67
|
+
|
68
|
+
async def get(self):
|
69
|
+
"""
|
70
|
+
GET Method.
|
71
|
+
---
|
72
|
+
description: get all tasks, or a task by ID (or get status of execution)
|
73
|
+
tags:
|
74
|
+
- tasks
|
75
|
+
- DataIntegration
|
76
|
+
consumes:
|
77
|
+
- application/json
|
78
|
+
produces:
|
79
|
+
- application/json
|
80
|
+
responses:
|
81
|
+
"200":
|
82
|
+
description: Existing Task was retrieved.
|
83
|
+
"403":
|
84
|
+
description: Forbidden Call
|
85
|
+
"404":
|
86
|
+
description: No Task(s) were found
|
87
|
+
"406":
|
88
|
+
description: Query Error
|
89
|
+
"""
|
90
|
+
qp = self.query_parameters(self.request)
|
91
|
+
args = self.match_parameters(self.request)
|
92
|
+
try:
|
93
|
+
task_id = args["task_id"]
|
94
|
+
except KeyError:
|
95
|
+
task_id = None
|
96
|
+
if is_uuid(task_id):
|
97
|
+
slug = None
|
98
|
+
else:
|
99
|
+
slug = task_id
|
100
|
+
task_id = None
|
101
|
+
try:
|
102
|
+
program_slug = args["program"]
|
103
|
+
except KeyError:
|
104
|
+
program_slug = None
|
105
|
+
try:
|
106
|
+
meta = args["meta"]
|
107
|
+
except KeyError:
|
108
|
+
meta = None
|
109
|
+
try:
|
110
|
+
if meta == ":meta":
|
111
|
+
# returning JSON schema of Model:
|
112
|
+
response = TaskModel.schema(as_dict=True)
|
113
|
+
return JSONResponse(response, status=200)
|
114
|
+
except (ValueError, TypeError) as ex:
|
115
|
+
self.logger.warning(str(ex))
|
116
|
+
#### getting all tasks:
|
117
|
+
if "fields" in qp:
|
118
|
+
args = {"fields": qp["fields"]}
|
119
|
+
else:
|
120
|
+
args = {"fields": self._fields}
|
121
|
+
try:
|
122
|
+
del qp["fields"]
|
123
|
+
except KeyError:
|
124
|
+
pass
|
125
|
+
try:
|
126
|
+
db = self.request.app["qs_connection"]
|
127
|
+
async with await db.acquire() as conn:
|
128
|
+
TaskModel.Meta.connection = conn
|
129
|
+
if program_slug is not None:
|
130
|
+
TaskModel.Meta.schema = program_slug
|
131
|
+
if task_id is not None:
|
132
|
+
result = await TaskModel.get(**{"task_id": task_id})
|
133
|
+
elif slug and program_slug:
|
134
|
+
result = await TaskModel.get(
|
135
|
+
**{"task": slug, "program_slug": program_slug}
|
136
|
+
)
|
137
|
+
elif len(qp) > 0:
|
138
|
+
result = await TaskModel.filter(**qp)
|
139
|
+
else:
|
140
|
+
tasks = await TaskModel.all(fields=self._fields)
|
141
|
+
result = [row.dict() for row in tasks]
|
142
|
+
return self.json_response(result)
|
143
|
+
except NoDataFound as err:
|
144
|
+
headers = {
|
145
|
+
"X-STATUS": "EMPTY",
|
146
|
+
"X-ERROR": str(err),
|
147
|
+
"X-MESSAGE": f"Task {task_id}:{slug} not Found",
|
148
|
+
}
|
149
|
+
return self.no_content(headers=headers)
|
150
|
+
except Exception as err:
|
151
|
+
return self.error(reason=f"Error getting Task: {err}", exception=err)
|
152
|
+
|
153
|
+
async def put(self):
|
154
|
+
"""
|
155
|
+
PUT Method.
|
156
|
+
description: Send a Task as Code directly to Task Executor.
|
157
|
+
tags:
|
158
|
+
- tasks
|
159
|
+
- DataIntegration
|
160
|
+
produces:
|
161
|
+
- application/json
|
162
|
+
consumes:
|
163
|
+
- application/merge-patch+json
|
164
|
+
- application/json
|
165
|
+
responses:
|
166
|
+
"200":
|
167
|
+
description: Task Executed
|
168
|
+
"204":
|
169
|
+
description: success execution but no content on return (resource was deleted)
|
170
|
+
"400":
|
171
|
+
description: Invalid resource according data schema
|
172
|
+
"403":
|
173
|
+
description: Forbidden Call
|
174
|
+
"404":
|
175
|
+
description: No Data was found
|
176
|
+
"406":
|
177
|
+
description: Query Error
|
178
|
+
"409":
|
179
|
+
description: Conflict, a constraint was violated
|
180
|
+
"""
|
181
|
+
try:
|
182
|
+
body_task = await self.request.text()
|
183
|
+
if not body_task:
|
184
|
+
return self.no_content()
|
185
|
+
except Exception as e:
|
186
|
+
return self.error(
|
187
|
+
response={
|
188
|
+
"message": f"Error reading body: {e}",
|
189
|
+
"error": e
|
190
|
+
},
|
191
|
+
status=400
|
192
|
+
)
|
193
|
+
# Then, try to validate With Task
|
194
|
+
# generate an uuid as name:
|
195
|
+
task_name = uuid.uuid4()
|
196
|
+
task = Task(
|
197
|
+
task=task_name
|
198
|
+
)
|
199
|
+
async with task as t:
|
200
|
+
# Assign the Memory Storage
|
201
|
+
t.taskstore = self.taskstorage
|
202
|
+
stdout = io.StringIO()
|
203
|
+
stacktrace = None
|
204
|
+
error = None
|
205
|
+
result = None
|
206
|
+
stats = None
|
207
|
+
captured_stdout = None
|
208
|
+
if await t.start(payload=body_task):
|
209
|
+
try:
|
210
|
+
with redirect_stdout(stdout):
|
211
|
+
# can we run the task
|
212
|
+
result = await t.run()
|
213
|
+
except Exception as e:
|
214
|
+
result = None
|
215
|
+
stacktrace = traceback.format_exc()
|
216
|
+
error = str(e)
|
217
|
+
captured_stdout = stdout.getvalue()
|
218
|
+
stats = t.get_stats()
|
219
|
+
if isinstance(result, pd.DataFrame):
|
220
|
+
resultset = []
|
221
|
+
resultset.append(
|
222
|
+
{"result": str(result)}
|
223
|
+
)
|
224
|
+
for column, t in result.dtypes.items():
|
225
|
+
resultset.append(
|
226
|
+
f"{column}->{t}->{result[column].iloc[0]}"
|
227
|
+
)
|
228
|
+
result = resultset
|
229
|
+
result = {
|
230
|
+
"result": f"{result!s}",
|
231
|
+
"error": error,
|
232
|
+
"stacktrace": stacktrace,
|
233
|
+
"stdout": self.ansi_escape.sub('', captured_stdout),
|
234
|
+
"stat": stats
|
235
|
+
}
|
236
|
+
return JSONResponse(
|
237
|
+
result
|
238
|
+
)
|
239
|
+
return self.error(
|
240
|
+
response={
|
241
|
+
"message": "Error reading Task body"
|
242
|
+
},
|
243
|
+
status=400
|
244
|
+
)
|
245
|
+
|
246
|
+
async def delete(self):
|
247
|
+
"""
|
248
|
+
DELETE Method.
|
249
|
+
description: remove resource.
|
250
|
+
tags:
|
251
|
+
- tasks
|
252
|
+
- DataIntegration
|
253
|
+
produces:
|
254
|
+
- application/json
|
255
|
+
responses:
|
256
|
+
"200":
|
257
|
+
description: Existing Task was updated.
|
258
|
+
"201":
|
259
|
+
description: New Task was inserted
|
260
|
+
"403":
|
261
|
+
description: Forbidden Call
|
262
|
+
"404":
|
263
|
+
description: No Data was found
|
264
|
+
"406":
|
265
|
+
description: Query Error
|
266
|
+
"409":
|
267
|
+
description: Conflict, a constraint was violated
|
268
|
+
"""
|
269
|
+
|
270
|
+
async def patch(self):
|
271
|
+
"""
|
272
|
+
PATCH Method.
|
273
|
+
description: updating partially info about a Task
|
274
|
+
tags:
|
275
|
+
- tasks
|
276
|
+
- DataIntegration
|
277
|
+
produces:
|
278
|
+
- application/json
|
279
|
+
responses:
|
280
|
+
"200":
|
281
|
+
description: Existing Task was updated.
|
282
|
+
"201":
|
283
|
+
description: New Task was inserted
|
284
|
+
"304":
|
285
|
+
description: Task not modified, its currently the actual version of Task
|
286
|
+
"403":
|
287
|
+
description: Forbidden Call
|
288
|
+
"404":
|
289
|
+
description: No Data was found
|
290
|
+
"406":
|
291
|
+
description: Query Error
|
292
|
+
"409":
|
293
|
+
description: Conflict, a constraint was violated
|
294
|
+
"""
|
295
|
+
|
296
|
+
async def post(self):
|
297
|
+
"""
|
298
|
+
PATCH Method.
|
299
|
+
description: updating or creating tasks
|
300
|
+
tags:
|
301
|
+
- tasks
|
302
|
+
- DataIntegration
|
303
|
+
produces:
|
304
|
+
- application/json
|
305
|
+
responses:
|
306
|
+
"200":
|
307
|
+
description: Existing Task was updated.
|
308
|
+
"201":
|
309
|
+
description: New Task was inserted
|
310
|
+
"304":
|
311
|
+
description: Task not modified, its currently the actual version of Task
|
312
|
+
"403":
|
313
|
+
description: Forbidden Call
|
314
|
+
"404":
|
315
|
+
description: No Data was found
|
316
|
+
"406":
|
317
|
+
description: Query Error
|
318
|
+
"409":
|
319
|
+
description: Conflict, a constraint was violated
|
320
|
+
"""
|
321
|
+
|
322
|
+
async def head(self):
|
323
|
+
pass
|
@@ -0,0 +1,275 @@
|
|
1
|
+
"""
|
2
|
+
TaskService.
|
3
|
+
|
4
|
+
Service for running tasks.
|
5
|
+
"""
|
6
|
+
from typing import Any
|
7
|
+
import uuid
|
8
|
+
import random
|
9
|
+
import logging
|
10
|
+
from aiohttp import web
|
11
|
+
from aiohttp.web import StreamResponse
|
12
|
+
import pandas as pd
|
13
|
+
from navigator.views import BaseView
|
14
|
+
from navigator.conf import AUTH_SESSION_OBJECT
|
15
|
+
from navigator_session import get_session
|
16
|
+
from ...exceptions import (
|
17
|
+
TaskException,
|
18
|
+
TaskDefinition,
|
19
|
+
TaskParseError,
|
20
|
+
TaskNotFound,
|
21
|
+
DataNotFound,
|
22
|
+
FileNotFound,
|
23
|
+
)
|
24
|
+
from ...tasks.task import Task
|
25
|
+
|
26
|
+
|
27
|
+
class TaskService(BaseView):
|
28
|
+
"""TaskService.
|
29
|
+
|
30
|
+
Task As a Service: launch data-based tasks and returning the resultset in useful formats.
|
31
|
+
Args:
|
32
|
+
BaseView (_type_): _description_
|
33
|
+
"""
|
34
|
+
|
35
|
+
def get_user(self, session, idx: str = "user_id") -> int:
|
36
|
+
if not session:
|
37
|
+
self.error(response={"error": "Unauthorized"}, status=403)
|
38
|
+
try:
|
39
|
+
if AUTH_SESSION_OBJECT in session:
|
40
|
+
return session[AUTH_SESSION_OBJECT][idx]
|
41
|
+
else:
|
42
|
+
return session[idx]
|
43
|
+
except KeyError:
|
44
|
+
self.error(
|
45
|
+
response={
|
46
|
+
"error": "Unauthorized",
|
47
|
+
"message": "Hint: maybe you need to pass an Authorization token.",
|
48
|
+
},
|
49
|
+
status=403,
|
50
|
+
)
|
51
|
+
|
52
|
+
async def check_task(self):
|
53
|
+
# get the URL parameters
|
54
|
+
params = self.get_args()
|
55
|
+
try:
|
56
|
+
program = params["program"]
|
57
|
+
del params["program"]
|
58
|
+
except (ValueError, KeyError) as ex:
|
59
|
+
# we need a program
|
60
|
+
raise TaskDefinition("Resource Error: We need a Program Name") from ex
|
61
|
+
try:
|
62
|
+
task_id = params["task_id"]
|
63
|
+
del params["task_id"]
|
64
|
+
except (ValueError, KeyError) as ex:
|
65
|
+
# we need a Task ID
|
66
|
+
raise TaskDefinition("Resource Error: We need a Task Name") from ex
|
67
|
+
return task_id, program
|
68
|
+
|
69
|
+
async def get_task(self, task: str, program: str, **kwargs) -> Task:
|
70
|
+
# Task ID:
|
71
|
+
task_id = uuid.uuid1(node=random.getrandbits(48) | 0x010000000000)
|
72
|
+
try:
|
73
|
+
usr = await get_session(self.request)
|
74
|
+
user = self.get_user(usr)
|
75
|
+
except (TypeError, ValueError):
|
76
|
+
user = None
|
77
|
+
|
78
|
+
try:
|
79
|
+
task = Task(
|
80
|
+
task_id=task_id,
|
81
|
+
task=task,
|
82
|
+
program=program,
|
83
|
+
loop=self._loop,
|
84
|
+
userid=user,
|
85
|
+
**kwargs,
|
86
|
+
)
|
87
|
+
# disable statistics
|
88
|
+
task.enable_stat = False
|
89
|
+
except Exception as err:
|
90
|
+
raise TaskException(err) from err
|
91
|
+
# then, try to "start" the task:
|
92
|
+
logging.debug(f"::::: DI: Running Task: {task!r}")
|
93
|
+
return task
|
94
|
+
|
95
|
+
async def launch_task(self, task: Task) -> Any:
|
96
|
+
try:
|
97
|
+
await task.start()
|
98
|
+
except (TaskParseError, TaskDefinition, TaskNotFound) as ex:
|
99
|
+
raise TaskDefinition(str(ex)) from ex
|
100
|
+
except Exception as err:
|
101
|
+
logging.error(err)
|
102
|
+
raise TaskException(f"Unhandled Task Error: {err}") from err
|
103
|
+
# run task:
|
104
|
+
try:
|
105
|
+
return await task.run()
|
106
|
+
except Exception as err:
|
107
|
+
logging.exception(err)
|
108
|
+
raise
|
109
|
+
finally:
|
110
|
+
await task.close()
|
111
|
+
|
112
|
+
async def get(self):
|
113
|
+
"""
|
114
|
+
GET Method.
|
115
|
+
description: Executing a Task and returning the result
|
116
|
+
tags:
|
117
|
+
- tasks
|
118
|
+
- DataIntegration
|
119
|
+
consumes:
|
120
|
+
- application/json
|
121
|
+
produces:
|
122
|
+
- application/json
|
123
|
+
responses:
|
124
|
+
"200":
|
125
|
+
description: Existing Task was executed.
|
126
|
+
"202":
|
127
|
+
description: Task was accepted to queue (no return)
|
128
|
+
"204":
|
129
|
+
description: No data was found
|
130
|
+
"400":
|
131
|
+
description: Task Failed to execute
|
132
|
+
"403":
|
133
|
+
description: Forbidden Call
|
134
|
+
"404":
|
135
|
+
description: no Task was found.
|
136
|
+
"406":
|
137
|
+
description: Query Error
|
138
|
+
"409":
|
139
|
+
description: Task Conflict, a constraint was violated
|
140
|
+
"""
|
141
|
+
# get query parameters
|
142
|
+
query = self.query_parameters(request=self.request)
|
143
|
+
# Default launch a task on Queue
|
144
|
+
# no_worker = True
|
145
|
+
# TODO: check for user permissions
|
146
|
+
try:
|
147
|
+
task, program = await self.check_task()
|
148
|
+
except TaskDefinition as ex:
|
149
|
+
headers = {"X-STATUS": "Error", "X-MESSAGE": ex}
|
150
|
+
msg = {"state": "Failed", "message": f"Error: {ex}", "status": 400}
|
151
|
+
return self.error(response=msg, headers=headers, status=400)
|
152
|
+
try:
|
153
|
+
task = await self.get_task(task, program, **query)
|
154
|
+
except TaskException as ex:
|
155
|
+
headers = {"X-STATUS": "Error", "X-MESSAGE": ex}
|
156
|
+
msg = {"state": "Failed", "message": f"Error: {ex}", "status": 400}
|
157
|
+
return self.error(response=msg, headers=headers, status=400)
|
158
|
+
try:
|
159
|
+
result = await self.launch_task(task)
|
160
|
+
return await self.task_output(result)
|
161
|
+
except Exception as err: # pylint: disable=W0703
|
162
|
+
headers = {"X-STATUS": "Error", "X-MESSAGE": err}
|
163
|
+
msg = {"state": "Failed", "message": f"Error: {err}", "status": 400}
|
164
|
+
return self.error(response=msg, headers=headers, status=400)
|
165
|
+
|
166
|
+
async def post(self):
|
167
|
+
"""
|
168
|
+
GET Method.
|
169
|
+
description: Executing a Task and returning the result
|
170
|
+
tags:
|
171
|
+
- tasks
|
172
|
+
- DataIntegration
|
173
|
+
consumes:
|
174
|
+
- application/json
|
175
|
+
produces:
|
176
|
+
- application/json
|
177
|
+
responses:
|
178
|
+
"200":
|
179
|
+
description: Existing Task was executed.
|
180
|
+
"202":
|
181
|
+
description: Task was accepted to queue (no return)
|
182
|
+
"204":
|
183
|
+
description: No data was found
|
184
|
+
"400":
|
185
|
+
description: Task Failed to execute
|
186
|
+
"403":
|
187
|
+
description: Forbidden Call
|
188
|
+
"404":
|
189
|
+
description: no Task was found.
|
190
|
+
"406":
|
191
|
+
description: Query Error
|
192
|
+
"409":
|
193
|
+
description: Task Conflict, a constraint was violated
|
194
|
+
"""
|
195
|
+
# get post Data
|
196
|
+
data = await self.post_data()
|
197
|
+
# get query parameters
|
198
|
+
query = self.query_parameters(request=self.request)
|
199
|
+
# Default launch a task on Queue
|
200
|
+
# TODO: check for user permissions
|
201
|
+
try:
|
202
|
+
params = {**query, **data}
|
203
|
+
except (KeyError, TypeError):
|
204
|
+
params = data
|
205
|
+
try:
|
206
|
+
task, program = await self.check_task()
|
207
|
+
except TaskDefinition as ex:
|
208
|
+
headers = {"X-STATUS": "Error", "X-MESSAGE": ex}
|
209
|
+
msg = {"state": "Failed", "message": f"Error: {ex}", "status": 400}
|
210
|
+
return self.error(response=msg, headers=headers, status=400)
|
211
|
+
try:
|
212
|
+
task = await self.get_task(task, program, **params)
|
213
|
+
except TaskException as ex:
|
214
|
+
headers = {"X-STATUS": "Error", "X-MESSAGE": ex}
|
215
|
+
msg = {"state": "Failed", "message": f"Error: {ex}", "status": 400}
|
216
|
+
return self.error(response=msg, headers=headers, status=400)
|
217
|
+
try:
|
218
|
+
result = await self.launch_task(task)
|
219
|
+
return await self.task_output(result)
|
220
|
+
except (DataNotFound, FileNotFound) as ex:
|
221
|
+
headers = {"X-STATUS": "Not Found", "X-MESSAGE": f"Data not Found: {ex}"}
|
222
|
+
return self.no_content(headers=headers)
|
223
|
+
except Exception as err: # pylint: disable=W0703
|
224
|
+
headers = {"X-STATUS": "Error", "X-MESSAGE": err}
|
225
|
+
msg = {"state": "Failed", "message": f"Error: {err}", "status": 400}
|
226
|
+
return self.error(response=msg, headers=headers, status=400)
|
227
|
+
|
228
|
+
async def task_output(self, resultset: Any) -> web.Response:
|
229
|
+
## TODO: making output pluggable like QuerySource
|
230
|
+
response = None
|
231
|
+
buffer = None
|
232
|
+
if isinstance(resultset, pd.DataFrame):
|
233
|
+
buffer = resultset.to_json(orient="records")
|
234
|
+
response = self.get_response()
|
235
|
+
return await self.stream_response(response, bytes(buffer, "utf-8"))
|
236
|
+
|
237
|
+
def get_response(self) -> web.Response:
|
238
|
+
response = StreamResponse(
|
239
|
+
status=200,
|
240
|
+
reason="OK",
|
241
|
+
headers={
|
242
|
+
"Pragma": "public", # required,
|
243
|
+
"Expires": "0",
|
244
|
+
"Connection": "keep-alive",
|
245
|
+
"Cache-Control": "must-revalidate, post-check=0, pre-check=0",
|
246
|
+
"Content-Type": "application/json",
|
247
|
+
"X-APPLICATION": "QuerySource",
|
248
|
+
},
|
249
|
+
)
|
250
|
+
response.enable_compression(force=web.ContentCoding.gzip)
|
251
|
+
return response
|
252
|
+
|
253
|
+
async def stream_response(
|
254
|
+
self, response: web.StreamResponse, data: Any
|
255
|
+
) -> web.StreamResponse:
|
256
|
+
content_length = len(data)
|
257
|
+
response.content_length = content_length
|
258
|
+
chunk_size = 16384
|
259
|
+
response.headers["Content-Range"] = f"bytes 0-{chunk_size}/{content_length}"
|
260
|
+
try:
|
261
|
+
i = 0
|
262
|
+
await response.prepare(self.request)
|
263
|
+
while True:
|
264
|
+
chunk = data[i: i + chunk_size]
|
265
|
+
i += chunk_size
|
266
|
+
if not chunk:
|
267
|
+
break
|
268
|
+
await response.write(chunk)
|
269
|
+
await response.drain()
|
270
|
+
await response.write_eof()
|
271
|
+
return response
|
272
|
+
except Exception as ex: # pylint: disable=W0703
|
273
|
+
return self.error(
|
274
|
+
message="Error Starting Stream Transmision", exception=ex, status=500
|
275
|
+
)
|