flowtask 5.8.4__cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

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