Gitlab 996 index statistic

work and life balance!

  1. // ==UserScript==
  2. // @name Gitlab 996 index statistic
  3. // @namespace http://996.icu/
  4. // @version 0.1
  5. // @description work and life balance!
  6. // @author wanghsinche
  7. // @match https://*/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=gitlab.com
  9. // @grant none
  10. // @run-at document-end
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. const meta = document.body.dataset;
  17. const projectId = meta?meta.projectId:null;
  18. const commitURL = `/api/v4/projects/${projectId}/repository/commits`
  19. const visualazation = 'https://hellodigua.github.io/code996/#/result'
  20. const mergeFlag = "Merge branch"
  21.  
  22. const pannelId = 'my-pannel'
  23.  
  24. let loading = false
  25.  
  26. function print(...param) {
  27. const myPannel = document.getElementById(pannelId)
  28. myPannel.style.display = 'block'
  29. myPannel.innerText = param.toString()
  30. }
  31.  
  32.  
  33.  
  34. async function getCommit(maxNum = 1000, page = 1) {
  35. if (maxNum < 1) {
  36. return []
  37. }
  38.  
  39. const perPage = 100
  40. const param = new URLSearchParams()
  41. param.set('per_page', perPage)
  42. param.set('page', page)
  43. const rawData = await fetch(`${commitURL}?${param.toString()}`).then(res => res.json())
  44.  
  45. const data = rawData.filter(el => !el.title.startsWith(mergeFlag))
  46.  
  47. if (rawData.length < perPage) {
  48. return data
  49. }
  50. print('processing... page ', page)
  51. const rest = await getCommit(maxNum - data.length, page + 1)
  52. return data.concat(rest)
  53.  
  54. }
  55.  
  56. async function processData() {
  57. const totalCommit = await getCommit()
  58. print('got the commits, start to analyze', totalCommit.length)
  59. const startTime = new Date(totalCommit.reduce((am, ele) => {
  60. if (!am) return ele.committed_date
  61. return am < ele.committed_date ? am : ele.committed_date
  62. }, null))
  63. const endTime = new Date(totalCommit.reduce((am, ele) => {
  64. if (!am) return ele.committed_date
  65. return am > ele.committed_date ? am : ele.committed_date
  66. }, null))
  67.  
  68. // const timezone = Object.entries(totalCommit.reduce((am, ele)=>{
  69. // const tz = ele.committed_date.split('+').pop()
  70. // am[tz] = (am[tz]||0)+1
  71. // return am
  72. // }, {})).reduce((am, ele)=>{
  73. // if (ele[1] > am[1]){
  74. // return ele
  75. // }
  76. // }, ['00:00', 0])[0]
  77.  
  78. const byDayResult = totalCommit.reduce((am, ele) => {
  79. const day = new Date(ele.committed_date).getDay()
  80. am[day] = (am[day] || 0) + 1
  81. return am
  82. }, {})
  83.  
  84. const byHourResult = totalCommit.reduce((am, ele) => {
  85. const hour = new Date(ele.committed_date).getHours()
  86. am[hour] = (am[hour] || 0) + 1
  87. return am
  88. }, {})
  89. // month start from 0
  90. const formattedStartTime = `${startTime.getFullYear()}-${startTime.getMonth()+1}-${startTime.getDate()}`
  91. const formattedEndTime = `${endTime.getFullYear()}-${endTime.getMonth()+1}-${endTime.getDate()}`
  92.  
  93. const formattedDayResult = Object.entries(byDayResult).map(([day, freq]) => `${freq}_${day}`).join(',')
  94. const formattedHourResult = Object.entries(byHourResult).map(([hour, freq]) => `${freq}_${hour}`).join(',')
  95.  
  96. const param = new URLSearchParams()
  97. param.set('time', `${formattedStartTime}_${formattedEndTime}`)
  98. param.set('week', formattedDayResult)
  99. param.set('hour', formattedHourResult)
  100. const finalLink = `${visualazation}?${param.toString()}`
  101.  
  102. return finalLink
  103. }
  104.  
  105.  
  106. function buildGUI() {
  107. const myButton = document.createElement('button')
  108. myButton.id = 'my-button'
  109. myButton.className = 'gl-button btn btn-default'
  110. myButton.textContent = 'Get 996 Index'
  111.  
  112. myButton.addEventListener('click', () => {
  113. if (loading) return
  114.  
  115. loading = true
  116. processData().then(link => {
  117.  
  118. const myPannel = document.getElementById(pannelId)
  119. const a = document.createElement('a')
  120. a.href = link
  121. a.textContent = ' the statistic result'
  122. a.target = 'blank'
  123. myPannel.innerHTML = 'got it! click the link to see '
  124. myPannel.append(a)
  125.  
  126. }, err => {
  127. print(err)
  128. }).finally(() => {
  129. loading = false
  130. })
  131. })
  132.  
  133.  
  134. const container = document.getElementsByClassName('shortcuts-find-file')[0].parentElement
  135. container.append(myButton)
  136.  
  137. const myPannel = document.createElement('div')
  138. myPannel.id = 'my-pannel'
  139. myPannel.style.padding = '20px'
  140. myPannel.className = 'info-well'
  141. myPannel.style.display = 'none'
  142.  
  143. const infoWell = document.getElementsByClassName('info-well')[0]
  144.  
  145. infoWell.parentElement.insertBefore(myPannel, infoWell)
  146.  
  147. }
  148.  
  149.  
  150. function main() {
  151. if (!projectId){
  152. console.log('it isn\'t a gitlab project page, abort')
  153. return
  154. }
  155. const targetNode = document.getElementsByClassName('nav-block')[0];
  156.  
  157. // Options for the observer (which mutations to observe)
  158. const config = { attributes: false, childList: true, subtree: true };
  159.  
  160. // Callback function to execute when mutations are observed
  161. const callback = (mutationList, observer) => {
  162. let isChild = false
  163. for (const mutation of mutationList) {
  164. if (mutation.type === 'childList') {
  165. console.log('A child node has been added or removed.');
  166. isChild = true
  167. }
  168. }
  169. if (isChild) {
  170. observer.disconnect();
  171. buildGUI()
  172. }
  173. };
  174.  
  175. // Create an observer instance linked to the callback function
  176. const observer = new MutationObserver(callback);
  177.  
  178. // Start observing the target node for configured mutations
  179. observer.observe(targetNode, config);
  180.  
  181. }
  182. // launch the program
  183. main()
  184.  
  185.  
  186.  
  187.  
  188. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址