flowtask 5.8.4__cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flowtask/__init__.py +93 -0
- flowtask/__main__.py +38 -0
- flowtask/bots/__init__.py +6 -0
- flowtask/bots/check.py +93 -0
- flowtask/bots/codebot.py +51 -0
- flowtask/components/ASPX.py +148 -0
- flowtask/components/AddDataset.py +352 -0
- flowtask/components/Amazon.py +523 -0
- flowtask/components/AutoTask.py +314 -0
- flowtask/components/Azure.py +80 -0
- flowtask/components/AzureUsers.py +106 -0
- flowtask/components/BaseAction.py +91 -0
- flowtask/components/BaseLoop.py +198 -0
- flowtask/components/BestBuy.py +800 -0
- flowtask/components/CSVToGCS.py +120 -0
- flowtask/components/CompanyScraper/__init__.py +1 -0
- flowtask/components/CompanyScraper/parsers/__init__.py +6 -0
- flowtask/components/CompanyScraper/parsers/base.py +102 -0
- flowtask/components/CompanyScraper/parsers/explorium.py +192 -0
- flowtask/components/CompanyScraper/parsers/leadiq.py +206 -0
- flowtask/components/CompanyScraper/parsers/rocket.py +133 -0
- flowtask/components/CompanyScraper/parsers/siccode.py +109 -0
- flowtask/components/CompanyScraper/parsers/visualvisitor.py +130 -0
- flowtask/components/CompanyScraper/parsers/zoominfo.py +118 -0
- flowtask/components/CompanyScraper/scrapper.py +1054 -0
- flowtask/components/CopyTo.py +177 -0
- flowtask/components/CopyToBigQuery.py +243 -0
- flowtask/components/CopyToMongoDB.py +291 -0
- flowtask/components/CopyToPg.py +609 -0
- flowtask/components/CopyToRethink.py +207 -0
- flowtask/components/CreateGCSBucket.py +102 -0
- flowtask/components/CreateReport/CreateReport.py +228 -0
- flowtask/components/CreateReport/__init__.py +9 -0
- flowtask/components/CreateReport/charts/__init__.py +15 -0
- flowtask/components/CreateReport/charts/bar.py +51 -0
- flowtask/components/CreateReport/charts/base.py +66 -0
- flowtask/components/CreateReport/charts/pie.py +64 -0
- flowtask/components/CreateReport/utils.py +9 -0
- flowtask/components/CustomerSatisfaction.py +196 -0
- flowtask/components/DataInput.py +200 -0
- flowtask/components/DateList.py +255 -0
- flowtask/components/DbClient.py +163 -0
- flowtask/components/DialPad.py +146 -0
- flowtask/components/DocumentDBQuery.py +200 -0
- flowtask/components/DownloadFrom.py +371 -0
- flowtask/components/DownloadFromD2L.py +113 -0
- flowtask/components/DownloadFromFTP.py +181 -0
- flowtask/components/DownloadFromIMAP.py +315 -0
- flowtask/components/DownloadFromS3.py +198 -0
- flowtask/components/DownloadFromSFTP.py +265 -0
- flowtask/components/DownloadFromSharepoint.py +110 -0
- flowtask/components/DownloadFromSmartSheet.py +114 -0
- flowtask/components/DownloadS3File.py +229 -0
- flowtask/components/Dummy.py +59 -0
- flowtask/components/DuplicatePhoto.py +411 -0
- flowtask/components/EmployeeEvaluation.py +237 -0
- flowtask/components/ExecuteSQL.py +323 -0
- flowtask/components/ExtractHTML.py +178 -0
- flowtask/components/FileBase.py +178 -0
- flowtask/components/FileCopy.py +181 -0
- flowtask/components/FileDelete.py +82 -0
- flowtask/components/FileExists.py +146 -0
- flowtask/components/FileIteratorDelete.py +112 -0
- flowtask/components/FileList.py +194 -0
- flowtask/components/FileOpen.py +75 -0
- flowtask/components/FileRead.py +120 -0
- flowtask/components/FileRename.py +106 -0
- flowtask/components/FilterIf.py +284 -0
- flowtask/components/FilterRows/FilterRows.py +200 -0
- flowtask/components/FilterRows/__init__.py +10 -0
- flowtask/components/FilterRows/functions.py +4 -0
- flowtask/components/GCSToBigQuery.py +103 -0
- flowtask/components/GoogleA4.py +150 -0
- flowtask/components/GoogleGeoCoding.py +344 -0
- flowtask/components/GooglePlaces.py +315 -0
- flowtask/components/GoogleSearch.py +539 -0
- flowtask/components/HTTPClient.py +268 -0
- flowtask/components/ICIMS.py +146 -0
- flowtask/components/IF.py +179 -0
- flowtask/components/IcimsFolderCopy.py +173 -0
- flowtask/components/ImageFeatures/__init__.py +5 -0
- flowtask/components/ImageFeatures/process.py +233 -0
- flowtask/components/IteratorBase.py +251 -0
- flowtask/components/LangchainLoader/__init__.py +5 -0
- flowtask/components/LangchainLoader/loader.py +194 -0
- flowtask/components/LangchainLoader/loaders/__init__.py +22 -0
- flowtask/components/LangchainLoader/loaders/abstract.py +362 -0
- flowtask/components/LangchainLoader/loaders/basepdf.py +50 -0
- flowtask/components/LangchainLoader/loaders/docx.py +91 -0
- flowtask/components/LangchainLoader/loaders/html.py +119 -0
- flowtask/components/LangchainLoader/loaders/pdfblocks.py +146 -0
- flowtask/components/LangchainLoader/loaders/pdfmark.py +79 -0
- flowtask/components/LangchainLoader/loaders/pdftables.py +135 -0
- flowtask/components/LangchainLoader/loaders/qa.py +67 -0
- flowtask/components/LangchainLoader/loaders/txt.py +55 -0
- flowtask/components/LeadIQ.py +650 -0
- flowtask/components/Loop.py +253 -0
- flowtask/components/Lowes.py +334 -0
- flowtask/components/MS365Usage.py +156 -0
- flowtask/components/MSTeamsMessages.py +320 -0
- flowtask/components/MarketClustering.py +1051 -0
- flowtask/components/MergeFiles.py +362 -0
- flowtask/components/MilvusOutput.py +87 -0
- flowtask/components/NearByStores.py +175 -0
- flowtask/components/NetworkNinja/__init__.py +6 -0
- flowtask/components/NetworkNinja/models/__init__.py +52 -0
- flowtask/components/NetworkNinja/models/abstract.py +177 -0
- flowtask/components/NetworkNinja/models/account.py +39 -0
- flowtask/components/NetworkNinja/models/client.py +19 -0
- flowtask/components/NetworkNinja/models/district.py +14 -0
- flowtask/components/NetworkNinja/models/events.py +101 -0
- flowtask/components/NetworkNinja/models/forms.py +499 -0
- flowtask/components/NetworkNinja/models/market.py +16 -0
- flowtask/components/NetworkNinja/models/organization.py +34 -0
- flowtask/components/NetworkNinja/models/photos.py +125 -0
- flowtask/components/NetworkNinja/models/project.py +44 -0
- flowtask/components/NetworkNinja/models/region.py +28 -0
- flowtask/components/NetworkNinja/models/store.py +203 -0
- flowtask/components/NetworkNinja/models/user.py +151 -0
- flowtask/components/NetworkNinja/router.py +854 -0
- flowtask/components/Odoo.py +175 -0
- flowtask/components/OdooInjector.py +192 -0
- flowtask/components/OpenFromXML.py +126 -0
- flowtask/components/OpenWeather.py +41 -0
- flowtask/components/OpenWithBase.py +616 -0
- flowtask/components/OpenWithPandas.py +715 -0
- flowtask/components/PGPDecrypt.py +199 -0
- flowtask/components/PandasIterator.py +187 -0
- flowtask/components/PandasToFile.py +189 -0
- flowtask/components/Paradox.py +339 -0
- flowtask/components/ParamIterator.py +117 -0
- flowtask/components/ParseHTML.py +84 -0
- flowtask/components/PlacerStores.py +249 -0
- flowtask/components/Pokemon.py +507 -0
- flowtask/components/PositiveBot.py +62 -0
- flowtask/components/PowerPointSlide.py +400 -0
- flowtask/components/PrintMessage.py +127 -0
- flowtask/components/ProductCompetitors/__init__.py +5 -0
- flowtask/components/ProductCompetitors/parsers/__init__.py +7 -0
- flowtask/components/ProductCompetitors/parsers/base.py +72 -0
- flowtask/components/ProductCompetitors/parsers/bestbuy.py +86 -0
- flowtask/components/ProductCompetitors/parsers/lowes.py +103 -0
- flowtask/components/ProductCompetitors/scrapper.py +155 -0
- flowtask/components/ProductCompliant.py +169 -0
- flowtask/components/ProductInfo/__init__.py +1 -0
- flowtask/components/ProductInfo/parsers/__init__.py +5 -0
- flowtask/components/ProductInfo/parsers/base.py +83 -0
- flowtask/components/ProductInfo/parsers/brother.py +97 -0
- flowtask/components/ProductInfo/parsers/canon.py +167 -0
- flowtask/components/ProductInfo/parsers/epson.py +118 -0
- flowtask/components/ProductInfo/parsers/hp.py +131 -0
- flowtask/components/ProductInfo/parsers/samsung.py +97 -0
- flowtask/components/ProductInfo/scraper.py +319 -0
- flowtask/components/ProductPricing.py +118 -0
- flowtask/components/QS.py +261 -0
- flowtask/components/QSBase.py +201 -0
- flowtask/components/QueryIterator.py +273 -0
- flowtask/components/QueryToInsert.py +327 -0
- flowtask/components/QueryToPandas.py +432 -0
- flowtask/components/RESTClient.py +195 -0
- flowtask/components/RethinkDBQuery.py +189 -0
- flowtask/components/Rsync.py +74 -0
- flowtask/components/RunSSH.py +59 -0
- flowtask/components/RunShell.py +71 -0
- flowtask/components/SalesForce.py +20 -0
- flowtask/components/SaveImageBank/__init__.py +257 -0
- flowtask/components/SchedulingVisits.py +592 -0
- flowtask/components/ScrapPage.py +216 -0
- flowtask/components/ScrapSearch.py +79 -0
- flowtask/components/SendNotify.py +257 -0
- flowtask/components/SentimentAnalysis.py +694 -0
- flowtask/components/ServiceScrapper/__init__.py +5 -0
- flowtask/components/ServiceScrapper/parsers/__init__.py +1 -0
- flowtask/components/ServiceScrapper/parsers/base.py +94 -0
- flowtask/components/ServiceScrapper/parsers/costco.py +93 -0
- flowtask/components/ServiceScrapper/scrapper.py +199 -0
- flowtask/components/SetVariables.py +156 -0
- flowtask/components/SubTask.py +182 -0
- flowtask/components/SuiteCRM.py +48 -0
- flowtask/components/Switch.py +175 -0
- flowtask/components/TableBase.py +148 -0
- flowtask/components/TableDelete.py +312 -0
- flowtask/components/TableInput.py +143 -0
- flowtask/components/TableOutput/TableOutput.py +384 -0
- flowtask/components/TableOutput/__init__.py +3 -0
- flowtask/components/TableSchema.py +534 -0
- flowtask/components/Target.py +223 -0
- flowtask/components/ThumbnailGenerator.py +156 -0
- flowtask/components/ToPandas.py +67 -0
- flowtask/components/TransformRows/TransformRows.py +507 -0
- flowtask/components/TransformRows/__init__.py +9 -0
- flowtask/components/TransformRows/functions.py +559 -0
- flowtask/components/TransposeRows.py +176 -0
- flowtask/components/UPCDatabase.py +86 -0
- flowtask/components/UnGzip.py +171 -0
- flowtask/components/Uncompress.py +172 -0
- flowtask/components/UniqueRows.py +126 -0
- flowtask/components/Unzip.py +107 -0
- flowtask/components/UpdateOperationalVars.py +147 -0
- flowtask/components/UploadTo.py +299 -0
- flowtask/components/UploadToS3.py +136 -0
- flowtask/components/UploadToSFTP.py +160 -0
- flowtask/components/UploadToSharepoint.py +205 -0
- flowtask/components/UserFunc.py +122 -0
- flowtask/components/VivaTracker.py +140 -0
- flowtask/components/WSDLClient.py +123 -0
- flowtask/components/Wait.py +18 -0
- flowtask/components/Walmart.py +199 -0
- flowtask/components/Workplace.py +134 -0
- flowtask/components/XMLToPandas.py +267 -0
- flowtask/components/Zammad/__init__.py +41 -0
- flowtask/components/Zammad/models.py +0 -0
- flowtask/components/ZoomInfoScraper.py +409 -0
- flowtask/components/__init__.py +104 -0
- flowtask/components/abstract.py +18 -0
- flowtask/components/flow.py +530 -0
- flowtask/components/google.py +335 -0
- flowtask/components/group.py +221 -0
- flowtask/components/py.typed +0 -0
- flowtask/components/reviewscrap.py +132 -0
- flowtask/components/tAutoincrement.py +117 -0
- flowtask/components/tConcat.py +109 -0
- flowtask/components/tExplode.py +119 -0
- flowtask/components/tFilter.py +184 -0
- flowtask/components/tGroup.py +236 -0
- flowtask/components/tJoin.py +270 -0
- flowtask/components/tMap/__init__.py +9 -0
- flowtask/components/tMap/functions.py +54 -0
- flowtask/components/tMap/tMap.py +450 -0
- flowtask/components/tMelt.py +112 -0
- flowtask/components/tMerge.py +114 -0
- flowtask/components/tOrder.py +93 -0
- flowtask/components/tPandas.py +94 -0
- flowtask/components/tPivot.py +71 -0
- flowtask/components/tPluckCols.py +76 -0
- flowtask/components/tUnnest.py +82 -0
- flowtask/components/user.py +401 -0
- flowtask/conf.py +457 -0
- flowtask/download.py +102 -0
- flowtask/events/__init__.py +11 -0
- flowtask/events/events/__init__.py +20 -0
- flowtask/events/events/abstract.py +95 -0
- flowtask/events/events/alerts/__init__.py +362 -0
- flowtask/events/events/alerts/colfunctions.py +131 -0
- flowtask/events/events/alerts/functions.py +158 -0
- flowtask/events/events/dummy.py +12 -0
- flowtask/events/events/exec.py +124 -0
- flowtask/events/events/file/__init__.py +7 -0
- flowtask/events/events/file/base.py +51 -0
- flowtask/events/events/file/copy.py +23 -0
- flowtask/events/events/file/delete.py +16 -0
- flowtask/events/events/interfaces/__init__.py +9 -0
- flowtask/events/events/interfaces/client.py +67 -0
- flowtask/events/events/interfaces/credentials.py +28 -0
- flowtask/events/events/interfaces/notifications.py +58 -0
- flowtask/events/events/jira.py +122 -0
- flowtask/events/events/log.py +26 -0
- flowtask/events/events/logerr.py +52 -0
- flowtask/events/events/notify.py +59 -0
- flowtask/events/events/notify_event.py +160 -0
- flowtask/events/events/publish.py +54 -0
- flowtask/events/events/sendfile.py +104 -0
- flowtask/events/events/task.py +97 -0
- flowtask/events/events/teams.py +98 -0
- flowtask/events/events/webhook.py +58 -0
- flowtask/events/manager.py +287 -0
- flowtask/exceptions.c +39393 -0
- flowtask/exceptions.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/extensions/__init__.py +3 -0
- flowtask/extensions/abstract.py +82 -0
- flowtask/extensions/logging/__init__.py +65 -0
- flowtask/hooks/__init__.py +9 -0
- flowtask/hooks/actions/__init__.py +22 -0
- flowtask/hooks/actions/abstract.py +66 -0
- flowtask/hooks/actions/dummy.py +23 -0
- flowtask/hooks/actions/jira.py +74 -0
- flowtask/hooks/actions/rest.py +320 -0
- flowtask/hooks/actions/sampledata.py +37 -0
- flowtask/hooks/actions/sensor.py +23 -0
- flowtask/hooks/actions/task.py +9 -0
- flowtask/hooks/actions/ticket.py +37 -0
- flowtask/hooks/actions/zammad.py +55 -0
- flowtask/hooks/hook.py +62 -0
- flowtask/hooks/models.py +17 -0
- flowtask/hooks/service.py +187 -0
- flowtask/hooks/step.py +91 -0
- flowtask/hooks/types/__init__.py +23 -0
- flowtask/hooks/types/base.py +129 -0
- flowtask/hooks/types/brokers/__init__.py +11 -0
- flowtask/hooks/types/brokers/base.py +54 -0
- flowtask/hooks/types/brokers/mqtt.py +35 -0
- flowtask/hooks/types/brokers/rabbitmq.py +82 -0
- flowtask/hooks/types/brokers/redis.py +83 -0
- flowtask/hooks/types/brokers/sqs.py +44 -0
- flowtask/hooks/types/fs.py +232 -0
- flowtask/hooks/types/http.py +49 -0
- flowtask/hooks/types/imap.py +200 -0
- flowtask/hooks/types/jira.py +279 -0
- flowtask/hooks/types/mail.py +205 -0
- flowtask/hooks/types/postgres.py +98 -0
- flowtask/hooks/types/responses/__init__.py +8 -0
- flowtask/hooks/types/responses/base.py +5 -0
- flowtask/hooks/types/sharepoint.py +288 -0
- flowtask/hooks/types/ssh.py +141 -0
- flowtask/hooks/types/tagged.py +59 -0
- flowtask/hooks/types/upload.py +85 -0
- flowtask/hooks/types/watch.py +71 -0
- flowtask/hooks/types/web.py +36 -0
- flowtask/interfaces/AzureClient.py +137 -0
- flowtask/interfaces/AzureGraph.py +839 -0
- flowtask/interfaces/Boto3Client.py +326 -0
- flowtask/interfaces/DropboxClient.py +173 -0
- flowtask/interfaces/ExcelHandler.py +94 -0
- flowtask/interfaces/FTPClient.py +131 -0
- flowtask/interfaces/GoogleCalendar.py +201 -0
- flowtask/interfaces/GoogleClient.py +133 -0
- flowtask/interfaces/GoogleDrive.py +127 -0
- flowtask/interfaces/GoogleGCS.py +89 -0
- flowtask/interfaces/GoogleGeocoding.py +93 -0
- flowtask/interfaces/GoogleLang.py +114 -0
- flowtask/interfaces/GooglePub.py +61 -0
- flowtask/interfaces/GoogleSheet.py +68 -0
- flowtask/interfaces/IMAPClient.py +137 -0
- flowtask/interfaces/O365Calendar.py +113 -0
- flowtask/interfaces/O365Client.py +220 -0
- flowtask/interfaces/OneDrive.py +284 -0
- flowtask/interfaces/Outlook.py +155 -0
- flowtask/interfaces/ParrotBot.py +130 -0
- flowtask/interfaces/SSHClient.py +378 -0
- flowtask/interfaces/Sharepoint.py +496 -0
- flowtask/interfaces/__init__.py +36 -0
- flowtask/interfaces/azureauth.py +119 -0
- flowtask/interfaces/cache.py +201 -0
- flowtask/interfaces/client.py +82 -0
- flowtask/interfaces/compress.py +525 -0
- flowtask/interfaces/credentials.py +124 -0
- flowtask/interfaces/d2l.py +239 -0
- flowtask/interfaces/databases/__init__.py +5 -0
- flowtask/interfaces/databases/db.py +223 -0
- flowtask/interfaces/databases/documentdb.py +55 -0
- flowtask/interfaces/databases/rethink.py +39 -0
- flowtask/interfaces/dataframes/__init__.py +11 -0
- flowtask/interfaces/dataframes/abstract.py +21 -0
- flowtask/interfaces/dataframes/arrow.py +71 -0
- flowtask/interfaces/dataframes/dt.py +69 -0
- flowtask/interfaces/dataframes/pandas.py +167 -0
- flowtask/interfaces/dataframes/polars.py +60 -0
- flowtask/interfaces/db.py +263 -0
- flowtask/interfaces/env.py +46 -0
- flowtask/interfaces/func.py +137 -0
- flowtask/interfaces/http.py +1780 -0
- flowtask/interfaces/locale.py +40 -0
- flowtask/interfaces/log.py +75 -0
- flowtask/interfaces/mask.py +143 -0
- flowtask/interfaces/notification.py +154 -0
- flowtask/interfaces/playwright.py +339 -0
- flowtask/interfaces/powerpoint.py +368 -0
- flowtask/interfaces/py.typed +0 -0
- flowtask/interfaces/qs.py +376 -0
- flowtask/interfaces/result.py +87 -0
- flowtask/interfaces/selenium_service.py +779 -0
- flowtask/interfaces/smartsheet.py +154 -0
- flowtask/interfaces/stat.py +39 -0
- flowtask/interfaces/task.py +96 -0
- flowtask/interfaces/template.py +118 -0
- flowtask/interfaces/vectorstores/__init__.py +1 -0
- flowtask/interfaces/vectorstores/abstract.py +133 -0
- flowtask/interfaces/vectorstores/milvus.py +669 -0
- flowtask/interfaces/zammad.py +107 -0
- flowtask/models.py +193 -0
- flowtask/parsers/__init__.py +15 -0
- flowtask/parsers/_yaml.c +11978 -0
- flowtask/parsers/_yaml.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/argparser.py +235 -0
- flowtask/parsers/base.c +15155 -0
- flowtask/parsers/base.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/json.c +11968 -0
- flowtask/parsers/json.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/maps.py +49 -0
- flowtask/parsers/toml.c +11968 -0
- flowtask/parsers/toml.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/plugins/__init__.py +16 -0
- flowtask/plugins/components/__init__.py +0 -0
- flowtask/plugins/handler/__init__.py +45 -0
- flowtask/plugins/importer.py +31 -0
- flowtask/plugins/sources/__init__.py +0 -0
- flowtask/runner.py +283 -0
- flowtask/scheduler/__init__.py +9 -0
- flowtask/scheduler/functions.py +493 -0
- flowtask/scheduler/handlers/__init__.py +8 -0
- flowtask/scheduler/handlers/manager.py +504 -0
- flowtask/scheduler/handlers/models.py +58 -0
- flowtask/scheduler/handlers/service.py +72 -0
- flowtask/scheduler/notifications.py +65 -0
- flowtask/scheduler/scheduler.py +993 -0
- flowtask/services/__init__.py +0 -0
- flowtask/services/bots/__init__.py +0 -0
- flowtask/services/bots/telegram.py +264 -0
- flowtask/services/files/__init__.py +11 -0
- flowtask/services/files/manager.py +522 -0
- flowtask/services/files/model.py +37 -0
- flowtask/services/files/service.py +767 -0
- flowtask/services/jira/__init__.py +3 -0
- flowtask/services/jira/jira_actions.py +191 -0
- flowtask/services/tasks/__init__.py +13 -0
- flowtask/services/tasks/launcher.py +213 -0
- flowtask/services/tasks/manager.py +323 -0
- flowtask/services/tasks/service.py +275 -0
- flowtask/services/tasks/task_manager.py +376 -0
- flowtask/services/tasks/tasks.py +155 -0
- flowtask/storages/__init__.py +16 -0
- flowtask/storages/exceptions.py +12 -0
- flowtask/storages/files/__init__.py +8 -0
- flowtask/storages/files/abstract.py +29 -0
- flowtask/storages/files/filesystem.py +66 -0
- flowtask/storages/tasks/__init__.py +19 -0
- flowtask/storages/tasks/abstract.py +26 -0
- flowtask/storages/tasks/database.py +33 -0
- flowtask/storages/tasks/filesystem.py +108 -0
- flowtask/storages/tasks/github.py +119 -0
- flowtask/storages/tasks/memory.py +45 -0
- flowtask/storages/tasks/row.py +25 -0
- flowtask/tasks/__init__.py +0 -0
- flowtask/tasks/abstract.py +526 -0
- flowtask/tasks/command.py +118 -0
- flowtask/tasks/pile.py +486 -0
- flowtask/tasks/py.typed +0 -0
- flowtask/tasks/task.py +778 -0
- flowtask/template/__init__.py +161 -0
- flowtask/tests.py +257 -0
- flowtask/types/__init__.py +8 -0
- flowtask/types/typedefs.c +11347 -0
- flowtask/types/typedefs.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/__init__.py +24 -0
- flowtask/utils/constants.py +117 -0
- flowtask/utils/encoders.py +21 -0
- flowtask/utils/executor.py +112 -0
- flowtask/utils/functions.cpp +14280 -0
- flowtask/utils/functions.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/json.cpp +13349 -0
- flowtask/utils/json.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/mail.py +63 -0
- flowtask/utils/parseqs.c +13324 -0
- flowtask/utils/parserqs.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/stats.py +308 -0
- flowtask/utils/transformations.py +74 -0
- flowtask/utils/uv.py +12 -0
- flowtask/utils/validators.py +97 -0
- flowtask/version.py +11 -0
- flowtask-5.8.4.dist-info/LICENSE +201 -0
- flowtask-5.8.4.dist-info/METADATA +209 -0
- flowtask-5.8.4.dist-info/RECORD +470 -0
- flowtask-5.8.4.dist-info/WHEEL +6 -0
- flowtask-5.8.4.dist-info/entry_points.txt +3 -0
- flowtask-5.8.4.dist-info/top_level.txt +2 -0
- plugins/components/CreateQR.py +39 -0
- plugins/components/TestComponent.py +28 -0
- plugins/components/Use1.py +13 -0
- plugins/components/Workplace.py +117 -0
- plugins/components/__init__.py +3 -0
- plugins/sources/__init__.py +0 -0
- plugins/sources/get_populartimes.py +78 -0
- plugins/sources/google.py +150 -0
- plugins/sources/hubspot.py +679 -0
- plugins/sources/icims.py +679 -0
- plugins/sources/mobileinsight.py +501 -0
- plugins/sources/newrelic.py +262 -0
- plugins/sources/uap.py +268 -0
- plugins/sources/venu.py +244 -0
- plugins/sources/vocinity.py +314 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
"""
|
2
|
+
DownloadFromSmartSheet
|
3
|
+
Download an Excel file from SmartSheet.
|
4
|
+
|
5
|
+
|
6
|
+
Example:
|
7
|
+
|
8
|
+
```yaml
|
9
|
+
DownloadFromFTP:
|
10
|
+
host: trendmicro_host
|
11
|
+
port: trendmicro_port
|
12
|
+
use_tls: false
|
13
|
+
file:
|
14
|
+
pattern: '{monday}.*-Limited852StoreReport.{saturday}.csv'
|
15
|
+
values:
|
16
|
+
monday:
|
17
|
+
- date_dow
|
18
|
+
- day_of_week: monday
|
19
|
+
mask: '%Y%m%d'
|
20
|
+
saturday:
|
21
|
+
- date_diff_dow
|
22
|
+
- diff: 7
|
23
|
+
day_of_week: saturday
|
24
|
+
mask: '%Y-%m-%d'
|
25
|
+
directory: /Limited852Report
|
26
|
+
credentials:
|
27
|
+
user: trendmicro_user
|
28
|
+
password: trendmicro_password
|
29
|
+
download:
|
30
|
+
directory: /home/ubuntu/symbits/trendmicro/files/market_share/
|
31
|
+
```
|
32
|
+
|
33
|
+
"""
|
34
|
+
import re
|
35
|
+
import asyncio
|
36
|
+
from pathlib import Path
|
37
|
+
from ..exceptions import ComponentError, FileError
|
38
|
+
from .DownloadFrom import DownloadFromBase
|
39
|
+
from ..interfaces.FTPClient import FTPClient
|
40
|
+
|
41
|
+
|
42
|
+
class DownloadFromFTP(FTPClient, DownloadFromBase):
|
43
|
+
"""
|
44
|
+
DownloadFromFTP
|
45
|
+
|
46
|
+
Overview
|
47
|
+
|
48
|
+
Downloads files from a remote FTP server using the functionality from DownloadFrom.
|
49
|
+
|
50
|
+
Properties
|
51
|
+
|
52
|
+
.. table:: Properties
|
53
|
+
:widths: auto
|
54
|
+
|
55
|
+
+--------------+----------+-----------+--------------------------------------------------------+
|
56
|
+
| Name | Required | Summary |
|
57
|
+
+--------------+----------+-----------+--------------------------------------------------------+
|
58
|
+
| credentials | Yes | Dictionary containing username and password for FTP authentication.|
|
59
|
+
| | | (default: {"user": str, "password": str}) |
|
60
|
+
+--------------+----------+-----------+--------------------------------------------------------+
|
61
|
+
| whole_dir | Yes | Boolean flag indicating whether to download the entire directory |
|
62
|
+
| | | or specific files. (default: False) |
|
63
|
+
+--------------+----------+-----------+--------------------------------------------------------+
|
64
|
+
| use_tls | Yes | Boolean for indicate if we need to use the TLS protocol |
|
65
|
+
+--------------+----------+-----------+--------------------------------------------------------+
|
66
|
+
|
67
|
+
Save the downloaded files on the new destination.
|
68
|
+
"""
|
69
|
+
_credentials: dict = {"user": str, "password": str}
|
70
|
+
|
71
|
+
async def start(self, **kwargs):
|
72
|
+
await super().start(**kwargs)
|
73
|
+
self._path: str = None
|
74
|
+
self.whole_dir: bool = False
|
75
|
+
if hasattr(self, "source"):
|
76
|
+
if "algorithms" in self.source:
|
77
|
+
self.algorithms.append(self.source["algorithms"])
|
78
|
+
# change filosophy of source/destination
|
79
|
+
self.whole_dir = (
|
80
|
+
self.source["whole_dir"] if "whole_dir" in self.source else False
|
81
|
+
)
|
82
|
+
await super(DownloadFromFTP, self).start(**kwargs)
|
83
|
+
if hasattr(self, "download"):
|
84
|
+
self.directory = Path(self.download["directory"])
|
85
|
+
return True
|
86
|
+
|
87
|
+
async def download_files(self):
|
88
|
+
try:
|
89
|
+
self._connection = await self.init_connection(
|
90
|
+
self.host, self.port, credentials=self.credentials, ssl=self.ssl
|
91
|
+
)
|
92
|
+
except asyncio.CancelledError:
|
93
|
+
self._logger.info(f"{self.host} CANCELED~")
|
94
|
+
# break
|
95
|
+
except ComponentError:
|
96
|
+
raise
|
97
|
+
except (TimeoutError, asyncio.TimeoutError) as ex:
|
98
|
+
raise ComponentError(f"Timeout: {ex}") from ex
|
99
|
+
except Exception as err:
|
100
|
+
raise ComponentError(f"DownloadFromFTP Error: {err}") from err
|
101
|
+
if self._connection:
|
102
|
+
if not await self.directory_exists(self.source_dir):
|
103
|
+
raise ComponentError(f"FTP Directory doesn't exists: {self.source_dir}")
|
104
|
+
try:
|
105
|
+
await self.change_directory(self.source_dir)
|
106
|
+
except Exception as err:
|
107
|
+
raise ComponentError(
|
108
|
+
f"FTP Unable to connect to: {self.source_dir}, error: {err}"
|
109
|
+
) from err
|
110
|
+
# getting stats for directory
|
111
|
+
filelist = []
|
112
|
+
files = []
|
113
|
+
stats = await self._connection.list(
|
114
|
+
self.source_dir, recursive=False, raw_command="MLSD"
|
115
|
+
)
|
116
|
+
pbar = self.start_pbar(total=len(stats))
|
117
|
+
if self.whole_dir is True:
|
118
|
+
await self.download_file(
|
119
|
+
file=self.source_dir, destination=self.directory
|
120
|
+
)
|
121
|
+
for path, info in stats:
|
122
|
+
if info["type"] == "file":
|
123
|
+
file = {"filename": path, **info}
|
124
|
+
# all files downloaded:
|
125
|
+
pbar.update(1)
|
126
|
+
files.append(file)
|
127
|
+
filelist.append(path)
|
128
|
+
self.add_metric("FTP_FILES", files)
|
129
|
+
pbar.close()
|
130
|
+
else:
|
131
|
+
if self.source_file is not None:
|
132
|
+
self._srcfiles = [self.source_file]
|
133
|
+
for file in self._srcfiles:
|
134
|
+
if not self.source_dir.endswith("/"):
|
135
|
+
self.source_dir = "{}/".format(self.source_dir)
|
136
|
+
pattern = re.compile(f"^{self.source_dir}{str(file)}+$")
|
137
|
+
self._logger.debug(f"Calculated Pattern for lookup {pattern}")
|
138
|
+
for path, info in stats:
|
139
|
+
if info["type"] == "file":
|
140
|
+
# first case, match the extension:
|
141
|
+
if hasattr(self, "source"):
|
142
|
+
if "suffix" in self.source:
|
143
|
+
# download all files match with suffix:
|
144
|
+
if path.suffix == self.source["suffix"]:
|
145
|
+
file = {"filename": path, **info}
|
146
|
+
await self.download_file(
|
147
|
+
file=path, destination=self.directory
|
148
|
+
)
|
149
|
+
files.append(file)
|
150
|
+
filelist.append(path)
|
151
|
+
elif pattern.match(str(path)):
|
152
|
+
self._logger.debug(f"Looking for matching file {path}")
|
153
|
+
file = {"filename": path, **info}
|
154
|
+
files.append(file)
|
155
|
+
filelist.append(path)
|
156
|
+
# using regex to check file
|
157
|
+
await self.download_file(
|
158
|
+
file=path, destination=self.directory
|
159
|
+
)
|
160
|
+
pbar.update(1)
|
161
|
+
pbar.set_description(f"Processing {path}")
|
162
|
+
self.add_metric("FTP_FILES", files)
|
163
|
+
pbar.close()
|
164
|
+
self._result = ["{}/{}".format(self.directory, v.name) for v in filelist]
|
165
|
+
print(self._result)
|
166
|
+
return True
|
167
|
+
else:
|
168
|
+
return False
|
169
|
+
|
170
|
+
async def run(self):
|
171
|
+
self._result = None
|
172
|
+
if self._debug:
|
173
|
+
self._logger.info(f"Downloading FTP files: {self._filenames}")
|
174
|
+
try:
|
175
|
+
status = await self.download_files()
|
176
|
+
except (ComponentError, Exception) as err:
|
177
|
+
self._logger.error(err)
|
178
|
+
raise
|
179
|
+
if not status:
|
180
|
+
raise FileError(f"File(s) Not Found: {self.source_file}")
|
181
|
+
return self._result
|
@@ -0,0 +1,315 @@
|
|
1
|
+
import asyncio
|
2
|
+
from collections.abc import Callable
|
3
|
+
import re
|
4
|
+
import fnmatch
|
5
|
+
from pathlib import Path, PurePath
|
6
|
+
import imaplib
|
7
|
+
import aiofiles
|
8
|
+
from ..conf import IMAP_RETRY_SELECT, FILES_PATH
|
9
|
+
from ..utils.mail import MailMessage
|
10
|
+
from ..exceptions import ComponentError, FileNotFound
|
11
|
+
from .DownloadFrom import DownloadFromBase
|
12
|
+
from ..interfaces.IMAPClient import IMAPClient
|
13
|
+
|
14
|
+
|
15
|
+
class DownloadFromIMAP(IMAPClient, DownloadFromBase):
|
16
|
+
"""
|
17
|
+
DownloadFromIMAP.
|
18
|
+
|
19
|
+
Overview
|
20
|
+
|
21
|
+
Download emails from an IMAP mailbox using the functionality from DownloadFrom.
|
22
|
+
|
23
|
+
.. table:: Properties
|
24
|
+
:widths: auto
|
25
|
+
|
26
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
27
|
+
| Name | Required | Summary |
|
28
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
29
|
+
| credentials | Yes | Credentials to access the IMAP mailbox. |
|
30
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
31
|
+
| mailbox | Yes | The IMAP mailbox name (default: "INBOX"). |
|
32
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
33
|
+
| search_terms | Yes | Dictionary containing search criteria in IMAP format. |
|
34
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
35
|
+
| attachments | Yes | Dictionary specifying download configuration for attachments: |
|
36
|
+
| | | - directory (str): Path to save downloaded attachments. |
|
37
|
+
| | | - filename (str, optional): Filename pattern for selection (fnmatch). |
|
38
|
+
| | | - pattern (str, optional): Regular expression pattern for selection. |
|
39
|
+
| | | - expected_mime (str, optional): Expected MIME type filter. |
|
40
|
+
| | | - rename (str, optional): Template string for renaming attachments (uses "{filename}"). |
|
41
|
+
| | | - download_existing (bool, optional): Skip existing files (default: True). |
|
42
|
+
| | | - create_destination (bool, optional): Create download directory if it doesn't exist (default: True). |
|
43
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
44
|
+
| download_existing | no | Flag indicating whether to skip downloading existing files. |
|
45
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
46
|
+
| results | Yes | Dictionary containing download results: |
|
47
|
+
| | | - attachments: List of downloaded attachment file paths. |
|
48
|
+
| | | - messages: List of `MailMessage` objects representing downloaded emails. |
|
49
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
50
|
+
| use_ssl | Yes | Boolean for indicate if we need to use the TLS protocol |
|
51
|
+
+-------------------+----------+-----------+---------------------------------------------------------------------------------------------+
|
52
|
+
|
53
|
+
Save the downloaded files on the new destination.
|
54
|
+
|
55
|
+
|
56
|
+
Example:
|
57
|
+
|
58
|
+
```yaml
|
59
|
+
DownloadFromIMAP:
|
60
|
+
credentials:
|
61
|
+
host: email_host
|
62
|
+
port: email_port
|
63
|
+
user: email_host_user
|
64
|
+
password: email_host_password
|
65
|
+
use_ssl: true
|
66
|
+
search_terms:
|
67
|
+
'ON': '{search_today}'
|
68
|
+
SUBJECT: Custom Punch with Pay Codes - Excel
|
69
|
+
FROM: eet_application@adp.com
|
70
|
+
overwrite: true
|
71
|
+
attachments:
|
72
|
+
directory: /home/ubuntu/symbits/bose/files/worked_hours/
|
73
|
+
masks:
|
74
|
+
'{search_today}':
|
75
|
+
- today
|
76
|
+
- mask: '%d-%b-%Y'
|
77
|
+
```
|
78
|
+
|
79
|
+
""" # noqa
|
80
|
+
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
loop: asyncio.AbstractEventLoop = None,
|
84
|
+
job: Callable = None,
|
85
|
+
stat: Callable = None,
|
86
|
+
**kwargs,
|
87
|
+
) -> None:
|
88
|
+
self.attachments = {"directory": None}
|
89
|
+
self.search_terms = None
|
90
|
+
super().__init__(
|
91
|
+
loop=loop, job=job, stat=stat, **kwargs
|
92
|
+
)
|
93
|
+
|
94
|
+
async def start(self, **kwargs): # pylint: disable=W0613
|
95
|
+
if hasattr(self, "attachments"):
|
96
|
+
# attachment directory
|
97
|
+
if "directory" not in self.attachments:
|
98
|
+
self.attachments["directory"] = FILES_PATH.joinpath(
|
99
|
+
self._program, "files", "download"
|
100
|
+
)
|
101
|
+
else:
|
102
|
+
self.attachments["directory"] = Path(
|
103
|
+
self.attachments["directory"]
|
104
|
+
).resolve()
|
105
|
+
try:
|
106
|
+
directory = self.attachments["directory"]
|
107
|
+
if not directory.exists():
|
108
|
+
if self.create_destination is True:
|
109
|
+
directory.mkdir(parents=True, exist_ok=True)
|
110
|
+
else:
|
111
|
+
raise ComponentError(
|
112
|
+
f"DownloadFromIMAP: Error creating directory: {directory}"
|
113
|
+
)
|
114
|
+
except Exception as err:
|
115
|
+
self._logger.error(
|
116
|
+
f"IMAP: Error creating directory: {err}"
|
117
|
+
)
|
118
|
+
raise ComponentError(
|
119
|
+
f"IMAP: Error creating directory: {err}"
|
120
|
+
) from err
|
121
|
+
# masking elements:
|
122
|
+
if hasattr(self, "masks"):
|
123
|
+
for mask, replace in self._mask.items():
|
124
|
+
for key, val in self.attachments.items():
|
125
|
+
value = str(val).replace(mask, str(replace))
|
126
|
+
if isinstance(val, PurePath):
|
127
|
+
self.attachments[key] = Path(value)
|
128
|
+
else:
|
129
|
+
self.attachments[key] = value
|
130
|
+
await super(DownloadFromIMAP, self).start(**kwargs)
|
131
|
+
return True
|
132
|
+
|
133
|
+
async def run(self):
|
134
|
+
self._result = None
|
135
|
+
filter_criteria = []
|
136
|
+
msgs = [""]
|
137
|
+
messages = []
|
138
|
+
files: list = []
|
139
|
+
try:
|
140
|
+
await self.open(self.host, self.port, self.credentials)
|
141
|
+
if not self._client:
|
142
|
+
raise ComponentError("IMAP Connection not Opened, exiting.")
|
143
|
+
except Exception as err:
|
144
|
+
self._logger.exception(err, stack_info=True)
|
145
|
+
raise
|
146
|
+
# getting search Terms
|
147
|
+
self.search_terms = self.process_mask("search_terms")
|
148
|
+
for term, value in self.search_terms.items():
|
149
|
+
try:
|
150
|
+
value = self.mask_replacement(value)
|
151
|
+
filter_criteria.append(f'({term} "{value}")')
|
152
|
+
except (ValueError, KeyError):
|
153
|
+
pass
|
154
|
+
self._logger.debug(f"FILTER CRITERIA: {filter_criteria}")
|
155
|
+
self.add_metric("SEARCH", filter_criteria)
|
156
|
+
try:
|
157
|
+
# getting the Mailbox
|
158
|
+
self._client.select(self.mailbox)
|
159
|
+
except Exception as err:
|
160
|
+
if "User is authenticated but not connected." in str(err):
|
161
|
+
tries = 0
|
162
|
+
while tries < IMAP_RETRY_SELECT:
|
163
|
+
self._client.logout()
|
164
|
+
await asyncio.sleep(10)
|
165
|
+
try:
|
166
|
+
await self.open(self.host, self.port, self.credentials)
|
167
|
+
if not self._client:
|
168
|
+
raise ComponentError(
|
169
|
+
"IMAP Connection not Opened, exiting."
|
170
|
+
) from err
|
171
|
+
self._client.select(self.mailbox)
|
172
|
+
break
|
173
|
+
except Exception as exc:
|
174
|
+
self._logger.exception(exc, stack_info=True)
|
175
|
+
if tries < (IMAP_RETRY_SELECT - 1):
|
176
|
+
tries += 1
|
177
|
+
continue
|
178
|
+
else:
|
179
|
+
raise RuntimeError(
|
180
|
+
f"IMAP: Error opening Mailbox {self.mailbox}: {exc}"
|
181
|
+
) from exc
|
182
|
+
try:
|
183
|
+
print("FILTER ", filter_criteria)
|
184
|
+
result, msgs = self._client.search(None, *filter_criteria)
|
185
|
+
if result == "NO" or result == "BAD":
|
186
|
+
self._logger.error(msgs[0])
|
187
|
+
raise ComponentError(
|
188
|
+
message=f"Error on Search: {msgs[0]}",
|
189
|
+
status=500
|
190
|
+
)
|
191
|
+
except imaplib.IMAP4.abort as err:
|
192
|
+
raise ComponentError(f"IMAP Illegal Search: {err}") from err
|
193
|
+
except Exception as err:
|
194
|
+
self._logger.exception(err, stack_info=True)
|
195
|
+
raise ComponentError(f"IMAP Error: {err}") from err
|
196
|
+
msgs = msgs[0].split()
|
197
|
+
i = 0
|
198
|
+
if not msgs:
|
199
|
+
raise FileNotFound("DownloadFromIMAP: File(s) doesn't exists")
|
200
|
+
if not isinstance(msgs, list):
|
201
|
+
raise ComponentError(f"DownloadFromIMAP: Invalid Email Response: {msgs}")
|
202
|
+
if "expected_mime" in self.attachments:
|
203
|
+
expected_mime = self.attachments["expected_mime"]
|
204
|
+
if expected_mime:
|
205
|
+
validate_mime = True
|
206
|
+
else:
|
207
|
+
validate_mime = False
|
208
|
+
else:
|
209
|
+
expected_mime = None
|
210
|
+
validate_mime = False
|
211
|
+
for emailid in msgs:
|
212
|
+
i += 1
|
213
|
+
resp, data = self._client.fetch(emailid.decode("utf-8"), "(RFC822)")
|
214
|
+
if resp == "OK": # mail was retrieved
|
215
|
+
msg = MailMessage(
|
216
|
+
self.attachments["directory"],
|
217
|
+
data[0][1].decode("utf-8"),
|
218
|
+
data[1],
|
219
|
+
validate_mime=validate_mime,
|
220
|
+
)
|
221
|
+
messages.append(msg)
|
222
|
+
for attachment in msg.attachments:
|
223
|
+
if expected_mime is not None:
|
224
|
+
# checking only for valid MIME types:
|
225
|
+
if expected_mime != attachment["content_type"]:
|
226
|
+
continue
|
227
|
+
if "filename" in self.attachments:
|
228
|
+
fname = self.attachments[
|
229
|
+
"filename"
|
230
|
+
] # we only need to save selected files
|
231
|
+
if not fnmatch.fnmatch(attachment["filename"], fname):
|
232
|
+
continue
|
233
|
+
else:
|
234
|
+
if "rename" in self.attachments:
|
235
|
+
filename = self.attachments["rename"]
|
236
|
+
filename = filename.replace(
|
237
|
+
"{filename}", Path(attachment["filename"]).stem
|
238
|
+
)
|
239
|
+
self._logger.debug(f"NEW FILENAME IS {filename}")
|
240
|
+
file_path = self.attachments["directory"].joinpath(
|
241
|
+
filename
|
242
|
+
)
|
243
|
+
else:
|
244
|
+
file_path = self.attachments["directory"].joinpath(
|
245
|
+
attachment["filename"]
|
246
|
+
)
|
247
|
+
elif (
|
248
|
+
"pattern" in self.attachments
|
249
|
+
): # only save files that match the pattern
|
250
|
+
fpattern = self.attachments["pattern"]
|
251
|
+
if bool(re.match(fpattern, attachment["filename"])):
|
252
|
+
file_path = self.attachments["directory"].joinpath(
|
253
|
+
attachment["filename"]
|
254
|
+
)
|
255
|
+
else:
|
256
|
+
continue
|
257
|
+
else:
|
258
|
+
# I need to save everything
|
259
|
+
if "rename" in self.attachments:
|
260
|
+
filename = self.attachments["rename"]
|
261
|
+
if hasattr(self, "masks"):
|
262
|
+
filename = self.mask_replacement(filename)
|
263
|
+
# for mask, replace in self._mask.items():
|
264
|
+
# filename = filename.replace(mask, replace)
|
265
|
+
filename = filename.replace(
|
266
|
+
"{filename}", Path(attachment["filename"]).stem
|
267
|
+
)
|
268
|
+
file_path = self.attachments["directory"].joinpath(filename)
|
269
|
+
else:
|
270
|
+
file_path = self.attachments["directory"].joinpath(
|
271
|
+
attachment["filename"]
|
272
|
+
)
|
273
|
+
# saving the filename in the attachment
|
274
|
+
attachment["filename"] = file_path
|
275
|
+
if "download_existing" in self.attachments:
|
276
|
+
if (
|
277
|
+
file_path.exists()
|
278
|
+
and self.attachments["download_existing"] is False
|
279
|
+
):
|
280
|
+
# we don't need to download again
|
281
|
+
self._logger.info(f"File Exists {file_path!s}, skipping")
|
282
|
+
continue
|
283
|
+
if file_path.exists() and self.overwrite is False:
|
284
|
+
# TODO: before to save a new renamed file,
|
285
|
+
# we need to check if we have it (checksum)
|
286
|
+
file_name = file_path.name
|
287
|
+
# dir_name = file_path.absolute()
|
288
|
+
ext = file_path.suffix
|
289
|
+
# calculated new filepath
|
290
|
+
file_path = self.attachments["directory"].joinpath(
|
291
|
+
f"{file_name}_{i}{ext}"
|
292
|
+
)
|
293
|
+
if file_path.exists():
|
294
|
+
# TODO: more efficient way (while) to check if file exists
|
295
|
+
files.append(file_path)
|
296
|
+
continue
|
297
|
+
await self.save_attachment(file_path, attachment["attachment"])
|
298
|
+
# saving this file in the list of files:
|
299
|
+
files.append(file_path)
|
300
|
+
else:
|
301
|
+
raise ComponentError(f"File was not fetch: {resp}")
|
302
|
+
# saving the result:
|
303
|
+
self.add_metric("ATTACHMENTS", files)
|
304
|
+
self.add_metric("NUM_ATTACHMENTS", len(files))
|
305
|
+
self.add_metric('NUM_MESSAGES', len(messages))
|
306
|
+
self._result = {"messages": messages, "files": files}
|
307
|
+
return self._result
|
308
|
+
|
309
|
+
async def save_attachment(self, file_path, content):
|
310
|
+
try:
|
311
|
+
self._logger.info(f"IMAP: Saving attachment file: {file_path}")
|
312
|
+
async with aiofiles.open(file_path, mode="wb") as fp:
|
313
|
+
await fp.write(content)
|
314
|
+
except Exception as err:
|
315
|
+
raise ComponentError(f"File {file_path} was not saved: {err}") from err
|