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,504 @@
1
+ """
2
+ Scheduler Manager.
3
+
4
+ API View for Managing the Scheduler.
5
+ """
6
+ from typing import Any, TypeVar
7
+ from collections import defaultdict
8
+ from collections.abc import Callable
9
+ import asyncio
10
+ from functools import wraps
11
+ from concurrent.futures import ThreadPoolExecutor
12
+ from aiohttp import web, hdrs
13
+ from aiohttp.abc import AbstractView
14
+ from functools import partial
15
+ from datamodel import BaseModel
16
+ from navigator_session import get_session
17
+ from navigator.views import (
18
+ BaseView,
19
+ ModelView
20
+ )
21
+ from navigator.conf import AUTH_SESSION_OBJECT
22
+ from navconfig.logging import logging
23
+ from ...exceptions import FlowTaskError, NotFound
24
+ from ...conf import SCHEDULER_SERVICE_GROUPS, SCHEDULER_ADMIN_GROUPS
25
+ from .models import Job
26
+
27
+
28
+ F = TypeVar("F", bound=Callable[..., Any])
29
+
30
+
31
+ class SchedulerManager(BaseView):
32
+ """Scheduler Manager Facility.
33
+
34
+ get: getting Scheduler and Jobs information, for Jobs or a single job
35
+ post: editing existing jobs
36
+ put: inserting a new task into the jobstore
37
+ delete: removing (or pausing) some jobs from the scheduler
38
+ patch: reload all jobs.
39
+ """
40
+
41
+ async def get_session(self):
42
+ self._session = None
43
+ try:
44
+ self._session = await get_session(self.request)
45
+ except (ValueError, RuntimeError) as err:
46
+ return self.critical(
47
+ response={"message": "Error Decoding Session", "error": str(err)},
48
+ exception=err,
49
+ )
50
+ if not self._session:
51
+ self.error(
52
+ response={
53
+ "error": "Unauthorized",
54
+ "message": "Hint: maybe need to login and pass Authorization token.",
55
+ },
56
+ status=403,
57
+ )
58
+
59
+ async def get_userid(self, session, idx: str = "user_id") -> int:
60
+ if not session:
61
+ self.error(response={"error": "Unauthorized"}, status=403)
62
+ try:
63
+ if AUTH_SESSION_OBJECT in session:
64
+ return session[AUTH_SESSION_OBJECT][idx]
65
+ else:
66
+ return session[idx]
67
+ except KeyError:
68
+ self.error(
69
+ response={
70
+ "error": "Unauthorized",
71
+ "message": "Hint: maybe you need to pass an Authorization token.",
72
+ },
73
+ status=403,
74
+ )
75
+
76
+ @staticmethod
77
+ def service_auth(groups: list, *args, **kwargs):
78
+ def _wrapper(fn: F) -> Any:
79
+ @wraps(fn)
80
+ async def _wrap(self, *args, **kwargs) -> web.StreamResponse:
81
+ ## get User Session:
82
+ await self.get_session()
83
+ request = self.request
84
+ if request.get("authenticated", False) is False:
85
+ # check credentials:
86
+ raise web.HTTPUnauthorized(
87
+ reason="Access Denied",
88
+ headers={
89
+ hdrs.CONTENT_TYPE: "application/json",
90
+ hdrs.CONNECTION: "keep-alive",
91
+ },
92
+ )
93
+ self._userid = await self.get_userid(self._session)
94
+ self.logger.info(f"Scheduler: Authenticated User: {self._userid}")
95
+ # already a superuser?
96
+ userinfo = self._session[AUTH_SESSION_OBJECT]
97
+ if userinfo.get("superuser", False) is True:
98
+ self.logger.info(f"Audit: User {self._userid} is calling {fn!r}")
99
+ return await fn(self, *args, **kwargs)
100
+ # Checking User Permissions:
101
+ if await self._post_auth(groups, *args, **kwargs):
102
+ self.logger.info(f"Audit: User {self._userid} is calling {fn!r}")
103
+ return await fn(self, *args, **kwargs)
104
+ else:
105
+ raise web.HTTPUnauthorized(
106
+ reason="Access Denied",
107
+ headers={
108
+ hdrs.CONTENT_TYPE: "application/json",
109
+ hdrs.CONNECTION: "keep-alive",
110
+ },
111
+ )
112
+
113
+ return _wrap
114
+
115
+ return _wrapper
116
+
117
+ async def _post_auth(self, groups: list, *args, **kwargs):
118
+ """Post-authorization Model."""
119
+ member = False
120
+ userinfo = {}
121
+ try:
122
+ userinfo = self._session[AUTH_SESSION_OBJECT]
123
+ except KeyError:
124
+ member = False
125
+ if "groups" in userinfo:
126
+ member = bool(not set(userinfo["groups"]).isdisjoint(groups))
127
+ else:
128
+ user = self._session.decode("user")
129
+ for group in user.groups:
130
+ if group.group in groups:
131
+ member = True
132
+ if member is True:
133
+ ## Check Groups belong to User
134
+ return True
135
+ else:
136
+ return False
137
+
138
+ @service_auth(groups=SCHEDULER_SERVICE_GROUPS)
139
+ async def get(self):
140
+ app = self.request.app
141
+ scheduler = app["scheduler"]
142
+ args = self.match_parameters(self.request)
143
+ qs = self.query_parameters(self.request)
144
+ try:
145
+ job = args["job"]
146
+ except KeyError:
147
+ job = None
148
+ if job is None:
149
+ if qs.get("calendar"):
150
+ filter_day = qs.get("day", None)
151
+ jobs_data = []
152
+ for job in scheduler.get_all_jobs():
153
+ if job:
154
+ ts = job.next_run_time
155
+ jobs_data.append(
156
+ {
157
+ "id": job.id,
158
+ "name": job.name,
159
+ "next_run_time": job.next_run_time,
160
+ "date": ts.date(),
161
+ "hour": ts.time(),
162
+ "day": ts.strftime("%a"),
163
+ "trigger": f"{job.trigger!r}",
164
+ }
165
+ )
166
+ # Organize by day
167
+ jobs_by_day = defaultdict(list)
168
+ job_data = sorted(jobs_data, key=lambda x: x["hour"])
169
+ if filter_day:
170
+ for job in job_data:
171
+ if job["day"] == filter_day:
172
+ jobs_by_day[filter_day].append(job)
173
+ return self.json_response(response=jobs_by_day, state=200)
174
+ else:
175
+ # returning all days data:
176
+ for job in job_data:
177
+ day_key = job["day"]
178
+ jobs_by_day[day_key].append(job)
179
+ days_order = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
180
+ job_data = {
181
+ day: jobs_by_day[day]
182
+ for day in days_order
183
+ if day in jobs_by_day
184
+ }
185
+ return self.json_response(response=job_data, state=200)
186
+ elif qs.get("tabular"):
187
+ job_list = []
188
+ for job in scheduler.get_all_jobs():
189
+ j = scheduler.get_job(job.id)
190
+ ts = job.next_run_time
191
+ obj = {
192
+ "job_id": job.id,
193
+ "name": job.name,
194
+ "trigger": f"{job.trigger!r}",
195
+ "next_run_time": job.next_run_time,
196
+ "date": ts.date(),
197
+ "hour": ts.time(),
198
+ "day": ts.strftime("%a"),
199
+ "function": f"{job.func!r}",
200
+ "last_status": "paused"
201
+ if not job.next_run_time
202
+ else j["status"],
203
+ }
204
+ job_list.append(obj)
205
+ return self.json_response(response=job_list, state=200)
206
+ job_list = []
207
+ try:
208
+ for job in scheduler.get_all_jobs():
209
+ j = scheduler.get_job(job.id)
210
+ if j is None:
211
+ continue
212
+ obj = {}
213
+ obj[job.id] = {
214
+ "job_id": job.id,
215
+ "name": job.name,
216
+ "trigger": f"{job.trigger!r}",
217
+ "next_run_time": job.next_run_time,
218
+ "function": f"{job.func!r}",
219
+ "last_status": "paused" if not job.next_run_time else j["status"],
220
+ }
221
+ job_list.append(obj)
222
+ except Exception as err:
223
+ self.logger.exception(f"Error getting Job Scheduler info: {err!s}")
224
+ return self.error(
225
+ response={"message": f"Error getting Job Scheduler info: {err!s}"},
226
+ state=406,
227
+ )
228
+ return self.json_response(response=job_list, state=200)
229
+ else:
230
+ # getting all information about a single job.
231
+ try:
232
+ obj = scheduler.get_job(job)
233
+ if not obj:
234
+ raise NotFound(f"There is no Job {job}")
235
+ data = obj["data"]
236
+ print(obj["data"], obj["status"])
237
+ job = obj["job"]
238
+ if job.next_run_time is None:
239
+ status = "Paused"
240
+ else:
241
+ status = obj["status"]
242
+ result = {
243
+ "job_id": job.id,
244
+ "name": job.name,
245
+ "trigger": f"{job.trigger!r}",
246
+ "next_run_time": job.next_run_time,
247
+ "last_exec_time": data["last_exec_time"],
248
+ "function": f"{job.func!r}",
249
+ "last_status": status,
250
+ "last_traceback": data["job_state"],
251
+ }
252
+ print(result)
253
+ return self.json_response(response=result, state=200)
254
+ except NotFound as exc:
255
+ return self.error(response={"message": f"{exc}"}, state=400)
256
+ except Exception as err:
257
+ self.logger.exception(f"Error getting Job Scheduler info: {err!s}")
258
+ return self.error(
259
+ response={"message": f"Error getting Job Scheduler info: {err!s}"},
260
+ state=406,
261
+ )
262
+
263
+ async def put(self):
264
+ app = self.request.app
265
+ scheduler = app["scheduler"]
266
+ return self.json_response(response="Empty", state=204)
267
+
268
+ async def reload_jobs(self, scheduler):
269
+ try:
270
+ return await scheduler.create_jobs()
271
+ except Exception as err:
272
+ raise FlowTaskError(f"{err!s}") from err
273
+
274
+ @service_auth(groups=SCHEDULER_ADMIN_GROUPS)
275
+ async def patch(self):
276
+ app = self.request.app
277
+ scheduler = app["scheduler"]
278
+ args = self.match_parameters(self.request)
279
+ try:
280
+ job = args["job"]
281
+ except KeyError:
282
+ job = None
283
+ if job is None:
284
+ self.logger.info(
285
+ f"Audit: User {self._userid} are Tearing down the JobStore (removing Jobs)"
286
+ )
287
+ try:
288
+ # Teardown the JobStore (without stopping the service)
289
+ jobstore = scheduler.jobstores["default"]
290
+ # then, remove all jobs:
291
+ jobstore.remove_all_jobs()
292
+ # after that, wait and reload again:
293
+ except Exception as err:
294
+ self.logger.exception(
295
+ f"Error Teardown the Scheduler {err!r}"
296
+ )
297
+ return self.error(
298
+ response={"message": f"Error Starting Scheduler {err!r}"}, state=406
299
+ )
300
+ await asyncio.sleep(0.1)
301
+ try:
302
+ loop = scheduler.event_loop
303
+ self.logger.info("Reloading the Scheduler JobStore ...")
304
+ error = None
305
+ result = []
306
+ try:
307
+ result = await self.reload_jobs(scheduler)
308
+ # with ThreadPoolExecutor(max_workers=2) as executor:
309
+ # fn = partial(self.reload_jobs, scheduler)
310
+ # result = await loop.run_in_executor(executor, fn)
311
+ except Exception as exc:
312
+ error = exc
313
+ self.logger.error(
314
+ f"Failed to reload jobs: {exc}"
315
+ )
316
+ result = {
317
+ "status": "Done",
318
+ "description": "Scheduler was reloaded.",
319
+ "reloaded_by": self._userid,
320
+ "errors": f"{error}",
321
+ "jobs_loaded": len(result)
322
+ }
323
+ return self.json_response(response=result, state=202)
324
+ except Exception as err:
325
+ self.logger.exception(f"Error Starting Scheduler {err!r}")
326
+ return self.error(
327
+ response={"message": f"Error Starting Scheduler {err!r}"}, state=406
328
+ )
329
+ else:
330
+ try:
331
+ job_struc = scheduler.get_job(job)
332
+ job = job_struc["job"]
333
+ # getting info about will be paused or removed (TODO removed).
334
+ job.resume()
335
+ return self.json_response(
336
+ response=f"Job {job} was Resumed from Pause state.", state=202
337
+ )
338
+ except Exception as err:
339
+ self.logger.exception(f"Invalid Job Id {job!s}: {err!s}")
340
+ return self.error(
341
+ response={"error": f"Invalid Job Id {job!s}: {err!s}"}, state=406
342
+ )
343
+
344
+ @service_auth(groups=SCHEDULER_ADMIN_GROUPS)
345
+ async def delete(self):
346
+ app = self.request.app
347
+ scheduler = app["scheduler"]
348
+ args = self.match_parameters(self.request)
349
+ try:
350
+ job = args["job"]
351
+ except KeyError:
352
+ job = None
353
+ if job is None:
354
+ # TODO: shutdown the Scheduler
355
+ self.logger.info(
356
+ f"Audit: User {self._userid} is shutting down the Scheduler."
357
+ )
358
+ # first: stop the server
359
+ scheduler.scheduler.shutdown(wait=False)
360
+ # second: remove (reload) all jobs from scheduler.
361
+ for job in scheduler.get_all_jobs():
362
+ # first: remove all existing jobs from scheduler
363
+ self.logger.debug(f"Scheduler: Removing Job {job.id} from job store")
364
+ job.remove()
365
+ # after this, call again create jobs.
366
+ self.logger.info("Restarting the Scheduler...")
367
+ try:
368
+ # loop = scheduler.event_loop
369
+ error = None
370
+ result = []
371
+ try:
372
+ result = await self.reload_jobs(scheduler)
373
+ # with ThreadPoolExecutor(max_workers=2) as executor:
374
+ # fn = partial(self.reload_jobs, scheduler)
375
+ # result = await loop.run_in_executor(executor, fn)
376
+ except Exception as e:
377
+ error = e
378
+ await asyncio.sleep(0.1)
379
+ # start server again
380
+ await scheduler.start()
381
+ result = {
382
+ "status": "Done",
383
+ "description": "Scheduler was restarted.",
384
+ "restarted_by": self._userid,
385
+ "jobs_loaded": len(result),
386
+ "errors": f"{error}",
387
+ }
388
+ return self.json_response(response=result, state=202)
389
+ except Exception as err:
390
+ self.logger.exception(f"Error Starting Scheduler {err!r}")
391
+ return self.error(
392
+ response={"message": f"Error Starting Scheduler {err!r}"}, state=406
393
+ )
394
+ else:
395
+ try:
396
+ job_struc = scheduler.get_job(job)
397
+ job = job_struc["job"]
398
+ # getting info about will be paused or removed (TODO removed).
399
+ job.pause()
400
+ return self.json_response(response=f"Job {job} was Paused.", state=202)
401
+ except Exception as err:
402
+ self.logger.exception(
403
+ f"Invalid Job Id {job!s}: {err!s}"
404
+ )
405
+ return self.error(
406
+ response={"message": f"Invalid Job Id {job!s}: {err!s}"}, state=406
407
+ )
408
+
409
+
410
+ class JobManager(ModelView):
411
+ model: BaseModel = Job
412
+ path: str = "/api/v1/scheduler/jobs"
413
+ pk: str = 'job_id'
414
+
415
+ async def _get_created_by(self, value, column, data):
416
+ return await self.get_userid(session=self._session)
417
+
418
+ async def _set_created_by(self, value, column, data):
419
+ return await self.get_userid(session=self._session)
420
+
421
+ async def _patch_response(self, result, status: int = 202) -> web.Response:
422
+ """_patch_data.
423
+
424
+ Post-processing data after saved and before summit.
425
+ """
426
+ try:
427
+ scheduler = self.request.app["scheduler"]
428
+ except KeyError:
429
+ return self.error(
430
+ response={"message": "Scheduler is not available"},
431
+ status=500
432
+ )
433
+ job = result.job_id
434
+ job_status = result.enabled
435
+ job_struc = scheduler.get_job(job)
436
+ if job_status is False:
437
+ # Job need to be paused
438
+ if not job_struc:
439
+ # there is no jobs in the scheduler
440
+ return self.json_response(result, status=status)
441
+ job_obj = job_struc.get('job', None)
442
+ if job_obj:
443
+ # Remove Job from Jobstore
444
+ job_obj.remove()
445
+ if job_status is True:
446
+ # Job need to be resumed
447
+ if not job_struc:
448
+ # there is no jobs in the scheduler, add it:
449
+ await scheduler.add_job(result.to_dict())
450
+ return self.json_response(result, status=status)
451
+ job_obj = job_struc.get('job', None)
452
+ if not job_obj:
453
+ # Add this job to the Jobstore
454
+ await scheduler.add_job(result.to_dict())
455
+ # I change the job and job is on the scheduler running:
456
+ if job_status is True and job_struc:
457
+ job_obj = job_struc.get('job', None)
458
+ if job_obj:
459
+ # Remove the job, and re-add it.
460
+ job_obj.remove()
461
+ await scheduler.add_job(result.to_dict())
462
+ return self.json_response(result, status=status)
463
+
464
+ async def _post_response(
465
+ self,
466
+ response: Any,
467
+ fields: list = None,
468
+ headers: dict = None,
469
+ status: int = 200
470
+ ) -> web.Response:
471
+ """_post_response.
472
+
473
+ Post-processing data after saved and before summit.
474
+ """
475
+ try:
476
+ scheduler = self.request.app["scheduler"]
477
+ except KeyError:
478
+ return self.error(
479
+ response={"message": "Scheduler is not available"},
480
+ status=500
481
+ )
482
+ job = response.job_id
483
+ job_status = response.enabled
484
+ job_struc = scheduler.get_job(job)
485
+ if job_status is False and not job_struc:
486
+ # there is no jobs in the scheduler, nothing to do
487
+ return self.json_response(response, status=status)
488
+ # Check if the job alredy exists in the scheduler
489
+ job_obj = job_struc.get('job', None)
490
+ if job_obj:
491
+ # Remove Job from Jobstore before add-it
492
+ try:
493
+ job_obj.remove()
494
+ except Exception:
495
+ pass
496
+ if job_status is True:
497
+ await scheduler.add_job(response.to_dict())
498
+ elif job_obj is None and job_status is True:
499
+ # Job doesn't exists, please add it
500
+ await scheduler.add_job(response.to_dict())
501
+ elif job_status is True and not job_struc:
502
+ # there is no jobs in the scheduler, add it:
503
+ await scheduler.add_job(response.to_dict())
504
+ return self.json_response(response, status=status, headers=headers)
@@ -0,0 +1,58 @@
1
+ from typing import Dict, Optional, Any, Union
2
+ from uuid import UUID, uuid4
3
+ from datetime import datetime
4
+ from asyncdb.models import Model, Field
5
+
6
+
7
+ class Job(Model):
8
+ job_id: str = Field(required=True, primary_key=True)
9
+ job_uuid: UUID = Field(
10
+ required=False,
11
+ primary_key=True,
12
+ db_default="auto",
13
+ default_factory=uuid4,
14
+ repr=False
15
+ )
16
+ job: Optional[dict] = Field(required=True, default_factory=dict)
17
+ attributes: Optional[dict] = Field(default_factory=dict)
18
+ schedule_type: str = Field(required=False, default="interval")
19
+ schedule: Optional[dict] = Field(default_factory=dict)
20
+ jitter: Optional[int] = Field(required=False, default=0)
21
+ is_coroutine: bool = Field(required=False, default=True)
22
+ jobstore: str = Field(required=False, default="default")
23
+ executor: str = Field(required=False, default="default")
24
+ run_date: Optional[datetime]
25
+ start_date: Optional[datetime]
26
+ end_date: Optional[datetime]
27
+ last_exec_time: Optional[datetime]
28
+ next_run_time: Optional[datetime]
29
+ job_status: int = Field(required=False, default=0)
30
+ job_state: Optional[bytes] = Field(required=False, repr=False)
31
+ traceback: Optional[str] = Field(required=False, repr=False)
32
+ params: Optional[dict] = Field(default_factory=dict)
33
+ enabled: bool = Field(required=False, default=True)
34
+ reschedule: bool = Field(required=False, default=False)
35
+ reschedule_jitter: Optional[int] = Field(required=False, default=0)
36
+ rescheduled_max: Optional[int] = Field(required=False, default=0)
37
+ notification: bool = Field(required=False, default=False)
38
+ priority: Optional[str] = Field(required=False)
39
+ created_at: datetime = Field(
40
+ required=False,
41
+ default=datetime.now(),
42
+ repr=False
43
+ )
44
+ updated_at: datetime = Field(
45
+ required=False,
46
+ default=datetime.now(),
47
+ repr=False
48
+ )
49
+ ## ALTER TABLE troc.jobs add column if not exists created_by integer;
50
+ created_by: int = Field(required=False)
51
+
52
+ class Meta:
53
+ driver = "pg"
54
+ name = "jobs"
55
+ schema = "troc"
56
+ app_label = "troc"
57
+ strict = True
58
+ frozen = False
@@ -0,0 +1,72 @@
1
+ """
2
+ Scheduler Service.
3
+
4
+ API View for Managing Jobs in NAV Scheduler.
5
+ """
6
+ from navigator.views import BaseView
7
+ from datetime import datetime
8
+
9
+
10
+ class SchedulerService(BaseView):
11
+ """Scheduler Manager Facility.
12
+
13
+ Facility for a remotely accessible Scheduler Service.
14
+ can add, modify, remove, pause or re-schedule jobs, looking for job information, etc.
15
+
16
+ TODO: Can we use it also as RPC Service.
17
+
18
+ get: Get all information about a Job.
19
+ put: inserting a new Job into the jobstore.
20
+ post: modify a Job or re-schedule a Job.
21
+ delete: removing (or pausing) a Job.
22
+ patch: restart a Job o submitting a Job.
23
+ """
24
+
25
+ def next_runtime(self):
26
+ app = self.request.app
27
+ scheduler = app["scheduler"]
28
+ args = self.match_parameters(self.request)
29
+ try:
30
+ job = args["job"]
31
+ except KeyError:
32
+ job = None
33
+ obj = scheduler.get_job(job)
34
+ job = obj["job"]
35
+ if job and job.next_run_time:
36
+ status = job.next_run_time.strftime("%Y-%m-%d %H:%M:%S")
37
+ result = {"job_id": job.id, "name": job.name, "next_run_time": status}
38
+ return self.json_response(response=result, state=200)
39
+ else:
40
+ return self.error(
41
+ request=self.request,
42
+ response=f"There is not such Job Scheduled {job!s}",
43
+ state=404,
44
+ )
45
+
46
+ def time_left(self):
47
+ app = self.request.app
48
+ scheduler = app["scheduler"]
49
+ args = self.match_parameters(self.request)
50
+ try:
51
+ job = args["job"]
52
+ except KeyError:
53
+ job = None
54
+ obj = scheduler.get_job(job)
55
+ job = obj["job"]
56
+ if job and job.next_run_time:
57
+ delta = job.next_run_time.replace(tzinfo=None) - datetime.now()
58
+ hours, remainder = divmod(delta.seconds, 3600)
59
+ minutes, seconds = divmod(remainder, 60)
60
+ days = f"{delta.days} days, " if delta.days else ""
61
+ result = {
62
+ "job_id": job.id,
63
+ "name": job.name,
64
+ "time_left": f"{days}{hours}h:{minutes}m:{seconds}s",
65
+ }
66
+ return self.json_response(response=result, state=200)
67
+ else:
68
+ return self.error(
69
+ request=self.request,
70
+ response=f"There is not such Job Scheduled {job!s}",
71
+ state=404,
72
+ )
@@ -0,0 +1,65 @@
1
+ import asyncio
2
+
3
+ # config
4
+ from navconfig.logging import logging
5
+
6
+ # Notify:
7
+ from notify import Notify
8
+ from notify.models import Actor, Chat
9
+ from notify.providers.email import Email
10
+
11
+ from ..conf import (
12
+ DEFAULT_RECIPIENT,
13
+ EMAIL_HOST,
14
+ EMAIL_PASSWORD,
15
+ EMAIL_PORT,
16
+ EMAIL_USERNAME,
17
+ EVENT_CHAT_BOT,
18
+ EVENT_CHAT_ID,
19
+ )
20
+
21
+
22
+ def getNotify(notify, **kwargs):
23
+ args = {}
24
+ if notify == "telegram":
25
+ # defining the Default chat object:
26
+ recipient = Chat(**{"chat_id": EVENT_CHAT_ID, "chat_name": "Navigator"})
27
+ # send notifications to Telegram bot
28
+ args = {"bot_token": EVENT_CHAT_BOT, **kwargs}
29
+ ntf = Notify("telegram", **args)
30
+ elif notify == "email":
31
+ account = {
32
+ "host": EMAIL_HOST,
33
+ "port": EMAIL_PORT,
34
+ "username": EMAIL_USERNAME,
35
+ "password": EMAIL_PASSWORD,
36
+ }
37
+ recipient = Actor(**DEFAULT_RECIPIENT)
38
+ ntf = Email(debug=True, **account)
39
+ else:
40
+ recipient = Actor(**DEFAULT_RECIPIENT)
41
+ ntf = Notify(notify, **args)
42
+ return [ntf, recipient]
43
+
44
+
45
+ async def _send_(ntf, **kwargs):
46
+ async with ntf as conn:
47
+ await conn.send(**kwargs)
48
+
49
+
50
+ def send_notification(event_loop, message, provider):
51
+ asyncio.set_event_loop(event_loop)
52
+ ntf, recipients = getNotify(provider)
53
+ args = {
54
+ "recipient": [recipients],
55
+ "message": message,
56
+ }
57
+ if ntf.provider_type == "email":
58
+ args["subject"] = message
59
+ if ntf.provider == "telegram":
60
+ args["disable_notification"] = False
61
+ try:
62
+ result = event_loop.run_until_complete(_send_(ntf, **args))
63
+ logging.debug(f"NOTIFY result: {result}")
64
+ except Exception as err:
65
+ logging.exception(err)