flowtask 5.8.4__cp310-cp310-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-310-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-310-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/argparser.py +235 -0
- flowtask/parsers/base.c +15155 -0
- flowtask/parsers/base.cpython-310-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/json.c +11968 -0
- flowtask/parsers/json.cpython-310-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/maps.py +49 -0
- flowtask/parsers/toml.c +11968 -0
- flowtask/parsers/toml.cpython-310-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-310-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-310-x86_64-linux-gnu.so +0 -0
- flowtask/utils/json.cpp +13349 -0
- flowtask/utils/json.cpython-310-x86_64-linux-gnu.so +0 -0
- flowtask/utils/mail.py +63 -0
- flowtask/utils/parseqs.c +13324 -0
- flowtask/utils/parserqs.cpython-310-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,273 @@
|
|
1
|
+
import asyncio
|
2
|
+
from decimal import Decimal
|
3
|
+
from typing import Any
|
4
|
+
from collections.abc import Callable
|
5
|
+
from pathlib import Path, PurePath
|
6
|
+
import aiofiles
|
7
|
+
import numpy as np
|
8
|
+
import pandas as pd
|
9
|
+
from navconfig.logging import logging
|
10
|
+
from asyncdb.exceptions import NoDataFound, ProviderError
|
11
|
+
from querysource.queries.qs import QS
|
12
|
+
from ..conf import TASK_PATH
|
13
|
+
from ..exceptions import ComponentError, NotSupported, DataNotFound, FileError
|
14
|
+
from .IteratorBase import IteratorBase
|
15
|
+
from ..interfaces import TemplateSupport, DBSupport
|
16
|
+
|
17
|
+
|
18
|
+
class QueryIterator(DBSupport, TemplateSupport, IteratorBase):
|
19
|
+
"""
|
20
|
+
QueryIterator
|
21
|
+
|
22
|
+
|
23
|
+
Overview
|
24
|
+
|
25
|
+
This component creates a Pandas Iterator from a QuerySource query,
|
26
|
+
allowing for the iteration over data returned from a query.
|
27
|
+
|
28
|
+
.. table:: Properties
|
29
|
+
:widths: auto
|
30
|
+
|
31
|
+
|
32
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
33
|
+
| Name | Required | Summary |
|
34
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
35
|
+
| file_sql | No | The SQL file to read the query from. |
|
36
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
37
|
+
| query_slug | No | The query slug to use for fetching data. |
|
38
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
39
|
+
| query | No | The raw SQL query string to execute. |
|
40
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
41
|
+
| conditions | No | Conditions to apply to the query. |
|
42
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
43
|
+
| columns | No | Specific columns to extract from the result. |
|
44
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
45
|
+
| use_template | No | If True, use a query template for the query. Default is False. |
|
46
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
47
|
+
| drop_empty | No | If True, drop empty rows and columns from the DataFrame. |
|
48
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
49
|
+
| dropna | No | Subset of columns to check for NaN values before dropping rows. |
|
50
|
+
+------------------------+----------+-----------+--------------------------------------------------------+
|
51
|
+
|
52
|
+
Returns
|
53
|
+
|
54
|
+
This component returns the last value generated by the iteration process. Typically,
|
55
|
+
it returns a pandas DataFrame containing the query results.
|
56
|
+
"""
|
57
|
+
|
58
|
+
def __init__(
|
59
|
+
self,
|
60
|
+
loop: asyncio.AbstractEventLoop = None,
|
61
|
+
job: Callable = None,
|
62
|
+
stat: Callable = None,
|
63
|
+
**kwargs,
|
64
|
+
):
|
65
|
+
self.pk = []
|
66
|
+
self.data = None
|
67
|
+
self._iterator: Any = None
|
68
|
+
self._variables = {}
|
69
|
+
self.vars = {}
|
70
|
+
self._columns = []
|
71
|
+
self._query: str = None
|
72
|
+
self.use_template: bool = bool(kwargs.get("use_template", False))
|
73
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
74
|
+
|
75
|
+
async def open_sqlfile(self, file: PurePath, **kwargs) -> str:
|
76
|
+
if file.exists() and file.is_file():
|
77
|
+
content = None
|
78
|
+
# open SQL File:
|
79
|
+
async with aiofiles.open(file, "r+") as afp:
|
80
|
+
content = await afp.read()
|
81
|
+
# check if we need to replace masks
|
82
|
+
if hasattr(self, "masks"):
|
83
|
+
content = self.mask_replacement(content)
|
84
|
+
if self.use_template is True:
|
85
|
+
content = self._templateparser.from_string(content, kwargs)
|
86
|
+
return content
|
87
|
+
else:
|
88
|
+
raise FileError(f"{self.__name__}: Missing SQL File: {file}")
|
89
|
+
|
90
|
+
async def start(self, **kwargs):
|
91
|
+
"""Getting kind of Query."""
|
92
|
+
if hasattr(self, "file_sql"):
|
93
|
+
try:
|
94
|
+
file_path = Path(TASK_PATH).joinpath(
|
95
|
+
self._program, "sql", self.file_sql
|
96
|
+
)
|
97
|
+
if file_path.exists() and file_path.is_file():
|
98
|
+
self._query = await self.open_sqlfile(file_path)
|
99
|
+
except Exception as e:
|
100
|
+
raise FileError(
|
101
|
+
f"File SQL doesn't exists: {self.file_sql!s}, {e}"
|
102
|
+
) from e
|
103
|
+
elif hasattr(self, "query_slug"):
|
104
|
+
self.query_slug = self.mask_replacement(self.query_slug)
|
105
|
+
self._query = self.query_slug
|
106
|
+
elif hasattr(self, "query"):
|
107
|
+
self._query = self.query
|
108
|
+
if hasattr(self, "masks"):
|
109
|
+
self._query = self.mask_replacement(self._query)
|
110
|
+
try:
|
111
|
+
self._query = self._query.format(**self._variables)
|
112
|
+
except Exception as err:
|
113
|
+
print("Error replacing Vars in Query: ", err)
|
114
|
+
if hasattr(self, "conditions"):
|
115
|
+
self.set_conditions("conditions")
|
116
|
+
await super().start(**kwargs)
|
117
|
+
self.processing_credentials()
|
118
|
+
return True
|
119
|
+
|
120
|
+
async def close(self, job=None):
|
121
|
+
close = getattr(job, "close", None)
|
122
|
+
if job:
|
123
|
+
if asyncio.iscoroutinefunction(close):
|
124
|
+
await job.close()
|
125
|
+
else:
|
126
|
+
job.close()
|
127
|
+
|
128
|
+
def createJob(self, target, params, row):
|
129
|
+
"""Create the Job Component."""
|
130
|
+
self._result = self.data
|
131
|
+
dt = {}
|
132
|
+
for column in self._columns:
|
133
|
+
value = row[column]
|
134
|
+
if isinstance(value, (int, np.int64, np.integer)):
|
135
|
+
value = int(value)
|
136
|
+
elif isinstance(value, (float, Decimal)):
|
137
|
+
value = float(value)
|
138
|
+
self.setVar(column, value)
|
139
|
+
# print('ITER ', value, type(value))
|
140
|
+
params[column] = value
|
141
|
+
dt[column] = value
|
142
|
+
for name, value in self.vars.items():
|
143
|
+
# TODO: check this logic
|
144
|
+
print("VARS: ", name, value, column)
|
145
|
+
val = row[column]
|
146
|
+
# print('VAL ', val)
|
147
|
+
# need to build this attribute
|
148
|
+
if isinstance(value, list):
|
149
|
+
pass
|
150
|
+
# TODO: logic to use functions with dataframes
|
151
|
+
# # need to calculate the value
|
152
|
+
else:
|
153
|
+
if "{" in str(value):
|
154
|
+
value = value.format(**dt)
|
155
|
+
else:
|
156
|
+
value = val
|
157
|
+
params[name] = value
|
158
|
+
self.setVar(name, value)
|
159
|
+
return self.get_job(target, **params)
|
160
|
+
|
161
|
+
async def run(self):
|
162
|
+
"""Async Run Method."""
|
163
|
+
# first: getting data:
|
164
|
+
df = None
|
165
|
+
result = None
|
166
|
+
if not self._query:
|
167
|
+
raise ComponentError("QueryToPandas: Empty Query/Slug or File")
|
168
|
+
if hasattr(self, "query") or hasattr(self, "file_sql"):
|
169
|
+
try:
|
170
|
+
connection = self.get_connection(event_loop=self._loop)
|
171
|
+
except Exception as err:
|
172
|
+
self._logger.exception(err, stack_info=True)
|
173
|
+
raise
|
174
|
+
async with await connection.connection() as conn:
|
175
|
+
try:
|
176
|
+
res, error = await conn.query(self._query)
|
177
|
+
if error:
|
178
|
+
self._logger.error(f"QueryIterator: {error}")
|
179
|
+
raise NoDataFound(error)
|
180
|
+
result = [dict(row) for row in res]
|
181
|
+
df = await self.get_dataframe(result)
|
182
|
+
except NoDataFound:
|
183
|
+
result = []
|
184
|
+
except Exception as err:
|
185
|
+
self._logger.error(err)
|
186
|
+
raise
|
187
|
+
elif hasattr(self, "query_slug"):
|
188
|
+
conditions = {}
|
189
|
+
if hasattr(self, "conditions"):
|
190
|
+
conditions = self.conditions
|
191
|
+
result = await self.get_query(self._query, conditions)
|
192
|
+
df = await self.get_dataframe(result)
|
193
|
+
else:
|
194
|
+
raise NotSupported(f"{self.__name__}: Method not allowed")
|
195
|
+
# getting the iterator:
|
196
|
+
if not hasattr(self, "columns"):
|
197
|
+
# iterate over the total columns of dataframe
|
198
|
+
self._columns = df.columns
|
199
|
+
else:
|
200
|
+
self._columns = self.columns
|
201
|
+
self._iterator = df.iterrows()
|
202
|
+
# iterate over next task
|
203
|
+
step, target, params = self.get_step()
|
204
|
+
step_name = step.name
|
205
|
+
for index, row in self._iterator:
|
206
|
+
self._logger.debug(f"ITER: index:{index} row: {row}")
|
207
|
+
# iterate over every row
|
208
|
+
# get I got all values, create a job:
|
209
|
+
job = self.createJob(target, params, row)
|
210
|
+
# print('JOB: ', job)
|
211
|
+
if job:
|
212
|
+
try:
|
213
|
+
self._result = await self.async_job(job, step_name)
|
214
|
+
except (NoDataFound, DataNotFound) as err:
|
215
|
+
# its a data component a no data was found
|
216
|
+
self._logger.debug(f"Data not Found for Task {step_name}, got: {err}")
|
217
|
+
continue
|
218
|
+
except (ProviderError, ComponentError) as err:
|
219
|
+
raise ComponentError(f"Error on {step_name}, error: {err}") from err
|
220
|
+
except NotSupported as err:
|
221
|
+
raise NotSupported(f"Not Supported: {err}") from err
|
222
|
+
except Exception as err:
|
223
|
+
raise ComponentError(
|
224
|
+
f"Component Error {step_name}, error: {err}"
|
225
|
+
) from err
|
226
|
+
finally:
|
227
|
+
await self.close(job)
|
228
|
+
# returning last value generated by iteration
|
229
|
+
return self._result
|
230
|
+
|
231
|
+
async def get_dataframe(self, result) -> pd.DataFrame:
|
232
|
+
try:
|
233
|
+
df = pd.DataFrame(result)
|
234
|
+
except Exception as err:
|
235
|
+
self._logger.exception(err, stack_info=True)
|
236
|
+
# Attempt to infer better dtypes for object columns.
|
237
|
+
df.infer_objects()
|
238
|
+
df = df.convert_dtypes(convert_string=True)
|
239
|
+
if hasattr(self, "drop_empty"):
|
240
|
+
df.dropna(axis=1, how="all", inplace=True)
|
241
|
+
df.dropna(axis=0, how="all", inplace=True)
|
242
|
+
if hasattr(self, "dropna"):
|
243
|
+
df.dropna(subset=self.dropna, how="all", inplace=True)
|
244
|
+
return df
|
245
|
+
|
246
|
+
async def get_query(self, slug, conditions: dict = None):
|
247
|
+
result: Any = []
|
248
|
+
if not conditions:
|
249
|
+
conditions = self.conditions
|
250
|
+
try:
|
251
|
+
qry = QS(slug=slug, conditions=conditions, loop=self._loop, lazy=True)
|
252
|
+
await qry.get_query()
|
253
|
+
except (DataNotFound, NoDataFound) as ex:
|
254
|
+
raise DataNotFound(f"{ex!s}") from ex
|
255
|
+
except Exception as err:
|
256
|
+
raise ComponentError(f"{err}") from err
|
257
|
+
try:
|
258
|
+
res, error = await qry.query()
|
259
|
+
if not res:
|
260
|
+
raise NoDataFound(f"{slug}: Data Not Found")
|
261
|
+
if error:
|
262
|
+
raise ComponentError(f"Error on Query: {error}")
|
263
|
+
result = result + [dict(row) for row in res]
|
264
|
+
return result
|
265
|
+
except (DataNotFound, NoDataFound) as err:
|
266
|
+
raise DataNotFound(f"{err!s}") from err
|
267
|
+
except Exception as err:
|
268
|
+
raise ComponentError(f"Error on Query: {err}") from err
|
269
|
+
finally:
|
270
|
+
try:
|
271
|
+
await qry.close()
|
272
|
+
except Exception as err:
|
273
|
+
self._logger.exception(err, stack_info=True)
|
@@ -0,0 +1,327 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from typing import Any
|
3
|
+
from collections.abc import Callable
|
4
|
+
import asyncio
|
5
|
+
from datetime import datetime
|
6
|
+
from pathlib import PosixPath
|
7
|
+
from tqdm import tqdm
|
8
|
+
import orjson
|
9
|
+
from datamodel.parsers.json import json_encoder
|
10
|
+
from asyncdb.drivers.postgres import postgres
|
11
|
+
from asyncdb.exceptions import ProviderError
|
12
|
+
from ..exceptions import ComponentError
|
13
|
+
from ..utils import SafeDict
|
14
|
+
from ..conf import default_dsn
|
15
|
+
from .flow import FlowComponent
|
16
|
+
|
17
|
+
|
18
|
+
class QueryToInsert(FlowComponent):
|
19
|
+
"""
|
20
|
+
QueryToInsert.
|
21
|
+
|
22
|
+
|
23
|
+
Overview
|
24
|
+
|
25
|
+
This component allows me to insert data into a database schema
|
26
|
+
|
27
|
+
.. table:: Properties
|
28
|
+
:widths: auto
|
29
|
+
|
30
|
+
|
31
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
32
|
+
| Name | Required | Summary |
|
33
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
34
|
+
| schema | Yes | Name of the schema where is to the table |
|
35
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
36
|
+
| tablename | Yes | Name of the table in the database |
|
37
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
38
|
+
| action | Yes | Sets the action to execute in this case an insert |
|
39
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
40
|
+
| pk | Yes | Primary key to the table in the database |
|
41
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
42
|
+
| directory | Yes | Source directory where the file is located |
|
43
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
44
|
+
| filter | Yes | This attribute allows me to apply a filter to the data |
|
45
|
+
+--------------+----------+-----------+-------------------------------------------------------+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
Return the list of arbitrary days
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
Example:
|
54
|
+
|
55
|
+
```yaml
|
56
|
+
QueryToInsert:
|
57
|
+
schema: public
|
58
|
+
tablename: queries
|
59
|
+
action: insert
|
60
|
+
pk:
|
61
|
+
- query_slug
|
62
|
+
directory: /home/ubuntu/symbits/
|
63
|
+
filter:
|
64
|
+
query_slug: walmart_stores
|
65
|
+
```
|
66
|
+
|
67
|
+
"""
|
68
|
+
"""
|
69
|
+
QueryToInsert
|
70
|
+
|
71
|
+
Overview
|
72
|
+
|
73
|
+
This component generates SQL INSERT or UPDATE statements from a query and saves them to a file.
|
74
|
+
|
75
|
+
.. table:: Properties
|
76
|
+
:widths: auto
|
77
|
+
|
78
|
+
|
79
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
80
|
+
| Name | Required | Summary |
|
81
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
82
|
+
| schema | Yes | The schema of the table to insert or update data in. |
|
83
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
84
|
+
| tablename | Yes | The name of the table to insert or update data in. |
|
85
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
86
|
+
| action | Yes | The action to perform, either "insert" or "update". |
|
87
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
88
|
+
| pk | Yes | The primary key(s) of the table. |
|
89
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
90
|
+
| filter | No | Filters to apply to the query. |
|
91
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
92
|
+
| directory | Yes | The directory to save the SQL file. |
|
93
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
94
|
+
| fields | No | Specific fields to select in the query. |
|
95
|
+
+------------------------+----------+-----------+-------------------------------------------------------+
|
96
|
+
|
97
|
+
Returns
|
98
|
+
|
99
|
+
This component returns True if the SQL file is successfully created, otherwise raises a ComponentError.
|
100
|
+
"""
|
101
|
+
where = ""
|
102
|
+
query = "SELECT {fields} FROM {schema}.{table} {where} order by ctid;"
|
103
|
+
insert = "INSERT INTO {}.{} ({}) VALUES({}) {};\n"
|
104
|
+
conflict = "ON CONFLICT ({}) DO UPDATE SET {}"
|
105
|
+
conflict_update = "{} = EXCLUDED.{}"
|
106
|
+
update = "UPDATE {}.{} SET {} {};\n"
|
107
|
+
|
108
|
+
def __init__(
|
109
|
+
self,
|
110
|
+
loop: asyncio.AbstractEventLoop = None,
|
111
|
+
job: Callable = None,
|
112
|
+
stat: Callable = None,
|
113
|
+
**kwargs,
|
114
|
+
):
|
115
|
+
"""Init Method."""
|
116
|
+
self.action: str = None
|
117
|
+
self.schema: str = ""
|
118
|
+
self.tablename: str = ""
|
119
|
+
self.pk: Any = None
|
120
|
+
self.filter: Any = None
|
121
|
+
self._connection: Callable = None
|
122
|
+
self._fields = kwargs.pop('fields', [])
|
123
|
+
super(QueryToInsert, self).__init__(
|
124
|
+
loop=loop,
|
125
|
+
job=job,
|
126
|
+
stat=stat,
|
127
|
+
**kwargs
|
128
|
+
)
|
129
|
+
|
130
|
+
def _escapeString(self, value):
|
131
|
+
v = value if value != "None" or value is not None else ""
|
132
|
+
v = str(v).replace("'", "''") if type(v) == str else v
|
133
|
+
v = "'{}'".format(v) if type(v) == str else v
|
134
|
+
v = "array{}".format(v) if type(v) == list else v
|
135
|
+
return v
|
136
|
+
|
137
|
+
async def start(self, **kwargs):
|
138
|
+
"""Start."""
|
139
|
+
if not hasattr(self, "schema"):
|
140
|
+
raise ComponentError("Schema not defined:")
|
141
|
+
if not hasattr(self, "tablename"):
|
142
|
+
raise ComponentError("Tablename not defined:")
|
143
|
+
if not hasattr(self, "action"):
|
144
|
+
raise ComponentError("Action not defined:")
|
145
|
+
if not hasattr(self, "pk"):
|
146
|
+
raise ComponentError("Primary keys not defined:")
|
147
|
+
else:
|
148
|
+
if isinstance(self.pk, str):
|
149
|
+
try:
|
150
|
+
self.pk = orjson.loads(self.pk)
|
151
|
+
except Exception:
|
152
|
+
self.pk = self.pk
|
153
|
+
if not hasattr(self, "directory"):
|
154
|
+
raise ComponentError("Directory not defined:")
|
155
|
+
if hasattr(self, "filter"):
|
156
|
+
filter = []
|
157
|
+
if isinstance(self.filter, str):
|
158
|
+
self.filter = orjson.loads(self.filter)
|
159
|
+
for key, value in self.filter.items():
|
160
|
+
filter.append(
|
161
|
+
"{} = {}".format(
|
162
|
+
key, ("'{}'".format(value) if type(value) == str else value)
|
163
|
+
)
|
164
|
+
)
|
165
|
+
if len(filter) > 0:
|
166
|
+
self.where = "WHERE {}".format(" AND ".join(filter))
|
167
|
+
today = datetime.today().strftime("%Y-%m-%d")
|
168
|
+
# Create directory if not exists
|
169
|
+
try:
|
170
|
+
PosixPath(self.directory).mkdir(parents=True, exist_ok=True)
|
171
|
+
except Exception as err:
|
172
|
+
self.logger.error(f"Error creating directory {self.directory}: {err}")
|
173
|
+
raise ComponentError(
|
174
|
+
f"Error creating directory {self.directory}: {err}"
|
175
|
+
) from err
|
176
|
+
self.filename = PosixPath(
|
177
|
+
self.directory,
|
178
|
+
"{}_{}_{}.{}.sql".format(today, self.action, self.schema, self.tablename),
|
179
|
+
)
|
180
|
+
if not self._fields:
|
181
|
+
fields = '*'
|
182
|
+
else:
|
183
|
+
fields = ', '.join(self._fields)
|
184
|
+
self.query = self.query.format_map(
|
185
|
+
SafeDict(fields=fields)
|
186
|
+
)
|
187
|
+
self.query = self.query.format(
|
188
|
+
schema=self.schema,
|
189
|
+
table=self.tablename,
|
190
|
+
where=self.where
|
191
|
+
)
|
192
|
+
self._logger.notice(
|
193
|
+
f"Query: {self.query}"
|
194
|
+
)
|
195
|
+
return True
|
196
|
+
|
197
|
+
async def get_connection(self):
|
198
|
+
try:
|
199
|
+
self._connection = postgres(dsn=default_dsn, loop=self._loop)
|
200
|
+
await self._connection.connection()
|
201
|
+
except Exception as err:
|
202
|
+
raise ProviderError(f"Error configuring pg Connection: {err!s}") from err
|
203
|
+
return self._connection
|
204
|
+
|
205
|
+
async def run(self):
|
206
|
+
"""Async Method."""
|
207
|
+
# get connection
|
208
|
+
self._connection = await self.get_connection()
|
209
|
+
res, err = await self._connection.query(self.query)
|
210
|
+
if err:
|
211
|
+
raise ComponentError(
|
212
|
+
'Query Error "{}": {}'.format(self.query, err)
|
213
|
+
)
|
214
|
+
colinfo = self.column_info("{}.{}".format(self.schema, self.tablename))
|
215
|
+
if res:
|
216
|
+
query = []
|
217
|
+
if self.action == "insert":
|
218
|
+
total = len(res)
|
219
|
+
with tqdm(total=total) as pbar:
|
220
|
+
for row in res:
|
221
|
+
columns_update = row.keys()
|
222
|
+
columns = row.keys()
|
223
|
+
values = []
|
224
|
+
for col in row.keys():
|
225
|
+
values.append(self.get_values(col, colinfo[col], row[col]))
|
226
|
+
update = ", ".join(
|
227
|
+
[self.conflict_update.format(c, c) for c in columns_update]
|
228
|
+
)
|
229
|
+
conflict = self.conflict.format(",\n".join(self.pk), update)
|
230
|
+
query.append(
|
231
|
+
self.insert.format(
|
232
|
+
self.schema,
|
233
|
+
self.tablename,
|
234
|
+
", ".join(columns),
|
235
|
+
", ".join(values),
|
236
|
+
conflict,
|
237
|
+
)
|
238
|
+
)
|
239
|
+
pbar.update(1)
|
240
|
+
elif self.action == "update":
|
241
|
+
for row in res:
|
242
|
+
columns = row.keys()
|
243
|
+
values = []
|
244
|
+
for col in row.keys():
|
245
|
+
values.append(self.get_values(col, colinfo[col], row[col]))
|
246
|
+
vals = dict(zip(list(columns), values))
|
247
|
+
pkwhere = "AND".join(["{}={}".format(v, vals[v]) for v in self.pk])
|
248
|
+
where = (
|
249
|
+
"{} AND {}".format(self.where, pkwhere)
|
250
|
+
if self.where != ""
|
251
|
+
else "WHERE {}".format(pkwhere)
|
252
|
+
)
|
253
|
+
update_list = ["{}={}".format(k, v) for k, v in vals.items()]
|
254
|
+
query.append(
|
255
|
+
self.update.format(
|
256
|
+
self.schema, self.tablename, ", ".join(update_list), where
|
257
|
+
)
|
258
|
+
)
|
259
|
+
|
260
|
+
f = open(self.filename, "w")
|
261
|
+
f.write("".join(query))
|
262
|
+
f.close()
|
263
|
+
self._result = "".join(query)
|
264
|
+
self.add_metric('NUM_ROWS', len(query))
|
265
|
+
self.add_metric('QUERY', self.query)
|
266
|
+
# return self._result
|
267
|
+
return True
|
268
|
+
else:
|
269
|
+
raise ComponentError(
|
270
|
+
"Error creating Query File: Empty Result."
|
271
|
+
)
|
272
|
+
|
273
|
+
def close(self):
|
274
|
+
"""Method."""
|
275
|
+
pass
|
276
|
+
|
277
|
+
def column_info(self, table):
|
278
|
+
result = None
|
279
|
+
sql = f"""
|
280
|
+
SELECT attname AS column_name, atttypid::regtype AS data_type, attnotnull::boolean as notnull
|
281
|
+
FROM pg_attribute WHERE attrelid = '{table}'::regclass AND attnum > 0 AND NOT attisdropped
|
282
|
+
ORDER BY attnum"""
|
283
|
+
db = postgres(dsn=default_dsn)
|
284
|
+
conn = db.connect()
|
285
|
+
result, error = conn.fetchall(sql)
|
286
|
+
if error:
|
287
|
+
raise ComponentError(f"Error executing query: {error}")
|
288
|
+
if result:
|
289
|
+
return {item["column_name"]: item["data_type"] for item in result}
|
290
|
+
else:
|
291
|
+
print(f"The table {self.schema}.{self.tablename} does not exist")
|
292
|
+
return None
|
293
|
+
|
294
|
+
def get_values(self, column, type, value):
|
295
|
+
if value is None or value == "None":
|
296
|
+
return "NULL"
|
297
|
+
elif type in [
|
298
|
+
"text",
|
299
|
+
"character varying",
|
300
|
+
"timestamp with time zone",
|
301
|
+
"timestamp without time zone",
|
302
|
+
"date",
|
303
|
+
"uuid",
|
304
|
+
"inet",
|
305
|
+
]:
|
306
|
+
return "'{}'".format(str(value).replace("'", "''"))
|
307
|
+
elif type in ["hstore"]:
|
308
|
+
return "'{}'".format(
|
309
|
+
", ".join(
|
310
|
+
[
|
311
|
+
'"{}"=>"{}"'.format(k, v).replace("'", "''")
|
312
|
+
for k, v in value.items()
|
313
|
+
]
|
314
|
+
)
|
315
|
+
)
|
316
|
+
elif type in ["jsonb"]:
|
317
|
+
return "'{}'".format(json_encoder(value).replace("'", "''"))
|
318
|
+
elif type in ["character varying[]"]:
|
319
|
+
return "'{{{}}}'".format(
|
320
|
+
", ".join(['"{}"'.format(v) for v in value]).replace("'", "''")
|
321
|
+
)
|
322
|
+
elif type in ["integer[]"]:
|
323
|
+
return "'{{{}}}'".format(
|
324
|
+
", ".join([str(v) for v in value]).replace("'", "''")
|
325
|
+
)
|
326
|
+
else:
|
327
|
+
return str(value)
|