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,432 @@
|
|
1
|
+
import logging
|
2
|
+
import asyncio
|
3
|
+
from pathlib import Path, PurePath
|
4
|
+
from collections.abc import Callable
|
5
|
+
from pprint import pprint
|
6
|
+
import aiofiles
|
7
|
+
import urllib3
|
8
|
+
import pandas as pd
|
9
|
+
from asyncdb.exceptions import NoDataFound
|
10
|
+
from querysource.types.validators import Entity
|
11
|
+
from ..exceptions import (
|
12
|
+
ComponentError,
|
13
|
+
DataNotFound,
|
14
|
+
NotSupported,
|
15
|
+
FileError
|
16
|
+
)
|
17
|
+
from ..interfaces.qs import QSSupport
|
18
|
+
from ..utils import cPrint, is_empty, SafeDict
|
19
|
+
from ..conf import TASK_PATH
|
20
|
+
from .flow import FlowComponent
|
21
|
+
from ..interfaces import TemplateSupport
|
22
|
+
|
23
|
+
urllib3.disable_warnings()
|
24
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
25
|
+
|
26
|
+
|
27
|
+
class QueryToPandas(FlowComponent, TemplateSupport, QSSupport):
|
28
|
+
"""
|
29
|
+
QueryToPandas.
|
30
|
+
|
31
|
+
Overview
|
32
|
+
|
33
|
+
This component fetches data using QuerySource and transforms it into a Pandas DataFrame.
|
34
|
+
|
35
|
+
.. table:: Properties
|
36
|
+
:widths: auto
|
37
|
+
|
38
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
39
|
+
| Name | Required | Summary |
|
40
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
41
|
+
| query | Yes | Represents an SQL query |
|
42
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
43
|
+
| query_slug | Yes | Named queries that are saved in Navigator (QuerySource) |
|
44
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
45
|
+
| as_dict | Yes | True | False. if true, it returns the data in JSON format |
|
46
|
+
| | | instead of a dataframe |
|
47
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
48
|
+
| raw_result | Yes | Returns the data in the NATIVE FORMAT of the database for |
|
49
|
+
| | | example ( pg RECORDSET) |
|
50
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
51
|
+
| file_sql | Yes | SQL comes from a sql file |
|
52
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
53
|
+
| use_template | Yes | The component is passed to the SQL file through a template |
|
54
|
+
| | | replacement system |
|
55
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
56
|
+
| infer_types | Yes | Type inference, give the component the power to decide the data |
|
57
|
+
| | | types of each column |
|
58
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
59
|
+
| drop_empty | Yes | False | True delete (drop) any column that is empty |
|
60
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
61
|
+
| dropna | Yes | False | True delete all NA (Not a Number) |
|
62
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
63
|
+
| fillna | Yes | False | True fills with an EMPTY SPACE all the NAs of the |
|
64
|
+
| | | dataframe |
|
65
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
66
|
+
| clean_strings| Yes | Fills with an empty space the NA,but ONLY of the fields of |
|
67
|
+
| | | type string |
|
68
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
69
|
+
| clean_dates | Yes | Declares NONE any date field that has a NAT (Not a Time) |
|
70
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
71
|
+
| conditions | Yes | This attribute allows me to apply conditions to filter the data |
|
72
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
73
|
+
| formit | Yes | Form id (i have doubts about this) |
|
74
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
75
|
+
| orgid | Yes | Organization id (i have doubts about this) |
|
76
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
77
|
+
| refresh | Yes | Refreshes the data in the QueryToPandas |
|
78
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
79
|
+
| to_string | No | Whether to convert all data to string type. Default is True. |
|
80
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
81
|
+
| as_objects | No | Whether to return the result as objects. Default is True. |
|
82
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
83
|
+
| datatypes | No | A dictionary specifying data types for columns. |
|
84
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
85
|
+
| datasource | No | The datasource to fetch the data from. Default is "db". |
|
86
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
87
|
+
Returns
|
88
|
+
|
89
|
+
This component returns a Pandas DataFrame if the query is successfully executed and data is fetched,
|
90
|
+
otherwise raises a ComponentError.
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
Example:
|
95
|
+
|
96
|
+
```yaml
|
97
|
+
QueryToPandas:
|
98
|
+
query: SELECT formid, orgid FROM banco.forms WHERE enabled = true
|
99
|
+
```
|
100
|
+
|
101
|
+
""" # noqa
|
102
|
+
def __init__(
|
103
|
+
self,
|
104
|
+
loop: asyncio.AbstractEventLoop = None,
|
105
|
+
job: Callable = None,
|
106
|
+
stat: Callable = None,
|
107
|
+
**kwargs,
|
108
|
+
):
|
109
|
+
"""Init Method."""
|
110
|
+
self.data = None
|
111
|
+
self.infer_types: bool = True
|
112
|
+
self.to_string: bool = True
|
113
|
+
self._query: dict = {}
|
114
|
+
self.as_dict: bool = False
|
115
|
+
self.as_objects: bool = True
|
116
|
+
self._dtypes: dict = {}
|
117
|
+
self.datatypes: dict = {}
|
118
|
+
self.use_template: bool = kwargs.get('use_template', False)
|
119
|
+
self._driver = kwargs.get("driver", "pg")
|
120
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
121
|
+
|
122
|
+
async def open_sqlfile(self, file: PurePath, **kwargs) -> str:
|
123
|
+
if file.exists() and file.is_file():
|
124
|
+
content = None
|
125
|
+
# open SQL File:
|
126
|
+
async with aiofiles.open(file, "r+") as afp:
|
127
|
+
content = await afp.read()
|
128
|
+
# check if we need to replace masks
|
129
|
+
if hasattr(self, "masks"):
|
130
|
+
self._logger.debug(f"QueryToPandas Masks: {self.masks}")
|
131
|
+
if "{" in content:
|
132
|
+
content = self.mask_replacement(content)
|
133
|
+
if self.use_template is True:
|
134
|
+
content = self._templateparser.from_string(content, kwargs)
|
135
|
+
elif hasattr(self, "conditions"):
|
136
|
+
content = self.conditions_replacement(content)
|
137
|
+
return content
|
138
|
+
else:
|
139
|
+
raise FileError(f"{self.__name__}: Missing SQL File: {file}")
|
140
|
+
|
141
|
+
async def start(self, **kwargs):
|
142
|
+
await super(QueryToPandas, self).start(**kwargs)
|
143
|
+
# compute conditions:
|
144
|
+
self.set_conditions()
|
145
|
+
# check if sql comes from a filename:
|
146
|
+
if hasattr(self, "file_sql") or hasattr(self, "query_file"):
|
147
|
+
# based on a list/dict of queries
|
148
|
+
if hasattr(self, "file_sql"):
|
149
|
+
query = self.file_sql
|
150
|
+
else:
|
151
|
+
query = self.query_file
|
152
|
+
if isinstance(query, PurePath):
|
153
|
+
self._query = []
|
154
|
+
if query.exists() and query.is_file():
|
155
|
+
sql = await self.open_sqlfile(query)
|
156
|
+
self._query.append(sql)
|
157
|
+
elif isinstance(query, str):
|
158
|
+
self._query = []
|
159
|
+
try:
|
160
|
+
file_path = Path(TASK_PATH).joinpath(self._program, "sql", query)
|
161
|
+
if file_path.exists() and file_path.is_file():
|
162
|
+
sql = await self.open_sqlfile(file_path)
|
163
|
+
self._query.append(sql)
|
164
|
+
except Exception as ex:
|
165
|
+
raise FileError(f"File SQL doesn't exists: {query!s}: {ex}") from ex
|
166
|
+
elif isinstance(query, list): # list of queries
|
167
|
+
self._query = []
|
168
|
+
for file_sql in query:
|
169
|
+
file_path = Path(TASK_PATH).joinpath(self._program, "sql", file_sql)
|
170
|
+
if file_path.exists() and file_path.is_file():
|
171
|
+
sql = await self.open_sqlfile(file_path)
|
172
|
+
self._query.append(sql)
|
173
|
+
elif isinstance(query, dict): # named queries
|
174
|
+
self._query = {}
|
175
|
+
for key, file_sql in query.items():
|
176
|
+
file_path = Path(TASK_PATH).joinpath(self._program, "sql", file_sql)
|
177
|
+
if file_path.exists() and file_path.is_file():
|
178
|
+
sql = await self.open_sqlfile(file_path)
|
179
|
+
self._query[key] = sql
|
180
|
+
elif hasattr(self, "query_slug"):
|
181
|
+
if isinstance(self.query_slug, str): # pylint: disable=E0203
|
182
|
+
if "{" in self.query_slug: # pylint: disable=E0203 # noqa
|
183
|
+
self.query_slug = self.mask_replacement(self.query_slug)
|
184
|
+
self._query[self.query_slug] = self.query_slug
|
185
|
+
elif isinstance(self.query_slug, list):
|
186
|
+
for slug in self.query_slug:
|
187
|
+
self._query[slug] = slug
|
188
|
+
elif isinstance(self.query_slug, dict):
|
189
|
+
# iterate over all conditions and search in masks:
|
190
|
+
for key, data in self.query_slug.items():
|
191
|
+
slug = data["slug"]
|
192
|
+
for mask, replace in self._mask.items():
|
193
|
+
if mask in data["conditions"]:
|
194
|
+
self.query_slug[key]["conditions"][mask] = replace
|
195
|
+
self._query[key] = slug
|
196
|
+
elif hasattr(self, "query"):
|
197
|
+
if isinstance(self.query, str): # pylint: disable=E0203
|
198
|
+
self._query = []
|
199
|
+
if hasattr(self, "masks"):
|
200
|
+
self.query = self.mask_replacement(self.query)
|
201
|
+
elif "{" in self.query and hasattr(self, "conditions"):
|
202
|
+
try:
|
203
|
+
self.query = self.query.format(**self.conditions)
|
204
|
+
except Exception as err:
|
205
|
+
self._logger.warning(f"Error replacing Vars in Query: {err}")
|
206
|
+
try:
|
207
|
+
self.query = self.query.format(**self._variables)
|
208
|
+
except Exception as err:
|
209
|
+
self._logger.warning(f"Error replacing Vars in Query: {err}")
|
210
|
+
self._query.append(self.query)
|
211
|
+
elif isinstance(self.query, dict): # named queries
|
212
|
+
self._query = {}
|
213
|
+
for key, query in self.query.items():
|
214
|
+
query = self.mask_replacement(query)
|
215
|
+
try:
|
216
|
+
query = query.format(**self._variables)
|
217
|
+
except Exception:
|
218
|
+
pass
|
219
|
+
self._query[key] = query
|
220
|
+
elif isinstance(self.query, list): # need to be concatenated
|
221
|
+
self._query = []
|
222
|
+
for query in self.query:
|
223
|
+
query = self.mask_replacement(query)
|
224
|
+
try:
|
225
|
+
for val in self._variables:
|
226
|
+
if isinstance(self._variables[val], list):
|
227
|
+
result = ", ".join(self._variables[val])
|
228
|
+
else:
|
229
|
+
result = ", ".join(
|
230
|
+
"'{}'".format(v) for v in self._variables[val]
|
231
|
+
)
|
232
|
+
query = query.format(**self._variables)
|
233
|
+
except Exception:
|
234
|
+
pass
|
235
|
+
self._query.append(query)
|
236
|
+
if hasattr(self, "conditions"):
|
237
|
+
self.set_conditions("conditions")
|
238
|
+
cPrint("NEW CONDITIONS ARE> ", level="WARN")
|
239
|
+
pprint(self.conditions)
|
240
|
+
|
241
|
+
# Replace variables
|
242
|
+
if isinstance(self._query, list):
|
243
|
+
queries = []
|
244
|
+
for query in self._query:
|
245
|
+
values = {}
|
246
|
+
for key, val in self._variables.items():
|
247
|
+
if isinstance(val, list):
|
248
|
+
value = ", ".join(
|
249
|
+
"'{}'".format(Entity.quoteString(v)) for v in val
|
250
|
+
)
|
251
|
+
else:
|
252
|
+
value = val
|
253
|
+
query = query.replace("{{{}}}".format(str(key)), str(value))
|
254
|
+
values[key] = value
|
255
|
+
# using safeDict
|
256
|
+
query.format_map(SafeDict(**values))
|
257
|
+
queries.append(query)
|
258
|
+
self._query = queries
|
259
|
+
return True
|
260
|
+
|
261
|
+
async def close(self):
|
262
|
+
"""Method."""
|
263
|
+
|
264
|
+
async def run(self):
|
265
|
+
# TODO: support for datasources
|
266
|
+
# TODO: using maps to transform data types
|
267
|
+
if not self._query:
|
268
|
+
raise ComponentError(
|
269
|
+
"QueryToPandas: Empty Query/Slug or File"
|
270
|
+
)
|
271
|
+
if hasattr(self, "query") or hasattr(self, "file_sql"):
|
272
|
+
try:
|
273
|
+
connection = await self.create_connection()
|
274
|
+
except Exception as err:
|
275
|
+
self._logger.exception(err, stack_info=True)
|
276
|
+
raise
|
277
|
+
if isinstance(self._query, list): # list of queries
|
278
|
+
results = []
|
279
|
+
async with await connection.connection() as conn:
|
280
|
+
for query in self._query:
|
281
|
+
try:
|
282
|
+
res, error = await conn.query(query)
|
283
|
+
if error:
|
284
|
+
raise DataNotFound(error)
|
285
|
+
result = [dict(row) for row in res]
|
286
|
+
except NoDataFound:
|
287
|
+
result = []
|
288
|
+
except Exception as err:
|
289
|
+
self._logger.error(err)
|
290
|
+
raise
|
291
|
+
ln = len(result)
|
292
|
+
st = {
|
293
|
+
"result": ln
|
294
|
+
}
|
295
|
+
self.add_metric("Query", st)
|
296
|
+
if ln == 1 and self.as_dict is True:
|
297
|
+
# saving only one row
|
298
|
+
result = dict(result[0])
|
299
|
+
results.append(result)
|
300
|
+
else:
|
301
|
+
results.extend(result)
|
302
|
+
if hasattr(self, "raw_result"):
|
303
|
+
self._result = results
|
304
|
+
self._variables[f"{self.StepName}_NUMROWS"] = len(results)
|
305
|
+
else:
|
306
|
+
self._result = await self.get_dataframe(
|
307
|
+
results, infer_types=self.infer_types
|
308
|
+
)
|
309
|
+
numrows = len(self._result.index)
|
310
|
+
self._variables[f"{self.StepName}_NUMROWS"] = numrows
|
311
|
+
elif isinstance(self._query, dict): # Named queries
|
312
|
+
self._result = {}
|
313
|
+
results = []
|
314
|
+
async with await connection.connection() as conn:
|
315
|
+
for key, query in self._query.items():
|
316
|
+
try:
|
317
|
+
res, error = await conn.query(query)
|
318
|
+
if error:
|
319
|
+
raise DataNotFound(error)
|
320
|
+
result = [dict(row) for row in res]
|
321
|
+
except NoDataFound:
|
322
|
+
result = []
|
323
|
+
except Exception as err:
|
324
|
+
self._logger.error(err)
|
325
|
+
raise
|
326
|
+
ln = len(result)
|
327
|
+
st = {"query": key, "result": ln}
|
328
|
+
self.add_metric("Query", st)
|
329
|
+
if ln == 1:
|
330
|
+
# saving only one row
|
331
|
+
result = dict(result[0])
|
332
|
+
if hasattr(self, "raw_result"):
|
333
|
+
self._result[key] = result
|
334
|
+
self._variables[f"{self.StepName}_{key}_NUMROWS"] = len(
|
335
|
+
result
|
336
|
+
)
|
337
|
+
else:
|
338
|
+
df = await self.get_dataframe(
|
339
|
+
result, infer_types=self.infer_types
|
340
|
+
)
|
341
|
+
self._result[key] = df
|
342
|
+
self._variables[f"{self.StepName}_{key}_NUMROWS"] = len(
|
343
|
+
df.index
|
344
|
+
)
|
345
|
+
else:
|
346
|
+
raise NotSupported(f"{self.__name__}: Incompatible Query Method.")
|
347
|
+
elif hasattr(self, "query_slug"):
|
348
|
+
# TODO: assign the datasource to QuerySource connection
|
349
|
+
self.add_metric("SLUG", self.query_slug)
|
350
|
+
if isinstance(self.query_slug, dict):
|
351
|
+
# return a list of queries
|
352
|
+
self._result = {}
|
353
|
+
for key, data in self.query_slug.items():
|
354
|
+
slug = data["slug"]
|
355
|
+
conditions = data["conditions"]
|
356
|
+
try:
|
357
|
+
result = await self.get_qs(slug, conditions)
|
358
|
+
ln = len(result)
|
359
|
+
st = {"query": key, "result": ln}
|
360
|
+
if ln == 1 and self.as_dict is True:
|
361
|
+
# saving only one row
|
362
|
+
result = dict(result[0])
|
363
|
+
except (DataNotFound, NoDataFound) as ex:
|
364
|
+
raise DataNotFound(str(ex)) from ex
|
365
|
+
if hasattr(self, "raw_result"):
|
366
|
+
self._result[key] = result
|
367
|
+
self._variables[f"{self.StepName}_{key}_NUMROWS"] = len(result)
|
368
|
+
self.add_metric("NUMROWS", len(result))
|
369
|
+
else:
|
370
|
+
df = await self.get_dataframe(
|
371
|
+
result, infer_types=self.infer_types
|
372
|
+
)
|
373
|
+
self._result[key] = df
|
374
|
+
self._variables[f"{self.StepName}_{key}_NUMROWS"] = len(
|
375
|
+
df.index
|
376
|
+
)
|
377
|
+
self.add_metric("NUMROWS", len(df.index))
|
378
|
+
else:
|
379
|
+
results = []
|
380
|
+
for key, slug in self._query.items():
|
381
|
+
conditions = {}
|
382
|
+
if hasattr(self, "conditions"):
|
383
|
+
conditions = self.conditions
|
384
|
+
try:
|
385
|
+
result = await self.get_qs(slug, conditions)
|
386
|
+
ln = len(result)
|
387
|
+
self._logger.debug(f"QS {key}: length: {ln}")
|
388
|
+
st = {"query": key, "result": ln}
|
389
|
+
if ln == 1 and self.as_dict is True:
|
390
|
+
# saving only one row
|
391
|
+
result = dict(result[0])
|
392
|
+
except (DataNotFound, NoDataFound):
|
393
|
+
result = {}
|
394
|
+
except Exception as err:
|
395
|
+
self._logger.exception(err, stack_info=False)
|
396
|
+
raise
|
397
|
+
results.extend(result)
|
398
|
+
if hasattr(self, "raw_result"):
|
399
|
+
self._result = results
|
400
|
+
self._variables[f"{self.StepName}_NUMROWS"] = len(results)
|
401
|
+
self.add_metric("NUMROWS", len(result))
|
402
|
+
else:
|
403
|
+
self._result = await self.get_dataframe(
|
404
|
+
results, infer_types=self.infer_types
|
405
|
+
)
|
406
|
+
numrows = len(self._result.index)
|
407
|
+
self._variables[f"{self.StepName}_NUMROWS"] = numrows
|
408
|
+
self.add_metric("NUMROWS", numrows)
|
409
|
+
else:
|
410
|
+
raise NotSupported(f"{self.__name__}: Method not allowed")
|
411
|
+
if is_empty(self._result):
|
412
|
+
raise DataNotFound(f"{self.__name__}: Data Not Found")
|
413
|
+
else:
|
414
|
+
### making traspose of data:
|
415
|
+
if hasattr(self, "transpose"):
|
416
|
+
# transpose rows to columns:
|
417
|
+
# self._result = self._result.transpose()
|
418
|
+
self._result = pd.melt(self._result, id_vars=self.transpose["columns"])
|
419
|
+
if "variable" in self.transpose:
|
420
|
+
# rename variable to a new name:
|
421
|
+
self._result.rename(
|
422
|
+
columns={"variable": self.transpose["variable"]}, inplace=True
|
423
|
+
)
|
424
|
+
if "value" in self.transpose:
|
425
|
+
self._result.rename(
|
426
|
+
columns={"value": self.transpose["value"]}, inplace=True
|
427
|
+
)
|
428
|
+
if self._debug is True:
|
429
|
+
print("== DATA PREVIEW ==")
|
430
|
+
print(self._result)
|
431
|
+
print()
|
432
|
+
return self._result
|
@@ -0,0 +1,195 @@
|
|
1
|
+
"""RESTClient.
|
2
|
+
|
3
|
+
Basic component for making RESTful queries to URLs.
|
4
|
+
|
5
|
+
|
6
|
+
Example:
|
7
|
+
|
8
|
+
```yaml
|
9
|
+
RESTClient:
|
10
|
+
url: https://api.upcdatabase.org/product/{barcode}
|
11
|
+
barcode: '0111222333446'
|
12
|
+
credentials:
|
13
|
+
apikey: UPC_API_KEY
|
14
|
+
as_dataframe: true
|
15
|
+
```
|
16
|
+
|
17
|
+
"""
|
18
|
+
import asyncio
|
19
|
+
from abc import ABC
|
20
|
+
from typing import List, Dict, Union
|
21
|
+
from collections.abc import Callable
|
22
|
+
from urllib.parse import urlencode
|
23
|
+
from navconfig.logging import logging
|
24
|
+
from ..exceptions import DataNotFound, ComponentError
|
25
|
+
from .HTTPClient import HTTPClient
|
26
|
+
|
27
|
+
|
28
|
+
class RESTClient(HTTPClient):
|
29
|
+
"""
|
30
|
+
RESTClient
|
31
|
+
|
32
|
+
Overview
|
33
|
+
|
34
|
+
The RESTClient class is a component for making RESTful queries to URLs. It extends the HTTPClient class and provides
|
35
|
+
functionality to send requests and process responses from REST APIs. It supports creating DataFrames from JSON responses
|
36
|
+
if specified.
|
37
|
+
|
38
|
+
.. table:: Properties
|
39
|
+
:widths: auto
|
40
|
+
|
41
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
42
|
+
| Name | Required | Description |
|
43
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
44
|
+
| _result | No | The result of the REST query, can be a list or dictionary. |
|
45
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
46
|
+
| accept | No | The accepted response type, defaults to "application/json". |
|
47
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
48
|
+
| url | Yes | The URL to send the REST query to. |
|
49
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
50
|
+
| method | No | The HTTP method to use for the request, defaults to the method specified in the class. |
|
51
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
52
|
+
|
53
|
+
Return
|
54
|
+
|
55
|
+
The methods in this class manage the execution of RESTful queries and handle the response. It includes functionality to
|
56
|
+
convert JSON responses into DataFrames if specified.
|
57
|
+
|
58
|
+
""" # noqa
|
59
|
+
|
60
|
+
def __init__(
|
61
|
+
self,
|
62
|
+
loop: asyncio.AbstractEventLoop = None,
|
63
|
+
job: Callable = None,
|
64
|
+
stat: Callable = None,
|
65
|
+
**kwargs,
|
66
|
+
) -> None:
|
67
|
+
"""Init Method."""
|
68
|
+
self._result: Union[List, Dict] = None
|
69
|
+
self._data: dict = kwargs.pop("data", {})
|
70
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
71
|
+
self.accept: str = "application/json" # by default
|
72
|
+
|
73
|
+
async def run(self):
|
74
|
+
self.url = self.build_url(
|
75
|
+
self.url,
|
76
|
+
args=self.arguments,
|
77
|
+
queryparams=urlencode(self.parameters)
|
78
|
+
)
|
79
|
+
try:
|
80
|
+
if self.use_async is True:
|
81
|
+
result, error = await self.async_request(
|
82
|
+
url=self.url,
|
83
|
+
method=self.method,
|
84
|
+
data=self._data
|
85
|
+
)
|
86
|
+
else:
|
87
|
+
result, error = await self.request(
|
88
|
+
url=self.url,
|
89
|
+
method=self.method,
|
90
|
+
data=self._data
|
91
|
+
)
|
92
|
+
if not result:
|
93
|
+
raise DataNotFound(f"Data was not found on: {self.url}")
|
94
|
+
elif error is not None:
|
95
|
+
if isinstance(error, BaseException):
|
96
|
+
raise error
|
97
|
+
else:
|
98
|
+
raise ComponentError(f"RESTClient Error: {error}")
|
99
|
+
except Exception as err:
|
100
|
+
logging.exception(err, stack_info=True)
|
101
|
+
raise ComponentError(f"RESTClient Error: {err}") from err
|
102
|
+
# at here, processing Result
|
103
|
+
if self.as_dataframe is True:
|
104
|
+
try:
|
105
|
+
result = await self.create_dataframe(result)
|
106
|
+
except Exception as err:
|
107
|
+
raise ComponentError(f"RESTClient Error: {err}") from err
|
108
|
+
self._result = result
|
109
|
+
return self._result
|
110
|
+
|
111
|
+
|
112
|
+
class AbstractREST(RESTClient):
|
113
|
+
"""AbstractREST.
|
114
|
+
Abstract Method for RESTful Components.
|
115
|
+
"""
|
116
|
+
|
117
|
+
_default_method: str = None
|
118
|
+
base_url: str = None
|
119
|
+
|
120
|
+
def __init__(
|
121
|
+
self,
|
122
|
+
loop: asyncio.AbstractEventLoop = None,
|
123
|
+
job: Callable = None,
|
124
|
+
stat: Callable = None,
|
125
|
+
**kwargs,
|
126
|
+
) -> None:
|
127
|
+
"""Init Method."""
|
128
|
+
self._result: Union[List, Dict] = None
|
129
|
+
self.url: str = None
|
130
|
+
self._method: str = kwargs.pop('method', self._default_method)
|
131
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
132
|
+
self.accept: str = "application/json" # by default
|
133
|
+
self._args = self._params
|
134
|
+
|
135
|
+
async def start(self, **kwargs):
|
136
|
+
if not hasattr(self, self._method):
|
137
|
+
raise ComponentError(
|
138
|
+
f"{self.__name__} Error: has no Method {self._method}"
|
139
|
+
)
|
140
|
+
await super(AbstractREST, self).start(**kwargs)
|
141
|
+
|
142
|
+
async def run(self):
|
143
|
+
method = getattr(self, self._method)
|
144
|
+
try:
|
145
|
+
await method()
|
146
|
+
except Exception as err:
|
147
|
+
logging.exception(err, stack_info=True)
|
148
|
+
raise ComponentError(
|
149
|
+
f"{self.__name__}: Error calling Method {self._method}: {err}"
|
150
|
+
) from err
|
151
|
+
self.url = self.build_url(
|
152
|
+
self.url,
|
153
|
+
args=self.arguments,
|
154
|
+
queryparams=urlencode(self.parameters)
|
155
|
+
)
|
156
|
+
try:
|
157
|
+
if self.use_async is True:
|
158
|
+
result, error = await self.async_request(
|
159
|
+
url=self.url,
|
160
|
+
method=self.method,
|
161
|
+
data=self._data
|
162
|
+
)
|
163
|
+
else:
|
164
|
+
result, error = await self.request(
|
165
|
+
url=self.url,
|
166
|
+
method=self.method,
|
167
|
+
data=self._data
|
168
|
+
)
|
169
|
+
if self._debug:
|
170
|
+
print(result)
|
171
|
+
if not result:
|
172
|
+
raise DataNotFound(
|
173
|
+
f"Data was not found on: {self.url}"
|
174
|
+
)
|
175
|
+
elif error is not None:
|
176
|
+
if isinstance(error, BaseException):
|
177
|
+
raise error
|
178
|
+
else:
|
179
|
+
raise ComponentError(f"HTTPClient Error: {error}")
|
180
|
+
# at here, processing Result
|
181
|
+
if self.as_dataframe is True:
|
182
|
+
try:
|
183
|
+
result = await self.create_dataframe(result)
|
184
|
+
if self._debug is True:
|
185
|
+
print(result)
|
186
|
+
print("::: Printing Column Information === ")
|
187
|
+
for column, t in result.dtypes.items():
|
188
|
+
print(column, "->", t, "->", result[column].iloc[0])
|
189
|
+
except Exception as err:
|
190
|
+
raise ComponentError(f"HTTPClient Error: {err}") from err
|
191
|
+
self._result = result
|
192
|
+
return self._result
|
193
|
+
except Exception as err:
|
194
|
+
logging.exception(err, stack_info=True)
|
195
|
+
raise ComponentError(f"HTTPClient Error: {err}") from err
|