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,409 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
import asyncio
|
3
|
+
import logging
|
4
|
+
import random
|
5
|
+
import backoff
|
6
|
+
import httpx
|
7
|
+
from typing import Optional, Dict, Any
|
8
|
+
import pandas as pd
|
9
|
+
from bs4 import BeautifulSoup
|
10
|
+
from ..exceptions import ComponentError, ConfigError
|
11
|
+
from ..interfaces import HTTPService, SeleniumService
|
12
|
+
from ..interfaces.http import ua, bad_gateway_exception
|
13
|
+
from .flow import FlowComponent
|
14
|
+
import json
|
15
|
+
|
16
|
+
|
17
|
+
class ZoomInfoScraper(FlowComponent, HTTPService, SeleniumService):
|
18
|
+
"""
|
19
|
+
ZoomInfo Scraper Component that can use either HTTP or Selenium for scraping.
|
20
|
+
|
21
|
+
Overview:
|
22
|
+
|
23
|
+
This component scrapes company information from ZoomInfo pages using HTTPService.
|
24
|
+
It can receive URLs from a previous component (like GoogleSearch) and extract
|
25
|
+
specific company information.
|
26
|
+
|
27
|
+
.. table:: Properties
|
28
|
+
:widths: auto
|
29
|
+
|
30
|
+
+-----------------------+----------+------------------------------------------------------------------------------------------------------+
|
31
|
+
| Name | Required | Description |
|
32
|
+
+-----------------------+----------+------------------------------------------------------------------------------------------------------+
|
33
|
+
| url_column (str) | Yes | Name of the column containing URLs to scrape (default: 'search_url') |
|
34
|
+
+-----------------------+----------+------------------------------------------------------------------------------------------------------+
|
35
|
+
| wait_for (tuple) | No | Element to wait for before scraping (default: ('class', 'company-overview')) |
|
36
|
+
+-----------------------+----------+------------------------------------------------------------------------------------------------------+
|
37
|
+
|
38
|
+
Return:
|
39
|
+
|
40
|
+
The component adds new columns to the DataFrame with company information:
|
41
|
+
- headquarters
|
42
|
+
- phone_number
|
43
|
+
- website
|
44
|
+
- stock_symbol
|
45
|
+
- naics_code
|
46
|
+
- employee_count
|
47
|
+
"""
|
48
|
+
|
49
|
+
def __init__(self, loop: asyncio.AbstractEventLoop = None, job: Callable = None, stat: Callable = None, **kwargs) -> None:
|
50
|
+
super().__init__(loop=loop, job=job, stat=stat, **kwargs)
|
51
|
+
|
52
|
+
# Flag to determine which service to use
|
53
|
+
self.use_selenium = kwargs.get('use_selenium', True)
|
54
|
+
|
55
|
+
# Configure common attributes
|
56
|
+
self.url_column = kwargs.get('url_column', 'search_url')
|
57
|
+
self._counter = 0
|
58
|
+
self._debug = kwargs.get('debug', False)
|
59
|
+
self._semaphore = asyncio.Semaphore(kwargs.get('max_concurrent', 5))
|
60
|
+
|
61
|
+
# Proxy configuration like BestBuy
|
62
|
+
self.use_proxy = True
|
63
|
+
self._free_proxy = False
|
64
|
+
self.paid_proxy = True
|
65
|
+
|
66
|
+
# Configure Selenium specific settings if needed
|
67
|
+
if self.use_selenium:
|
68
|
+
self.accept_cookies = kwargs.get('accept_cookies', ('id', 'onetrust-accept-btn-handler'))
|
69
|
+
self.wait_until = kwargs.get('wait_until', ('class', 'company-overview'))
|
70
|
+
self.timeout = kwargs.get('timeout', 30)
|
71
|
+
|
72
|
+
# Configure Selenium proxy if using Selenium
|
73
|
+
if self.use_proxy:
|
74
|
+
self.seleniumwire_options = {
|
75
|
+
'proxy': {
|
76
|
+
'http': f'http://{OXYLABS_USERNAME}:{OXYLABS_PASSWORD}@{OXYLABS_ENDPOINT}',
|
77
|
+
'https': f'http://{OXYLABS_USERNAME}:{OXYLABS_PASSWORD}@{OXYLABS_ENDPOINT}'
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
# Configure HTTP specific settings
|
82
|
+
else:
|
83
|
+
self.headers = {
|
84
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
85
|
+
"Accept-Language": "en-US,en;q=0.5",
|
86
|
+
"Accept-Encoding": "gzip, deflate, br",
|
87
|
+
"Connection": "keep-alive",
|
88
|
+
"Upgrade-Insecure-Requests": "1",
|
89
|
+
"User-Agent": random.choice(ua),
|
90
|
+
**kwargs.get('headers', {})
|
91
|
+
}
|
92
|
+
self.session_cookies = kwargs.get('session_cookies', {})
|
93
|
+
|
94
|
+
def split_parts(self, task_list, num_parts: int = 5) -> list:
|
95
|
+
"""Split task list into parts for concurrent processing."""
|
96
|
+
part_size = len(task_list) // num_parts
|
97
|
+
remainder = len(task_list) % num_parts
|
98
|
+
parts = []
|
99
|
+
start = 0
|
100
|
+
for i in range(num_parts):
|
101
|
+
# Distribute the remainder across the first `remainder` parts
|
102
|
+
end = start + part_size + (1 if i < remainder else 0)
|
103
|
+
parts.append(task_list[start:end])
|
104
|
+
start = end
|
105
|
+
return parts
|
106
|
+
|
107
|
+
async def _processing_tasks(self, tasks: list) -> pd.DataFrame:
|
108
|
+
"""Process tasks concurrently and format the results."""
|
109
|
+
results = []
|
110
|
+
for chunk in self.split_parts(tasks, self.task_parts):
|
111
|
+
result = await asyncio.gather(*chunk, return_exceptions=False)
|
112
|
+
results.extend(result)
|
113
|
+
|
114
|
+
# Filter out None results and separate index and data
|
115
|
+
valid_results = [(idx, data) for idx, data in results if data]
|
116
|
+
|
117
|
+
if not valid_results:
|
118
|
+
return pd.DataFrame()
|
119
|
+
|
120
|
+
# Split into indices and data dictionaries
|
121
|
+
indices, data_dicts = zip(*valid_results)
|
122
|
+
|
123
|
+
# Convert list of dictionaries to DataFrame
|
124
|
+
df = pd.DataFrame(data_dicts, index=indices)
|
125
|
+
|
126
|
+
# Ensure all expected columns exist
|
127
|
+
expected_columns = [
|
128
|
+
'company_name',
|
129
|
+
'logo_url',
|
130
|
+
'address',
|
131
|
+
'phone_number',
|
132
|
+
'website',
|
133
|
+
'stock_symbol',
|
134
|
+
'naics_code',
|
135
|
+
'sic_code',
|
136
|
+
'employee_count',
|
137
|
+
'revenue_range',
|
138
|
+
'similar_companies',
|
139
|
+
'search_term',
|
140
|
+
'search_url'
|
141
|
+
]
|
142
|
+
|
143
|
+
for col in expected_columns:
|
144
|
+
if col not in df.columns:
|
145
|
+
df[col] = None
|
146
|
+
|
147
|
+
return df
|
148
|
+
|
149
|
+
async def start(self, **kwargs) -> bool:
|
150
|
+
"""Initialize the component and validate required parameters."""
|
151
|
+
if self.previous:
|
152
|
+
self.data = self.input
|
153
|
+
|
154
|
+
if not isinstance(self.data, pd.DataFrame):
|
155
|
+
raise ComponentError(
|
156
|
+
"Input must be a DataFrame", status=404
|
157
|
+
)
|
158
|
+
|
159
|
+
if self.url_column not in self.data.columns:
|
160
|
+
raise ConfigError(
|
161
|
+
f"Column {self.url_column} not found in DataFrame"
|
162
|
+
)
|
163
|
+
|
164
|
+
# Initialize result columns
|
165
|
+
new_columns = [
|
166
|
+
'search_term',
|
167
|
+
'search_url',
|
168
|
+
'company_name',
|
169
|
+
'logo_url',
|
170
|
+
'address',
|
171
|
+
'phone_number', 'website',
|
172
|
+
'stock_symbol', 'naics_code', 'sic_code',
|
173
|
+
'employee_count', 'revenue_range', 'similar_companies'
|
174
|
+
]
|
175
|
+
for col in new_columns:
|
176
|
+
if col not in self.data.columns:
|
177
|
+
self.data[col] = None
|
178
|
+
|
179
|
+
return True
|
180
|
+
|
181
|
+
def extract_company_info(self, soup: BeautifulSoup, search_term: str, search_url: str) -> Dict[str, Any]:
|
182
|
+
"""Extract company information from the page."""
|
183
|
+
result = {}
|
184
|
+
result['search_term'] = search_term
|
185
|
+
result['search_url'] = search_url
|
186
|
+
|
187
|
+
# Get company name and logo URL from header
|
188
|
+
logo = soup.find('img', {'alt': True, 'width': '76.747'})
|
189
|
+
if logo:
|
190
|
+
result['company_name'] = logo.get('alt')
|
191
|
+
result['logo_url'] = logo.get('src')
|
192
|
+
|
193
|
+
# Extract information from About section
|
194
|
+
about_section = soup.find('app-about')
|
195
|
+
if about_section:
|
196
|
+
# Get headquarters/address
|
197
|
+
address_container = about_section.find('div', {'class': 'icon-text-container'})
|
198
|
+
if address_container and 'Headquarters' in address_container.text:
|
199
|
+
result['address'] = address_container.find('span', {'class': 'content'}).text.strip()
|
200
|
+
|
201
|
+
# Get phone number
|
202
|
+
phone_container = about_section.find_all('div', {'class': 'icon-text-container'})[1]
|
203
|
+
if phone_container and 'Phone Number' in phone_container.text:
|
204
|
+
result['phone_number'] = phone_container.find('span', {'class': 'content'}).text.strip()
|
205
|
+
|
206
|
+
# Get website
|
207
|
+
website_container = about_section.find_all('div', {'class': 'icon-text-container'})[2]
|
208
|
+
if website_container:
|
209
|
+
website_link = website_container.find('a', {'class': 'website-link'})
|
210
|
+
if website_link:
|
211
|
+
result['website'] = website_link.text.strip()
|
212
|
+
|
213
|
+
# Get revenue
|
214
|
+
revenue_container = about_section.find_all('div', {'class': 'icon-text-container'})[3]
|
215
|
+
if revenue_container and 'Revenue' in revenue_container.text:
|
216
|
+
result['revenue_range'] = revenue_container.find('span', {'class': 'content'}).text.strip()
|
217
|
+
|
218
|
+
# Get employee count from company compare section
|
219
|
+
company_compare = soup.find('app-company-compare-details', {'class': 'first-company'})
|
220
|
+
if company_compare:
|
221
|
+
emp_count = company_compare.find('div', {'class': 'num-of-emp'})
|
222
|
+
if emp_count:
|
223
|
+
result['employee_count'] = emp_count.text.strip()
|
224
|
+
|
225
|
+
# Get industry information
|
226
|
+
industry_chips = about_section.find('span', {'id': 'company-chips-wrapper'})
|
227
|
+
if industry_chips:
|
228
|
+
industries = [chip.text.strip() for chip in industry_chips.find_all('a', {'class': 'link'})]
|
229
|
+
result['industries'] = industries
|
230
|
+
|
231
|
+
# Get company description
|
232
|
+
overview = soup.find('app-company-overview')
|
233
|
+
if overview:
|
234
|
+
desc = overview.find('span', {'class': 'company-desc'})
|
235
|
+
if desc:
|
236
|
+
result['description'] = desc.text.strip()
|
237
|
+
|
238
|
+
# Get social media links
|
239
|
+
social_media = soup.find('span', {'id': 'social-media-icons-wrapper'})
|
240
|
+
if social_media:
|
241
|
+
social_links = {}
|
242
|
+
for link in social_media.find_all('a', {'class': 'social-media-icon'}):
|
243
|
+
platform = link.get('id').split('-')[1].lower()
|
244
|
+
social_links[platform] = link.get('href')
|
245
|
+
result['social_media'] = social_links
|
246
|
+
|
247
|
+
# Get SIC and NAICS codes
|
248
|
+
codes_wrapper = soup.find('span', {'id': 'codes-wrapper'})
|
249
|
+
if codes_wrapper:
|
250
|
+
for code in codes_wrapper.find_all('span', {'class': 'codes-content'}):
|
251
|
+
if 'SIC Code' in code.text:
|
252
|
+
result['sic_code'] = code.text.replace('SIC Code', '').strip()
|
253
|
+
elif 'NAICS Code' in code.text:
|
254
|
+
result['naics_code'] = code.text.replace('NAICS Code', '').strip()
|
255
|
+
|
256
|
+
# Get similar companies
|
257
|
+
similar_companies = []
|
258
|
+
company_compare = soup.find('app-company-compare')
|
259
|
+
if company_compare:
|
260
|
+
for company in company_compare.find_all('app-company-compare-details')[1:]: # Skip first (current company)
|
261
|
+
company_name = company.find('a', {'class': 'company-name'})
|
262
|
+
if not company_name:
|
263
|
+
continue
|
264
|
+
|
265
|
+
similar_company = {
|
266
|
+
'name': company_name.text.strip(),
|
267
|
+
'revenue': company.find('div', {'class': 'revenue'}).text.strip() if company.find('div', {'class': 'revenue'}) else None,
|
268
|
+
'employee_count': company.find('div', {'class': 'num-of-emp'}).text.strip() if company.find('div', {'class': 'num-of-emp'}) else None
|
269
|
+
}
|
270
|
+
similar_companies.append(similar_company)
|
271
|
+
|
272
|
+
if similar_companies:
|
273
|
+
try:
|
274
|
+
result['similar_companies'] = json.dumps(
|
275
|
+
similar_companies,
|
276
|
+
ensure_ascii=False,
|
277
|
+
allow_nan=False,
|
278
|
+
separators=(',', ':')
|
279
|
+
)
|
280
|
+
except Exception as e:
|
281
|
+
self._logger.error(f"Error formatting similar companies JSON: {str(e)}")
|
282
|
+
result['similar_companies'] = None
|
283
|
+
|
284
|
+
if not result:
|
285
|
+
self._logger.warning("No data was extracted from the page")
|
286
|
+
else:
|
287
|
+
self._logger.info(f"Successfully extracted data")
|
288
|
+
|
289
|
+
return result
|
290
|
+
|
291
|
+
@backoff.on_exception(
|
292
|
+
backoff.expo,
|
293
|
+
(httpx.ConnectTimeout, httpx.ReadTimeout, httpx.HTTPStatusError),
|
294
|
+
max_tries=3,
|
295
|
+
max_time=30,
|
296
|
+
giveup=lambda e: not bad_gateway_exception(e) and not isinstance(e, (httpx.ConnectTimeout, httpx.ReadTimeout))
|
297
|
+
)
|
298
|
+
async def scrape_url(self, idx: int, row: pd.Series) -> tuple[int, Optional[Dict[str, Any]]]:
|
299
|
+
"""Scrape a single ZoomInfo URL using either HTTP or Selenium."""
|
300
|
+
async with self._semaphore:
|
301
|
+
try:
|
302
|
+
url = row[self.url_column]
|
303
|
+
search_term = row['search_term']
|
304
|
+
search_url = row['search_url']
|
305
|
+
|
306
|
+
if pd.isna(url):
|
307
|
+
return idx, None
|
308
|
+
|
309
|
+
self._logger.notice(f"Scraping ZoomInfo URL: {url}")
|
310
|
+
|
311
|
+
if self.use_selenium:
|
312
|
+
# Use Selenium for scraping
|
313
|
+
try:
|
314
|
+
# Initialize driver with proxy if needed
|
315
|
+
if not self._driver and self.use_proxy:
|
316
|
+
await self.init_driver(
|
317
|
+
seleniumwire_options=self.seleniumwire_options
|
318
|
+
)
|
319
|
+
await self.get_page(url)
|
320
|
+
content = self._driver.page_source
|
321
|
+
soup = BeautifulSoup(content, 'html.parser')
|
322
|
+
except Exception as e:
|
323
|
+
self._logger.error(f"Selenium error for URL {url}: {str(e)}")
|
324
|
+
return idx, None
|
325
|
+
else:
|
326
|
+
# Use HTTP for scraping
|
327
|
+
try:
|
328
|
+
# Get proxies if using HTTP
|
329
|
+
if self.use_proxy:
|
330
|
+
proxies = await self.get_proxies()
|
331
|
+
proxy = proxies[0]
|
332
|
+
proxy_config = {
|
333
|
+
"http://": f"http://{proxy}",
|
334
|
+
"https://": f"http://{proxy}"
|
335
|
+
}
|
336
|
+
else:
|
337
|
+
proxy_config = None
|
338
|
+
|
339
|
+
response = await self._get(
|
340
|
+
url,
|
341
|
+
headers=self.headers,
|
342
|
+
cookies=self.session_cookies,
|
343
|
+
proxies=proxy_config
|
344
|
+
)
|
345
|
+
|
346
|
+
if response.status_code == 403:
|
347
|
+
self._logger.error(f"Access forbidden for URL {url}. Consider switching to Selenium.")
|
348
|
+
return idx, None
|
349
|
+
|
350
|
+
if response.status_code != 200:
|
351
|
+
self._logger.error(f"Failed to fetch URL {url}: {response.status_code}")
|
352
|
+
return idx, None
|
353
|
+
|
354
|
+
content = response.text
|
355
|
+
soup = BeautifulSoup(content, 'html.parser')
|
356
|
+
|
357
|
+
except Exception as e:
|
358
|
+
self._logger.error(f"HTTP error for URL {url}: {str(e)}")
|
359
|
+
return idx, None
|
360
|
+
|
361
|
+
# Check for blocks/captchas
|
362
|
+
if self._is_blocked(soup):
|
363
|
+
self._logger.error(f"Access blocked for URL {url}. Consider switching to Selenium.")
|
364
|
+
return idx, None
|
365
|
+
|
366
|
+
result = self.extract_company_info(soup, search_term, search_url)
|
367
|
+
|
368
|
+
if result:
|
369
|
+
self._counter += 1
|
370
|
+
return idx, result
|
371
|
+
|
372
|
+
except Exception as e:
|
373
|
+
self._logger.error(f"Error scraping URL {url}: {str(e)}")
|
374
|
+
return idx, None
|
375
|
+
|
376
|
+
def _is_blocked(self, soup: BeautifulSoup) -> bool:
|
377
|
+
"""Check if the response indicates we're blocked or need to solve a CAPTCHA."""
|
378
|
+
blocked_indicators = [
|
379
|
+
'captcha',
|
380
|
+
'blocked',
|
381
|
+
'access denied',
|
382
|
+
'please verify you are a human'
|
383
|
+
]
|
384
|
+
|
385
|
+
page_text = soup.get_text().lower()
|
386
|
+
return any(indicator in page_text for indicator in blocked_indicators)
|
387
|
+
|
388
|
+
async def run(self):
|
389
|
+
"""Execute scraping for each URL in the DataFrame."""
|
390
|
+
tasks = [
|
391
|
+
self.scrape_url(idx, row) for idx, row in self.data.iterrows()
|
392
|
+
]
|
393
|
+
df = await self._processing_tasks(tasks)
|
394
|
+
self.add_metric("PAGES_SCRAPED", self._counter)
|
395
|
+
|
396
|
+
if self._debug is True:
|
397
|
+
print(df)
|
398
|
+
print("::: Printing Column Information === ")
|
399
|
+
for column, t in df.dtypes.items():
|
400
|
+
print(column, "->", t, "->", df[column].iloc[0])
|
401
|
+
|
402
|
+
self._result = df
|
403
|
+
return self._result
|
404
|
+
|
405
|
+
async def close(self):
|
406
|
+
"""Clean up resources."""
|
407
|
+
if self.use_selenium:
|
408
|
+
self.close_driver()
|
409
|
+
return True
|
@@ -0,0 +1,104 @@
|
|
1
|
+
from typing import Any
|
2
|
+
import asyncio
|
3
|
+
from pathlib import Path
|
4
|
+
import importlib
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
6
|
+
from navconfig.logging import logging, logger
|
7
|
+
from ..exceptions import NotSupported, ComponentError
|
8
|
+
from ..download import download_component
|
9
|
+
from .abstract import AbstractFlow
|
10
|
+
from .flow import FlowComponent
|
11
|
+
from .user import UserComponent
|
12
|
+
from .group import GroupComponent
|
13
|
+
|
14
|
+
|
15
|
+
__all__ = (
|
16
|
+
"AbstractFlow",
|
17
|
+
"FlowComponent",
|
18
|
+
"UserComponent",
|
19
|
+
"GroupComponent",
|
20
|
+
)
|
21
|
+
|
22
|
+
_COMPONENTS: dict[str, Any] = {}
|
23
|
+
|
24
|
+
|
25
|
+
def importComponent(component: str, classpath: str = None, package: str = "components"):
|
26
|
+
if not classpath:
|
27
|
+
classpath = f"flowtask.components.{component}"
|
28
|
+
module = importlib.import_module(classpath, package=package)
|
29
|
+
obj = getattr(module, component)
|
30
|
+
return obj
|
31
|
+
|
32
|
+
|
33
|
+
def _download_helper(coroutine):
|
34
|
+
def _run_coro(coro):
|
35
|
+
event_loop = asyncio.new_event_loop()
|
36
|
+
try:
|
37
|
+
asyncio.set_event_loop(event_loop)
|
38
|
+
return event_loop.run_until_complete(coro)
|
39
|
+
finally:
|
40
|
+
event_loop.close()
|
41
|
+
|
42
|
+
with ThreadPoolExecutor(max_workers=1) as pool:
|
43
|
+
result = pool.submit(_run_coro, coroutine).result()
|
44
|
+
return result
|
45
|
+
|
46
|
+
|
47
|
+
def downloadComponent(component: str):
|
48
|
+
# Create a coroutine object
|
49
|
+
coro = download_component(component)
|
50
|
+
# Run the coroutine in a new event loop in a separate thread
|
51
|
+
result = _download_helper(coro)
|
52
|
+
return result
|
53
|
+
|
54
|
+
|
55
|
+
def loadComponent(component, program: str = None):
|
56
|
+
try:
|
57
|
+
# Getting Basic Components
|
58
|
+
classpath = f"flowtask.components.{component}"
|
59
|
+
return importComponent(component, classpath, package="components")
|
60
|
+
except ImportError as ex:
|
61
|
+
logging.warning(
|
62
|
+
f"Error Importing Component {component}: {ex}"
|
63
|
+
)
|
64
|
+
cpath = Path(__file__).parent.joinpath(f"{component}.py")
|
65
|
+
if cpath.exists():
|
66
|
+
logger.error(ex)
|
67
|
+
raise ComponentError(
|
68
|
+
f"Error Importing Component {component}: {ex}"
|
69
|
+
) from ex
|
70
|
+
try:
|
71
|
+
# another, check if task is an User-Defined Component
|
72
|
+
classpath = f"flowtask.plugins.components.{component}"
|
73
|
+
obj = importComponent(component, classpath, package="components")
|
74
|
+
if issubclass(obj, (UserComponent, FlowComponent)):
|
75
|
+
return obj
|
76
|
+
raise ImportError(
|
77
|
+
f"Cannot import {component} Hint: Component need inherits from UserComponent"
|
78
|
+
)
|
79
|
+
except ImportError as e:
|
80
|
+
### TODO: Download Component From Marketplace, installed on "plugins" folder.
|
81
|
+
if downloadComponent(component) is True:
|
82
|
+
## re-import from plugins path
|
83
|
+
return importComponent(component, classpath, package="components")
|
84
|
+
raise NotSupported(
|
85
|
+
f"Error: No Component {component!r} was Found: {e}"
|
86
|
+
) from e
|
87
|
+
|
88
|
+
|
89
|
+
def getComponent(component: str, program: str = None):
|
90
|
+
try:
|
91
|
+
if component in _COMPONENTS:
|
92
|
+
return _COMPONENTS[component]
|
93
|
+
else:
|
94
|
+
cls = loadComponent(component, program=program)
|
95
|
+
_COMPONENTS[component] = cls
|
96
|
+
return cls
|
97
|
+
except KeyError as err:
|
98
|
+
logger.exception(
|
99
|
+
f"Error loading component {component}: {err}",
|
100
|
+
stack_info=True
|
101
|
+
)
|
102
|
+
raise ComponentError(
|
103
|
+
f"Error loading component {component}: {err}"
|
104
|
+
) from err
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
|
4
|
+
class AbstractFlow(ABC):
|
5
|
+
|
6
|
+
@abstractmethod
|
7
|
+
async def start(self, **kwargs):
|
8
|
+
"""Start Method called on every component.
|
9
|
+
"""
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
async def close(self):
|
13
|
+
pass
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
async def run(self):
|
17
|
+
"""Execute the code for component.
|
18
|
+
"""
|