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,207 @@
|
|
1
|
+
import asyncio
|
2
|
+
from collections.abc import Callable
|
3
|
+
import math
|
4
|
+
import pandas as pd
|
5
|
+
from asyncdb import AsyncDB
|
6
|
+
from asyncdb.exceptions import (
|
7
|
+
StatementError,
|
8
|
+
DataError
|
9
|
+
)
|
10
|
+
from .CopyTo import CopyTo
|
11
|
+
from ..interfaces.dataframes import PandasDataframe
|
12
|
+
from ..exceptions import (
|
13
|
+
ComponentError,
|
14
|
+
DataNotFound
|
15
|
+
)
|
16
|
+
from ..conf import (
|
17
|
+
RT_DRIVER,
|
18
|
+
RT_HOST,
|
19
|
+
RT_PORT,
|
20
|
+
RT_USER,
|
21
|
+
RT_PASSWORD,
|
22
|
+
RT_DATABASE
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
class CopyToRethink(CopyTo, PandasDataframe):
|
27
|
+
"""
|
28
|
+
CopyToRethink.
|
29
|
+
|
30
|
+
Overview
|
31
|
+
|
32
|
+
This component allows copy data into a RethinkDB table,
|
33
|
+
Copy into main rethinkdb using write functionality.
|
34
|
+
|
35
|
+
.. table:: Properties
|
36
|
+
:widths: auto
|
37
|
+
|
38
|
+
|
39
|
+
+--------------+----------+-----------+--------------------------------------------+
|
40
|
+
| Name | Required | Summary |
|
41
|
+
+--------------+----------+-----------+--------------------------------------------+
|
42
|
+
| tablename | Yes | Name of the table in |
|
43
|
+
| | | the database |
|
44
|
+
+--------------+----------+-----------+--------------------------------------------+
|
45
|
+
| schema | Yes | Name of the schema |
|
46
|
+
| | | where is to the table, alias: database |
|
47
|
+
+--------------+----------+-----------+--------------------------------------------+
|
48
|
+
| truncate | Yes | This option indicates if the component should empty |
|
49
|
+
| | | before coping the new data to the table. If set to true|
|
50
|
+
| | | the table will be truncated before saving the new data.|
|
51
|
+
+--------------+----------+-----------+--------------------------------------------+
|
52
|
+
| use_buffer | No | When activated, this option allows optimizing the |
|
53
|
+
| | | performance of the task, when dealing with large |
|
54
|
+
| | | volumes of data. |
|
55
|
+
+--------------+----------+-----------+--------------------------------------------+
|
56
|
+
| credentials | No | Supporting manual rethinkdb credentials |
|
57
|
+
| | | |
|
58
|
+
+--------------+----------+-----------+--------------------------------------------+
|
59
|
+
| datasource | No | Using a Datasource instead manual credentials |
|
60
|
+
| | | |
|
61
|
+
+--------------+----------+-----------+--------------------------------------------+
|
62
|
+
|
63
|
+
|
64
|
+
Example:
|
65
|
+
|
66
|
+
```yaml
|
67
|
+
CopyToRethink:
|
68
|
+
tablename: product_availability
|
69
|
+
schema: bose
|
70
|
+
```
|
71
|
+
|
72
|
+
"""
|
73
|
+
def __init__(
|
74
|
+
self,
|
75
|
+
loop: asyncio.AbstractEventLoop = None,
|
76
|
+
job: Callable = None,
|
77
|
+
stat: Callable = None,
|
78
|
+
**kwargs,
|
79
|
+
):
|
80
|
+
self.pk = []
|
81
|
+
self.truncate: bool = False
|
82
|
+
self.data = None
|
83
|
+
self._engine = None
|
84
|
+
self.tablename: str = ""
|
85
|
+
self.schema: str = ""
|
86
|
+
self.use_chunks = False
|
87
|
+
self._chunksize: int = kwargs.pop('chunksize', 10)
|
88
|
+
self._connection: Callable = None
|
89
|
+
try:
|
90
|
+
self.multi = bool(kwargs["multi"])
|
91
|
+
del kwargs["multi"]
|
92
|
+
except KeyError:
|
93
|
+
self.multi = False
|
94
|
+
super().__init__(
|
95
|
+
loop=loop,
|
96
|
+
job=job,
|
97
|
+
stat=stat,
|
98
|
+
**kwargs
|
99
|
+
)
|
100
|
+
self._driver: str = RT_DRIVER
|
101
|
+
|
102
|
+
def default_connection(self):
|
103
|
+
"""default_connection.
|
104
|
+
|
105
|
+
Default Connection to RethinkDB.
|
106
|
+
"""
|
107
|
+
try:
|
108
|
+
kwargs: dict = {
|
109
|
+
"host": RT_HOST,
|
110
|
+
"port": int(RT_PORT),
|
111
|
+
"db": RT_DATABASE,
|
112
|
+
"user": RT_USER,
|
113
|
+
"password": RT_PASSWORD
|
114
|
+
}
|
115
|
+
self._connection = AsyncDB(
|
116
|
+
RT_DRIVER,
|
117
|
+
params=kwargs,
|
118
|
+
loop=self._loop,
|
119
|
+
**kwargs
|
120
|
+
)
|
121
|
+
return self._connection
|
122
|
+
except Exception as err:
|
123
|
+
raise ComponentError(
|
124
|
+
f"Error configuring Pg Connection: {err!s}"
|
125
|
+
) from err
|
126
|
+
|
127
|
+
# Function to clean invalid float values
|
128
|
+
def clean_floats(self, data):
|
129
|
+
def sanitize_value(value):
|
130
|
+
if isinstance(value, float) and (math.isnan(value) or math.isinf(value)):
|
131
|
+
return None # Replace invalid floats with None or other default
|
132
|
+
return value
|
133
|
+
|
134
|
+
if isinstance(data, dict):
|
135
|
+
return {k: sanitize_value(v) for k, v in data.items()}
|
136
|
+
elif isinstance(data, list):
|
137
|
+
return [self.clean_floats(item) for item in data]
|
138
|
+
return data
|
139
|
+
|
140
|
+
async def _create_table(self):
|
141
|
+
# Create a Table using Model
|
142
|
+
async with await self._connection.connection() as conn:
|
143
|
+
await conn.use(self.schema)
|
144
|
+
await self._connection.create_table(
|
145
|
+
table=self.tablename
|
146
|
+
)
|
147
|
+
|
148
|
+
async def _truncate_table(self):
|
149
|
+
async with await self._connection.connection() as conn:
|
150
|
+
await conn.use(self.schema)
|
151
|
+
await self._connection.delete(
|
152
|
+
table=self.tablename
|
153
|
+
)
|
154
|
+
|
155
|
+
async def _copy_dataframe(self):
|
156
|
+
# saving directly the dataframe with write
|
157
|
+
try:
|
158
|
+
# can remove NAT from str fields:
|
159
|
+
u = self.data.select_dtypes(include=["string"])
|
160
|
+
if not u.empty:
|
161
|
+
self.data[u.columns] = u.astype(object).where(
|
162
|
+
pd.notnull(u), None
|
163
|
+
)
|
164
|
+
# Convert the DataFrame to list of dictionaries and clean it
|
165
|
+
data_records = self.data.to_dict(orient='records')
|
166
|
+
cleaned_data = self.clean_floats(data_records)
|
167
|
+
async with await self._connection.connection() as conn:
|
168
|
+
await conn.use(self.schema)
|
169
|
+
await self._connection.write(
|
170
|
+
table=self.tablename,
|
171
|
+
data=cleaned_data,
|
172
|
+
batch_size=self._chunksize,
|
173
|
+
on_conflict="replace",
|
174
|
+
changes=True,
|
175
|
+
durability="soft",
|
176
|
+
)
|
177
|
+
except StatementError as err:
|
178
|
+
raise ComponentError(
|
179
|
+
f"Statement error: {err}"
|
180
|
+
) from err
|
181
|
+
except DataError as err:
|
182
|
+
raise ComponentError(
|
183
|
+
f"Data error: {err}"
|
184
|
+
) from err
|
185
|
+
except Exception as err:
|
186
|
+
raise ComponentError(
|
187
|
+
f"{self.StepName} Error: {err!s}"
|
188
|
+
) from err
|
189
|
+
|
190
|
+
async def _copy_iterable(self):
|
191
|
+
"""Copy an iterable to RethinkDB."""
|
192
|
+
try:
|
193
|
+
async with await self._connection.connection() as conn:
|
194
|
+
await conn.use(self.schema)
|
195
|
+
await conn.write(
|
196
|
+
data=self.data,
|
197
|
+
table=self.tablename,
|
198
|
+
database=self.schema,
|
199
|
+
batch_size=self._chunksize,
|
200
|
+
on_conflict="replace",
|
201
|
+
changes=True,
|
202
|
+
durability="soft",
|
203
|
+
)
|
204
|
+
except Exception as err:
|
205
|
+
raise ComponentError(
|
206
|
+
f"Error copying iterable to RethinkDB: {err}"
|
207
|
+
) from err
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import asyncio
|
2
|
+
from typing import Callable, Tuple
|
3
|
+
from asyncdb import AsyncDB
|
4
|
+
from querysource.datasources.drivers.bigquery import bigquery_default
|
5
|
+
from .flow import FlowComponent
|
6
|
+
from ..exceptions import ComponentError
|
7
|
+
|
8
|
+
|
9
|
+
class CreateGCSBucket(FlowComponent):
|
10
|
+
"""
|
11
|
+
CreateGCSBucket.
|
12
|
+
|
13
|
+
Este componente crea un bucket en Google Cloud Storage (GCS).
|
14
|
+
|
15
|
+
Properties:
|
16
|
+
|
17
|
+
- bucket_name: Nombre único para el bucket de GCS. (Requerido)
|
18
|
+
- location: Región geográfica donde se creará el bucket. (Opcional, por defecto 'US')
|
19
|
+
- storage_class: Clase de almacenamiento del bucket. (Opcional, por defecto 'STANDARD')
|
20
|
+
- overwrite: Permite proceder si el bucket ya existe. (Opcional, por defecto 'False')
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
loop: asyncio.AbstractEventLoop = None,
|
26
|
+
job: Callable = None,
|
27
|
+
stat: Callable = None,
|
28
|
+
**kwargs,
|
29
|
+
):
|
30
|
+
self.bucket_name: str = kwargs.pop('bucket_name')
|
31
|
+
self.location: str = kwargs.pop('location', 'US')
|
32
|
+
self.storage_class: str = kwargs.pop('storage_class', 'STANDARD')
|
33
|
+
self.overwrite: bool = kwargs.pop('overwrite', False)
|
34
|
+
self.bq = None # Instancia de AsyncDB
|
35
|
+
super(CreateGCSBucket, self).__init__(loop=loop, job=job, stat=stat, **kwargs)
|
36
|
+
|
37
|
+
async def start(self, **kwargs):
|
38
|
+
"""Inicializa el componente configurando la conexión AsyncDB."""
|
39
|
+
# Validar parámetro requerido
|
40
|
+
if not self.bucket_name:
|
41
|
+
raise ComponentError("CreateGCSBucket: 'bucket_name' es un parámetro requerido.")
|
42
|
+
|
43
|
+
if not bigquery_default:
|
44
|
+
raise ComponentError("CreateGCSBucket: 'bigquery_default' no está configurado correctamente.")
|
45
|
+
|
46
|
+
# Obtener credenciales y parámetros del driver
|
47
|
+
credentials = bigquery_default.get_credentials()
|
48
|
+
|
49
|
+
# Inicializar AsyncDB con el driver de BigQuery
|
50
|
+
try:
|
51
|
+
self.bq = AsyncDB("bigquery", params=credentials)
|
52
|
+
self._logger.info("CreateGCSBucket: Instancia de AsyncDB creada exitosamente.")
|
53
|
+
except Exception as e:
|
54
|
+
raise ComponentError(f"CreateGCSBucket: Error al inicializar AsyncDB: {e}") from e
|
55
|
+
|
56
|
+
async def run(self) -> Tuple[str, str]:
|
57
|
+
"""Ejecuta la creación del bucket en GCS."""
|
58
|
+
if not self.bq:
|
59
|
+
raise ComponentError("CreateGCSBucket: AsyncDB no está inicializado. Asegúrate de ejecutar 'start' antes de 'run'.")
|
60
|
+
|
61
|
+
try:
|
62
|
+
async with await self.bq.connection() as conn:
|
63
|
+
# Verificar si el bucket existe
|
64
|
+
bucket_exists = await conn.bucket_exists(self.bucket_name)
|
65
|
+
if not bucket_exists:
|
66
|
+
# Crear el bucket
|
67
|
+
await conn.create_bucket(
|
68
|
+
bucket_name=self.bucket_name,
|
69
|
+
location=self.location,
|
70
|
+
storage_class=self.storage_class
|
71
|
+
)
|
72
|
+
message = f"Bucket '{self.bucket_name}' creado exitosamente en la región '{self.location}' con clase de almacenamiento '{self.storage_class}'."
|
73
|
+
self._logger.info(message)
|
74
|
+
else:
|
75
|
+
if self.overwrite:
|
76
|
+
message = f"CreateGCSBucket: El bucket '{self.bucket_name}' ya existe."
|
77
|
+
self._logger.warning(message)
|
78
|
+
else:
|
79
|
+
raise ComponentError(f"CreateGCSBucket: El bucket '{self.bucket_name}' ya existe y 'overwrite' está establecido en False.")
|
80
|
+
|
81
|
+
# Generar bucket_uri
|
82
|
+
bucket_uri = f"gs://{self.bucket_name}"
|
83
|
+
|
84
|
+
# Guardar bucket_name y bucket_uri en las variables de la tarea
|
85
|
+
self.setTaskVar("bucket_name", self.bucket_name)
|
86
|
+
self.setTaskVar("bucket_uri", bucket_uri)
|
87
|
+
|
88
|
+
return bucket_uri, message
|
89
|
+
|
90
|
+
except ComponentError as ce:
|
91
|
+
raise ce # Re-lanzar errores específicos de componentes
|
92
|
+
except Exception as e:
|
93
|
+
raise ComponentError(f"CreateGCSBucket: Error durante la creación del bucket: {e}") from e
|
94
|
+
|
95
|
+
async def close(self):
|
96
|
+
"""Cierra la conexión AsyncDB."""
|
97
|
+
try:
|
98
|
+
if self.bq:
|
99
|
+
await self.bq.close()
|
100
|
+
self._logger.info("CreateGCSBucket: AsyncDB cerrado exitosamente.")
|
101
|
+
except Exception as e:
|
102
|
+
self._logger.error(f"CreateGCSBucket: Error al cerrar AsyncDB: {e}")
|
@@ -0,0 +1,228 @@
|
|
1
|
+
"""
|
2
|
+
CreateReport.
|
3
|
+
|
4
|
+
Using a Jinja2 Template to crete a Report and, optionally, send via Email.
|
5
|
+
|
6
|
+
|
7
|
+
Example:
|
8
|
+
|
9
|
+
```yaml
|
10
|
+
CreateReport:
|
11
|
+
template_file: echelon_program_overview_raw.html
|
12
|
+
create_pdf: true
|
13
|
+
masks:
|
14
|
+
'{today}':
|
15
|
+
- today
|
16
|
+
- mask: '%m/%d/%Y'
|
17
|
+
'{created}':
|
18
|
+
- today
|
19
|
+
- mask: '%Y-%m-%d %H:%M:%s'
|
20
|
+
'{firstdate}':
|
21
|
+
- date_diff
|
22
|
+
- value: today
|
23
|
+
diff: 8
|
24
|
+
mode: days
|
25
|
+
mask: '%b %d, %Y'
|
26
|
+
'{lastdate}':
|
27
|
+
- yesterday
|
28
|
+
- mask: '%b %d, %Y'
|
29
|
+
send:
|
30
|
+
via: email
|
31
|
+
list: echelon_program_overview
|
32
|
+
message:
|
33
|
+
subject: 'Echelon Kohl''s VIBA Report for: ({today})'
|
34
|
+
arguments:
|
35
|
+
today_report: '{today}'
|
36
|
+
generated_at: '{created}'
|
37
|
+
firstdate: '{firstdate}'
|
38
|
+
lastdate: '{lastdate}'
|
39
|
+
```
|
40
|
+
|
41
|
+
"""
|
42
|
+
import asyncio
|
43
|
+
import datetime
|
44
|
+
from typing import Dict, List, Callable
|
45
|
+
from pathlib import Path
|
46
|
+
from navconfig.logging import logging
|
47
|
+
from notify import Notify
|
48
|
+
from notify.models import Actor
|
49
|
+
from ..flow import FlowComponent
|
50
|
+
from ...template import getTemplateHandler
|
51
|
+
from ...exceptions import ComponentError, FileNotFound
|
52
|
+
from ...conf import BASE_DIR, TASK_PATH
|
53
|
+
from .charts import loadChart
|
54
|
+
from ..support import DBSupport
|
55
|
+
|
56
|
+
|
57
|
+
class CreateReport(FlowComponent, DBSupport):
|
58
|
+
"""
|
59
|
+
CreateReport
|
60
|
+
|
61
|
+
Overview
|
62
|
+
|
63
|
+
The CreateReport class is a component for creating rich reports and sending them via the Notify service. It uses
|
64
|
+
template handling and chart creation to generate the content of the reports and sends them to a list of recipients.
|
65
|
+
|
66
|
+
.. table:: Properties
|
67
|
+
:widths: auto
|
68
|
+
|
69
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
70
|
+
| Name | Required | Description |
|
71
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
72
|
+
| _data | Yes | A dictionary containing the input data for the report. |
|
73
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
74
|
+
| _parser | Yes | The template parser for generating the report content. |
|
75
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
76
|
+
| _chartparser | Yes | The template parser for generating charts. |
|
77
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
78
|
+
| template_file | Yes | The file name of the template to use for the report. |
|
79
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
80
|
+
| recipients | Yes | A list of recipients to send the report to. |
|
81
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
82
|
+
| send | Yes | A dictionary containing the sending options and configurations. |
|
83
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
84
|
+
| message | No | The message content for the report. |
|
85
|
+
+------------------+----------+-----------+--------------------------------------------------------------------------------------+
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
The input data after sending the report.
|
89
|
+
|
90
|
+
"""
|
91
|
+
|
92
|
+
def __init__(
|
93
|
+
self,
|
94
|
+
loop: asyncio.AbstractEventLoop = None,
|
95
|
+
job: Callable = None,
|
96
|
+
stat: Callable = None,
|
97
|
+
**kwargs,
|
98
|
+
):
|
99
|
+
"""Init Method."""
|
100
|
+
self._data: Dict = {}
|
101
|
+
self._parser: Callable = None
|
102
|
+
self._chartparser: Callable = None
|
103
|
+
self.template_file: str = None
|
104
|
+
self.recipients = []
|
105
|
+
self.send: Dict = None
|
106
|
+
self.message: str = None
|
107
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
108
|
+
|
109
|
+
async def start(self, **kwargs):
|
110
|
+
template_dir = Path(TASK_PATH, self._program, "templates")
|
111
|
+
echart_dir = Path(BASE_DIR, "DataIntegration", "dataintegration", "templates")
|
112
|
+
if self.previous:
|
113
|
+
self._data = self.input
|
114
|
+
try:
|
115
|
+
self._parser = getTemplateHandler(newdir=template_dir)
|
116
|
+
self._chartparser = getTemplateHandler(newdir=echart_dir)
|
117
|
+
except Exception as err:
|
118
|
+
logging.exception(err)
|
119
|
+
raise ComponentError(
|
120
|
+
f"CreateReport: Unable to load Template Parser: {err}"
|
121
|
+
) from err
|
122
|
+
template_file = template_dir.joinpath(self.template_file)
|
123
|
+
if not template_file.is_file():
|
124
|
+
raise FileNotFound(f"CreateReport: Missing Template File: {template_file}")
|
125
|
+
if hasattr(self, "masks"):
|
126
|
+
for key, value in self.send.items():
|
127
|
+
self.send[key] = self.mask_replacement(value)
|
128
|
+
# getting the mailing list:
|
129
|
+
try:
|
130
|
+
lst = self.send["list"]
|
131
|
+
except AttributeError:
|
132
|
+
lst = "owner"
|
133
|
+
sql = f"SELECT * FROM troc.get_mailing_list('{lst!s}')"
|
134
|
+
db = self.get_connection()
|
135
|
+
async with await db.connection() as conn:
|
136
|
+
try:
|
137
|
+
result, error = await conn.query(sql)
|
138
|
+
if error:
|
139
|
+
raise ComponentError(
|
140
|
+
f"CreateReport: Error on Recipients: {error!s}."
|
141
|
+
)
|
142
|
+
for r in result:
|
143
|
+
actor = Actor(**dict(r))
|
144
|
+
self.recipients.append(actor)
|
145
|
+
except Exception as err:
|
146
|
+
logging.exception(err)
|
147
|
+
if not self.recipients:
|
148
|
+
raise ComponentError("CreateReport: Invalid Number of Recipients.")
|
149
|
+
|
150
|
+
def status_sent(self, recipient, message, result, *args, **kwargs):
|
151
|
+
print(f"Notification status {result!s} to {recipient.account!s}")
|
152
|
+
# logger:
|
153
|
+
logging.info(f"Notification status {result!s} to {recipient.account!s}")
|
154
|
+
# TODO: register sent on a logger database
|
155
|
+
|
156
|
+
async def send_message(self, recipients: List, data: Dict = None):
|
157
|
+
try:
|
158
|
+
emailService = Notify(self.send["via"], loop=self._loop)
|
159
|
+
except Exception as err:
|
160
|
+
raise ComponentError(f"Error loading Notify Service: {err!s}") from err
|
161
|
+
# add event for sent function
|
162
|
+
emailService.sent = self.status_sent
|
163
|
+
self.message = self.send["message"]
|
164
|
+
# create echart graph based on template
|
165
|
+
graph = {}
|
166
|
+
if hasattr(self, "echarts"):
|
167
|
+
for dataset, chart in self.echarts["charts"].items():
|
168
|
+
ds = self._data[dataset]
|
169
|
+
graph_name = chart["name"]
|
170
|
+
try:
|
171
|
+
graphtype = chart["type"]
|
172
|
+
del chart["type"]
|
173
|
+
except KeyError:
|
174
|
+
graphtype = "bar"
|
175
|
+
try:
|
176
|
+
title = chart["title"]
|
177
|
+
del chart["title"]
|
178
|
+
except KeyError:
|
179
|
+
title = dataset
|
180
|
+
chart = loadChart(graphtype, data=ds, title=title, **chart)
|
181
|
+
img = chart.image()
|
182
|
+
graph[graph_name] = img
|
183
|
+
# else:
|
184
|
+
emailTemplate = self._parser.get_template(self.template_file)
|
185
|
+
for user in recipients:
|
186
|
+
# TODO: add dot notation to Actor Model
|
187
|
+
email = user.account["address"]
|
188
|
+
args = {
|
189
|
+
"user": user,
|
190
|
+
"html": emailTemplate,
|
191
|
+
"email": email,
|
192
|
+
"created_at": datetime.datetime.now(),
|
193
|
+
**graph,
|
194
|
+
}
|
195
|
+
self.message["created_at"] = datetime.datetime.now()
|
196
|
+
other = {}
|
197
|
+
if "arguments" in self.send:
|
198
|
+
other = self.send["arguments"]
|
199
|
+
args = {**data, **args, **other}
|
200
|
+
content = emailTemplate.render(args)
|
201
|
+
try:
|
202
|
+
logging.debug(f"Sending Email to {user} via {email}")
|
203
|
+
# TODO: Send email via Worker or thread (async)
|
204
|
+
await emailService.send(
|
205
|
+
recipient=user, template=None, message=content, **self.message
|
206
|
+
)
|
207
|
+
except Exception as err:
|
208
|
+
logging.exception(err)
|
209
|
+
raise ComponentError(f"Error Sending Email: {err}") from err
|
210
|
+
finally:
|
211
|
+
try:
|
212
|
+
await emailService.close()
|
213
|
+
except Exception as err:
|
214
|
+
logging.error(err)
|
215
|
+
|
216
|
+
def close(self):
|
217
|
+
pass
|
218
|
+
|
219
|
+
async def run(self):
|
220
|
+
try:
|
221
|
+
await self.send_message(self.recipients, self._data)
|
222
|
+
self._result = self.data
|
223
|
+
return self._result
|
224
|
+
except Exception as err:
|
225
|
+
logging.exception(err)
|
226
|
+
raise ComponentError(
|
227
|
+
f"CreateReport: Error sending Report: {err!s}"
|
228
|
+
) from err
|
@@ -0,0 +1,15 @@
|
|
1
|
+
"""Chart utilities using PyEcharts."""
|
2
|
+
from typing import Any
|
3
|
+
from .pie import pieChart
|
4
|
+
from .bar import barChart
|
5
|
+
|
6
|
+
__all__ = ["pieChart", "barChart"]
|
7
|
+
|
8
|
+
|
9
|
+
def loadChart(charttype: str, title: str, data: Any, **kwargs):
|
10
|
+
if charttype == "bar":
|
11
|
+
return barChart(title, data, **kwargs)
|
12
|
+
elif charttype == "pie":
|
13
|
+
return pieChart(title, data, **kwargs)
|
14
|
+
else:
|
15
|
+
raise RuntimeError("CreateReport: Invalid Chart Type")
|
@@ -0,0 +1,51 @@
|
|
1
|
+
from typing import Any, Union, List, Dict
|
2
|
+
import orjson
|
3
|
+
from pyecharts.charts import Bar
|
4
|
+
from borax.datasets.fetch import fetch
|
5
|
+
import pyecharts.options as opts
|
6
|
+
from .base import baseChart
|
7
|
+
|
8
|
+
|
9
|
+
class barChart(baseChart):
|
10
|
+
rosetype: str = "radius"
|
11
|
+
formatter: str = "{c}"
|
12
|
+
axis_type: str = "category"
|
13
|
+
|
14
|
+
def __init__(self, title: str, data: Union[List, Dict], **kwargs):
|
15
|
+
fields = kwargs["fields"]
|
16
|
+
self._x = fields["x_axis"]
|
17
|
+
self._y = fields["y_axis"]
|
18
|
+
super(barChart, self).__init__(title, data, **kwargs)
|
19
|
+
|
20
|
+
def get_chart(self):
|
21
|
+
return Bar(init_opts=self.init_opts)
|
22
|
+
|
23
|
+
def set_chart(self, data: Any, title: str, **kwargs):
|
24
|
+
# x, y = Base.cast(o_data)
|
25
|
+
dst = self._encoder(data)
|
26
|
+
dst = orjson.loads(dst)
|
27
|
+
names = fetch(dst, self._x)
|
28
|
+
self.chart.add_xaxis(names)
|
29
|
+
for y in self._y:
|
30
|
+
values = fetch(dst, y)
|
31
|
+
self.chart.add_yaxis(y, values)
|
32
|
+
self.chart.set_global_opts(
|
33
|
+
**self.get_global_opts(title, **kwargs)
|
34
|
+
).set_series_opts(**self.get_series_opts(**kwargs))
|
35
|
+
|
36
|
+
def get_basic_args(self, **kwargs):
|
37
|
+
return {}
|
38
|
+
|
39
|
+
def get_series_opts(self, **kwargs):
|
40
|
+
return {
|
41
|
+
"label_opts": opts.LabelOpts(
|
42
|
+
position="outside", formatter=self.formatter, is_show=True
|
43
|
+
)
|
44
|
+
}
|
45
|
+
|
46
|
+
def get_global_opts(self, title: str, **kwargs):
|
47
|
+
return {
|
48
|
+
"title_opts": opts.TitleOpts(title=title),
|
49
|
+
"legend_opts": opts.LegendOpts(is_show=True, pos_right="15%"),
|
50
|
+
"xaxis_opts": opts.AxisOpts(type_=self.axis_type),
|
51
|
+
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
"""Base Class for all Chart types."""
|
2
|
+
from abc import (
|
3
|
+
ABC,
|
4
|
+
abstractmethod,
|
5
|
+
)
|
6
|
+
from typing import Any, Union, List, Dict
|
7
|
+
from pyecharts.globals import ThemeType
|
8
|
+
import pyecharts.options as opts
|
9
|
+
from pyecharts.render import make_snapshot
|
10
|
+
from snapshot_selenium import snapshot as driver
|
11
|
+
from asyncdb.utils.encoders import DefaultEncoder
|
12
|
+
|
13
|
+
|
14
|
+
class baseChart(ABC):
|
15
|
+
theme: str = "LIGHT"
|
16
|
+
|
17
|
+
def __init__(self, title: str, data: Union[List, Dict], **kwargs):
|
18
|
+
self.formatter = None
|
19
|
+
if "theme" in kwargs:
|
20
|
+
self.theme = kwargs["theme"]
|
21
|
+
del kwargs["theme"]
|
22
|
+
try:
|
23
|
+
self._fields = kwargs["fields"]
|
24
|
+
except KeyError:
|
25
|
+
self._fields = ["name", "value"]
|
26
|
+
self._encoder = DefaultEncoder(sort_keys=False)
|
27
|
+
self._theme = getattr(ThemeType, self.theme)
|
28
|
+
# TODO: making more initializations
|
29
|
+
self.init_opts = opts.InitOpts(theme=self._theme, width="620px", height="420px")
|
30
|
+
self.chart = self.get_chart()
|
31
|
+
self.args = self.get_basic_args(**kwargs)
|
32
|
+
self.set_chart(data, title, **kwargs)
|
33
|
+
|
34
|
+
@abstractmethod
|
35
|
+
def get_chart(self):
|
36
|
+
pass
|
37
|
+
|
38
|
+
@abstractmethod
|
39
|
+
def set_chart(self, data: Any, title: str, **kwargs):
|
40
|
+
pass
|
41
|
+
|
42
|
+
@abstractmethod
|
43
|
+
def get_basic_args(self, **kwargs):
|
44
|
+
pass
|
45
|
+
|
46
|
+
@abstractmethod
|
47
|
+
def get_global_opts(self, title: str, **kwargs):
|
48
|
+
pass
|
49
|
+
|
50
|
+
def get_series_opts(self, **kwargs):
|
51
|
+
return {
|
52
|
+
"label_opts": opts.LabelOpts(
|
53
|
+
position="outside", formatter=self.formatter, is_show=True
|
54
|
+
)
|
55
|
+
}
|
56
|
+
|
57
|
+
def chart(self):
|
58
|
+
return self.chart
|
59
|
+
|
60
|
+
def image(self):
|
61
|
+
"""Returns the Base64 version of Image."""
|
62
|
+
img = None
|
63
|
+
make_snapshot(driver, self.chart.render(), "graph.base64")
|
64
|
+
with open("graph.base64", "r", encoding="utf-8") as f:
|
65
|
+
img = f.read()
|
66
|
+
return img
|