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,767 @@
1
+ """
2
+ FileService.
3
+
4
+ Work with slug-based file definitions, upload, download, enable tasks, etc.
5
+ """
6
+ import asyncio
7
+ import logging
8
+ import os
9
+ import re
10
+ import traceback
11
+ import uuid
12
+ from io import BytesIO, StringIO
13
+ from pathlib import Path
14
+ import cchardet
15
+ import magic
16
+ import pandas
17
+ import aiofiles
18
+ from asyncdb import AsyncDB
19
+ from asyncdb.exceptions import NoDataFound
20
+ from navigator.conf import asyncpg_url
21
+ from navigator.views import BaseView
22
+ from ...exceptions import (
23
+ FileError,
24
+ FileNotFound,
25
+ NotSupported,
26
+ TaskFailed,
27
+ TaskNotFound
28
+ )
29
+ # tasks
30
+ from ...services.tasks import launch_task
31
+ from ...conf import FILES_PATH
32
+ from .model import FileModel
33
+
34
+
35
+ excel_based = [
36
+ "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
37
+ "application/vnd.ms-excel.sheet.macroEnabled.12",
38
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
39
+ "application/vnd.ms-excel",
40
+ "text/xml"
41
+ ]
42
+
43
+
44
+ class FileUploaded:
45
+ """
46
+ FileUploaded.
47
+ processing a FileField
48
+ """
49
+ _file = None
50
+ _filefield: str = 'file'
51
+ _content = None
52
+ _directory = None
53
+ _path = None
54
+ _df = None
55
+ _numRows = 0
56
+ _columns = []
57
+ _mimetype = 'application/octet-stream'
58
+ _encoding = 'ISO-8859-1'
59
+
60
+ def __init__(self, post, mimetype=None):
61
+ self._file = post[self._filefield]
62
+ self._content = self._file.file.read()
63
+ if mimetype:
64
+ self._mimetype = mimetype
65
+ else:
66
+ self._mimetype = self._file.content_type
67
+
68
+ def content(self):
69
+ return self._content
70
+
71
+ @property
72
+ def filename(self):
73
+ return self._file.filename
74
+
75
+ @property
76
+ def df(self):
77
+ return self._df
78
+
79
+ def num_rows(self):
80
+ return self._numRows
81
+
82
+ @property
83
+ def columns(self):
84
+ return self._columns
85
+
86
+ @property
87
+ def content_type(self):
88
+ return self._mimetype
89
+
90
+ def is_empty(self):
91
+ return not bool(self._content)
92
+
93
+ def is_mime_valid(self, type):
94
+ if type is not None:
95
+ if self._mimetype == type or self._file.content_type == type:
96
+ return True
97
+ else:
98
+ return False
99
+ else:
100
+ f = magic.Magic(mime=True)
101
+ try:
102
+ mime = f.from_buffer(self._content)
103
+ return bool(mime)
104
+ except Exception:
105
+ # return True # cant enforcing Mime check using mime Magic
106
+ return False
107
+
108
+ def valid_name(self, name, pattern, rename=False):
109
+ # TODO: using pattern to validate name structure
110
+ if rename:
111
+ return True
112
+ # print(self._file.filename, pattern, name)
113
+ if pattern is not None:
114
+ return re.match(pattern, self._file.filename)
115
+ elif self._file.filename != name:
116
+ return False
117
+ else:
118
+ return True
119
+
120
+ def valid_content(self, **kwargs):
121
+ """
122
+ valid_content.
123
+ check if is a valid content-type
124
+ ex: if is a csv, json or excel, open with pandas, if txt with stream, if image, etc
125
+ """
126
+ if self._mimetype in ('application/octet-stream', 'application/x-zip-compressed', 'application/zip'):
127
+ return [True, None]
128
+
129
+ self._encoding = self._get_encoding()
130
+ s = self._get_decoded_content()
131
+ try:
132
+ data = StringIO(s)
133
+ bdata = BytesIO(self._content)
134
+ except Exception as err:
135
+ logging.error(f'Error encoding data: {err}')
136
+ s = str(self._content.decode('latin1').encode('utf-8'), 'utf-8')
137
+ data = StringIO(s)
138
+ bdata = BytesIO(self._content)
139
+ # check if is a valid content-type
140
+ try:
141
+ if self._mimetype == 'text/csv' or self._mimetype == 'text/plain':
142
+ self._df = pandas.read_csv(
143
+ data,
144
+ decimal=',',
145
+ engine='c',
146
+ keep_default_na=False,
147
+ na_values=['TBD', 'NULL', 'null', ''],
148
+ encoding=self._encoding,
149
+ skipinitialspace=True,
150
+ low_memory=False,
151
+ **kwargs
152
+ )
153
+ elif self._mimetype == 'application/json':
154
+ self._df = pandas.read_json(
155
+ data,
156
+ orient='records',
157
+ encoding=self._encoding,
158
+ low_memory=False,
159
+ **kwargs
160
+ )
161
+ elif self._mimetype in excel_based:
162
+ if self._mimetype == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
163
+ # xlsx or any openxml based document
164
+ file_engine = 'openpyxl'
165
+ elif self._mimetype == 'application/vnd.ms-excel.sheet.binary.macroEnabled.12':
166
+ file_engine = 'pyxlsb'
167
+ elif self._mimetype == "application/vnd.ms-excel":
168
+ file_engine = 'xlrd'
169
+ else:
170
+ try:
171
+ ext = Path(self._file.filename).suffix
172
+ except (AttributeError, ValueError):
173
+ ext = '.xls'
174
+ if ext == '.xls':
175
+ file_engine = 'xlrd'
176
+ else:
177
+ file_engine = 'openpyxl'
178
+ self._df = pandas.read_excel(
179
+ bdata,
180
+ engine=file_engine,
181
+ keep_default_na=True,
182
+ # low_memory=False,
183
+ **kwargs
184
+ )
185
+ else:
186
+ # TODO: currently is and invalid content
187
+ return [False, f'Error: Invalid content type {self._file.content_type}']
188
+ except pandas.errors.EmptyDataError as err:
189
+ raise Exception(
190
+ f"Error on Empty File: {self._file.filename}, error: {err}"
191
+ ) from err
192
+ except pandas.errors.ParserError as err:
193
+ raise Exception(
194
+ f"Error on Parsing File: {self._file.filename}, error: {err}"
195
+ ) from err
196
+ except ValueError as err:
197
+ raise Exception(
198
+ f"Error on File: {self._file.filename}, error: {err}"
199
+ ) from err
200
+ except Exception as err:
201
+ raise Exception(
202
+ f"Cannot Process File: {self._file.filename}, error: {err}"
203
+ ) from err
204
+ # exists dataframe:
205
+ print(self._df)
206
+ try:
207
+ if self._df.empty:
208
+ return [True, 'DataFrame is Empty']
209
+ else:
210
+ # self._df = self._df.fillna('')
211
+ self._columns = list(self._df.columns)
212
+ self._numRows = len(self._df.index)
213
+ return [True, None]
214
+ except Exception as err:
215
+ return [False, f'Error Processing Dataframe, {err.__class__} {err}']
216
+
217
+ def _get_encoding(self):
218
+ try:
219
+ result_charset = cchardet.detect(self._content)
220
+ confidence = result_charset['confidence']
221
+ encoding = result_charset['encoding']
222
+ if confidence < 0.6:
223
+ logging.warning(
224
+ f'Warning: charset confidence was only {confidence}'
225
+ )
226
+ if result_charset['encoding'] == 'ascii' or result_charset['encoding'] == 'ASCII':
227
+ encoding = "utf-8"
228
+ except Exception as e:
229
+ print('Error: ', e)
230
+ encoding = 'ISO-8859–1'
231
+ finally:
232
+ if encoding is None:
233
+ encoding = "ISO-8859–1"
234
+
235
+ logging.debug(f'Detected Encoding is: {encoding}')
236
+ return encoding
237
+
238
+ def _get_decoded_content(self) -> str:
239
+ for enc in (self._encoding, 'utf-8', 'latin1', 'ascii'):
240
+ try:
241
+ return str(self._content, enc)
242
+ except UnicodeDecodeError:
243
+ continue
244
+ else:
245
+ raise FileError(message="Can't decode file!", status=422)
246
+
247
+ @property
248
+ def path(self):
249
+ return self._path
250
+
251
+ def directory(self, dirname, filename: str = None):
252
+ if not filename:
253
+ filename = self._file.filename
254
+ self._directory = dirname
255
+ self._path = os.path.join(dirname, filename)
256
+
257
+ async def save(self, directory=None, forcing_charset: bool = False):
258
+ if directory:
259
+ self.directory(directory)
260
+ async with aiofiles.open(self._path, 'w+b') as fp:
261
+ try:
262
+ if forcing_charset is True:
263
+ await fp.write(self._content.decode(self._encoding).encode('utf-8'))
264
+ else:
265
+ await fp.write(self._content)
266
+ await fp.flush()
267
+ except (ValueError, asyncio.InvalidStateError) as err:
268
+ logging.exception(f'Saving File Error: {err!s}')
269
+ raise
270
+ except Exception as err:
271
+ logging.exception(f'Saving File Error: {err!s}')
272
+ raise
273
+
274
+
275
+ class FileService(BaseView):
276
+
277
+ log_data = {}
278
+ file_slug = None
279
+ user_id = None
280
+ scheduler = None
281
+ job = None
282
+ permission = None
283
+
284
+ async def get(self):
285
+ """
286
+ ---
287
+ description: Get all the Files objects in the current scope and program
288
+ summary: get the files information and attributes
289
+ tags:
290
+ - FileService
291
+ produces:
292
+ - application/json
293
+ parameters:
294
+ - name: user_id
295
+ description: user id to filter
296
+ in: path
297
+ required: true
298
+ type: integer
299
+ - name: file_slug
300
+ description: file slug
301
+ in: path
302
+ required: true
303
+ type: string
304
+ responses:
305
+ "200":
306
+ description: returns valid data
307
+ "204":
308
+ description: No data
309
+ "403":
310
+ description: Forbidden Call
311
+ "404":
312
+ description: Program o File not found
313
+ "406":
314
+ description: Query Error
315
+ """
316
+ try:
317
+ data = {}
318
+ user_id = self.request.rel_url.query['user_id']
319
+ file_slug = self.request.rel_url.query['file_slug']
320
+ except Exception as e:
321
+ headers = {
322
+ 'X-STATUS': 'EMPTY',
323
+ 'X-MESSAGE': 'Data not Found',
324
+ 'X-ERROR': str(e)
325
+ }
326
+ return self.no_content(headers=headers)
327
+ finally:
328
+ await self.close()
329
+
330
+ async def valid_permission(self, user_id, codename):
331
+ """
332
+ valid_permission.
333
+ Check if the user have permission for one func on the system.
334
+ """
335
+ # get from db
336
+ try:
337
+ sql = f"SELECT DISTINCT(tug.groups_id), ap.id as permission_id, ap.name as permission_name, ap.codename \
338
+ FROM troc.troc_user_group tug \
339
+ LEFT JOIN public.auth_group_permissions agp on agp.group_id = tug.groups_id \
340
+ LEFT JOIN public.auth_permission ap on ap.id = agp.permission_id \
341
+ WHERE tug.user_id = {user_id} and ap.codename = '{codename}';"
342
+ result = await self.query(sql)
343
+ if result:
344
+ self.permission = result[0]
345
+ return True
346
+ else:
347
+ return False
348
+ except Exception as e:
349
+ return False
350
+
351
+ async def put(self):
352
+ """ PUT FileService.
353
+ description: Upload a File and, optionally, running an associated Task
354
+ Parameters:
355
+ file_slug: slug of the file in TROC files table
356
+ program_slug: associated program
357
+ mimetype: optional mime-type, default csv
358
+ module_id: optional module ID
359
+ task: boolean in query-params to disable running task.
360
+ long_running: query-param to attach a Task in a Thread Pool
361
+ """
362
+ error = ''
363
+ no_worker = False
364
+ try:
365
+ qp = self.query_parameters(self.request)
366
+ except ValueError as e:
367
+ logging.exception(f'Error getting Parameters for FileService: {e}')
368
+ return self.critical(
369
+ reason=f'Error getting Parameters for FileService: {e}'
370
+ )
371
+ try:
372
+ runtask = bool(qp['task'])
373
+ except KeyError:
374
+ runtask = False
375
+ logging.debug(f'Run Task is: {runtask}')
376
+ try:
377
+ queued = qp['long_running']
378
+ except KeyError:
379
+ queued = False
380
+ try:
381
+ no_worker = qp['no_worker']
382
+ except KeyError:
383
+ no_worker = False
384
+ try:
385
+ post = await self.request.post()
386
+ except Exception as e:
387
+ return self.critical(
388
+ f'Error getting Parameters for FileService: {e}'
389
+ )
390
+ # file slug
391
+ try:
392
+ file_slug = post['file_slug']
393
+ except (TypeError, KeyError):
394
+ file_slug = None
395
+ try:
396
+ program_slug = post['program_slug']
397
+ except (TypeError, KeyError):
398
+ if file_slug:
399
+ program_slug = file_slug.split('_')[0]
400
+ else:
401
+ program_slug = 'troc'
402
+ # TODO: get program id from program_slug
403
+ try:
404
+ subdir = qp['subdir']
405
+ except KeyError:
406
+ try:
407
+ subdir = post['subdir']
408
+ except KeyError:
409
+ subdir = ''
410
+ # mime type
411
+ try:
412
+ mimetype = post['mimetype']
413
+ except (TypeError, KeyError):
414
+ mimetype = 'text/csv'
415
+ logging.debug(
416
+ f'File Upload for program {program_slug} for slug {file_slug}'
417
+ )
418
+ try:
419
+ # getting the FileField Object
420
+ f = FileUploaded(post, mimetype=mimetype)
421
+ filename = f.filename
422
+ content_type = f.content_type
423
+ logging.debug(
424
+ f'Opening File: {filename} with content type: {content_type}'
425
+ )
426
+ except Exception as e:
427
+ return self.error(
428
+ reason=f"Error on Uploaded File: {e}"
429
+ )
430
+ if f.is_empty():
431
+ headers = {
432
+ 'X-STATUS': 'EMPTY',
433
+ 'X-MESSAGE': 'File is Empty'
434
+ }
435
+ return self.no_content(headers=headers)
436
+ elif not f.is_mime_valid(mimetype):
437
+ headers = {
438
+ 'X-STATUS': 'Error',
439
+ 'X-MESSAGE': 'File has wrong mimetype',
440
+ 'X-FILENAME': f.filename
441
+ }
442
+ data = {
443
+ "message": f"Wrong mimetype on file {f.filename}: got {mimetype}, expected: {f.content_type}"
444
+ }
445
+ logging.error(
446
+ f"Wrong mimetype on {f.filename}: got {mimetype}, expected: {f.content_type}"
447
+ )
448
+ return self.error(response=data, headers=headers, status=401)
449
+ if not file_slug:
450
+ filepath = FILES_PATH.joinpath(program_slug, 'files', subdir)
451
+ if not filepath.exists():
452
+ try:
453
+ filepath.mkdir(parents=True)
454
+ except Exception as err:
455
+ logging.error(
456
+ f'Error creating Directory: {err}'
457
+ )
458
+ # TODO: get configuration for saving files from frontend
459
+ # TODO: validate user permissions
460
+ # TODO: using the current program directory to upload the file
461
+ f.directory(filepath)
462
+ try:
463
+ await f.save()
464
+ headers = {
465
+ 'X-STATUS': 'OK',
466
+ 'X-MESSAGE': 'File Uploaded'
467
+ }
468
+ data = {
469
+ "message": f"file was uploaded to directory: {f.path}"
470
+ }
471
+ return self.json_response(response=data, headers=headers)
472
+ except Exception as e:
473
+ return self.critical(reason=f'Uncaught Error on File Uploaded: {e}')
474
+ else:
475
+ try:
476
+ headers = {
477
+ 'X-STATUS': 'Error',
478
+ 'X-MESSAGE': f'File Slug doesnt exists or is disabled {file_slug}',
479
+ 'X-FILENAME': f.filename
480
+ }
481
+ db = AsyncDB('pg', dsn=asyncpg_url)
482
+ async with await db.connection() as conn:
483
+ FileModel.Meta.connection = conn
484
+ # getting from model:
485
+ filedef = await FileModel.get(file_slug=file_slug)
486
+ logging.debug(f'Detected File Definition: {filedef}')
487
+ if not filedef:
488
+ return self.error(
489
+ response=f"File Slug doesn't Exists: {file_slug}",
490
+ headers=headers
491
+ )
492
+ except NoDataFound:
493
+ return self.error(
494
+ response=f"File Service: Slug not Found: {file_slug}",
495
+ status=404
496
+ )
497
+ except Exception as err:
498
+ print(err, type(err))
499
+ return self.error(
500
+ response=f"Error querying File Service: {file_slug}: {err!s}"
501
+ )
502
+ # start validation
503
+ mime = filedef.mimetype
504
+ if mime and not f.is_mime_valid(mime):
505
+ data = {
506
+ "message": f"Wrong mimetype for File, got: {f.content_type} expected: {mime}"
507
+ }
508
+ return self.error(response=data)
509
+ name = filedef.filename
510
+ attributes = filedef.attributes
511
+ params = filedef.params
512
+ logging.debug(
513
+ f'File Definition: {name}, Attributes: {attributes}, {params}'
514
+ )
515
+ # rename file
516
+ try:
517
+ rename = name['rename']
518
+ except (KeyError, TypeError):
519
+ rename = False
520
+ # create directory
521
+ try:
522
+ create = attributes['create_dir']
523
+ except (KeyError, TypeError):
524
+ create = False
525
+
526
+ if 'name' in name:
527
+ if not f.valid_name(name['name'], name['pattern'], rename=rename):
528
+ data = {
529
+ "message": f"Wrong Filename for File, expected: {name['name']}"
530
+ }
531
+ return self.error(response=data)
532
+ # file is valid, needs to save:
533
+ try:
534
+ path = name['path']
535
+ except KeyError:
536
+ path = params['data_path']
537
+ path = Path(path).resolve()
538
+ if not path.exists():
539
+ # if doesnt exists path, is a relative path
540
+ logging.debug(f'Saving file on path: {path!s}')
541
+ try:
542
+ if create is True:
543
+ path.mkdir(exist_ok=True, parents=True)
544
+ else:
545
+ # directory to upload file doesn't exists
546
+ data = {
547
+ "message": f"Directory for upload File doesn't exists {path}"
548
+ }
549
+ return self.error(response=data)
550
+ except Exception as err:
551
+ data = {
552
+ "message": f"Error creating Directory: {err!s}"
553
+ }
554
+ return self.error(
555
+ exception=err,
556
+ response=data
557
+ )
558
+ # validacion de contenido (verificar csv, validate columns, data structure, etc)
559
+ try:
560
+ validate = params['validate']
561
+ except (KeyError, TypeError):
562
+ validate = True
563
+ if validate:
564
+ try:
565
+ pargs = params['pandas']
566
+ except KeyError:
567
+ pargs = {}
568
+ result, error = f.valid_content(**pargs)
569
+ logging.debug(
570
+ f'Validate pandas File: {result}, error: {error}')
571
+ if error and not result:
572
+ data = {"status": error}
573
+ return self.error(response=data, status=403)
574
+ elif result and error:
575
+ data = {
576
+ "status": error,
577
+ "message": "Empty File"
578
+ }
579
+ return self.error(response=data, status=404)
580
+ else:
581
+ # dataframe is valid, we need to make other validations
582
+ if filedef.fields:
583
+ try:
584
+ case_sensitive = filedef.case_sensitive
585
+ except Exception:
586
+ case_sensitive = False
587
+ if case_sensitive is True:
588
+ validate_cols = filedef.fields
589
+ columns = f.columns
590
+ else:
591
+ validate_cols = [f.lower() for f in filedef.fields]
592
+ columns = [c.lower() for c in f.columns]
593
+ if validate_cols:
594
+ if validate_cols != columns:
595
+ data = {
596
+ "status": "Error",
597
+ "message": "Invalid Column Names",
598
+ "expected": validate_cols,
599
+ "columns": f.columns
600
+ }
601
+ return self.error(response=data, status=401)
602
+ # TODO: make other validations, as data validations, and data quality
603
+ # define the directory to save file
604
+ f.directory(path)
605
+ try:
606
+ forcing_charset = attributes['forcing_charset']
607
+ except KeyError:
608
+ forcing_charset = False
609
+ if os.path.exists(f.path) and attributes['overwrite'] is False:
610
+ # file exists
611
+ data = {
612
+ "message": f"File already exists {f.path}"
613
+ }
614
+ return self.error(response=data)
615
+ else:
616
+ try:
617
+ await f.save(
618
+ directory=path,
619
+ forcing_charset=forcing_charset
620
+ )
621
+ except OSError as err:
622
+ logging.exception(
623
+ f'Connection Aborted on Upload: {err!s}'
624
+ )
625
+ except Exception as err:
626
+ print(err)
627
+ data = {
628
+ "message": f"Error Saving File on Path {f.path}"
629
+ }
630
+ return self.error(response=data)
631
+ # file exists and was uploaded
632
+ headers = {
633
+ 'X-STATUS': 'OK',
634
+ 'X-MESSAGE': 'File was Uploaded'
635
+ }
636
+ response = {
637
+ "message": f"file {f.filename} was uploaded to {f.path}",
638
+ "state": 202
639
+ }
640
+ logging.debug(response['message'])
641
+ result = {}
642
+ run_task = filedef.task_enabled if filedef.task_enabled else runtask
643
+ if run_task is True:
644
+ # if runtask is True or filedef.task_enabled is True:
645
+ try:
646
+ args = params['args']
647
+ except KeyError:
648
+ args = {}
649
+ # need to instanciate a Task Object to launch Task
650
+ try:
651
+ queued = params['long_running']
652
+ except KeyError:
653
+ pass
654
+ try:
655
+ task_id = params['task_id']
656
+ except (TypeError, ValueError, KeyError):
657
+ task_id = filedef.file_slug
658
+ program_slug = filedef.program_slug
659
+ try:
660
+ # loop = asyncio.new_event_loop()
661
+ task_uuid = uuid.uuid4()
662
+ if not filedef.params:
663
+ params = {}
664
+ else:
665
+ params = filedef.params
666
+ args = {**args, **params}
667
+ status, action, result = await launch_task(
668
+ program_slug=program_slug,
669
+ # task_id=task_id,
670
+ loop=self._loop,
671
+ task_uuid=task_uuid,
672
+ queued=queued,
673
+ no_worker=no_worker,
674
+ **args
675
+ )
676
+ result = {
677
+ "task": f"{program_slug}.{task_id}",
678
+ "task_execution": task_uuid,
679
+ "result": f"{status!s}"
680
+ }
681
+ if action == 'Executed':
682
+ state = 200
683
+ else:
684
+ state = 202
685
+ response = {
686
+ "state": state,
687
+ "message": f"Task associated with file {f.filename} was {action}",
688
+ **result
689
+ }
690
+ except OSError as err:
691
+ logging.exception(f'Connection Aborted: {err!s}')
692
+ return self.error(
693
+ reason=f'Connection Aborted: {err!s}'
694
+ )
695
+ except FileNotFound as err:
696
+ headers = {
697
+ 'X-STATUS': 'Task Failed',
698
+ 'X-MESSAGE': 'File Not Found'
699
+ }
700
+ response = {
701
+ "message": f"File Not Found {f.filename}",
702
+ "task": f"{program_slug}.{task_id}",
703
+ "state": 404,
704
+ "exception": str(err),
705
+ **result
706
+ }
707
+ return self.error(
708
+ response=response
709
+ )
710
+ except (NotSupported, TaskNotFound, TaskFailed, FileError) as err:
711
+ headers = {
712
+ 'X-STATUS': 'Task Failed',
713
+ 'X-MESSAGE': 'Task Execution Failed'
714
+ }
715
+ response = {
716
+ "message": f"Task error on associated file {f.filename}",
717
+ "task": f"{program_slug}.{task_id}",
718
+ "state": 406,
719
+ "exception": str(err),
720
+ **result
721
+ }
722
+ return self.error(
723
+ response=response
724
+ )
725
+ except Exception as err:
726
+ return self.critical(
727
+ exception=err, stacktrace=traceback.format_exc()
728
+ )
729
+ try:
730
+ show_preview = attributes['show_preview']
731
+ except KeyError:
732
+ show_preview = True
733
+ try:
734
+ num_preview = attributes['num_preview']
735
+ except KeyError:
736
+ num_preview = 10
737
+ if show_preview is True:
738
+ dt = f.df.head(num_preview).fillna('')
739
+ preview = dt.to_dict(orient='records')
740
+ else:
741
+ preview = None
742
+ try:
743
+ data = {
744
+ "task": f"{program_slug}.{task_id}",
745
+ 'NumRows': f.num_rows(),
746
+ 'columns': f.columns,
747
+ 'data': preview,
748
+ 'status': f"Upload completed: {f.filename}",
749
+ **response
750
+ }
751
+ if response['state'] > 300:
752
+ return self.error(
753
+ response=data,
754
+ headers=headers,
755
+ exception=response['exception'],
756
+ status=response['state']
757
+ )
758
+ else:
759
+ return self.json_response(
760
+ response=data,
761
+ headers=headers,
762
+ status=response['state']
763
+ )
764
+ except OSError as err:
765
+ logging.exception(f'Connection Aborted: {err!s}')
766
+ finally:
767
+ del filedef