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.
Files changed (470) hide show
  1. flowtask/__init__.py +93 -0
  2. flowtask/__main__.py +38 -0
  3. flowtask/bots/__init__.py +6 -0
  4. flowtask/bots/check.py +93 -0
  5. flowtask/bots/codebot.py +51 -0
  6. flowtask/components/ASPX.py +148 -0
  7. flowtask/components/AddDataset.py +352 -0
  8. flowtask/components/Amazon.py +523 -0
  9. flowtask/components/AutoTask.py +314 -0
  10. flowtask/components/Azure.py +80 -0
  11. flowtask/components/AzureUsers.py +106 -0
  12. flowtask/components/BaseAction.py +91 -0
  13. flowtask/components/BaseLoop.py +198 -0
  14. flowtask/components/BestBuy.py +800 -0
  15. flowtask/components/CSVToGCS.py +120 -0
  16. flowtask/components/CompanyScraper/__init__.py +1 -0
  17. flowtask/components/CompanyScraper/parsers/__init__.py +6 -0
  18. flowtask/components/CompanyScraper/parsers/base.py +102 -0
  19. flowtask/components/CompanyScraper/parsers/explorium.py +192 -0
  20. flowtask/components/CompanyScraper/parsers/leadiq.py +206 -0
  21. flowtask/components/CompanyScraper/parsers/rocket.py +133 -0
  22. flowtask/components/CompanyScraper/parsers/siccode.py +109 -0
  23. flowtask/components/CompanyScraper/parsers/visualvisitor.py +130 -0
  24. flowtask/components/CompanyScraper/parsers/zoominfo.py +118 -0
  25. flowtask/components/CompanyScraper/scrapper.py +1054 -0
  26. flowtask/components/CopyTo.py +177 -0
  27. flowtask/components/CopyToBigQuery.py +243 -0
  28. flowtask/components/CopyToMongoDB.py +291 -0
  29. flowtask/components/CopyToPg.py +609 -0
  30. flowtask/components/CopyToRethink.py +207 -0
  31. flowtask/components/CreateGCSBucket.py +102 -0
  32. flowtask/components/CreateReport/CreateReport.py +228 -0
  33. flowtask/components/CreateReport/__init__.py +9 -0
  34. flowtask/components/CreateReport/charts/__init__.py +15 -0
  35. flowtask/components/CreateReport/charts/bar.py +51 -0
  36. flowtask/components/CreateReport/charts/base.py +66 -0
  37. flowtask/components/CreateReport/charts/pie.py +64 -0
  38. flowtask/components/CreateReport/utils.py +9 -0
  39. flowtask/components/CustomerSatisfaction.py +196 -0
  40. flowtask/components/DataInput.py +200 -0
  41. flowtask/components/DateList.py +255 -0
  42. flowtask/components/DbClient.py +163 -0
  43. flowtask/components/DialPad.py +146 -0
  44. flowtask/components/DocumentDBQuery.py +200 -0
  45. flowtask/components/DownloadFrom.py +371 -0
  46. flowtask/components/DownloadFromD2L.py +113 -0
  47. flowtask/components/DownloadFromFTP.py +181 -0
  48. flowtask/components/DownloadFromIMAP.py +315 -0
  49. flowtask/components/DownloadFromS3.py +198 -0
  50. flowtask/components/DownloadFromSFTP.py +265 -0
  51. flowtask/components/DownloadFromSharepoint.py +110 -0
  52. flowtask/components/DownloadFromSmartSheet.py +114 -0
  53. flowtask/components/DownloadS3File.py +229 -0
  54. flowtask/components/Dummy.py +59 -0
  55. flowtask/components/DuplicatePhoto.py +411 -0
  56. flowtask/components/EmployeeEvaluation.py +237 -0
  57. flowtask/components/ExecuteSQL.py +323 -0
  58. flowtask/components/ExtractHTML.py +178 -0
  59. flowtask/components/FileBase.py +178 -0
  60. flowtask/components/FileCopy.py +181 -0
  61. flowtask/components/FileDelete.py +82 -0
  62. flowtask/components/FileExists.py +146 -0
  63. flowtask/components/FileIteratorDelete.py +112 -0
  64. flowtask/components/FileList.py +194 -0
  65. flowtask/components/FileOpen.py +75 -0
  66. flowtask/components/FileRead.py +120 -0
  67. flowtask/components/FileRename.py +106 -0
  68. flowtask/components/FilterIf.py +284 -0
  69. flowtask/components/FilterRows/FilterRows.py +200 -0
  70. flowtask/components/FilterRows/__init__.py +10 -0
  71. flowtask/components/FilterRows/functions.py +4 -0
  72. flowtask/components/GCSToBigQuery.py +103 -0
  73. flowtask/components/GoogleA4.py +150 -0
  74. flowtask/components/GoogleGeoCoding.py +344 -0
  75. flowtask/components/GooglePlaces.py +315 -0
  76. flowtask/components/GoogleSearch.py +539 -0
  77. flowtask/components/HTTPClient.py +268 -0
  78. flowtask/components/ICIMS.py +146 -0
  79. flowtask/components/IF.py +179 -0
  80. flowtask/components/IcimsFolderCopy.py +173 -0
  81. flowtask/components/ImageFeatures/__init__.py +5 -0
  82. flowtask/components/ImageFeatures/process.py +233 -0
  83. flowtask/components/IteratorBase.py +251 -0
  84. flowtask/components/LangchainLoader/__init__.py +5 -0
  85. flowtask/components/LangchainLoader/loader.py +194 -0
  86. flowtask/components/LangchainLoader/loaders/__init__.py +22 -0
  87. flowtask/components/LangchainLoader/loaders/abstract.py +362 -0
  88. flowtask/components/LangchainLoader/loaders/basepdf.py +50 -0
  89. flowtask/components/LangchainLoader/loaders/docx.py +91 -0
  90. flowtask/components/LangchainLoader/loaders/html.py +119 -0
  91. flowtask/components/LangchainLoader/loaders/pdfblocks.py +146 -0
  92. flowtask/components/LangchainLoader/loaders/pdfmark.py +79 -0
  93. flowtask/components/LangchainLoader/loaders/pdftables.py +135 -0
  94. flowtask/components/LangchainLoader/loaders/qa.py +67 -0
  95. flowtask/components/LangchainLoader/loaders/txt.py +55 -0
  96. flowtask/components/LeadIQ.py +650 -0
  97. flowtask/components/Loop.py +253 -0
  98. flowtask/components/Lowes.py +334 -0
  99. flowtask/components/MS365Usage.py +156 -0
  100. flowtask/components/MSTeamsMessages.py +320 -0
  101. flowtask/components/MarketClustering.py +1051 -0
  102. flowtask/components/MergeFiles.py +362 -0
  103. flowtask/components/MilvusOutput.py +87 -0
  104. flowtask/components/NearByStores.py +175 -0
  105. flowtask/components/NetworkNinja/__init__.py +6 -0
  106. flowtask/components/NetworkNinja/models/__init__.py +52 -0
  107. flowtask/components/NetworkNinja/models/abstract.py +177 -0
  108. flowtask/components/NetworkNinja/models/account.py +39 -0
  109. flowtask/components/NetworkNinja/models/client.py +19 -0
  110. flowtask/components/NetworkNinja/models/district.py +14 -0
  111. flowtask/components/NetworkNinja/models/events.py +101 -0
  112. flowtask/components/NetworkNinja/models/forms.py +499 -0
  113. flowtask/components/NetworkNinja/models/market.py +16 -0
  114. flowtask/components/NetworkNinja/models/organization.py +34 -0
  115. flowtask/components/NetworkNinja/models/photos.py +125 -0
  116. flowtask/components/NetworkNinja/models/project.py +44 -0
  117. flowtask/components/NetworkNinja/models/region.py +28 -0
  118. flowtask/components/NetworkNinja/models/store.py +203 -0
  119. flowtask/components/NetworkNinja/models/user.py +151 -0
  120. flowtask/components/NetworkNinja/router.py +854 -0
  121. flowtask/components/Odoo.py +175 -0
  122. flowtask/components/OdooInjector.py +192 -0
  123. flowtask/components/OpenFromXML.py +126 -0
  124. flowtask/components/OpenWeather.py +41 -0
  125. flowtask/components/OpenWithBase.py +616 -0
  126. flowtask/components/OpenWithPandas.py +715 -0
  127. flowtask/components/PGPDecrypt.py +199 -0
  128. flowtask/components/PandasIterator.py +187 -0
  129. flowtask/components/PandasToFile.py +189 -0
  130. flowtask/components/Paradox.py +339 -0
  131. flowtask/components/ParamIterator.py +117 -0
  132. flowtask/components/ParseHTML.py +84 -0
  133. flowtask/components/PlacerStores.py +249 -0
  134. flowtask/components/Pokemon.py +507 -0
  135. flowtask/components/PositiveBot.py +62 -0
  136. flowtask/components/PowerPointSlide.py +400 -0
  137. flowtask/components/PrintMessage.py +127 -0
  138. flowtask/components/ProductCompetitors/__init__.py +5 -0
  139. flowtask/components/ProductCompetitors/parsers/__init__.py +7 -0
  140. flowtask/components/ProductCompetitors/parsers/base.py +72 -0
  141. flowtask/components/ProductCompetitors/parsers/bestbuy.py +86 -0
  142. flowtask/components/ProductCompetitors/parsers/lowes.py +103 -0
  143. flowtask/components/ProductCompetitors/scrapper.py +155 -0
  144. flowtask/components/ProductCompliant.py +169 -0
  145. flowtask/components/ProductInfo/__init__.py +1 -0
  146. flowtask/components/ProductInfo/parsers/__init__.py +5 -0
  147. flowtask/components/ProductInfo/parsers/base.py +83 -0
  148. flowtask/components/ProductInfo/parsers/brother.py +97 -0
  149. flowtask/components/ProductInfo/parsers/canon.py +167 -0
  150. flowtask/components/ProductInfo/parsers/epson.py +118 -0
  151. flowtask/components/ProductInfo/parsers/hp.py +131 -0
  152. flowtask/components/ProductInfo/parsers/samsung.py +97 -0
  153. flowtask/components/ProductInfo/scraper.py +319 -0
  154. flowtask/components/ProductPricing.py +118 -0
  155. flowtask/components/QS.py +261 -0
  156. flowtask/components/QSBase.py +201 -0
  157. flowtask/components/QueryIterator.py +273 -0
  158. flowtask/components/QueryToInsert.py +327 -0
  159. flowtask/components/QueryToPandas.py +432 -0
  160. flowtask/components/RESTClient.py +195 -0
  161. flowtask/components/RethinkDBQuery.py +189 -0
  162. flowtask/components/Rsync.py +74 -0
  163. flowtask/components/RunSSH.py +59 -0
  164. flowtask/components/RunShell.py +71 -0
  165. flowtask/components/SalesForce.py +20 -0
  166. flowtask/components/SaveImageBank/__init__.py +257 -0
  167. flowtask/components/SchedulingVisits.py +592 -0
  168. flowtask/components/ScrapPage.py +216 -0
  169. flowtask/components/ScrapSearch.py +79 -0
  170. flowtask/components/SendNotify.py +257 -0
  171. flowtask/components/SentimentAnalysis.py +694 -0
  172. flowtask/components/ServiceScrapper/__init__.py +5 -0
  173. flowtask/components/ServiceScrapper/parsers/__init__.py +1 -0
  174. flowtask/components/ServiceScrapper/parsers/base.py +94 -0
  175. flowtask/components/ServiceScrapper/parsers/costco.py +93 -0
  176. flowtask/components/ServiceScrapper/scrapper.py +199 -0
  177. flowtask/components/SetVariables.py +156 -0
  178. flowtask/components/SubTask.py +182 -0
  179. flowtask/components/SuiteCRM.py +48 -0
  180. flowtask/components/Switch.py +175 -0
  181. flowtask/components/TableBase.py +148 -0
  182. flowtask/components/TableDelete.py +312 -0
  183. flowtask/components/TableInput.py +143 -0
  184. flowtask/components/TableOutput/TableOutput.py +384 -0
  185. flowtask/components/TableOutput/__init__.py +3 -0
  186. flowtask/components/TableSchema.py +534 -0
  187. flowtask/components/Target.py +223 -0
  188. flowtask/components/ThumbnailGenerator.py +156 -0
  189. flowtask/components/ToPandas.py +67 -0
  190. flowtask/components/TransformRows/TransformRows.py +507 -0
  191. flowtask/components/TransformRows/__init__.py +9 -0
  192. flowtask/components/TransformRows/functions.py +559 -0
  193. flowtask/components/TransposeRows.py +176 -0
  194. flowtask/components/UPCDatabase.py +86 -0
  195. flowtask/components/UnGzip.py +171 -0
  196. flowtask/components/Uncompress.py +172 -0
  197. flowtask/components/UniqueRows.py +126 -0
  198. flowtask/components/Unzip.py +107 -0
  199. flowtask/components/UpdateOperationalVars.py +147 -0
  200. flowtask/components/UploadTo.py +299 -0
  201. flowtask/components/UploadToS3.py +136 -0
  202. flowtask/components/UploadToSFTP.py +160 -0
  203. flowtask/components/UploadToSharepoint.py +205 -0
  204. flowtask/components/UserFunc.py +122 -0
  205. flowtask/components/VivaTracker.py +140 -0
  206. flowtask/components/WSDLClient.py +123 -0
  207. flowtask/components/Wait.py +18 -0
  208. flowtask/components/Walmart.py +199 -0
  209. flowtask/components/Workplace.py +134 -0
  210. flowtask/components/XMLToPandas.py +267 -0
  211. flowtask/components/Zammad/__init__.py +41 -0
  212. flowtask/components/Zammad/models.py +0 -0
  213. flowtask/components/ZoomInfoScraper.py +409 -0
  214. flowtask/components/__init__.py +104 -0
  215. flowtask/components/abstract.py +18 -0
  216. flowtask/components/flow.py +530 -0
  217. flowtask/components/google.py +335 -0
  218. flowtask/components/group.py +221 -0
  219. flowtask/components/py.typed +0 -0
  220. flowtask/components/reviewscrap.py +132 -0
  221. flowtask/components/tAutoincrement.py +117 -0
  222. flowtask/components/tConcat.py +109 -0
  223. flowtask/components/tExplode.py +119 -0
  224. flowtask/components/tFilter.py +184 -0
  225. flowtask/components/tGroup.py +236 -0
  226. flowtask/components/tJoin.py +270 -0
  227. flowtask/components/tMap/__init__.py +9 -0
  228. flowtask/components/tMap/functions.py +54 -0
  229. flowtask/components/tMap/tMap.py +450 -0
  230. flowtask/components/tMelt.py +112 -0
  231. flowtask/components/tMerge.py +114 -0
  232. flowtask/components/tOrder.py +93 -0
  233. flowtask/components/tPandas.py +94 -0
  234. flowtask/components/tPivot.py +71 -0
  235. flowtask/components/tPluckCols.py +76 -0
  236. flowtask/components/tUnnest.py +82 -0
  237. flowtask/components/user.py +401 -0
  238. flowtask/conf.py +457 -0
  239. flowtask/download.py +102 -0
  240. flowtask/events/__init__.py +11 -0
  241. flowtask/events/events/__init__.py +20 -0
  242. flowtask/events/events/abstract.py +95 -0
  243. flowtask/events/events/alerts/__init__.py +362 -0
  244. flowtask/events/events/alerts/colfunctions.py +131 -0
  245. flowtask/events/events/alerts/functions.py +158 -0
  246. flowtask/events/events/dummy.py +12 -0
  247. flowtask/events/events/exec.py +124 -0
  248. flowtask/events/events/file/__init__.py +7 -0
  249. flowtask/events/events/file/base.py +51 -0
  250. flowtask/events/events/file/copy.py +23 -0
  251. flowtask/events/events/file/delete.py +16 -0
  252. flowtask/events/events/interfaces/__init__.py +9 -0
  253. flowtask/events/events/interfaces/client.py +67 -0
  254. flowtask/events/events/interfaces/credentials.py +28 -0
  255. flowtask/events/events/interfaces/notifications.py +58 -0
  256. flowtask/events/events/jira.py +122 -0
  257. flowtask/events/events/log.py +26 -0
  258. flowtask/events/events/logerr.py +52 -0
  259. flowtask/events/events/notify.py +59 -0
  260. flowtask/events/events/notify_event.py +160 -0
  261. flowtask/events/events/publish.py +54 -0
  262. flowtask/events/events/sendfile.py +104 -0
  263. flowtask/events/events/task.py +97 -0
  264. flowtask/events/events/teams.py +98 -0
  265. flowtask/events/events/webhook.py +58 -0
  266. flowtask/events/manager.py +287 -0
  267. flowtask/exceptions.c +39393 -0
  268. flowtask/exceptions.cpython-39-x86_64-linux-gnu.so +0 -0
  269. flowtask/extensions/__init__.py +3 -0
  270. flowtask/extensions/abstract.py +82 -0
  271. flowtask/extensions/logging/__init__.py +65 -0
  272. flowtask/hooks/__init__.py +9 -0
  273. flowtask/hooks/actions/__init__.py +22 -0
  274. flowtask/hooks/actions/abstract.py +66 -0
  275. flowtask/hooks/actions/dummy.py +23 -0
  276. flowtask/hooks/actions/jira.py +74 -0
  277. flowtask/hooks/actions/rest.py +320 -0
  278. flowtask/hooks/actions/sampledata.py +37 -0
  279. flowtask/hooks/actions/sensor.py +23 -0
  280. flowtask/hooks/actions/task.py +9 -0
  281. flowtask/hooks/actions/ticket.py +37 -0
  282. flowtask/hooks/actions/zammad.py +55 -0
  283. flowtask/hooks/hook.py +62 -0
  284. flowtask/hooks/models.py +17 -0
  285. flowtask/hooks/service.py +187 -0
  286. flowtask/hooks/step.py +91 -0
  287. flowtask/hooks/types/__init__.py +23 -0
  288. flowtask/hooks/types/base.py +129 -0
  289. flowtask/hooks/types/brokers/__init__.py +11 -0
  290. flowtask/hooks/types/brokers/base.py +54 -0
  291. flowtask/hooks/types/brokers/mqtt.py +35 -0
  292. flowtask/hooks/types/brokers/rabbitmq.py +82 -0
  293. flowtask/hooks/types/brokers/redis.py +83 -0
  294. flowtask/hooks/types/brokers/sqs.py +44 -0
  295. flowtask/hooks/types/fs.py +232 -0
  296. flowtask/hooks/types/http.py +49 -0
  297. flowtask/hooks/types/imap.py +200 -0
  298. flowtask/hooks/types/jira.py +279 -0
  299. flowtask/hooks/types/mail.py +205 -0
  300. flowtask/hooks/types/postgres.py +98 -0
  301. flowtask/hooks/types/responses/__init__.py +8 -0
  302. flowtask/hooks/types/responses/base.py +5 -0
  303. flowtask/hooks/types/sharepoint.py +288 -0
  304. flowtask/hooks/types/ssh.py +141 -0
  305. flowtask/hooks/types/tagged.py +59 -0
  306. flowtask/hooks/types/upload.py +85 -0
  307. flowtask/hooks/types/watch.py +71 -0
  308. flowtask/hooks/types/web.py +36 -0
  309. flowtask/interfaces/AzureClient.py +137 -0
  310. flowtask/interfaces/AzureGraph.py +839 -0
  311. flowtask/interfaces/Boto3Client.py +326 -0
  312. flowtask/interfaces/DropboxClient.py +173 -0
  313. flowtask/interfaces/ExcelHandler.py +94 -0
  314. flowtask/interfaces/FTPClient.py +131 -0
  315. flowtask/interfaces/GoogleCalendar.py +201 -0
  316. flowtask/interfaces/GoogleClient.py +133 -0
  317. flowtask/interfaces/GoogleDrive.py +127 -0
  318. flowtask/interfaces/GoogleGCS.py +89 -0
  319. flowtask/interfaces/GoogleGeocoding.py +93 -0
  320. flowtask/interfaces/GoogleLang.py +114 -0
  321. flowtask/interfaces/GooglePub.py +61 -0
  322. flowtask/interfaces/GoogleSheet.py +68 -0
  323. flowtask/interfaces/IMAPClient.py +137 -0
  324. flowtask/interfaces/O365Calendar.py +113 -0
  325. flowtask/interfaces/O365Client.py +220 -0
  326. flowtask/interfaces/OneDrive.py +284 -0
  327. flowtask/interfaces/Outlook.py +155 -0
  328. flowtask/interfaces/ParrotBot.py +130 -0
  329. flowtask/interfaces/SSHClient.py +378 -0
  330. flowtask/interfaces/Sharepoint.py +496 -0
  331. flowtask/interfaces/__init__.py +36 -0
  332. flowtask/interfaces/azureauth.py +119 -0
  333. flowtask/interfaces/cache.py +201 -0
  334. flowtask/interfaces/client.py +82 -0
  335. flowtask/interfaces/compress.py +525 -0
  336. flowtask/interfaces/credentials.py +124 -0
  337. flowtask/interfaces/d2l.py +239 -0
  338. flowtask/interfaces/databases/__init__.py +5 -0
  339. flowtask/interfaces/databases/db.py +223 -0
  340. flowtask/interfaces/databases/documentdb.py +55 -0
  341. flowtask/interfaces/databases/rethink.py +39 -0
  342. flowtask/interfaces/dataframes/__init__.py +11 -0
  343. flowtask/interfaces/dataframes/abstract.py +21 -0
  344. flowtask/interfaces/dataframes/arrow.py +71 -0
  345. flowtask/interfaces/dataframes/dt.py +69 -0
  346. flowtask/interfaces/dataframes/pandas.py +167 -0
  347. flowtask/interfaces/dataframes/polars.py +60 -0
  348. flowtask/interfaces/db.py +263 -0
  349. flowtask/interfaces/env.py +46 -0
  350. flowtask/interfaces/func.py +137 -0
  351. flowtask/interfaces/http.py +1780 -0
  352. flowtask/interfaces/locale.py +40 -0
  353. flowtask/interfaces/log.py +75 -0
  354. flowtask/interfaces/mask.py +143 -0
  355. flowtask/interfaces/notification.py +154 -0
  356. flowtask/interfaces/playwright.py +339 -0
  357. flowtask/interfaces/powerpoint.py +368 -0
  358. flowtask/interfaces/py.typed +0 -0
  359. flowtask/interfaces/qs.py +376 -0
  360. flowtask/interfaces/result.py +87 -0
  361. flowtask/interfaces/selenium_service.py +779 -0
  362. flowtask/interfaces/smartsheet.py +154 -0
  363. flowtask/interfaces/stat.py +39 -0
  364. flowtask/interfaces/task.py +96 -0
  365. flowtask/interfaces/template.py +118 -0
  366. flowtask/interfaces/vectorstores/__init__.py +1 -0
  367. flowtask/interfaces/vectorstores/abstract.py +133 -0
  368. flowtask/interfaces/vectorstores/milvus.py +669 -0
  369. flowtask/interfaces/zammad.py +107 -0
  370. flowtask/models.py +193 -0
  371. flowtask/parsers/__init__.py +15 -0
  372. flowtask/parsers/_yaml.c +11978 -0
  373. flowtask/parsers/_yaml.cpython-39-x86_64-linux-gnu.so +0 -0
  374. flowtask/parsers/argparser.py +235 -0
  375. flowtask/parsers/base.c +15155 -0
  376. flowtask/parsers/base.cpython-39-x86_64-linux-gnu.so +0 -0
  377. flowtask/parsers/json.c +11968 -0
  378. flowtask/parsers/json.cpython-39-x86_64-linux-gnu.so +0 -0
  379. flowtask/parsers/maps.py +49 -0
  380. flowtask/parsers/toml.c +11968 -0
  381. flowtask/parsers/toml.cpython-39-x86_64-linux-gnu.so +0 -0
  382. flowtask/plugins/__init__.py +16 -0
  383. flowtask/plugins/components/__init__.py +0 -0
  384. flowtask/plugins/handler/__init__.py +45 -0
  385. flowtask/plugins/importer.py +31 -0
  386. flowtask/plugins/sources/__init__.py +0 -0
  387. flowtask/runner.py +283 -0
  388. flowtask/scheduler/__init__.py +9 -0
  389. flowtask/scheduler/functions.py +493 -0
  390. flowtask/scheduler/handlers/__init__.py +8 -0
  391. flowtask/scheduler/handlers/manager.py +504 -0
  392. flowtask/scheduler/handlers/models.py +58 -0
  393. flowtask/scheduler/handlers/service.py +72 -0
  394. flowtask/scheduler/notifications.py +65 -0
  395. flowtask/scheduler/scheduler.py +993 -0
  396. flowtask/services/__init__.py +0 -0
  397. flowtask/services/bots/__init__.py +0 -0
  398. flowtask/services/bots/telegram.py +264 -0
  399. flowtask/services/files/__init__.py +11 -0
  400. flowtask/services/files/manager.py +522 -0
  401. flowtask/services/files/model.py +37 -0
  402. flowtask/services/files/service.py +767 -0
  403. flowtask/services/jira/__init__.py +3 -0
  404. flowtask/services/jira/jira_actions.py +191 -0
  405. flowtask/services/tasks/__init__.py +13 -0
  406. flowtask/services/tasks/launcher.py +213 -0
  407. flowtask/services/tasks/manager.py +323 -0
  408. flowtask/services/tasks/service.py +275 -0
  409. flowtask/services/tasks/task_manager.py +376 -0
  410. flowtask/services/tasks/tasks.py +155 -0
  411. flowtask/storages/__init__.py +16 -0
  412. flowtask/storages/exceptions.py +12 -0
  413. flowtask/storages/files/__init__.py +8 -0
  414. flowtask/storages/files/abstract.py +29 -0
  415. flowtask/storages/files/filesystem.py +66 -0
  416. flowtask/storages/tasks/__init__.py +19 -0
  417. flowtask/storages/tasks/abstract.py +26 -0
  418. flowtask/storages/tasks/database.py +33 -0
  419. flowtask/storages/tasks/filesystem.py +108 -0
  420. flowtask/storages/tasks/github.py +119 -0
  421. flowtask/storages/tasks/memory.py +45 -0
  422. flowtask/storages/tasks/row.py +25 -0
  423. flowtask/tasks/__init__.py +0 -0
  424. flowtask/tasks/abstract.py +526 -0
  425. flowtask/tasks/command.py +118 -0
  426. flowtask/tasks/pile.py +486 -0
  427. flowtask/tasks/py.typed +0 -0
  428. flowtask/tasks/task.py +778 -0
  429. flowtask/template/__init__.py +161 -0
  430. flowtask/tests.py +257 -0
  431. flowtask/types/__init__.py +8 -0
  432. flowtask/types/typedefs.c +11347 -0
  433. flowtask/types/typedefs.cpython-39-x86_64-linux-gnu.so +0 -0
  434. flowtask/utils/__init__.py +24 -0
  435. flowtask/utils/constants.py +117 -0
  436. flowtask/utils/encoders.py +21 -0
  437. flowtask/utils/executor.py +112 -0
  438. flowtask/utils/functions.cpp +14280 -0
  439. flowtask/utils/functions.cpython-39-x86_64-linux-gnu.so +0 -0
  440. flowtask/utils/json.cpp +13349 -0
  441. flowtask/utils/json.cpython-39-x86_64-linux-gnu.so +0 -0
  442. flowtask/utils/mail.py +63 -0
  443. flowtask/utils/parseqs.c +13324 -0
  444. flowtask/utils/parserqs.cpython-39-x86_64-linux-gnu.so +0 -0
  445. flowtask/utils/stats.py +308 -0
  446. flowtask/utils/transformations.py +74 -0
  447. flowtask/utils/uv.py +12 -0
  448. flowtask/utils/validators.py +97 -0
  449. flowtask/version.py +11 -0
  450. flowtask-5.8.4.dist-info/LICENSE +201 -0
  451. flowtask-5.8.4.dist-info/METADATA +209 -0
  452. flowtask-5.8.4.dist-info/RECORD +470 -0
  453. flowtask-5.8.4.dist-info/WHEEL +6 -0
  454. flowtask-5.8.4.dist-info/entry_points.txt +3 -0
  455. flowtask-5.8.4.dist-info/top_level.txt +2 -0
  456. plugins/components/CreateQR.py +39 -0
  457. plugins/components/TestComponent.py +28 -0
  458. plugins/components/Use1.py +13 -0
  459. plugins/components/Workplace.py +117 -0
  460. plugins/components/__init__.py +3 -0
  461. plugins/sources/__init__.py +0 -0
  462. plugins/sources/get_populartimes.py +78 -0
  463. plugins/sources/google.py +150 -0
  464. plugins/sources/hubspot.py +679 -0
  465. plugins/sources/icims.py +679 -0
  466. plugins/sources/mobileinsight.py +501 -0
  467. plugins/sources/newrelic.py +262 -0
  468. plugins/sources/uap.py +268 -0
  469. plugins/sources/venu.py +244 -0
  470. plugins/sources/vocinity.py +314 -0
@@ -0,0 +1,131 @@
1
+ """
2
+ FTP Client.
3
+
4
+ Overview
5
+
6
+ This class provides functionalities for asynchronous operations with an FTP server, including establishing connections,
7
+ checking directory existence, changing working directories, and downloading files.
8
+
9
+ """
10
+ from typing import List, Dict
11
+ from collections.abc import Callable
12
+ import aioftp
13
+ from ..exceptions import ComponentError
14
+ from .client import ClientInterface
15
+
16
+
17
+ """
18
+ FOR FTP over SSL
19
+
20
+ from ftplib import FTP_TLS
21
+
22
+ ftps = FTP_TLS(timeout=10)
23
+ ftps.set_debuglevel(2)
24
+ ftps.context.set_ciphers('DEFAULT@SECLEVEL=1')
25
+
26
+ ftps.connect(host, port)
27
+
28
+ ftps.login('user', 'password')
29
+ # enable TLS
30
+ ftps.auth()
31
+ ftps.prot_p()
32
+ ftps.retrlines('LIST')
33
+ """
34
+
35
+
36
+ class FTPClient(ClientInterface):
37
+ block_size = 8192
38
+ algorithms: List = [
39
+ "ssh-rsa",
40
+ "ssh-dss",
41
+ "sk-ssh-ed25519-cert-v01@openssh.com",
42
+ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com",
43
+ "ssh-ed25519-cert-v01@openssh.com",
44
+ "ssh-ed448-cert-v01@openssh.com",
45
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com",
46
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com",
47
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com",
48
+ "ecdsa-sha2-1.3.132.0.10-cert-v01@openssh.com",
49
+ "ssh-rsa-cert-v01@openssh.com",
50
+ "ssh-dss-cert-v01@openssh.com",
51
+ "sk-ssh-ed25519@openssh.com",
52
+ "sk-ecdsa-sha2-nistp256@openssh.com",
53
+ "ssh-ed25519",
54
+ "ssh-ed448",
55
+ "ecdsa-sha2-nistp521",
56
+ "ecdsa-sha2-nistp384",
57
+ "ecdsa-sha2-nistp256",
58
+ "ecdsa-sha2-1.3.132.0.10",
59
+ "rsa-sha2-256",
60
+ "rsa-sha2-512",
61
+ ]
62
+
63
+ async def close(self):
64
+ """Close Method."""
65
+ try:
66
+ await self._connection.quit()
67
+ except Exception as err:
68
+ print(f"Error on FTP disconnection, reason: {err!s}")
69
+
70
+ async def init_connection(
71
+ self, host, port, credentials: Dict, ssl: Callable = None
72
+ ):
73
+ """
74
+ init an FTP connection
75
+ """
76
+ args = {
77
+ "socket_timeout": 120,
78
+ "path_timeout": 30,
79
+ "encoding": "utf-8",
80
+ "ssl": ssl,
81
+ }
82
+ connection = None
83
+ try:
84
+ connection = aioftp.Client(**args)
85
+ await connection.connect(host, port)
86
+ except ValueError as err:
87
+ raise ComponentError(f"{err!s}") from err
88
+ except OSError as err:
89
+ raise ComponentError(f"FTP connection failed: {err!s}") from err
90
+ except Exception as err:
91
+ raise ComponentError(f"FTP Exception: {err!s}") from err
92
+ try:
93
+ await connection.login(*credentials.values())
94
+ return connection
95
+ except aioftp.StatusCodeError as err:
96
+ raise ComponentError(f"FTP connection Error: {err!s}") from err
97
+ except Exception as err:
98
+ raise ComponentError(f"FTP Exception: {err!s}") from err
99
+
100
+ def err_handler(self, err):
101
+ print(f"FTP Error: reason: {err.reason}, error: {err}")
102
+ return False
103
+
104
+ async def directory_exists(self, directory: str):
105
+ return await self._connection.exists(directory)
106
+
107
+ async def change_directory(self, directory: str):
108
+ await self._connection.change_directory(directory)
109
+
110
+ async def open(self):
111
+ pass
112
+
113
+ async def download_file(self, file: str, destination: str, rewrite: bool = False):
114
+ """download_file
115
+
116
+ Download a File from FTP based on Path.
117
+ Args:
118
+ file (str): file to be downloaded
119
+ destination (str): path to destination
120
+ rewrite (bool): file if exists, will be overwrite
121
+ TODO: Support for write_into and Renaming Files.
122
+ """
123
+ try:
124
+ await self._connection.download(
125
+ file,
126
+ destination=destination,
127
+ write_into=rewrite,
128
+ block_size=self.block_size,
129
+ )
130
+ except Exception as err:
131
+ raise ComponentError(f"FTP Download Exception: {err!s}") from err
@@ -0,0 +1,201 @@
1
+ from abc import ABC
2
+ from typing import List, Dict
3
+ import asyncio
4
+ from datetime import datetime, timedelta
5
+ import requests
6
+ from googleapiclient.discovery import build
7
+ from .GoogleClient import GoogleClient
8
+ from ..exceptions import ComponentError
9
+
10
+
11
+ class GoogleCalendarClient(GoogleClient, ABC):
12
+ """
13
+ Google Calendar Client for managing calendar events.
14
+ """
15
+
16
+ async def get_client(self):
17
+ """Get the Google Calendar client, with caching."""
18
+ if not hasattr(self, '_client'):
19
+ self.service = await asyncio.to_thread(build, 'calendar', 'v3', credentials=self.credentials)
20
+ return self.service
21
+
22
+ async def create_event(self, calendar_id: str, event: Dict) -> Dict:
23
+ """
24
+ Create an event in the specified Google Calendar.
25
+
26
+ Args:
27
+ calendar_id (str): The ID of the calendar to add the event to.
28
+ event (dict): The event details.
29
+
30
+ Returns:
31
+ dict: Details of the created event.
32
+ """
33
+ client = await self.get_client()
34
+ created_event = await asyncio.to_thread(client.events().insert(calendarId=calendar_id, body=event).execute)
35
+ print(f"Event created: {created_event.get('htmlLink')}")
36
+ return created_event
37
+
38
+ async def list_events(self, calendar_id: str, time_min: datetime, time_max: datetime, max_results: int = 10) -> List[Dict]:
39
+ """
40
+ List events in a specified time range.
41
+
42
+ Args:
43
+ calendar_id (str): The ID of the calendar to list events from.
44
+ time_min (datetime): Start time to retrieve events.
45
+ time_max (datetime): End time to retrieve events.
46
+ max_results (int): Maximum number of events to retrieve (default is 10).
47
+
48
+ Returns:
49
+ list: List of events within the time range.
50
+ """
51
+ client = await self.get_client()
52
+ events_result = await asyncio.to_thread(
53
+ client.events().list,
54
+ calendarId=calendar_id,
55
+ timeMin=time_min.isoformat() + 'Z',
56
+ timeMax=time_max.isoformat() + 'Z',
57
+ maxResults=max_results,
58
+ singleEvents=True,
59
+ orderBy="startTime"
60
+ )
61
+ events = events_result.execute().get('items', [])
62
+ return events
63
+
64
+ async def update_event(self, calendar_id: str, event_id: str, updated_event: Dict) -> Dict:
65
+ """
66
+ Update an existing event in the calendar.
67
+
68
+ Args:
69
+ calendar_id (str): The ID of the calendar containing the event.
70
+ event_id (str): The ID of the event to update.
71
+ updated_event (dict): The updated event details.
72
+
73
+ Returns:
74
+ dict: Details of the updated event.
75
+ """
76
+ client = await self.get_client()
77
+ event = await asyncio.to_thread(client.events().update, calendarId=calendar_id, eventId=event_id, body=updated_event)
78
+ return event.execute()
79
+
80
+ async def delete_event(self, calendar_id: str, event_id: str):
81
+ """
82
+ Delete an event from the specified calendar.
83
+
84
+ Args:
85
+ calendar_id (str): The ID of the calendar.
86
+ event_id (str): The ID of the event to delete.
87
+ """
88
+ client = await self.get_client()
89
+ await asyncio.to_thread(client.events().delete(calendarId=calendar_id, eventId=event_id).execute)
90
+ print(f"Event {event_id} deleted.")
91
+
92
+ async def get_event(self, calendar_id: str, event_id: str) -> Dict:
93
+ """
94
+ Retrieve details of a specific event.
95
+
96
+ Args:
97
+ calendar_id (str): The ID of the calendar.
98
+ event_id (str): The ID of the event.
99
+
100
+ Returns:
101
+ dict: Details of the event.
102
+ """
103
+ client = await self.get_client()
104
+ event = await asyncio.to_thread(client.events().get(calendarId=calendar_id, eventId=event_id).execute)
105
+ return event
106
+
107
+ async def setup_watch(self, calendar_id: str, webhook_url: str, channel_id: str) -> Dict:
108
+ """
109
+ Sets up a watch on the specified calendar to receive notifications when events are created, updated, or deleted.
110
+
111
+ Args:
112
+ calendar_id (str): The ID of the calendar to monitor.
113
+ webhook_url (str): The URL of the webhook endpoint to receive notifications.
114
+ channel_id (str): Unique ID for this notification channel.
115
+
116
+ Returns:
117
+ dict: The response from the Google Calendar API containing the channel information.
118
+ """
119
+ # Define request body for the watch request
120
+ request_body = {
121
+ "id": channel_id, # Unique identifier for the channel
122
+ "type": "webhook",
123
+ "address": webhook_url, # Webhook URL to receive notifications
124
+ "params": {
125
+ "ttl": "86400" # Time-to-live in seconds, max 604800 (7 days)
126
+ }
127
+ }
128
+ if not self.service:
129
+ await self.get_client()
130
+
131
+ try:
132
+ # Set up the watch on the specified calendar
133
+ response = self.service.events().watch(calendarId=calendar_id, body=request_body).execute()
134
+ print("Watch setup successful:", response)
135
+ return response
136
+ except Exception as e:
137
+ print(f"Error setting up watch: {e}")
138
+ raise
139
+
140
+ async def check_event_start_time(self, event_start_time: datetime):
141
+ """
142
+ Check if the current time has reached or passed the event's start time and trigger an action if so.
143
+
144
+ Args:
145
+ event_start_time (datetime): The start time of the event to check.
146
+
147
+ """
148
+ while True:
149
+ now = datetime.utcnow()
150
+ if now >= event_start_time:
151
+ # Trigger the action when event time is reached
152
+ print("Event time has been reached! Triggering action...")
153
+ # You can add your custom action here
154
+ break
155
+ await asyncio.sleep(60) # Check every minute
156
+
157
+ async def create_subscription(self, webhook_url: str, client_state: str = "secret_string", expiration_hours: int = 1) -> dict:
158
+ """
159
+ Create a subscription to receive notifications when events are created, updated, or deleted in the calendar.
160
+
161
+ Args:
162
+ webhook_url (str): The webhook URL that will receive the notifications.
163
+ client_state (str): A client secret string for verifying notifications.
164
+ expiration_hours (int): Duration for which the subscription should be valid (maximum is 4230 minutes or 7 days).
165
+
166
+ Returns:
167
+ dict: The response from Microsoft Graph API with subscription details.
168
+ """
169
+ # Set up expiration for subscription (max 7 days)
170
+ expiration_date = datetime.utcnow() + timedelta(hours=expiration_hours)
171
+ expiration_datetime = expiration_date.isoformat() + "Z"
172
+
173
+ # Define the subscription request body
174
+ request_body = {
175
+ "changeType": "created,updated,deleted",
176
+ "notificationUrl": webhook_url,
177
+ "resource": "me/events", # Subscribe to the user's calendar events
178
+ "expirationDateTime": expiration_datetime,
179
+ "clientState": client_state
180
+ }
181
+
182
+ # Acquire access token for authentication
183
+ access_token = self._access_token
184
+ headers = {
185
+ "Authorization": f"Bearer {access_token}",
186
+ "Content-Type": "application/json"
187
+ }
188
+
189
+ # Send the subscription request to Microsoft Graph API
190
+ url = "https://graph.microsoft.com/v1.0/subscriptions"
191
+ response = requests.post(url, headers=headers, json=request_body)
192
+
193
+ # Check for successful response
194
+ if response.status_code == 201:
195
+ subscription_info = response.json()
196
+ print("Subscription created successfully:", subscription_info)
197
+ return subscription_info
198
+ else:
199
+ error_message = response.json().get("error", {}).get("message", "Unknown error")
200
+ print(f"Failed to create subscription: {error_message}")
201
+ raise ComponentError(f"Failed to create subscription: {error_message}")
@@ -0,0 +1,133 @@
1
+ from pathlib import Path, PurePath
2
+ from typing import Union, List, Dict, Callable
3
+ from abc import ABC
4
+ from oauth2client.service_account import ServiceAccountCredentials
5
+ from googleapiclient.discovery import build
6
+ from navconfig import BASE_DIR
7
+ from ..exceptions import ComponentError, ConfigError
8
+ from ..conf import GOOGLE_CREDENTIALS_FILE
9
+
10
+
11
+ # Define the scope
12
+ default_scopes = [
13
+ "https://spreadsheets.google.com/feeds",
14
+ "https://www.googleapis.com/auth/drive"
15
+ ]
16
+
17
+
18
+ class GoogleClient(ABC):
19
+ """
20
+ Google Client Client.
21
+
22
+ Managing Authentication and resources from Google Apps.
23
+
24
+ """
25
+ def __init__(self, *args, credentials: Union[str, dict] = None, **kwargs):
26
+ self.credentials_file: PurePath = None
27
+ self.credentials_str: str = None
28
+ self.credentials_dict: dict = None
29
+ self.scopes: list = kwargs.pop('scopes', default_scopes)
30
+ if credentials is None:
31
+ if not GOOGLE_CREDENTIALS_FILE.exists():
32
+ raise ComponentError(
33
+ "Google: No credentials provided."
34
+ )
35
+ self.credentials_file = GOOGLE_CREDENTIALS_FILE
36
+ if isinstance(credentials, str):
37
+ # end with JSON, then are a credentials file:
38
+ if credentials.endswith(".json"):
39
+ self.credentials_file = Path(credentials).resolve()
40
+ if not self.credentials_file.exists():
41
+ # Check if File is on BASE PATH env.
42
+ self.credentials_file = BASE_DIR.joinpath(credentials).resolve()
43
+ if not self.credentials_file.exists():
44
+ raise ConfigError(
45
+ f"Google: Credentials file not found: {self.credentials_file}"
46
+ )
47
+ else:
48
+ # Assume is a JSON string
49
+ self.credentials_str = credentials
50
+ elif isinstance(credentials, PurePath):
51
+ self.credentials_file = Path(credentials).resolve()
52
+ if not self.credentials_file.exists():
53
+ raise ConfigError(
54
+ f"Google: No credentials file on {self.credentials_file}"
55
+ )
56
+ elif isinstance(credentials, dict):
57
+ self.credentials_dict = credentials
58
+
59
+ super().__init__(*args, **kwargs)
60
+
61
+ def connection(self):
62
+ if self.credentials_file:
63
+ self.credentials = ServiceAccountCredentials.from_json_keyfile_name(
64
+ self.credentials_file,
65
+ scopes=self.scopes,
66
+ )
67
+ elif self.credentials_dict:
68
+ self.credentials = ServiceAccountCredentials.from_json_keyfile_dict(
69
+ self.credentials_dict,
70
+ scopes=self.scopes
71
+ )
72
+ elif self.credentials_str:
73
+ self.credentials = ServiceAccountCredentials.from_json(
74
+ self.credentials_str,
75
+ scopes=self.scopes
76
+ )
77
+ else:
78
+ self.credentials = ServiceAccountCredentials.from_json_keyfile_name(
79
+ GOOGLE_CREDENTIALS_FILE,
80
+ scopes=self.scopes
81
+ )
82
+ return self
83
+
84
+ def get_service(self, service: str, version: str = 'v3'):
85
+ """
86
+ Get a cached Google API service instance or create one if not cached.
87
+
88
+ Args:
89
+ service (str): Name of the Google service (e.g., 'drive', 'sheets').
90
+ version (str): Version of the API (default: 'v3').
91
+
92
+ Returns:
93
+ googleapiclient.discovery.Resource: The requested Google API service client.
94
+ """
95
+ if not self.credentials:
96
+ self.connection()
97
+ if (srv := getattr(self, f"_{service}", None)):
98
+ return srv
99
+ srv = build(service, version, credentials=self.credentials)
100
+ setattr(self, f"_{service}", srv)
101
+ return srv
102
+
103
+ def get_drive_client(self):
104
+ """Shortcut for accessing the Google Drive client."""
105
+ return self.get_service("drive", "v3")
106
+
107
+ def get_sheets_client(self):
108
+ """Shortcut for accessing the Google Sheets client."""
109
+ return self.get_service("sheets", "v4")
110
+
111
+ def close(self):
112
+ """Clears cached credentials and services."""
113
+ self.credentials = None
114
+ self._services_cache.clear()
115
+
116
+ def get_search(self, query: str, version: str = 'v1', cse_id: str = None, **kwargs):
117
+ """
118
+ Get a cached Google API service instance or create one if not cached.
119
+
120
+ Args:
121
+
122
+ query (str): The search query.
123
+ version (str): Version of the API (default: 'v1').
124
+ cse_id (str): The Custom Search Engine ID.
125
+ **kwargs: Additional arguments for the API request.
126
+
127
+ Returns:
128
+ googleapiclient.discovery.Resource: The requested Google API service client.
129
+ """
130
+ if not self.credentials:
131
+ self.connection()
132
+ srv = build("customsearch", version, credentials=self.credentials)
133
+ return srv.cse().list(q=query, cx=cse_id, **kwargs)
@@ -0,0 +1,127 @@
1
+ from abc import ABC
2
+ from typing import Union
3
+ from pathlib import Path, PurePath
4
+ import asyncio
5
+ import aiofiles
6
+ import pandas as pd
7
+ from googleapiclient.errors import HttpError
8
+ from googleapiclient.http import MediaIoBaseDownload
9
+ from .GoogleClient import GoogleClient
10
+ from ..exceptions import ComponentError
11
+
12
+
13
+ class GoogleDriveClient(GoogleClient, ABC):
14
+ """
15
+ Google Drive Client for downloading files from Google Drive.
16
+ """
17
+
18
+ async def download_file(
19
+ self,
20
+ source_filename: str,
21
+ destination_dir: Union[str, Path] = "."
22
+ ) -> PurePath:
23
+ """
24
+ Download a file from Google Drive by its name.
25
+
26
+ Args:
27
+ source_filename (str): The name of the file to download.
28
+ destination_dir (str or Path): Directory where the file will be saved (default is current directory).
29
+
30
+ Returns:
31
+ str: Path to the downloaded file.
32
+ """
33
+ try:
34
+ drive_service = await asyncio.to_thread(self.get_drive_client)
35
+
36
+ # Search for the file by name
37
+ results = await asyncio.to_thread(
38
+ drive_service.files().list,
39
+ q=f"name='{source_filename}'",
40
+ fields="files(id, name)",
41
+ pageSize=1
42
+ )
43
+ files = results.execute().get('files', [])
44
+ if not files:
45
+ raise ComponentError(f"File '{source_filename}' not found on Google Drive.")
46
+
47
+ file_id = files[0]['id']
48
+ request = drive_service.files().get_media(fileId=file_id)
49
+ file_path = Path(destination_dir) / source_filename
50
+
51
+ # Asynchronously open the file with aiofiles
52
+ async with aiofiles.open(file_path, 'wb') as f:
53
+ downloader = MediaIoBaseDownload(f, request)
54
+ done = False
55
+ while not done:
56
+ # Run the next_chunk call in a separate thread
57
+ status, done = await asyncio.to_thread(downloader.next_chunk)
58
+ print(f"Download {int(status.progress() * 100)}%.")
59
+
60
+ return file_path
61
+
62
+ except HttpError as error:
63
+ raise ComponentError(f"Error downloading file from Google Drive: {error}")
64
+
65
+ async def download_folder(
66
+ self,
67
+ folder_name: str,
68
+ destination_dir: Union[str, Path] = "."
69
+ ) -> None:
70
+ """
71
+ Download all files within a specified Google Drive folder by name.
72
+
73
+ Args:
74
+ folder_name (str): The name of the folder to download.
75
+ destination_dir (str or Path): Directory where the files will be saved.
76
+
77
+ Returns:
78
+ None
79
+ """
80
+ try:
81
+ drive_service = await asyncio.to_thread(self.get_drive_client)
82
+
83
+ # Search for the folder by name to get the folder ID
84
+ folder_results = await asyncio.to_thread(
85
+ drive_service.files().list,
86
+ q=f"name='{folder_name}' and mimeType='application/vnd.google-apps.folder'",
87
+ fields="files(id, name)",
88
+ pageSize=1
89
+ )
90
+ folders = folder_results.execute().get('files', [])
91
+ if not folders:
92
+ raise ComponentError(f"Folder '{folder_name}' not found on Google Drive.")
93
+
94
+ folder_id = folders[0]['id']
95
+ destination_dir = Path(destination_dir) / folder_name
96
+ destination_dir.mkdir(parents=True, exist_ok=True)
97
+
98
+ # List all files in the folder
99
+ file_results = await asyncio.to_thread(
100
+ drive_service.files().list,
101
+ q=f"'{folder_id}' in parents and mimeType != 'application/vnd.google-apps.folder'",
102
+ fields="files(id, name)"
103
+ )
104
+ files = file_results.execute().get('files', [])
105
+
106
+ if not files:
107
+ print(f"No files found in folder '{folder_name}'.")
108
+ return
109
+
110
+ # Download each file in the folder
111
+ for file_info in files:
112
+ file_id = file_info['id']
113
+ file_name = file_info['name']
114
+ file_path = destination_dir / file_name
115
+
116
+ request = drive_service.files().get_media(fileId=file_id)
117
+ async with aiofiles.open(file_path, 'wb') as f:
118
+ downloader = MediaIoBaseDownload(f, request)
119
+ done = False
120
+ while not done:
121
+ status, done = await asyncio.to_thread(downloader.next_chunk)
122
+ print(f"Downloading '{file_name}' - {int(status.progress() * 100)}% complete.")
123
+
124
+ print(f"Downloaded '{file_name}' to '{file_path}'.")
125
+
126
+ except HttpError as error:
127
+ raise ComponentError(f"Error downloading folder '{folder_name}' from Google Drive: {error}")
@@ -0,0 +1,89 @@
1
+ from abc import ABC
2
+ from pathlib import Path
3
+ from typing import Union
4
+ import asyncio
5
+ from google.cloud import storage
6
+ from google.auth.exceptions import GoogleAuthError
7
+ from .GoogleClient import GoogleClient
8
+ from ..exceptions import ComponentError
9
+
10
+
11
+ class GoogleCloudStorageClient(GoogleClient, ABC):
12
+ """
13
+ Google Cloud Storage Client for interacting with Google Cloud Storage (GCS).
14
+ Provides methods for file and folder operations.
15
+ """
16
+
17
+ def __init__(self, *args, bucket_name: str, **kwargs):
18
+ super().__init__(*args, **kwargs)
19
+ self.bucket_name = bucket_name
20
+ self._bucket = None
21
+
22
+ async def get_bucket(self):
23
+ """Get the GCS bucket, with caching."""
24
+ if not self._bucket:
25
+ try:
26
+ client = await asyncio.to_thread(storage.Client, credentials=self.credentials)
27
+ self._bucket = client.bucket(self.bucket_name)
28
+ except GoogleAuthError as e:
29
+ raise ComponentError(f"Google GCS authentication error: {e}")
30
+ return self._bucket
31
+
32
+ async def create_folder(self, folder_name: str):
33
+ """Create a folder in GCS by creating an empty blob with a trailing '/'."""
34
+ bucket = await self.get_bucket()
35
+ blob = bucket.blob(f"{folder_name}/")
36
+ await asyncio.to_thread(blob.upload_from_string, "")
37
+ print(f"Folder '{folder_name}' created in GCS.")
38
+
39
+ async def upload_file(self, source_path: Union[str, Path], destination_path: str):
40
+ """Upload a file to GCS."""
41
+ bucket = await self.get_bucket()
42
+ blob = bucket.blob(destination_path)
43
+ await asyncio.to_thread(blob.upload_from_filename, str(source_path))
44
+ print(f"File '{source_path}' uploaded to '{destination_path}' in GCS.")
45
+
46
+ async def download_file(self, source_path: str, destination_dir: Union[str, Path]):
47
+ """Download a file from GCS."""
48
+ bucket = await self.get_bucket()
49
+ blob = bucket.blob(source_path)
50
+ destination_file = Path(destination_dir) / Path(source_path).name
51
+ await asyncio.to_thread(blob.download_to_filename, str(destination_file))
52
+ print(f"File '{source_path}' downloaded to '{destination_file}'.")
53
+
54
+ async def delete_file(self, file_path: str):
55
+ """Delete a file from GCS."""
56
+ bucket = await self.get_bucket()
57
+ blob = bucket.blob(file_path)
58
+ await asyncio.to_thread(blob.delete)
59
+ print(f"File '{file_path}' deleted from GCS.")
60
+
61
+ async def upload_folder(self, source_folder: Union[str, Path], destination_folder: str):
62
+ """Upload all files from a local folder to a GCS folder."""
63
+ source_folder = Path(source_folder)
64
+ tasks = []
65
+ for file_path in source_folder.glob("**/*"):
66
+ if file_path.is_file():
67
+ relative_path = file_path.relative_to(source_folder)
68
+ destination_path = f"{destination_folder}/{relative_path}"
69
+ tasks.append(self.upload_file(file_path, destination_path))
70
+ await asyncio.gather(*tasks)
71
+ print(f"Folder '{source_folder}' uploaded to '{destination_folder}' in GCS.")
72
+
73
+ async def download_folder(self, source_folder: str, destination_folder: Union[str, Path]):
74
+ """Download all files from a GCS folder to a local folder."""
75
+ bucket = await self.get_bucket()
76
+ blobs = bucket.list_blobs(prefix=f"{source_folder}/")
77
+ destination_folder = Path(destination_folder) / source_folder
78
+ destination_folder.mkdir(parents=True, exist_ok=True)
79
+
80
+ tasks = []
81
+ async for blob in blobs:
82
+ if not blob.name.endswith("/"): # Skip "directory" entries
83
+ relative_path = Path(blob.name).relative_to(source_folder)
84
+ local_file_path = destination_folder / relative_path
85
+ local_file_path.parent.mkdir(parents=True, exist_ok=True)
86
+ tasks.append(asyncio.to_thread(blob.download_to_filename, str(local_file_path)))
87
+ await asyncio.gather(*tasks)
88
+ print(f"Folder '{source_folder}' downloaded to '{destination_folder}'.")
89
+