wmglobalqueue 2.4.5.1__py3-none-any.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 (347) hide show
  1. Utils/CPMetrics.py +270 -0
  2. Utils/CertTools.py +100 -0
  3. Utils/EmailAlert.py +50 -0
  4. Utils/ExtendedUnitTestCase.py +62 -0
  5. Utils/FileTools.py +182 -0
  6. Utils/IteratorTools.py +80 -0
  7. Utils/MathUtils.py +31 -0
  8. Utils/MemoryCache.py +119 -0
  9. Utils/Patterns.py +24 -0
  10. Utils/Pipeline.py +137 -0
  11. Utils/PortForward.py +97 -0
  12. Utils/ProcFS.py +112 -0
  13. Utils/ProcessStats.py +194 -0
  14. Utils/PythonVersion.py +17 -0
  15. Utils/Signals.py +36 -0
  16. Utils/TemporaryEnvironment.py +27 -0
  17. Utils/Throttled.py +227 -0
  18. Utils/Timers.py +130 -0
  19. Utils/Timestamps.py +86 -0
  20. Utils/TokenManager.py +143 -0
  21. Utils/Tracing.py +60 -0
  22. Utils/TwPrint.py +98 -0
  23. Utils/Utilities.py +318 -0
  24. Utils/__init__.py +11 -0
  25. Utils/wmcoreDTools.py +707 -0
  26. WMCore/ACDC/Collection.py +57 -0
  27. WMCore/ACDC/CollectionTypes.py +12 -0
  28. WMCore/ACDC/CouchCollection.py +67 -0
  29. WMCore/ACDC/CouchFileset.py +238 -0
  30. WMCore/ACDC/CouchService.py +73 -0
  31. WMCore/ACDC/DataCollectionService.py +485 -0
  32. WMCore/ACDC/Fileset.py +94 -0
  33. WMCore/ACDC/__init__.py +11 -0
  34. WMCore/Algorithms/Alarm.py +39 -0
  35. WMCore/Algorithms/MathAlgos.py +274 -0
  36. WMCore/Algorithms/MiscAlgos.py +67 -0
  37. WMCore/Algorithms/ParseXMLFile.py +115 -0
  38. WMCore/Algorithms/Permissions.py +27 -0
  39. WMCore/Algorithms/Singleton.py +58 -0
  40. WMCore/Algorithms/SubprocessAlgos.py +129 -0
  41. WMCore/Algorithms/__init__.py +7 -0
  42. WMCore/Cache/GenericDataCache.py +98 -0
  43. WMCore/Cache/WMConfigCache.py +572 -0
  44. WMCore/Cache/__init__.py +0 -0
  45. WMCore/Configuration.py +659 -0
  46. WMCore/DAOFactory.py +47 -0
  47. WMCore/DataStructs/File.py +177 -0
  48. WMCore/DataStructs/Fileset.py +140 -0
  49. WMCore/DataStructs/Job.py +182 -0
  50. WMCore/DataStructs/JobGroup.py +142 -0
  51. WMCore/DataStructs/JobPackage.py +49 -0
  52. WMCore/DataStructs/LumiList.py +734 -0
  53. WMCore/DataStructs/Mask.py +219 -0
  54. WMCore/DataStructs/MathStructs/ContinuousSummaryHistogram.py +197 -0
  55. WMCore/DataStructs/MathStructs/DiscreteSummaryHistogram.py +92 -0
  56. WMCore/DataStructs/MathStructs/SummaryHistogram.py +117 -0
  57. WMCore/DataStructs/MathStructs/__init__.py +0 -0
  58. WMCore/DataStructs/Pickleable.py +24 -0
  59. WMCore/DataStructs/Run.py +256 -0
  60. WMCore/DataStructs/Subscription.py +175 -0
  61. WMCore/DataStructs/WMObject.py +47 -0
  62. WMCore/DataStructs/WorkUnit.py +112 -0
  63. WMCore/DataStructs/Workflow.py +60 -0
  64. WMCore/DataStructs/__init__.py +8 -0
  65. WMCore/Database/CMSCouch.py +1430 -0
  66. WMCore/Database/ConfigDBMap.py +29 -0
  67. WMCore/Database/CouchMonitoring.py +450 -0
  68. WMCore/Database/CouchUtils.py +118 -0
  69. WMCore/Database/DBCore.py +198 -0
  70. WMCore/Database/DBCreator.py +113 -0
  71. WMCore/Database/DBExceptionHandler.py +59 -0
  72. WMCore/Database/DBFactory.py +117 -0
  73. WMCore/Database/DBFormatter.py +177 -0
  74. WMCore/Database/Dialects.py +13 -0
  75. WMCore/Database/ExecuteDAO.py +327 -0
  76. WMCore/Database/MongoDB.py +241 -0
  77. WMCore/Database/MySQL/Destroy.py +42 -0
  78. WMCore/Database/MySQL/ListUserContent.py +20 -0
  79. WMCore/Database/MySQL/__init__.py +9 -0
  80. WMCore/Database/MySQLCore.py +132 -0
  81. WMCore/Database/Oracle/Destroy.py +56 -0
  82. WMCore/Database/Oracle/ListUserContent.py +19 -0
  83. WMCore/Database/Oracle/__init__.py +9 -0
  84. WMCore/Database/ResultSet.py +44 -0
  85. WMCore/Database/Transaction.py +91 -0
  86. WMCore/Database/__init__.py +9 -0
  87. WMCore/Database/ipy_profile_couch.py +438 -0
  88. WMCore/GlobalWorkQueue/CherryPyThreads/CleanUpTask.py +29 -0
  89. WMCore/GlobalWorkQueue/CherryPyThreads/HeartbeatMonitor.py +105 -0
  90. WMCore/GlobalWorkQueue/CherryPyThreads/LocationUpdateTask.py +28 -0
  91. WMCore/GlobalWorkQueue/CherryPyThreads/ReqMgrInteractionTask.py +35 -0
  92. WMCore/GlobalWorkQueue/CherryPyThreads/__init__.py +0 -0
  93. WMCore/GlobalWorkQueue/__init__.py +0 -0
  94. WMCore/GroupUser/CouchObject.py +127 -0
  95. WMCore/GroupUser/Decorators.py +51 -0
  96. WMCore/GroupUser/Group.py +33 -0
  97. WMCore/GroupUser/Interface.py +73 -0
  98. WMCore/GroupUser/User.py +96 -0
  99. WMCore/GroupUser/__init__.py +11 -0
  100. WMCore/Lexicon.py +836 -0
  101. WMCore/REST/Auth.py +202 -0
  102. WMCore/REST/CherryPyPeriodicTask.py +166 -0
  103. WMCore/REST/Error.py +333 -0
  104. WMCore/REST/Format.py +642 -0
  105. WMCore/REST/HeartbeatMonitorBase.py +90 -0
  106. WMCore/REST/Main.py +636 -0
  107. WMCore/REST/Server.py +2435 -0
  108. WMCore/REST/Services.py +24 -0
  109. WMCore/REST/Test.py +120 -0
  110. WMCore/REST/Tools.py +38 -0
  111. WMCore/REST/Validation.py +250 -0
  112. WMCore/REST/__init__.py +1 -0
  113. WMCore/ReqMgr/DataStructs/RequestStatus.py +209 -0
  114. WMCore/ReqMgr/DataStructs/RequestType.py +13 -0
  115. WMCore/ReqMgr/DataStructs/__init__.py +0 -0
  116. WMCore/ReqMgr/__init__.py +1 -0
  117. WMCore/Services/AlertManager/AlertManagerAPI.py +111 -0
  118. WMCore/Services/AlertManager/__init__.py +0 -0
  119. WMCore/Services/CRIC/CRIC.py +238 -0
  120. WMCore/Services/CRIC/__init__.py +0 -0
  121. WMCore/Services/DBS/DBS3Reader.py +1044 -0
  122. WMCore/Services/DBS/DBSConcurrency.py +44 -0
  123. WMCore/Services/DBS/DBSErrors.py +112 -0
  124. WMCore/Services/DBS/DBSReader.py +23 -0
  125. WMCore/Services/DBS/DBSUtils.py +166 -0
  126. WMCore/Services/DBS/DBSWriterObjects.py +381 -0
  127. WMCore/Services/DBS/ProdException.py +133 -0
  128. WMCore/Services/DBS/__init__.py +8 -0
  129. WMCore/Services/FWJRDB/FWJRDBAPI.py +118 -0
  130. WMCore/Services/FWJRDB/__init__.py +0 -0
  131. WMCore/Services/HTTPS/HTTPSAuthHandler.py +66 -0
  132. WMCore/Services/HTTPS/__init__.py +0 -0
  133. WMCore/Services/LogDB/LogDB.py +201 -0
  134. WMCore/Services/LogDB/LogDBBackend.py +191 -0
  135. WMCore/Services/LogDB/LogDBExceptions.py +11 -0
  136. WMCore/Services/LogDB/LogDBReport.py +85 -0
  137. WMCore/Services/LogDB/__init__.py +0 -0
  138. WMCore/Services/MSPileup/__init__.py +0 -0
  139. WMCore/Services/MSUtils/MSUtils.py +54 -0
  140. WMCore/Services/MSUtils/__init__.py +0 -0
  141. WMCore/Services/McM/McM.py +173 -0
  142. WMCore/Services/McM/__init__.py +8 -0
  143. WMCore/Services/MonIT/Grafana.py +133 -0
  144. WMCore/Services/MonIT/__init__.py +0 -0
  145. WMCore/Services/PyCondor/PyCondorAPI.py +154 -0
  146. WMCore/Services/PyCondor/__init__.py +0 -0
  147. WMCore/Services/ReqMgr/ReqMgr.py +261 -0
  148. WMCore/Services/ReqMgr/__init__.py +0 -0
  149. WMCore/Services/ReqMgrAux/ReqMgrAux.py +419 -0
  150. WMCore/Services/ReqMgrAux/__init__.py +0 -0
  151. WMCore/Services/RequestDB/RequestDBReader.py +267 -0
  152. WMCore/Services/RequestDB/RequestDBWriter.py +39 -0
  153. WMCore/Services/RequestDB/__init__.py +0 -0
  154. WMCore/Services/Requests.py +624 -0
  155. WMCore/Services/Rucio/Rucio.py +1290 -0
  156. WMCore/Services/Rucio/RucioUtils.py +74 -0
  157. WMCore/Services/Rucio/__init__.py +0 -0
  158. WMCore/Services/RucioConMon/RucioConMon.py +121 -0
  159. WMCore/Services/RucioConMon/__init__.py +0 -0
  160. WMCore/Services/Service.py +400 -0
  161. WMCore/Services/StompAMQ/__init__.py +0 -0
  162. WMCore/Services/TagCollector/TagCollector.py +155 -0
  163. WMCore/Services/TagCollector/XMLUtils.py +98 -0
  164. WMCore/Services/TagCollector/__init__.py +0 -0
  165. WMCore/Services/UUIDLib.py +13 -0
  166. WMCore/Services/UserFileCache/UserFileCache.py +160 -0
  167. WMCore/Services/UserFileCache/__init__.py +8 -0
  168. WMCore/Services/WMAgent/WMAgent.py +63 -0
  169. WMCore/Services/WMAgent/__init__.py +0 -0
  170. WMCore/Services/WMArchive/CMSSWMetrics.py +526 -0
  171. WMCore/Services/WMArchive/DataMap.py +463 -0
  172. WMCore/Services/WMArchive/WMArchive.py +33 -0
  173. WMCore/Services/WMArchive/__init__.py +0 -0
  174. WMCore/Services/WMBS/WMBS.py +97 -0
  175. WMCore/Services/WMBS/__init__.py +0 -0
  176. WMCore/Services/WMStats/DataStruct/RequestInfoCollection.py +300 -0
  177. WMCore/Services/WMStats/DataStruct/__init__.py +0 -0
  178. WMCore/Services/WMStats/WMStatsPycurl.py +145 -0
  179. WMCore/Services/WMStats/WMStatsReader.py +445 -0
  180. WMCore/Services/WMStats/WMStatsWriter.py +273 -0
  181. WMCore/Services/WMStats/__init__.py +0 -0
  182. WMCore/Services/WMStatsServer/WMStatsServer.py +134 -0
  183. WMCore/Services/WMStatsServer/__init__.py +0 -0
  184. WMCore/Services/WorkQueue/WorkQueue.py +492 -0
  185. WMCore/Services/WorkQueue/__init__.py +0 -0
  186. WMCore/Services/__init__.py +8 -0
  187. WMCore/Services/pycurl_manager.py +574 -0
  188. WMCore/WMBase.py +50 -0
  189. WMCore/WMConnectionBase.py +164 -0
  190. WMCore/WMException.py +183 -0
  191. WMCore/WMExceptions.py +269 -0
  192. WMCore/WMFactory.py +76 -0
  193. WMCore/WMInit.py +377 -0
  194. WMCore/WMLogging.py +104 -0
  195. WMCore/WMSpec/ConfigSectionTree.py +442 -0
  196. WMCore/WMSpec/Persistency.py +135 -0
  197. WMCore/WMSpec/Steps/BuildMaster.py +87 -0
  198. WMCore/WMSpec/Steps/BuildTools.py +201 -0
  199. WMCore/WMSpec/Steps/Builder.py +97 -0
  200. WMCore/WMSpec/Steps/Diagnostic.py +89 -0
  201. WMCore/WMSpec/Steps/Emulator.py +62 -0
  202. WMCore/WMSpec/Steps/ExecuteMaster.py +208 -0
  203. WMCore/WMSpec/Steps/Executor.py +210 -0
  204. WMCore/WMSpec/Steps/StepFactory.py +213 -0
  205. WMCore/WMSpec/Steps/TaskEmulator.py +75 -0
  206. WMCore/WMSpec/Steps/Template.py +204 -0
  207. WMCore/WMSpec/Steps/Templates/AlcaHarvest.py +76 -0
  208. WMCore/WMSpec/Steps/Templates/CMSSW.py +613 -0
  209. WMCore/WMSpec/Steps/Templates/DQMUpload.py +59 -0
  210. WMCore/WMSpec/Steps/Templates/DeleteFiles.py +70 -0
  211. WMCore/WMSpec/Steps/Templates/LogArchive.py +84 -0
  212. WMCore/WMSpec/Steps/Templates/LogCollect.py +105 -0
  213. WMCore/WMSpec/Steps/Templates/StageOut.py +105 -0
  214. WMCore/WMSpec/Steps/Templates/__init__.py +10 -0
  215. WMCore/WMSpec/Steps/WMExecutionFailure.py +21 -0
  216. WMCore/WMSpec/Steps/__init__.py +8 -0
  217. WMCore/WMSpec/Utilities.py +63 -0
  218. WMCore/WMSpec/WMSpecErrors.py +12 -0
  219. WMCore/WMSpec/WMStep.py +347 -0
  220. WMCore/WMSpec/WMTask.py +1997 -0
  221. WMCore/WMSpec/WMWorkload.py +2288 -0
  222. WMCore/WMSpec/WMWorkloadTools.py +382 -0
  223. WMCore/WMSpec/__init__.py +9 -0
  224. WMCore/WorkQueue/DataLocationMapper.py +273 -0
  225. WMCore/WorkQueue/DataStructs/ACDCBlock.py +47 -0
  226. WMCore/WorkQueue/DataStructs/Block.py +48 -0
  227. WMCore/WorkQueue/DataStructs/CouchWorkQueueElement.py +148 -0
  228. WMCore/WorkQueue/DataStructs/WorkQueueElement.py +274 -0
  229. WMCore/WorkQueue/DataStructs/WorkQueueElementResult.py +152 -0
  230. WMCore/WorkQueue/DataStructs/WorkQueueElementsSummary.py +185 -0
  231. WMCore/WorkQueue/DataStructs/__init__.py +0 -0
  232. WMCore/WorkQueue/Policy/End/EndPolicyInterface.py +44 -0
  233. WMCore/WorkQueue/Policy/End/SingleShot.py +22 -0
  234. WMCore/WorkQueue/Policy/End/__init__.py +32 -0
  235. WMCore/WorkQueue/Policy/PolicyInterface.py +17 -0
  236. WMCore/WorkQueue/Policy/Start/Block.py +258 -0
  237. WMCore/WorkQueue/Policy/Start/Dataset.py +180 -0
  238. WMCore/WorkQueue/Policy/Start/MonteCarlo.py +131 -0
  239. WMCore/WorkQueue/Policy/Start/ResubmitBlock.py +171 -0
  240. WMCore/WorkQueue/Policy/Start/StartPolicyInterface.py +316 -0
  241. WMCore/WorkQueue/Policy/Start/__init__.py +34 -0
  242. WMCore/WorkQueue/Policy/__init__.py +57 -0
  243. WMCore/WorkQueue/WMBSHelper.py +772 -0
  244. WMCore/WorkQueue/WorkQueue.py +1237 -0
  245. WMCore/WorkQueue/WorkQueueBackend.py +750 -0
  246. WMCore/WorkQueue/WorkQueueBase.py +39 -0
  247. WMCore/WorkQueue/WorkQueueExceptions.py +44 -0
  248. WMCore/WorkQueue/WorkQueueReqMgrInterface.py +278 -0
  249. WMCore/WorkQueue/WorkQueueUtils.py +130 -0
  250. WMCore/WorkQueue/__init__.py +13 -0
  251. WMCore/Wrappers/JsonWrapper/JSONThunker.py +342 -0
  252. WMCore/Wrappers/JsonWrapper/__init__.py +7 -0
  253. WMCore/Wrappers/__init__.py +6 -0
  254. WMCore/__init__.py +10 -0
  255. wmglobalqueue-2.4.5.1.data/data/bin/wmc-dist-patch +15 -0
  256. wmglobalqueue-2.4.5.1.data/data/bin/wmc-dist-unpatch +8 -0
  257. wmglobalqueue-2.4.5.1.data/data/bin/wmc-httpd +3 -0
  258. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/.couchapprc +1 -0
  259. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/README.md +40 -0
  260. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/index.html +264 -0
  261. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/ElementInfoByWorkflow.js +96 -0
  262. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/StuckElementInfo.js +57 -0
  263. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/WorkloadInfoTable.js +80 -0
  264. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/dataTable.js +70 -0
  265. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/namespace.js +23 -0
  266. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/style/main.css +75 -0
  267. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/couchapp.json +4 -0
  268. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/filters/childQueueFilter.js +13 -0
  269. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/filters/filterDeletedDocs.js +3 -0
  270. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/filters/queueFilter.js +11 -0
  271. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/language +1 -0
  272. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lib/mustache.js +333 -0
  273. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lib/validate.js +27 -0
  274. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lib/workqueue_utils.js +61 -0
  275. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/elementsDetail.js +28 -0
  276. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/filter.js +86 -0
  277. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/stuckElements.js +38 -0
  278. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/workRestrictions.js +153 -0
  279. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/workflowSummary.js +28 -0
  280. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/rewrites.json +73 -0
  281. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/shows/redirect.js +23 -0
  282. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/shows/status.js +40 -0
  283. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/ElementSummaryByWorkflow.html +27 -0
  284. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/StuckElementSummary.html +26 -0
  285. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/TaskStatus.html +23 -0
  286. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/WorkflowSummary.html +27 -0
  287. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/partials/workqueue-common-lib.html +2 -0
  288. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/partials/yui-lib-remote.html +16 -0
  289. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/partials/yui-lib.html +18 -0
  290. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/updates/in-place.js +50 -0
  291. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/validate_doc_update.js +8 -0
  292. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/vendor/couchapp/_attachments/jquery.couch.app.js +235 -0
  293. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/vendor/couchapp/_attachments/jquery.pathbinder.js +173 -0
  294. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeData/map.js +8 -0
  295. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeData/reduce.js +2 -0
  296. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeParentData/map.js +8 -0
  297. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeParentData/reduce.js +2 -0
  298. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activePileupData/map.js +8 -0
  299. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activePileupData/reduce.js +2 -0
  300. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/analyticsData/map.js +11 -0
  301. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/analyticsData/reduce.js +1 -0
  302. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/availableByPriority/map.js +6 -0
  303. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/conflicts/map.js +5 -0
  304. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elements/map.js +5 -0
  305. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByData/map.js +8 -0
  306. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByParent/map.js +8 -0
  307. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByParentData/map.js +8 -0
  308. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByPileupData/map.js +8 -0
  309. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByStatus/map.js +8 -0
  310. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsBySubscription/map.js +6 -0
  311. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByWorkflow/map.js +8 -0
  312. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByWorkflow/reduce.js +3 -0
  313. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsDetailByWorkflowAndStatus/map.js +26 -0
  314. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobInjectStatusByRequest/map.js +10 -0
  315. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobInjectStatusByRequest/reduce.js +1 -0
  316. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobStatusByRequest/map.js +6 -0
  317. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobStatusByRequest/reduce.js +1 -0
  318. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndPriority/map.js +6 -0
  319. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndPriority/reduce.js +1 -0
  320. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndStatus/map.js +6 -0
  321. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndStatus/reduce.js +1 -0
  322. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByRequest/map.js +6 -0
  323. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByRequest/reduce.js +1 -0
  324. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatus/map.js +6 -0
  325. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatus/reduce.js +1 -0
  326. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatusAndPriority/map.js +6 -0
  327. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatusAndPriority/reduce.js +1 -0
  328. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/openRequests/map.js +6 -0
  329. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/recent-items/map.js +5 -0
  330. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/siteWhitelistByRequest/map.js +6 -0
  331. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/siteWhitelistByRequest/reduce.js +1 -0
  332. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/specsByWorkflow/map.js +5 -0
  333. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/stuckElements/map.js +38 -0
  334. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsInjectStatusByRequest/map.js +12 -0
  335. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsInjectStatusByRequest/reduce.js +3 -0
  336. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrl/map.js +6 -0
  337. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrl/reduce.js +2 -0
  338. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrlByRequest/map.js +6 -0
  339. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrlByRequest/reduce.js +2 -0
  340. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/workflowSummary/map.js +9 -0
  341. wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/workflowSummary/reduce.js +10 -0
  342. wmglobalqueue-2.4.5.1.dist-info/METADATA +26 -0
  343. wmglobalqueue-2.4.5.1.dist-info/RECORD +347 -0
  344. wmglobalqueue-2.4.5.1.dist-info/WHEEL +5 -0
  345. wmglobalqueue-2.4.5.1.dist-info/licenses/LICENSE +202 -0
  346. wmglobalqueue-2.4.5.1.dist-info/licenses/NOTICE +16 -0
  347. wmglobalqueue-2.4.5.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,300 @@
1
+ from builtins import object
2
+ from future.utils import viewitems, viewvalues
3
+
4
+
5
+ class JobSummary(object):
6
+ """
7
+ job summary data structure from job format in couchdb
8
+ """
9
+
10
+ def __init__(self, jobStatus=None):
11
+ self.jobStatus = {
12
+ "success": 0,
13
+ "canceled": 0,
14
+ "transition": 0,
15
+ "queued": {"first": 0, "retry": 0},
16
+ "submitted": {"first": 0, "retry": 0, "pending": 0, "running": 0},
17
+ "failure": {"create": 0, "submit": 0, "exception": 0},
18
+ "cooloff": {"create": 0, "submit": 0, "job": 0},
19
+ "paused": {"create": 0, "submit": 0, "job": 0},
20
+ }
21
+ if jobStatus != None:
22
+ self.addJobStatusInfo(jobStatus)
23
+
24
+ def addJobStatusInfo(self, jobStatus):
25
+
26
+ # TODO need to validate the structure.
27
+ for key, value in viewitems(self.jobStatus):
28
+ if isinstance(value, int):
29
+ self.jobStatus[key] += jobStatus.get(key, 0)
30
+ elif isinstance(value, dict):
31
+ for secondKey in value:
32
+ if key in jobStatus and secondKey in jobStatus[key]:
33
+ self.jobStatus[key][secondKey] += jobStatus[key][secondKey]
34
+
35
+ def addJobSummary(self, jobSummary):
36
+ self.addJobStatusInfo(jobSummary.jobStatus)
37
+
38
+ def getTotalJobs(self):
39
+ return (self.getSuccess() +
40
+ self.jobStatus["canceled"] +
41
+ self.jobStatus["transition"] +
42
+ self.getFailure() +
43
+ self.getCooloff() +
44
+ self.getPaused() +
45
+ self.getQueued() +
46
+ self.getRunning() +
47
+ self.getPending())
48
+
49
+ def getSuccess(self):
50
+ return self.jobStatus["success"]
51
+
52
+ def getFailure(self):
53
+
54
+ return (self.jobStatus["failure"]["create"] +
55
+ self.jobStatus["failure"]["submit"] +
56
+ self.jobStatus["failure"]["exception"])
57
+
58
+ def getCompleted(self):
59
+ return self.getSuccess() + self.getFailure()
60
+
61
+ def getSubmitted(self):
62
+ return (self.jobStatus["submitted"]["first"] +
63
+ self.jobStatus["submitted"]["retry"])
64
+
65
+ def getRunning(self):
66
+ return self.jobStatus["submitted"]["running"]
67
+
68
+ def getPending(self):
69
+ return self.jobStatus["submitted"]["pending"]
70
+
71
+ def getCooloff(self):
72
+ return (self.jobStatus["cooloff"]["create"] +
73
+ self.jobStatus["cooloff"]["submit"] +
74
+ self.jobStatus["cooloff"]["job"])
75
+
76
+ def getPaused(self):
77
+ return (self.jobStatus["paused"]["create"] +
78
+ self.jobStatus["paused"]["submit"] +
79
+ self.jobStatus["paused"]["job"])
80
+
81
+ def getQueued(self):
82
+ return (self.jobStatus["queued"]["first"] +
83
+ self.jobStatus["queued"]["retry"])
84
+
85
+ def getJSONStatus(self):
86
+ return {'success': self.getSuccess(),
87
+ 'failure': self.getFailure(),
88
+ 'cooloff': self.getCooloff(),
89
+ 'running': self.getRunning(),
90
+ 'queued': self.getQueued(),
91
+ 'pending': self.getPending(),
92
+ 'paused': self.getPaused(),
93
+ 'created': self.getTotalJobs()
94
+ }
95
+
96
+
97
+ class ProgressSummary(object):
98
+
99
+ def __init__(self, progressReport=None):
100
+ self.progress = {
101
+ "totalLumis": 0,
102
+ "events": 0,
103
+ "size": 0
104
+ }
105
+
106
+ if progressReport != None:
107
+ self.addProgressReport(progressReport)
108
+
109
+ def addProgressReport(self, progressReport):
110
+
111
+ # TODO need to validate the structure.
112
+ for key in self.progress:
113
+ self.progress[key] += progressReport.get(key, 0)
114
+
115
+ def getReport(self):
116
+ return self.progress
117
+
118
+
119
+ class TaskInfo(object):
120
+
121
+ def __init__(self, requestName, taskName, data):
122
+ self.requestName = requestName
123
+ self.taskName = taskName
124
+ self.taskType = data.get('jobtype', "N/A")
125
+ self.jobSummary = JobSummary(data.get('status', {}))
126
+
127
+ def addTaskInfo(self, taskInfo):
128
+ if not (self.requestName == taskInfo.requestName and
129
+ self.taskName == taskInfo.taskName):
130
+ msg = "%s: %s, %s: %s, %s: %s" % (self.requestName, taskInfo.requestName,
131
+ self.taskName, taskInfo.taskName,
132
+ self.taskType, taskInfo.taskType)
133
+ raise Exception("task doesn't match %s" % msg)
134
+
135
+ self.jobSummary.addJobSummary(taskInfo.jobSummary)
136
+
137
+ def getRequestName(self):
138
+ return self.requestName
139
+
140
+ def getTaskName(self):
141
+ return self.taskName
142
+
143
+ def getTaskType(self):
144
+ return self.taskType
145
+
146
+ def getJobSummary(self):
147
+ return self.jobSummary
148
+
149
+ def isTaskCompleted(self):
150
+ totalJobs = self.jobSummary.getTotalJobs()
151
+ completedJobs = self.jobSummary.getCompleted()
152
+ return (totalJobs != 0 and totalJobs == completedJobs)
153
+
154
+
155
+ class RequestInfo(object):
156
+
157
+ def __init__(self, data):
158
+ """
159
+ data structure is
160
+ {'request_name1':
161
+ {'agent_url1': {'status'
162
+
163
+ }
164
+ """
165
+ self.setData(data)
166
+
167
+ def setData(self, data):
168
+ # If RequestName doesn't exist, try legacy format (workflow)
169
+ if 'RequestName' in data:
170
+ self.requestName = data['RequestName']
171
+ else:
172
+ self.requestName = data['workflow']
173
+ self.data = data
174
+ self.jobSummaryByAgent = {}
175
+ self.tasks = {}
176
+ self.tasksByAgent = {}
177
+ self.jobSummary = JobSummary()
178
+ if 'AgentJobInfo' in data:
179
+ for agentUrl, agentRequestInfo in viewitems(data['AgentJobInfo']):
180
+ self.jobSummary.addJobStatusInfo(agentRequestInfo.get('status', {}))
181
+ self.jobSummaryByAgent[agentUrl] = JobSummary(agentRequestInfo.get('status', {}))
182
+
183
+ if 'tasks' in agentRequestInfo:
184
+ self.tasksByAgent[agentUrl] = {}
185
+ for taskName, taskData in viewitems(agentRequestInfo['tasks']):
186
+ if taskName not in self.tasks:
187
+ self.tasks[taskName] = TaskInfo(self.requestName, taskName, taskData)
188
+ else:
189
+ self.tasks[taskName].addTaskInfo(TaskInfo(self.requestName, taskName, taskData))
190
+ # only one task by one agent - don't need to combine
191
+ self.tasksByAgent[agentUrl][taskName] = TaskInfo(self.requestName, taskName, taskData)
192
+
193
+ def getJobSummary(self):
194
+ return self.jobSummary
195
+
196
+ def getJobSummaryByAgent(self, agentUrl=None):
197
+ if agentUrl:
198
+ return self.jobSummaryByAgent[agentUrl]
199
+ return self.jobSummaryByAgent
200
+
201
+ def getTasksByAgent(self, agentUrl=None):
202
+ if agentUrl:
203
+ return self.tasksByAgent[agentUrl]
204
+ return self.tasksByAgent
205
+
206
+ def getTasks(self):
207
+ return self.tasks
208
+
209
+ def getTotalTopLevelJobs(self):
210
+ return self.data.get("total_jobs", "N/A")
211
+
212
+ def getTotalTopLevelJobsInWMBS(self):
213
+ inWMBS = 0
214
+ if "AgentJobInfo" in self.data:
215
+ for agentRequestInfo in viewvalues(self.data["AgentJobInfo"]):
216
+ inWMBS += agentRequestInfo['status'].get('inWMBS', 0)
217
+ return inWMBS
218
+
219
+ def getTotalInputLumis(self):
220
+ return self.data.get("input_lumis", "N/A")
221
+
222
+ def getTotalInputEvents(self):
223
+ return self.data.get("input_events", "N/A")
224
+
225
+ def getProgressSummaryByOutputDataset(self):
226
+ """
227
+ check sampleResult.json for datastructure
228
+ """
229
+ datasets = {}
230
+
231
+ if "AgentJobInfo" not in self.data:
232
+ # ther is no report yet (no agent has reported)
233
+ return datasets
234
+
235
+ for agentRequestInfo in viewvalues(self.data["AgentJobInfo"]):
236
+
237
+ tasks = agentRequestInfo.get("tasks", [])
238
+ for task in tasks:
239
+ for site in tasks[task].get("sites", []):
240
+ for outputDS in tasks[task]["sites"][site].get("dataset", {}):
241
+ # TODO: need update the record instead of replacing.
242
+ datasets.setdefault(outputDS, ProgressSummary())
243
+ datasets[outputDS].addProgressReport(tasks[task]["sites"][site]["dataset"][outputDS])
244
+
245
+ return datasets
246
+
247
+ def filterRequest(self, conditionFunc):
248
+ return conditionFunc(self.data)
249
+
250
+ def getRequestTransition(self):
251
+ return self.data["request_status"]
252
+
253
+ def getRequestStatus(self, timeFlag=False):
254
+ if timeFlag:
255
+ return self.data["request_status"][-1]
256
+ return self.data["request_status"][-1]['status']
257
+
258
+ def isWorkflowFinished(self):
259
+ """
260
+ check whether workflow is completed including LogCollect and CleanUp tasks
261
+ TODO: If the parent task all failed and next task are not created at all,
262
+ It can't detect complete status.
263
+ If the one of the task doesn't contain any jobs, it will return False
264
+ """
265
+ if not self.tasks:
266
+ return False
267
+
268
+ for taskInfo in viewvalues(self.tasks):
269
+ if not taskInfo.isTaskCompleted():
270
+ return False
271
+ return True
272
+
273
+
274
+ class RequestInfoCollection(object):
275
+
276
+ def __init__(self, data):
277
+ self.collection = {}
278
+ self.setData(data)
279
+
280
+ def setData(self, data):
281
+ for requestName, requestInfo in viewitems(data):
282
+ self.collection[requestName] = RequestInfo(requestInfo)
283
+
284
+ def getData(self):
285
+ return self.collection
286
+
287
+ def filterRequests(self, conditionFunc):
288
+ filtered = {}
289
+ for name, reqInfo in viewitems(self.collection):
290
+ if reqInfo.filterRequest(conditionFunc):
291
+ filtered[name] = reqInfo
292
+ return filtered
293
+
294
+ def getJSONData(self):
295
+ result = {}
296
+ for requestInfo in viewvalues(self.collection):
297
+ result[requestInfo.requestName] = {}
298
+ for agentUrl, jobSummary in viewitems(requestInfo.getJobSummaryByAgent()):
299
+ result[requestInfo.requestName][agentUrl] = jobSummary.getJSONStatus()
300
+ return result
File without changes
@@ -0,0 +1,145 @@
1
+ """
2
+ This module provides some functions to create concurrent HTTP requests
3
+ for CouchDB, as an alternative to the sequential modules available under:
4
+ * WMCore/Services/WMStats/WMStatsReader.getTaskJobSummaryByRequest
5
+ * WMCore/Services/WMStats/WMStatsReader.jobDetailByTasks
6
+
7
+ Documentation for the parameters supported in views query can be found at:
8
+ https://docs.couchdb.org/en/stable/api/ddoc/views.html#get--db-_design-ddoc-_view-view
9
+ """
10
+ import json
11
+
12
+ from urllib.parse import urljoin, urlencode
13
+ from Utils.CertTools import ckey, cert
14
+ from WMCore.Services.pycurl_manager import getdata as multi_getdata
15
+
16
+
17
+ def getTaskJobSummaryByRequestPycurl(rowsSummary, sampleSize, serviceOpts):
18
+ """
19
+ Pycurl-based implementation of WMStatsReader.getTaskJobSummaryByRequest
20
+ :param rowsSummary: a dictionary with rows from CouchDB
21
+ :param sampleSize: integer with number of documents to retrieve
22
+ :param serviceOpts: dictionary with CouchDB options (url, db name and couchapp name)
23
+ :return: a dictionary with job detail
24
+ """
25
+ paramQueries = []
26
+ for row in rowsSummary['rows']:
27
+ thisQuery = {"startkey": [], "endkey": [], "numOfError": 0}
28
+ # row["key"] = ['workflow', 'task', 'jobstatus', 'exitCode', 'site']
29
+ thisQuery["startkey"] = row["key"][:4]
30
+ if row["key"][4]:
31
+ thisQuery["startkey"].append(row["key"][4]) # site
32
+
33
+ thisQuery["endkey"] = []
34
+ thisQuery["endkey"].extend(thisQuery["startkey"])
35
+ thisQuery["endkey"].append({})
36
+ # append amount of errors matching the 5 keys above
37
+ thisQuery["numOfError"] = row["value"]
38
+ # add query to the pool
39
+ paramQueries.append(thisQuery)
40
+
41
+ return jobDetailByTasksPycurl(paramQueries, sampleSize, serviceOpts)
42
+
43
+
44
+ def jobDetailByTasksPycurl(queries, limit, serviceOpts):
45
+ """
46
+ Pycurl-based implementation of WMStatsReader.jobDetailByTasks.
47
+ In short, for each tuple of errors, it returns "limit" number of documents.
48
+ :param queries: list of CouchDB query parameters
49
+ :param limit: number of documents to retrieve from CouchDB
50
+ :param serviceOpts: dictionary with CouchDB options (url, db name and couchapp name)
51
+ :return: dictionary with job detail information, in a format like:
52
+ {"WORKFLOW_NAME":
53
+ {"/WORKFLOW_NAME/TASK_NAME":
54
+ {"JOB_STATE":
55
+ {"EXIT_CODE":
56
+ {"SITE_NAME":
57
+ "samples": [
58
+ {"_id": "123abc",
59
+ "_rev": "12-abc",
60
+ "wmbsid": 2006208,
61
+ "type": "jobsummary",
62
+ "retrycount": 3,
63
+ "errors": {"JobSubmit": [{
64
+ "type": "SubmitFailed",
65
+ "details": "The job can blah blah",
66
+ "exitCode": 71103}]
67
+ },
68
+ "timestamp": 1698749157,
69
+ ... etc etc}],
70
+ "errorCount": 1}
71
+ }
72
+ }
73
+ },
74
+ {"/WORKFLOW_NAME/TASK_NAME-2":
75
+ {"JOB_STATE": {... etc etc
76
+
77
+ A decoded example of this query would be:
78
+ scurl "https://cmsweb-test9.cern.ch/couchdb/wmstats/_design/WMStatsErl3/_view/jobsByStatusWorkflow?reduce=false&include_docs=true&startkey=["WORKFLOW_NAME","/WORKFLOW_NAME/TASK_NAME","JOB_STATE",EXIT_CODE,"SITE_NAME"]&endkey=["WORKFLOW_NAME","/WORKFLOW_NAME/TASK_NAME","JOB_STATE",EXIT_CODE,"SITE_NAME",{}]&limit=1&stale=update_after"
79
+ while encoding the url would result in:
80
+ scurl "https://****.cern.ch/couchdb/wmstats/_design/WMStatsErl3/_view/jobsByStatusWorkflow?reduce=false&include_docs=true&startkey=%5B%22WORKFLOW_NAME%22%2C+%22%2FWORKFLOW_NAME%2FTASK_NAME%22%2C+%22JOB_STATE%22%2C+EXIT_CODE%2C+%22SITE_NAME%22%5D&endkey=%5B%22WORKFLOW_NAME%22%2C+%22%2FWORKFLOW_NAME%2FTASK_NAME%22%2C+%22JOB_STATE%22%2C+EXIT_CODE%2C+%22SITE_NAME%22%2C+%7B%7D%5D&limit=1&stale=update_after"
81
+
82
+ Example of output is:
83
+ {"total_rows":4764135,"offset":4056263,"rows":[
84
+ {"id":"12703ce5-xxx","key":["WORKFLOW_NAME","/WORKFLOW_NAME/TASK_NAME","jobfailed",8006,"SITE_NAME","https://xxx.cern.ch/couchdb/acdcserver","vocms0255.cern.ch",["Fatal Exception"]],
85
+ "value":{"id":"12703ce5-xxx","rev":"4-xxx"},
86
+ "doc":{"_id":"12703ce5-xx","_rev":"4-xxx","wmbsid":2709786,"type":"jobsummary", ...
87
+
88
+ where (for the examples above):
89
+ WORKFLOW_NAME is, e.g.: pdmvserv_Run2017G_DoubleMuon_UL2017_MiniAODv2_BParking_230917_124108_9876
90
+ TASK_NAME is, e.g.: DataProcessing
91
+ SITE_NAME is, e.g.: T2_US_Wisconsin
92
+ JOB_STATE is, e.g.: jobfailed
93
+ EXIT_CODE is, e.g.: 8006
94
+ """
95
+ uri = f"couchdb/{serviceOpts['dbName']}/_design/{serviceOpts['couchapp']}/_view/jobsByStatusWorkflow"
96
+ baseUrl = urljoin(serviceOpts['couchURL'], uri)
97
+
98
+ encoder = json.JSONEncoder()
99
+ urlsPool = []
100
+ for query in queries:
101
+ options = {'include_docs': encoder.encode(True),
102
+ 'reduce': encoder.encode(False),
103
+ 'startkey': encoder.encode(query["startkey"]),
104
+ 'endkey': encoder.encode(query["endkey"]),
105
+ 'limit': encoder.encode(limit)}
106
+ # we cannot encode the 'stale' parameter
107
+ options.setdefault("stale", "update_after")
108
+ # encode url data for the GET request
109
+ thisUrl = f"{baseUrl}?{urlencode(options, doseq=True)}"
110
+ urlsPool.append(thisUrl)
111
+
112
+ jobInfoDoc = {}
113
+ # now run all of these calls in parallel
114
+ for response in multi_getdata(urlsPool, ckey(), cert()):
115
+ if 'error' in response:
116
+ raise RuntimeError(f"Unexpected error in HTTP call. Details: {response}")
117
+ data = json.loads(response.get('data', ''))
118
+ if 'error' in data:
119
+ raise RuntimeError(f"CouchDB query failed. Details: {data}")
120
+
121
+ for row in data.get('rows', []):
122
+ keys = row['key']
123
+ workflow = keys[0]
124
+ task = keys[1]
125
+ jobStatus = keys[2]
126
+ exitCode = keys[3]
127
+ site = keys[4]
128
+
129
+ jobInfoDoc.setdefault(workflow, {})
130
+ jobInfoDoc[workflow].setdefault(task, {})
131
+ jobInfoDoc[workflow][task].setdefault(jobStatus, {})
132
+ jobInfoDoc[workflow][task][jobStatus].setdefault(exitCode, {})
133
+ jobInfoDoc[workflow][task][jobStatus][exitCode].setdefault(site, {})
134
+ finalStruct = jobInfoDoc[workflow][task][jobStatus][exitCode][site]
135
+ finalStruct.setdefault("samples", [])
136
+ finalStruct["samples"].append(row["doc"])
137
+ # now painfully find out the number of errors based on the original query
138
+ finalStruct.setdefault("errorCount", 0)
139
+ keysJson = json.dumps(keys[:5])
140
+ for query in queries:
141
+ if json.dumps(query["startkey"]) in keysJson:
142
+ finalStruct["errorCount"] = query["numOfError"]
143
+ break
144
+
145
+ return jobInfoDoc