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,400 @@
|
|
1
|
+
import re
|
2
|
+
import json
|
3
|
+
import yaml
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import List, Dict, Any, Optional, Tuple
|
6
|
+
import pandas as pd
|
7
|
+
from pathlib import Path
|
8
|
+
from flowtask.interfaces.powerpoint import PowerPointClient
|
9
|
+
from .flow import FlowComponent
|
10
|
+
from ..exceptions import DataNotFound
|
11
|
+
|
12
|
+
|
13
|
+
class PowerPointSlide(FlowComponent, PowerPointClient):
|
14
|
+
"""
|
15
|
+
PowerPointSlide
|
16
|
+
|
17
|
+
Overview:
|
18
|
+
This component dynamically generates PowerPoint slides from a Pandas DataFrame and a .pptx template.
|
19
|
+
It supports multiple custom modes (e.g., `single`, `double`, `retailer_view`, `overview_3up`, etc.)
|
20
|
+
defined declaratively via a YAML configuration. Each mode specifies the layout and number of images per slide.
|
21
|
+
|
22
|
+
You can name your modes arbitrarily — e.g., `single`, `double`, `quadruple`, `1x1`, `retailer_block`, etc.
|
23
|
+
The logic does not assume or enforce fixed names. What matters is that each mode entry in `mode_content`
|
24
|
+
defines a valid layout index and list of image placeholders.
|
25
|
+
|
26
|
+
The component intelligently splits grouped rows into chunks based on the selected mode’s image capacity.
|
27
|
+
If there are leftover rows that don’t fill a full chunk, the component will automatically fall back to
|
28
|
+
another mode with fewer images, based on availability.
|
29
|
+
|
30
|
+
Configuration Parameters (YAML):
|
31
|
+
|
32
|
+
.. table:: Properties
|
33
|
+
:widths: auto
|
34
|
+
|
35
|
+
+---------------------+----------+-----------------------------------------------------------------+
|
36
|
+
| Name | Required | Summary |
|
37
|
+
+---------------------+----------+-----------------------------------------------------------------+
|
38
|
+
| template_path | Yes | Absolute path to the `.pptx` PowerPoint template file. |
|
39
|
+
+---------------------+----------+-----------------------------------------------------------------+
|
40
|
+
| output_file_path | Yes | Output path where the final `.pptx` will be saved. |
|
41
|
+
+---------------------+----------+-----------------------------------------------------------------+
|
42
|
+
| mode | Yes | Primary mode to use (e.g., `double`, `retailer_block`, etc.) |
|
43
|
+
| | | Controls the layout and chunking logic for slides. |
|
44
|
+
+---------------------+----------+-----------------------------------------------------------------+
|
45
|
+
| mode_content | Yes | Either a dict of modes, or a dict with key `file` pointing to a |
|
46
|
+
| | | `.yaml` or `.json` file containing the full mode structure. |
|
47
|
+
+---------------------+----------+-----------------------------------------------------------------+
|
48
|
+
|
49
|
+
|
50
|
+
Variable Masking:
|
51
|
+
Text and image fields in the YAML configuration can embed dynamic variables using curly-brace syntax.
|
52
|
+
These placeholders are automatically replaced at runtime using each row's values from the DataFrame.
|
53
|
+
|
54
|
+
Example:
|
55
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
56
|
+
→ becomes: "Target #1234, Miami, FL"
|
57
|
+
|
58
|
+
Missing columns will leave the placeholder unresolved (e.g., `{missing_key}`).
|
59
|
+
|
60
|
+
Text and Image Rendering:
|
61
|
+
Each `images:` block must define:
|
62
|
+
- an image placeholder name
|
63
|
+
- a `scale_factor`
|
64
|
+
- a `path` (can include variable masks)
|
65
|
+
- an optional nested `text:` block:
|
66
|
+
- `placeholder_id`: text placeholder where the caption will be rendered
|
67
|
+
- `text`: masked string
|
68
|
+
- plus optional formatting: `font_size`, `font_color`, `bold`, etc.
|
69
|
+
|
70
|
+
Text content shared across the slide (like headers) is defined in `text_content:`
|
71
|
+
and rendered using the first row in the chunk.
|
72
|
+
|
73
|
+
Grouping and Fallback Logic:
|
74
|
+
- If `group_by` is defined, rows will be grouped accordingly.
|
75
|
+
- Each group is divided into chunks of size N, where N = number of images in the selected mode.
|
76
|
+
- Leftover rows that don’t fill a full slide are rendered using another mode that supports
|
77
|
+
exactly the number of remaining images.
|
78
|
+
- The fallback is automatically selected by checking all other modes and picking the one
|
79
|
+
with the largest number of images ≤ remaining.
|
80
|
+
|
81
|
+
Example mode_content (YAML snippet):
|
82
|
+
```yaml
|
83
|
+
mode: double
|
84
|
+
mode_content:
|
85
|
+
# file: "slides_mode_content.yaml" (Optional, excludes following mode declarations)
|
86
|
+
single:
|
87
|
+
default_master_index: 0
|
88
|
+
default_layout_index: 1
|
89
|
+
text_content:
|
90
|
+
- "Text Placeholder 3":
|
91
|
+
text: "{retailer} | "
|
92
|
+
- "Text Placeholder 3":
|
93
|
+
text: "{category}"
|
94
|
+
font_size: 24
|
95
|
+
font_color: "808080"
|
96
|
+
bold: False
|
97
|
+
images:
|
98
|
+
- "Picture Placeholder 1":
|
99
|
+
scale_factor: 0.32 (optional)
|
100
|
+
path: "{file_path}"
|
101
|
+
# data: "{file_data}" (optional, excludes using path)
|
102
|
+
text:
|
103
|
+
placeholder_id: "Text Placeholder 2"
|
104
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
105
|
+
|
106
|
+
double:
|
107
|
+
group_by: ["retailer", "category"]
|
108
|
+
default_master_index: 0
|
109
|
+
default_layout_index: 2
|
110
|
+
text_content:
|
111
|
+
- "Text Placeholder 5":
|
112
|
+
text: "{retailer} | "
|
113
|
+
- "Text Placeholder 5":
|
114
|
+
text: "{category}"
|
115
|
+
font_size: 24
|
116
|
+
font_color: "808080"
|
117
|
+
bold: False
|
118
|
+
images:
|
119
|
+
- "Picture Placeholder 2":
|
120
|
+
scale_factor: 0.32 (optional)
|
121
|
+
path: "{file_path}"
|
122
|
+
# data: "{file_data}" (optional, excludes using path)
|
123
|
+
text:
|
124
|
+
placeholder_id: "Text Placeholder 1"
|
125
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
126
|
+
|
127
|
+
- "Picture Placeholder 3":
|
128
|
+
scale_factor: 0.32 (optional)
|
129
|
+
path: "{file_path}"
|
130
|
+
# data: "{file_data}" (optional, excludes using path)
|
131
|
+
text:
|
132
|
+
placeholder_id: "Text Placeholder 4"
|
133
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
134
|
+
|
135
|
+
triple:
|
136
|
+
group_by: ["retailer", "category"]
|
137
|
+
default_master_index: 0
|
138
|
+
default_layout_index: 3
|
139
|
+
text_content:
|
140
|
+
- "Text Placeholder 5":
|
141
|
+
text: "{retailer} | "
|
142
|
+
- "Text Placeholder 5":
|
143
|
+
text: "{category}"
|
144
|
+
font_size: 24
|
145
|
+
font_color: "808080"
|
146
|
+
bold: False
|
147
|
+
images:
|
148
|
+
- "Picture Placeholder 2":
|
149
|
+
scale_factor: 0.32 (optional)
|
150
|
+
path: "{file_path}"
|
151
|
+
# data: "{file_data}" (optional, excludes using path)
|
152
|
+
text:
|
153
|
+
placeholder_id: "Text Placeholder 1"
|
154
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
155
|
+
|
156
|
+
- "Picture Placeholder 3":
|
157
|
+
scale_factor: 0.32(optional)
|
158
|
+
path: "{file_path}"
|
159
|
+
# data: "{file_data}" (optional, excludes using path)
|
160
|
+
text:
|
161
|
+
placeholder_id: "Text Placeholder 4"
|
162
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
163
|
+
|
164
|
+
- "Picture Placeholder 6":
|
165
|
+
scale_factor: 0.32(optional)
|
166
|
+
path: "{file_path}"
|
167
|
+
# data: "{file_data}" (optional, excludes using path)
|
168
|
+
text:
|
169
|
+
placeholder_id: "Text Placeholder 7"
|
170
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
171
|
+
|
172
|
+
quadruple:
|
173
|
+
group_by: ["retailer", "category"]
|
174
|
+
default_master_index: 0
|
175
|
+
default_layout_index: 4
|
176
|
+
text_content:
|
177
|
+
- "Text Placeholder 5":
|
178
|
+
text: "{retailer} | "
|
179
|
+
- "Text Placeholder 5":
|
180
|
+
text: "{category}"
|
181
|
+
font_size: 24
|
182
|
+
font_color: "808080"
|
183
|
+
bold: False
|
184
|
+
images:
|
185
|
+
- "Picture Placeholder 2":
|
186
|
+
scale_factor: 0.32(optional)
|
187
|
+
path: "{file_path}"
|
188
|
+
# data: "{file_data}" (optional, excludes using path)
|
189
|
+
text:
|
190
|
+
placeholder_id: "Text Placeholder 1"
|
191
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
192
|
+
|
193
|
+
- "Picture Placeholder 3":
|
194
|
+
scale_factor: 0.32(optional)
|
195
|
+
path: "{file_path}"
|
196
|
+
# data: "{file_data}" (optional, excludes using path)
|
197
|
+
text:
|
198
|
+
placeholder_id: "Text Placeholder 4"
|
199
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
200
|
+
|
201
|
+
- "Picture Placeholder 6":
|
202
|
+
scale_factor: 0.32(optional)
|
203
|
+
path: "{file_path}"
|
204
|
+
# data: "{file_data}" (optional, excludes using path)
|
205
|
+
text:
|
206
|
+
placeholder_id: "Text Placeholder 8"
|
207
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
208
|
+
|
209
|
+
- "Picture Placeholder 7":
|
210
|
+
scale_factor: 0.32(optional)
|
211
|
+
path: "{file_path}"
|
212
|
+
# data: "{file_data}" (optional, excludes using path)
|
213
|
+
text:
|
214
|
+
placeholder_id: "Text Placeholder 9"
|
215
|
+
text: "{retailer} #{location_code}, {city}, {state_code}"
|
216
|
+
|
217
|
+
retailer_4up:
|
218
|
+
default_layout_index: 4
|
219
|
+
...
|
220
|
+
```
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
str: Absolute path to the saved PowerPoint presentation file.
|
224
|
+
|
225
|
+
Raises:
|
226
|
+
DataNotFound: If the DataFrame input is empty or not provided.
|
227
|
+
ValueError: If no suitable fallback layout is available for remaining records in a group.
|
228
|
+
|
229
|
+
"""
|
230
|
+
|
231
|
+
async def start(self, **kwargs):
|
232
|
+
# Validate required base configuration attributes
|
233
|
+
required_fields = ['template_path', 'output_file_path', 'mode', 'mode_content']
|
234
|
+
for field in required_fields:
|
235
|
+
if not getattr(self, field, None):
|
236
|
+
raise ValueError(f"Missing required parameter: `{field}`")
|
237
|
+
|
238
|
+
if not self.previous or self.input.empty:
|
239
|
+
raise DataNotFound("No Data Provided to create slides from.")
|
240
|
+
|
241
|
+
# Load external YAML/JSON if mode_content is a file reference
|
242
|
+
if "file" in self.mode_content:
|
243
|
+
file_path = self._taskstore.get_path().joinpath(self._program, self.mode_content["file"])
|
244
|
+
|
245
|
+
if not file_path.exists():
|
246
|
+
raise FileNotFoundError(f"mode_content file not found at: {file_path}")
|
247
|
+
|
248
|
+
self.mode_content = self._parse_file(file_path)
|
249
|
+
|
250
|
+
# Now validate that the mode exists inside mode_content
|
251
|
+
if self.mode not in self.mode_content:
|
252
|
+
raise ValueError(f"Mode '{self.mode}' not found in `mode_content` configuration.")
|
253
|
+
|
254
|
+
# Assign parsed configuration
|
255
|
+
self.all_modes = self.mode_content
|
256
|
+
self.current_mode = self.all_modes[self.mode]
|
257
|
+
self.data = self.input
|
258
|
+
|
259
|
+
# Generate structured slide content
|
260
|
+
self.slide_contents = self._parse_slide_contents(self.data)
|
261
|
+
|
262
|
+
|
263
|
+
async def run(self):
|
264
|
+
result_file_path = self.create_presentation_from_template(
|
265
|
+
template_path=Path(self.template_path),
|
266
|
+
slide_contents=self.slide_contents,
|
267
|
+
file_path=Path(self.output_file_path),
|
268
|
+
default_master_index=self.current_mode.get("default_master_index", 0),
|
269
|
+
default_layout_index=self.current_mode.get("default_layout_index", 1),
|
270
|
+
)
|
271
|
+
self._result = result_file_path
|
272
|
+
self.add_metric("Slides Result Path", f"{self._result!s}")
|
273
|
+
# Please return the number of slides if you need a metric.
|
274
|
+
# self.add_metric("# of Slides created", len(prs.slides))
|
275
|
+
return self._result
|
276
|
+
|
277
|
+
def _parse_file(self, file_path: Path) -> dict:
|
278
|
+
"""
|
279
|
+
Load a file (YAML, JSON, or TOML) and return its parsed content as a dictionary.
|
280
|
+
|
281
|
+
Args:
|
282
|
+
file_path (Path): Path to the external config file.
|
283
|
+
|
284
|
+
Returns:
|
285
|
+
dict: Parsed content.
|
286
|
+
|
287
|
+
Raises:
|
288
|
+
ValueError: If the file extension is unsupported.
|
289
|
+
"""
|
290
|
+
with file_path.open("r", encoding="utf-8") as f:
|
291
|
+
if file_path.suffix in [".yaml", ".yml"]:
|
292
|
+
return yaml.safe_load(f)
|
293
|
+
if file_path.suffix == ".json":
|
294
|
+
return json.load(f)
|
295
|
+
raise ValueError(f"Unsupported mode_content file type: {file_path.suffix}")
|
296
|
+
|
297
|
+
|
298
|
+
async def close(self):
|
299
|
+
pass
|
300
|
+
|
301
|
+
def _parse_slide_contents(self, data: pd.DataFrame) -> List[Dict[str, Any]]:
|
302
|
+
def resolve_template(template: str, row: Dict[str, Any]) -> Any:
|
303
|
+
"""
|
304
|
+
If template is a plain key like "{file_path}", return row['file_path'] as-is,
|
305
|
+
which may be a BytesIO. Otherwise, perform full substitution and return a string.
|
306
|
+
"""
|
307
|
+
# Direct match for single variable placeholder only
|
308
|
+
match = re.fullmatch(r"\{(\w+)\}", template)
|
309
|
+
if match:
|
310
|
+
key = match.group(1)
|
311
|
+
return row.get(key, f"{{{key}}}")
|
312
|
+
|
313
|
+
# Generic substitution for text templates
|
314
|
+
return re.sub(r"\{(\w+)\}", lambda m: str(row.get(m.group(1), f"{{{m.group(1)}}}")), template)
|
315
|
+
|
316
|
+
|
317
|
+
def resolve_image_entry(row: Dict[str, Any], entry: Dict[str, Any]) \
|
318
|
+
-> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]:
|
319
|
+
image_conf: Dict[str, Any] = {}
|
320
|
+
text_conf: Optional[Dict[str, Any]] = None
|
321
|
+
|
322
|
+
for placeholder_id, props in entry.items():
|
323
|
+
for key, val in props.items():
|
324
|
+
if key == "text" and isinstance(val, dict):
|
325
|
+
pid = val["placeholder_id"]
|
326
|
+
text_conf = {
|
327
|
+
pid: {
|
328
|
+
k: resolve_template(v, row)
|
329
|
+
for k, v in val.items()
|
330
|
+
if k != "placeholder_id"
|
331
|
+
}
|
332
|
+
}
|
333
|
+
else:
|
334
|
+
image_conf[key] = resolve_template(val, row) if isinstance(val, str) else val
|
335
|
+
image_conf["placeholder_id"] = placeholder_id
|
336
|
+
|
337
|
+
return image_conf, text_conf
|
338
|
+
|
339
|
+
slides: List[Dict[str, Any]] = []
|
340
|
+
mode_cfg = self.current_mode
|
341
|
+
primary_image_count = len(mode_cfg.get("images", []))
|
342
|
+
|
343
|
+
# Map number of images per mode
|
344
|
+
image_mode_map = {
|
345
|
+
len(cfg.get("images", [])): cfg
|
346
|
+
for _, cfg in self.all_modes.items()
|
347
|
+
}
|
348
|
+
|
349
|
+
group_keys = mode_cfg.get("group_by", [])
|
350
|
+
groups = data.groupby(group_keys) if group_keys else [(None, data)]
|
351
|
+
|
352
|
+
for _, df_group in groups:
|
353
|
+
records = df_group.to_dict(orient="records")
|
354
|
+
idx = 0
|
355
|
+
total = len(records)
|
356
|
+
|
357
|
+
# Always start with the user-selected mode
|
358
|
+
while idx < total:
|
359
|
+
remaining = total - idx
|
360
|
+
|
361
|
+
# First chunk uses the explicitly selected mode
|
362
|
+
if idx == 0 and remaining >= primary_image_count:
|
363
|
+
sub_cfg = mode_cfg
|
364
|
+
chunk_size = primary_image_count
|
365
|
+
else:
|
366
|
+
# For the remaining, find best fallback that fits
|
367
|
+
possible_sizes = sorted([n for n in image_mode_map if n <= remaining], reverse=True)
|
368
|
+
if not possible_sizes:
|
369
|
+
raise ValueError(f"No fallback mode defined for remaining chunk size {remaining}")
|
370
|
+
chunk_size = possible_sizes[0]
|
371
|
+
sub_cfg = image_mode_map[chunk_size]
|
372
|
+
|
373
|
+
slide = {
|
374
|
+
"master_index": sub_cfg.get("default_master_index", 0),
|
375
|
+
"layout_index": sub_cfg.get("default_layout_index", 0),
|
376
|
+
"text_content": [],
|
377
|
+
"image": []
|
378
|
+
}
|
379
|
+
|
380
|
+
row0 = records[idx]
|
381
|
+
for txt in sub_cfg.get("text_content", []):
|
382
|
+
for pid, props in txt.items():
|
383
|
+
resolved = {
|
384
|
+
k: resolve_template(v, row0) if isinstance(v, str) else v
|
385
|
+
for k, v in props.items()
|
386
|
+
}
|
387
|
+
slide["text_content"].append({pid: resolved})
|
388
|
+
|
389
|
+
for j, img_entry in enumerate(sub_cfg["images"]):
|
390
|
+
if idx + j >= total:
|
391
|
+
break
|
392
|
+
img_conf, txt_conf = resolve_image_entry(records[idx + j], img_entry)
|
393
|
+
slide["image"].append(img_conf)
|
394
|
+
if txt_conf:
|
395
|
+
slide["text_content"].append(txt_conf)
|
396
|
+
|
397
|
+
slides.append(slide)
|
398
|
+
idx += chunk_size
|
399
|
+
|
400
|
+
return slides
|
@@ -0,0 +1,127 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
import asyncio
|
3
|
+
from asyncdb.utils.functions import colors, cPrint
|
4
|
+
from ..utils import SafeDict
|
5
|
+
from .flow import FlowComponent
|
6
|
+
|
7
|
+
|
8
|
+
class PrintMessage(FlowComponent):
|
9
|
+
"""
|
10
|
+
PrintMessage
|
11
|
+
|
12
|
+
Overview
|
13
|
+
|
14
|
+
This component prints a formatted message to the console with optional coloring and logging.
|
15
|
+
|
16
|
+
.. table:: Properties
|
17
|
+
:widths: auto
|
18
|
+
|
19
|
+
|
20
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
21
|
+
| Name | Required | Summary |
|
22
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
23
|
+
| message | Yes | The message to print, with optional variable substitution. |
|
24
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
25
|
+
| color | No | The color to use for the message. Overrides the level-based color.|
|
26
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
27
|
+
| level | No | The log level of the message ("INFO", "DEBUG", "WARN", "ERROR", |
|
28
|
+
| | | "CRITICAL"). Default is "INFO". |
|
29
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
30
|
+
| condition | No | A condition to evaluate before printing the message. The message |
|
31
|
+
| | | is printed only if the condition is True. |
|
32
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
33
|
+
| first | Yes | First message |
|
34
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
35
|
+
| last | Yes | Last message |
|
36
|
+
+-------------+----------+-----------+-------------------------------------------------------+
|
37
|
+
|
38
|
+
Returns
|
39
|
+
|
40
|
+
This component returns the printed message.
|
41
|
+
|
42
|
+
|
43
|
+
Example:
|
44
|
+
|
45
|
+
```yaml
|
46
|
+
PrintMessage:
|
47
|
+
message: 'End Form Metadata: {orgid}/{formid}'
|
48
|
+
color: green
|
49
|
+
level: WARN
|
50
|
+
```
|
51
|
+
|
52
|
+
"""
|
53
|
+
|
54
|
+
coloring = None
|
55
|
+
color = None
|
56
|
+
level = "INFO"
|
57
|
+
|
58
|
+
def __init__(
|
59
|
+
self,
|
60
|
+
loop: asyncio.AbstractEventLoop = None,
|
61
|
+
job: Callable = None,
|
62
|
+
stat: Callable = None,
|
63
|
+
**kwargs,
|
64
|
+
):
|
65
|
+
self.message: str = kwargs.get("message", '')
|
66
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
67
|
+
|
68
|
+
async def start(self, **kwargs):
|
69
|
+
"""Initialize the color setup."""
|
70
|
+
if self.previous:
|
71
|
+
self.data = self.input
|
72
|
+
try:
|
73
|
+
if self.color:
|
74
|
+
try:
|
75
|
+
self.coloring = colors.bold + getattr(colors.fg, self.color)
|
76
|
+
except Exception as err:
|
77
|
+
self._logger.error(f"Wrong color schema {self.color}, error: {err}")
|
78
|
+
self.coloring = colors.reset
|
79
|
+
elif self.level:
|
80
|
+
if self.level == "INFO":
|
81
|
+
self.coloring = colors.bold + colors.fg.green
|
82
|
+
elif self.level == "DEBUG":
|
83
|
+
self.coloring = colors.fg.lightblue
|
84
|
+
elif self.level == "WARN":
|
85
|
+
self.coloring = colors.bold + colors.fg.yellow
|
86
|
+
elif self.level == "ERROR":
|
87
|
+
self.coloring = colors.fg.lightred
|
88
|
+
elif self.level == "CRITICAL":
|
89
|
+
self.coloring = colors.bold + colors.fg.red
|
90
|
+
else:
|
91
|
+
self.coloring = colors.reset
|
92
|
+
else:
|
93
|
+
self.coloring = colors.reset
|
94
|
+
return True
|
95
|
+
except (NameError, ValueError):
|
96
|
+
self.coloring = colors.reset
|
97
|
+
|
98
|
+
async def run(self):
|
99
|
+
"""Run Message."""
|
100
|
+
self._result = self.data
|
101
|
+
try:
|
102
|
+
if hasattr(self, "condition"):
|
103
|
+
for val in self._variables:
|
104
|
+
self.condition = self.condition.replace(
|
105
|
+
"{{{}}}".format(str(val)), str(self._variables[val])
|
106
|
+
)
|
107
|
+
# if not eval(self.condition): # pylint: disable=W0123
|
108
|
+
# return False
|
109
|
+
msg = self.message.format_map(SafeDict(**self._params))
|
110
|
+
for val in self._variables:
|
111
|
+
msg = msg.replace("{{{}}}".format(str(val)), str(self._variables[val]))
|
112
|
+
print(self.coloring + msg, colors.reset)
|
113
|
+
if self._debug:
|
114
|
+
self._logger.debug(msg)
|
115
|
+
if "PRINT_MESSAGE" not in self._variables:
|
116
|
+
self._variables["PRINT_MESSAGE"] = {}
|
117
|
+
if self.level not in self._variables["PRINT_MESSAGE"]:
|
118
|
+
self._variables["PRINT_MESSAGE"][self.level] = []
|
119
|
+
self._variables["PRINT_MESSAGE"][self.level].append(msg)
|
120
|
+
self.add_metric("message", msg)
|
121
|
+
except Exception as err:
|
122
|
+
self._logger.exception(f"PrintMessage Error: {err}")
|
123
|
+
return False
|
124
|
+
return self._result
|
125
|
+
|
126
|
+
async def close(self):
|
127
|
+
"""Method."""
|
@@ -0,0 +1,72 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from typing import Any, List
|
3
|
+
import pandas as pd
|
4
|
+
from bs4 import BeautifulSoup as bs
|
5
|
+
from ....interfaces import SeleniumService, HTTPService
|
6
|
+
import logging
|
7
|
+
|
8
|
+
class ProductCompetitorsBase(SeleniumService, HTTPService):
|
9
|
+
"""
|
10
|
+
ProductCompetitorsBase Model.
|
11
|
+
Define how competitor product scrappers should work.
|
12
|
+
"""
|
13
|
+
url: str = ''
|
14
|
+
domain: str = ''
|
15
|
+
cookies: Any = None
|
16
|
+
# Columnas estándar que todos los parsers deben retornar
|
17
|
+
standard_columns: List[str] = [
|
18
|
+
'competitor_brand',
|
19
|
+
'competitor_name',
|
20
|
+
'competitor_url',
|
21
|
+
'competitor_sku',
|
22
|
+
'competitor_price',
|
23
|
+
'competitor_rating',
|
24
|
+
'competitor_reviews'
|
25
|
+
]
|
26
|
+
|
27
|
+
def __init__(self, *args, **kwargs):
|
28
|
+
self._driver = None
|
29
|
+
self.cookies = kwargs.get('cookies', None)
|
30
|
+
self._logger = logging.getLogger(self.__class__.__name__)
|
31
|
+
self.url = kwargs.get('url', self.url)
|
32
|
+
self.use_proxy = True
|
33
|
+
self._free_proxy = False
|
34
|
+
super().__init__(*args, **kwargs)
|
35
|
+
|
36
|
+
def get_bs(self, response: object) -> Any:
|
37
|
+
if isinstance(response, str):
|
38
|
+
return bs(response, 'html.parser')
|
39
|
+
return bs(response.text, 'html.parser')
|
40
|
+
|
41
|
+
async def get(self, url: str, headers: dict = None):
|
42
|
+
try:
|
43
|
+
await self.get_page(url=url)
|
44
|
+
return self._driver.page_source
|
45
|
+
except Exception as err:
|
46
|
+
self._logger.error(f'Error getting page: {err}')
|
47
|
+
return None
|
48
|
+
|
49
|
+
def set_columns(self, df: pd.DataFrame) -> None:
|
50
|
+
for col in self.standard_columns:
|
51
|
+
if col not in df.columns:
|
52
|
+
df[col] = None
|
53
|
+
|
54
|
+
@abstractmethod
|
55
|
+
async def connect(self):
|
56
|
+
"""Creates the Driver and Connects to the Site."""
|
57
|
+
|
58
|
+
@abstractmethod
|
59
|
+
async def disconnect(self):
|
60
|
+
"""Disconnects the Driver and closes the Connection."""
|
61
|
+
|
62
|
+
async def __aenter__(self):
|
63
|
+
await self.connect()
|
64
|
+
return self
|
65
|
+
|
66
|
+
async def __aexit__(self, exc_type, exc_value, traceback):
|
67
|
+
await self.disconnect()
|
68
|
+
|
69
|
+
def set_empty_values(self, row: dict, brand: str) -> None:
|
70
|
+
"""Set empty values for all standard columns for a given brand"""
|
71
|
+
for col in self.standard_columns:
|
72
|
+
row[f"{col}_{brand}"] = None
|