flowtask 5.8.4__cp312-cp312-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-312-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-312-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/argparser.py +235 -0
- flowtask/parsers/base.c +15155 -0
- flowtask/parsers/base.cpython-312-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/json.c +11968 -0
- flowtask/parsers/json.cpython-312-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/maps.py +49 -0
- flowtask/parsers/toml.c +11968 -0
- flowtask/parsers/toml.cpython-312-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-312-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-312-x86_64-linux-gnu.so +0 -0
- flowtask/utils/json.cpp +13349 -0
- flowtask/utils/json.cpython-312-x86_64-linux-gnu.so +0 -0
- flowtask/utils/mail.py +63 -0
- flowtask/utils/parseqs.c +13324 -0
- flowtask/utils/parserqs.cpython-312-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,140 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from urllib.parse import urljoin
|
3
|
+
from bs4 import BeautifulSoup
|
4
|
+
import magic
|
5
|
+
import orjson
|
6
|
+
import pandas as pd
|
7
|
+
from ..exceptions import ComponentError
|
8
|
+
from .ASPX import ASPX
|
9
|
+
from ..conf import TASK_PATH
|
10
|
+
|
11
|
+
|
12
|
+
class VivaTracker(ASPX):
|
13
|
+
async def start(self, **kwargs):
|
14
|
+
directory = Path(TASK_PATH, self._program, "reports")
|
15
|
+
report_json_path = Path(directory).joinpath(self.report_filename)
|
16
|
+
|
17
|
+
try:
|
18
|
+
with open(report_json_path) as f:
|
19
|
+
report_values_dict = orjson.loads(f.read())
|
20
|
+
except FileNotFoundError as ex:
|
21
|
+
raise ComponentError(
|
22
|
+
f"Can't read the additional params file. Make sure there's a file {report_json_path}"
|
23
|
+
) from ex
|
24
|
+
|
25
|
+
additional_component_attrs = {
|
26
|
+
"base_url": "https://mso.vivatracker.com",
|
27
|
+
"login_user_payload_key": "ctl00$ContentPlaceHolder1$txtEmail",
|
28
|
+
"login_password_payload_key": "ctl00$ContentPlaceHolder1$txtPassword",
|
29
|
+
"login_button_payload_key": "ctl00$ContentPlaceHolder1$btnSubmit",
|
30
|
+
"login_button_payload_value": "Login",
|
31
|
+
**report_values_dict,
|
32
|
+
}
|
33
|
+
|
34
|
+
for attr, value in additional_component_attrs.items():
|
35
|
+
self.__setattr__(attr, value)
|
36
|
+
|
37
|
+
self.additional_payload_params = self.process_mask("additional_payload_params")
|
38
|
+
|
39
|
+
self._report_url = urljoin(self.base_url, f"{self.report_path}.aspx")
|
40
|
+
|
41
|
+
return await super().start()
|
42
|
+
|
43
|
+
async def _go_to_report(self):
|
44
|
+
await self.aspx_session(self._report_url)
|
45
|
+
|
46
|
+
def _get_report_payload(self, btn_id, btn_value):
|
47
|
+
return {
|
48
|
+
**self._views,
|
49
|
+
**self.additional_payload_params,
|
50
|
+
f"ctl00$ContentPlaceHolder1${btn_id}": btn_value,
|
51
|
+
}
|
52
|
+
|
53
|
+
def _extract_additional_values(self, soup: BeautifulSoup):
|
54
|
+
"""Extract additional values from the html report given a df column and
|
55
|
+
an element id present in the DOM
|
56
|
+
|
57
|
+
Args:
|
58
|
+
response (requests.Response): http response
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
dict: key: dataframe column, value: value extracted
|
62
|
+
|
63
|
+
|
64
|
+
Example:
|
65
|
+
|
66
|
+
```yaml
|
67
|
+
VivaTracker:
|
68
|
+
credentials:
|
69
|
+
username: VIVATRACKER_USERNAME
|
70
|
+
password: VIVATRACKER_PASSWORD
|
71
|
+
report_filename: sales_by_invoice_report.json
|
72
|
+
masks:
|
73
|
+
'{yesterday}':
|
74
|
+
- yesterday
|
75
|
+
- mask: '%m-%d-%Y'
|
76
|
+
'{today}':
|
77
|
+
- today
|
78
|
+
- mask: '%m-%d-%Y'
|
79
|
+
```
|
80
|
+
|
81
|
+
"""
|
82
|
+
self._extracted_additional_values = {}
|
83
|
+
|
84
|
+
if hasattr(self, "additional_values_to_extract"):
|
85
|
+
for _field, _elem_id in self.additional_values_to_extract.items():
|
86
|
+
s_elem = soup.find(id=_elem_id)
|
87
|
+
|
88
|
+
if s_elem:
|
89
|
+
self._extracted_additional_values[_field] = s_elem.get(
|
90
|
+
"value", s_elem.text
|
91
|
+
)
|
92
|
+
|
93
|
+
async def _get_report_html(self):
|
94
|
+
self._logger.info("Waiting for data to be displayed in html...")
|
95
|
+
report_payload = self._get_report_payload(
|
96
|
+
btn_id="btnSubmit", btn_value="Get Report"
|
97
|
+
)
|
98
|
+
payload = self._get_payload_with_views(**report_payload)
|
99
|
+
result = await self.aspx_session(self._report_url, method="post", data=payload)
|
100
|
+
self._extract_additional_values(result)
|
101
|
+
|
102
|
+
async def _extract_file_report(self) -> bytes:
|
103
|
+
self._logger.info("Downloading file...")
|
104
|
+
report_payload = self._get_report_payload(
|
105
|
+
btn_id=self.export_to_excel_btn_id,
|
106
|
+
btn_value="Export to Excel",
|
107
|
+
)
|
108
|
+
payload = self._get_payload_with_views(**report_payload)
|
109
|
+
|
110
|
+
self.accept = "application/xml"
|
111
|
+
result = await self.aspx_session(
|
112
|
+
self._report_url,
|
113
|
+
method="post",
|
114
|
+
data=payload,
|
115
|
+
follow_redirects=True,
|
116
|
+
)
|
117
|
+
|
118
|
+
return result
|
119
|
+
|
120
|
+
async def _get_df_from_report_bytes(self, result: bytes) -> pd.DataFrame:
|
121
|
+
if "text/html" in magic.from_buffer(result, mime=True):
|
122
|
+
df = pd.read_html(result, header=0)[0]
|
123
|
+
else:
|
124
|
+
df = pd.read_excel(result, header=0)
|
125
|
+
|
126
|
+
df = df.assign(**self._extracted_additional_values)
|
127
|
+
df.reset_index(drop=True, inplace=True)
|
128
|
+
|
129
|
+
return await self.create_dataframe(df)
|
130
|
+
|
131
|
+
async def run(self):
|
132
|
+
await self._go_to_report()
|
133
|
+
await self._get_report_html()
|
134
|
+
result = await self._extract_file_report()
|
135
|
+
df = await self._get_df_from_report_bytes(result)
|
136
|
+
|
137
|
+
await super().run()
|
138
|
+
|
139
|
+
self._result = df
|
140
|
+
return self._result
|
@@ -0,0 +1,123 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
# zeep integration
|
5
|
+
from zeep import Client, Settings, helpers
|
6
|
+
from zeep.transports import Transport
|
7
|
+
from ..exceptions import ComponentError
|
8
|
+
from .flow import FlowComponent
|
9
|
+
|
10
|
+
|
11
|
+
class WSDLClient(FlowComponent):
|
12
|
+
"""
|
13
|
+
WSDLClient.
|
14
|
+
|
15
|
+
Client for WSDL SOAP Web Services using Zeep
|
16
|
+
"""
|
17
|
+
|
18
|
+
transport = None
|
19
|
+
transport_options = {"timeout": 10}
|
20
|
+
method = ""
|
21
|
+
settings = None
|
22
|
+
settings_options = {"strict": True, "xml_huge_tree": False}
|
23
|
+
_wsdl = None
|
24
|
+
url: str = ""
|
25
|
+
raw_response = False
|
26
|
+
_filename: str = ""
|
27
|
+
_directory: str = ""
|
28
|
+
saving_xml = False
|
29
|
+
|
30
|
+
async def start(self, **kwargs):
|
31
|
+
"""Obtain Pandas Dataframe."""
|
32
|
+
if self.previous:
|
33
|
+
self.data = self.input
|
34
|
+
# defining methods for WSDL Client
|
35
|
+
self.transport = Transport(**self.transport_options)
|
36
|
+
self.settings = Settings(**self.settings_options)
|
37
|
+
if not hasattr(self, "method"):
|
38
|
+
raise ComponentError(
|
39
|
+
"WSDL Error: You need to define a Method using *method* attribute"
|
40
|
+
)
|
41
|
+
if not self.url:
|
42
|
+
raise ComponentError(
|
43
|
+
"WSDL Error: You need to define a WSDL endpoint using the *url* attribute"
|
44
|
+
)
|
45
|
+
# creating a client:
|
46
|
+
self._wsdl = Client(self.url, settings=self.settings)
|
47
|
+
# check if we need to save to an xml file:
|
48
|
+
if hasattr(self, "to_file"):
|
49
|
+
self.saving_xml = True
|
50
|
+
self._directory = Path(self.to_file["directory"])
|
51
|
+
if "filename" in self.to_file:
|
52
|
+
self._filename = self.to_file["filename"]
|
53
|
+
else:
|
54
|
+
# using a pattern:
|
55
|
+
f = self.to_file["file"]
|
56
|
+
file = f["pattern"]
|
57
|
+
if hasattr(self, "masks"):
|
58
|
+
for mask, replace in self.masks.items():
|
59
|
+
if isinstance(replace, str):
|
60
|
+
m = mask.translate({ord("{"): None, ord("}"): None})
|
61
|
+
if m in self.params.keys():
|
62
|
+
# using params instead mask value
|
63
|
+
file = file.replace(mask, self.params[m])
|
64
|
+
else:
|
65
|
+
# replace inmediately
|
66
|
+
file = file.replace(mask, replace)
|
67
|
+
# else:
|
68
|
+
# file = file.replace(mask, convert(replace))
|
69
|
+
self._filename = file
|
70
|
+
|
71
|
+
@property
|
72
|
+
def client(self):
|
73
|
+
return self._wsdl
|
74
|
+
|
75
|
+
async def close(self):
|
76
|
+
"""Method."""
|
77
|
+
self._wsdl = None
|
78
|
+
|
79
|
+
def saving_file(self, content):
|
80
|
+
if not self._directory.exists():
|
81
|
+
raise ComponentError(
|
82
|
+
f"Directory for saving XML file doesn't exists: {self._directory}"
|
83
|
+
)
|
84
|
+
path = self._directory.joinpath(self._filename)
|
85
|
+
if path.exists():
|
86
|
+
if "replace" in self.to_file:
|
87
|
+
os.remove(path)
|
88
|
+
with open(path, "w", encoding="utf-8") as f:
|
89
|
+
f.write(content)
|
90
|
+
if self._debug:
|
91
|
+
print(f"Saving XML File on: {path}")
|
92
|
+
# self._result = path
|
93
|
+
|
94
|
+
def queryMethod(self, method, **kwargs):
|
95
|
+
response = None
|
96
|
+
if not method:
|
97
|
+
method = self.method
|
98
|
+
try:
|
99
|
+
fn = getattr(self._wsdl.service, method)
|
100
|
+
response = fn(**kwargs)
|
101
|
+
except Exception as err:
|
102
|
+
raise ComponentError(
|
103
|
+
f"Error Calling method {method} over WSDL client, error: {err}"
|
104
|
+
) from err
|
105
|
+
finally:
|
106
|
+
return response
|
107
|
+
|
108
|
+
async def run(self):
|
109
|
+
response = None
|
110
|
+
try:
|
111
|
+
with self._wsdl.settings(raw_response=self.raw_response):
|
112
|
+
obj = self.queryMethod(self.method, **self.params)
|
113
|
+
if obj:
|
114
|
+
if hasattr(self, "serialize"):
|
115
|
+
response = helpers.serialize_object(obj, dict)
|
116
|
+
else:
|
117
|
+
response = obj
|
118
|
+
self._result = response
|
119
|
+
return self._result
|
120
|
+
else:
|
121
|
+
return False
|
122
|
+
except (ComponentError, Exception) as err:
|
123
|
+
raise ComponentError(str(err)) from err
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import asyncio
|
2
|
+
from .flow import FlowComponent
|
3
|
+
|
4
|
+
|
5
|
+
class Wait(FlowComponent):
|
6
|
+
async def start(self, **kwargs):
|
7
|
+
if self.previous:
|
8
|
+
self.data = self.input
|
9
|
+
return True
|
10
|
+
|
11
|
+
async def close(self):
|
12
|
+
pass
|
13
|
+
|
14
|
+
async def run(self):
|
15
|
+
await asyncio.sleep(self.wait)
|
16
|
+
self.add_metric("WAIT", self.wait)
|
17
|
+
self._result = self.data
|
18
|
+
return self._result
|
@@ -0,0 +1,199 @@
|
|
1
|
+
"""
|
2
|
+
Scrapping a Web Page Using Selenium + ChromeDriver + BeautifulSoup.
|
3
|
+
"""
|
4
|
+
import asyncio
|
5
|
+
from collections.abc import Callable
|
6
|
+
import random
|
7
|
+
import json
|
8
|
+
from urllib.parse import quote_plus
|
9
|
+
import httpx
|
10
|
+
import pandas as pd
|
11
|
+
import backoff
|
12
|
+
from ..interfaces.http import ua
|
13
|
+
from .reviewscrap import ReviewScrapper, bad_gateway_exception
|
14
|
+
|
15
|
+
|
16
|
+
class Walmart(ReviewScrapper):
|
17
|
+
"""Walmart.
|
18
|
+
|
19
|
+
Combining API Key and Web Scrapping, this component will be able to extract
|
20
|
+
Walmart Information (reviews, etc).
|
21
|
+
"""
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
loop: asyncio.AbstractEventLoop = None,
|
25
|
+
job: Callable = None,
|
26
|
+
stat: Callable = None,
|
27
|
+
**kwargs,
|
28
|
+
):
|
29
|
+
super(Walmart, self).__init__(
|
30
|
+
loop=loop,
|
31
|
+
job=job,
|
32
|
+
stat=stat,
|
33
|
+
**kwargs
|
34
|
+
)
|
35
|
+
# Always use proxies:
|
36
|
+
self.use_proxy: bool = True
|
37
|
+
self._free_proxy: bool = False
|
38
|
+
self.cookies = {
|
39
|
+
"_pxhd": "dbf5757b1f867196173eab3a4ab6377bbcd202cdd59b89b4372a1cf3f681b1aa:69fe9bd8-cfbd-11ef-9827-8c8dc170f864", # noqa
|
40
|
+
}
|
41
|
+
self.headers: dict = {
|
42
|
+
"Accept": "application/json",
|
43
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
44
|
+
"Accept-Language": "en-US",
|
45
|
+
"Content-Type": "application/json",
|
46
|
+
"Referer": "https://www.walmart.com/reviews/",
|
47
|
+
"Sec-CH-UA": '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"',
|
48
|
+
"Sec-CH-UA-Mobile": "?0",
|
49
|
+
"Sec-CH-UA-Platform": '"Linux"',
|
50
|
+
"Sec-Fetch-Dest": "empty",
|
51
|
+
"Sec-Fetch-Mode": "cors",
|
52
|
+
"Sec-Fetch-Site": "same-origin",
|
53
|
+
"Connection": "keep-alive",
|
54
|
+
"User-Agent": random.choice(ua),
|
55
|
+
}
|
56
|
+
self.semaphore = asyncio.Semaphore(10)
|
57
|
+
|
58
|
+
@backoff.on_exception(
|
59
|
+
backoff.expo,
|
60
|
+
(httpx.ConnectTimeout, httpx.HTTPStatusError),
|
61
|
+
max_tries=2,
|
62
|
+
giveup=lambda e: not bad_gateway_exception(e) and not isinstance(e, httpx.ConnectTimeout)
|
63
|
+
)
|
64
|
+
async def _product_reviews(self, idx, row, cookies):
|
65
|
+
async with self.semaphore:
|
66
|
+
# Prepare payload for the API request
|
67
|
+
sku = row['itemId']
|
68
|
+
max_pages = 2 # Maximum number of pages to fetch
|
69
|
+
all_reviews = []
|
70
|
+
total_reviews = 0
|
71
|
+
current_page = 1
|
72
|
+
try:
|
73
|
+
variables = {
|
74
|
+
"itemId": sku,
|
75
|
+
"page": 1, # Start with page 1
|
76
|
+
"sort": "submission-desc",
|
77
|
+
"limit": 10,
|
78
|
+
"filters": [],
|
79
|
+
"aspect": None,
|
80
|
+
"filterCriteria": {
|
81
|
+
"rating": [],
|
82
|
+
"reviewAttributes": [],
|
83
|
+
"aspectId": None
|
84
|
+
}
|
85
|
+
}
|
86
|
+
while True:
|
87
|
+
self.headers['Referer'] = f"https://www.walmart.com/reviews/product/{sku}?sort=submission-desc&page=1" # noqa
|
88
|
+
variables['page'] = current_page
|
89
|
+
variables_json = json.dumps(variables) # Proper JSON encoding
|
90
|
+
variables_encoded = quote_plus(variables_json)
|
91
|
+
url = f"https://www.walmart.com/orchestra/home/graphql/ReviewsById/{self.api_token}?variables={variables_encoded}" # noqa
|
92
|
+
print('URL > ', url)
|
93
|
+
result = await self.api_get(
|
94
|
+
url=url,
|
95
|
+
cookies=cookies,
|
96
|
+
headers=self.headers
|
97
|
+
)
|
98
|
+
if not result:
|
99
|
+
self._logger.warning(
|
100
|
+
f"No Product Reviews found for {sku}."
|
101
|
+
)
|
102
|
+
break
|
103
|
+
# Extract the reviews data from the API response
|
104
|
+
data = result.get('data', {})
|
105
|
+
reviews_data = data.get('reviews', {})
|
106
|
+
customer_reviews = reviews_data.get('customerReviews', [])
|
107
|
+
pagination = reviews_data.get('pagination', {})
|
108
|
+
# Update total_reviews
|
109
|
+
total_reviews = data.get('reviews', {}).get('totalReviewCount', 0)
|
110
|
+
if not customer_reviews:
|
111
|
+
self._logger.info(f"No more reviews found for itemId {row['itemId']} on page {current_page}.")
|
112
|
+
break
|
113
|
+
|
114
|
+
all_reviews.extend(customer_reviews)
|
115
|
+
if len(all_reviews) >= total_reviews:
|
116
|
+
self._logger.info(f"Fetched all reviews for itemId {row['itemId']}.")
|
117
|
+
break
|
118
|
+
|
119
|
+
current_page += 1
|
120
|
+
if current_page > max_pages:
|
121
|
+
self._logger.warning(f"Reached maximum page limit for itemId {row['itemId']}.")
|
122
|
+
break
|
123
|
+
except (httpx.TimeoutException, httpx.HTTPError) as ex:
|
124
|
+
self._logger.warning(f"Request failed: {ex}")
|
125
|
+
return []
|
126
|
+
except Exception as ex:
|
127
|
+
self._logger.error(f"An error occurred: {ex}")
|
128
|
+
return []
|
129
|
+
|
130
|
+
# Extract the reviews data from the API response
|
131
|
+
reviews = []
|
132
|
+
for item in all_reviews:
|
133
|
+
# Exclude certain keys
|
134
|
+
# Extract relevant fields
|
135
|
+
# Combine with original row data
|
136
|
+
review_data = row.to_dict()
|
137
|
+
review = {
|
138
|
+
**review_data,
|
139
|
+
"authorId": item.get("authorId"),
|
140
|
+
"userNickname": item.get("userNickname"),
|
141
|
+
"rating": item.get("rating"),
|
142
|
+
"reviewTitle": item.get("reviewTitle"),
|
143
|
+
"review": item.get("reviewText"),
|
144
|
+
"reviewSubmissionTime": item.get("reviewSubmissionTime"),
|
145
|
+
"clientResponses": item.get("clientResponses"),
|
146
|
+
"media": item.get("media"),
|
147
|
+
"itemId": sku,
|
148
|
+
"productName": row.get("productName"),
|
149
|
+
"productCategory": row.get("productCategory"),
|
150
|
+
}
|
151
|
+
review['total_reviews'] = total_reviews
|
152
|
+
reviews.append(review)
|
153
|
+
self._logger.info(
|
154
|
+
f"Fetched {len(reviews)} reviews for SKU {sku}."
|
155
|
+
)
|
156
|
+
return reviews
|
157
|
+
|
158
|
+
async def reviews(self):
|
159
|
+
"""reviews.
|
160
|
+
|
161
|
+
Target Product Reviews.
|
162
|
+
"""
|
163
|
+
httpx_cookies = httpx.Cookies()
|
164
|
+
for key, value in self.cookies.items():
|
165
|
+
httpx_cookies.set(
|
166
|
+
key, value,
|
167
|
+
domain='.walmart.com',
|
168
|
+
path='/'
|
169
|
+
)
|
170
|
+
|
171
|
+
# Iterate over each row in the DataFrame
|
172
|
+
print('starting ...')
|
173
|
+
|
174
|
+
tasks = [
|
175
|
+
self._product_reviews(
|
176
|
+
idx,
|
177
|
+
row,
|
178
|
+
httpx_cookies
|
179
|
+
) for idx, row in self.data.iterrows()
|
180
|
+
]
|
181
|
+
# Gather results concurrently
|
182
|
+
all_reviews_nested = await self._processing_tasks(tasks)
|
183
|
+
|
184
|
+
# Flatten the list of lists
|
185
|
+
all_reviews = [review for reviews in all_reviews_nested for review in reviews]
|
186
|
+
|
187
|
+
# Convert to DataFrame
|
188
|
+
reviews_df = pd.DataFrame(all_reviews)
|
189
|
+
|
190
|
+
# show the num of rows in final dataframe:
|
191
|
+
self._logger.notice(
|
192
|
+
f"Ending Product Reviews: {len(reviews_df)}"
|
193
|
+
)
|
194
|
+
|
195
|
+
# Override previous dataframe:
|
196
|
+
self.data = reviews_df
|
197
|
+
|
198
|
+
# return existing data
|
199
|
+
return self.data
|
@@ -0,0 +1,134 @@
|
|
1
|
+
from asyncdb.utils.types import SafeDict
|
2
|
+
from .user import UserComponent
|
3
|
+
from ..conf import WORKPLACE_ACCESS_TOKEN
|
4
|
+
|
5
|
+
|
6
|
+
class Workplace(UserComponent):
|
7
|
+
_messages_url = "https://graph.facebook.com/{thread_id}/messages?access_token={access_token}&limit=10&user={member_id}&fields=id,message,created_time,attachments,from,to,tags"
|
8
|
+
_attachments_url = "https://graph.facebook.com/v18.0/{message_id}?access_token={access_token}&limit=20&user={member_id}&fields=id,created_time,attachments"
|
9
|
+
_members_url = "https://graph.facebook.com/community/members?access_token={access_token}&limit=100&fields=member_id,first_name,last_name,name,email,title,organization,division,department,primary_phone,primary_address,picture,link,locale,updated_time,account_invite_time,account_claim_time,account_deactivate_time,external_id,start_date,about,cost_center,work_locale,frontline,active"
|
10
|
+
_member_threads = "https://graph.facebook.com/{member_id}/conversations/?access_token={access_token}&limit=100&fields=id,name,subject,participants,updated_time,messages"
|
11
|
+
accept = "application/json"
|
12
|
+
|
13
|
+
async def start(self, **kwargs):
|
14
|
+
## Access Token
|
15
|
+
if hasattr(self, "access_token"):
|
16
|
+
access_token = self.access_token
|
17
|
+
else:
|
18
|
+
access_token = WORKPLACE_ACCESS_TOKEN
|
19
|
+
# Workplaces APIs.
|
20
|
+
if self.type == "messages":
|
21
|
+
self.member_id = self.set_variables(self.member_id)
|
22
|
+
self.thread_id = self.set_variables(self.thread_id)
|
23
|
+
self.messages_url = self._messages_url.format_map(
|
24
|
+
SafeDict(
|
25
|
+
access_token=access_token,
|
26
|
+
member_id=self.member_id,
|
27
|
+
thread_id=self.thread_id,
|
28
|
+
)
|
29
|
+
)
|
30
|
+
self._kwargs = {"url": self.messages_url, "method": "get"}
|
31
|
+
elif self.type == "attachments":
|
32
|
+
self.member_id = self.set_variables(self.member_id)
|
33
|
+
self.message_id = self.set_variables(self.message_id)
|
34
|
+
url = self._attachments_url.format_map(
|
35
|
+
SafeDict(
|
36
|
+
access_token=access_token,
|
37
|
+
message_id=self.message_id,
|
38
|
+
member_id=self.member_id,
|
39
|
+
)
|
40
|
+
)
|
41
|
+
self._kwargs = {"url": url, "method": "get"}
|
42
|
+
elif self.type == "members":
|
43
|
+
self.members_url = self._members_url.format_map(
|
44
|
+
SafeDict(access_token=access_token)
|
45
|
+
)
|
46
|
+
self._kwargs = {"url": self.members_url, "method": "get"}
|
47
|
+
elif self.type == "member_threads":
|
48
|
+
self.member_id = self.set_variables(self.member_id)
|
49
|
+
self.member_threads_url = self._member_threads.format_map(
|
50
|
+
SafeDict(access_token=access_token, member_id=self.member_id)
|
51
|
+
)
|
52
|
+
self._kwargs = {"url": self.member_threads_url, "method": "get"}
|
53
|
+
|
54
|
+
async def run(self):
|
55
|
+
results = []
|
56
|
+
result = await self.session(**self._kwargs)
|
57
|
+
if not result:
|
58
|
+
return False
|
59
|
+
if "data" in result:
|
60
|
+
results += result["data"]
|
61
|
+
elif isinstance(result, dict):
|
62
|
+
if self.type == "attachments":
|
63
|
+
if "attachments" not in result:
|
64
|
+
return False
|
65
|
+
results += [result]
|
66
|
+
else:
|
67
|
+
results += result
|
68
|
+
if "paging" in result:
|
69
|
+
url = result["paging"]["next"] if "next" in result["paging"] else None
|
70
|
+
while url is not None:
|
71
|
+
self._kwargs["url"] = url
|
72
|
+
resultset = await self.session(**self._kwargs)
|
73
|
+
results += resultset["data"]
|
74
|
+
if "paging" in result:
|
75
|
+
url = (
|
76
|
+
resultset["paging"]["next"]
|
77
|
+
if "next" in resultset["paging"]
|
78
|
+
else None
|
79
|
+
)
|
80
|
+
else:
|
81
|
+
url = None
|
82
|
+
# Iterate over a field to download more results:
|
83
|
+
if hasattr(self, "page_over"):
|
84
|
+
columns = self.page_over
|
85
|
+
for row in results:
|
86
|
+
for column in columns:
|
87
|
+
try:
|
88
|
+
rw = row[column]
|
89
|
+
except KeyError:
|
90
|
+
# there is no messages in this thread
|
91
|
+
continue
|
92
|
+
if "data" in rw:
|
93
|
+
# replacing "data" with current value of data:
|
94
|
+
row[column] = row[column]["data"]
|
95
|
+
if "paging" in row[column]:
|
96
|
+
url = (
|
97
|
+
row[column]["paging"]["next"]
|
98
|
+
if "next" in row[column]["paging"]
|
99
|
+
else None
|
100
|
+
)
|
101
|
+
while url is not None:
|
102
|
+
resultset = await self.session(url=url, method="get")
|
103
|
+
row[column] += resultset["data"]
|
104
|
+
if "paging" in result:
|
105
|
+
url = (
|
106
|
+
resultset["paging"]["next"]
|
107
|
+
if "next" in resultset["paging"]
|
108
|
+
else None
|
109
|
+
)
|
110
|
+
else:
|
111
|
+
url = None
|
112
|
+
if hasattr(self, "flatten_cols"):
|
113
|
+
columns = self.flatten_cols
|
114
|
+
for row in results:
|
115
|
+
for column in columns:
|
116
|
+
try:
|
117
|
+
if "data" in row[column]:
|
118
|
+
# replacing "data" with current value of data:
|
119
|
+
row[column] = row[column]["data"]
|
120
|
+
except KeyError:
|
121
|
+
# there is no column in this thread
|
122
|
+
continue
|
123
|
+
# Create a Dataframe from Results:
|
124
|
+
self._result = await self.create_dataframe(results)
|
125
|
+
if self.type == "member_threads":
|
126
|
+
# add the value of member_id over all rows:
|
127
|
+
self._result["member_id"] = self.member_id
|
128
|
+
elif self.type in ("messages"):
|
129
|
+
self._result["member_id"] = self.member_id
|
130
|
+
self._result["thread_id"] = self.thread_id
|
131
|
+
return self._result
|
132
|
+
|
133
|
+
async def close(self):
|
134
|
+
pass
|