Lattice Goals - Add Ideal

Determines ideal goal value based on Created on and Due dates

当前为 2020-04-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Lattice Goals - Add Ideal
  3. // @namespace Violentmonkey Scripts
  4. // @match http://*.latticehq.com/goals/*
  5. // @match https://*.latticehq.com/goals/*
  6. // @grant none
  7. // @version 1.1
  8. // @author pedro-mass
  9. // @description Determines ideal goal value based on Created on and Due dates
  10. // @run-at document-idle
  11. // @require http://code.jquery.com/jquery-3.5.0.min.js
  12. // ==/UserScript==
  13.  
  14. waitUntilTrue(shouldRun, run)
  15.  
  16. function isPercentageBasedGoal() {
  17. // todo: make this actually check if there is a percentage based check?
  18. return true
  19. }
  20.  
  21. function run() {
  22. console.log('Starting Lattice Goal Percentage calculations...')
  23. if (!isPercentageBasedGoal()) return
  24.  
  25. const Dates = getDates()
  26. const GoalStats = getGoalStats()
  27. const percentageByDate = getRelativePercentage(
  28. Dates.start,
  29. Dates.end,
  30. Dates.current
  31. )
  32.  
  33. const idealValue = Math.round(
  34. getRelativeValue(GoalStats.start, GoalStats.goal, percentageByDate)
  35. )
  36.  
  37. const goalDirection = GoalStats.start <= GoalStats.end ? 1 : -1
  38.  
  39. return insertIdeal(
  40. idealValue,
  41. GoalStats.unit,
  42. GoalStats.current,
  43. goalDirection
  44. )
  45. }
  46.  
  47. function getRelativeValue(start, end, percentage) {
  48. const offset = start
  49. return (end - offset) * percentage + offset
  50. }
  51.  
  52. function getDates() {
  53. const start = getDate(/^created\n\n/i)
  54. const end = getDate(/^due\n\n/i)
  55. const current = Date.now()
  56.  
  57. return {
  58. start,
  59. end,
  60. current,
  61. }
  62. }
  63.  
  64. function getGoalStats() {
  65. const unit = getValue(/^start: /i, 'span').replace(/\d+/, '')
  66. const getNumber = (regex) => Number(getValue(regex, 'span').replace(unit, ''))
  67. const start = getNumber(/^start: /i)
  68. const current = getNumber(/^current: /i)
  69. const goal = getNumber(/^goal: /i)
  70.  
  71. return {
  72. start,
  73. goal,
  74. current,
  75. unit,
  76. }
  77. }
  78.  
  79. function getProgressIndicator(ideal, current) {
  80. if (!current) return
  81.  
  82. if (ideal <= current) {
  83. return '🎉'
  84. }
  85.  
  86. return '😢'
  87. }
  88.  
  89. function insertIdeal(ideal, unit, current, isAscendingGoal) {
  90. if (contains('span', /^ideally: /i).length > 0) {
  91. return
  92. }
  93.  
  94. const progressIndicator = isAscendingGoal
  95. ? getProgressIndicator(ideal, current)
  96. : getProgressIndicator(current, ideal)
  97.  
  98. const idealElem = `<span class="css-1mddpa2">Ideally: <span>${ideal}${unit}</span> <span>${progressIndicator}</span></span>`
  99. const goalsContainer = contains('div', /^start:/i)
  100.  
  101. return $(goalsContainer).find('span').first().after(idealElem)
  102. }
  103.  
  104. function shouldRun() {
  105. const pageCheck = contains('span', /^start: /i)
  106. return pageCheck != null && pageCheck.length > 0
  107. }
  108.  
  109. function getRelativePercentage(startDate, endDate, currentDate) {
  110. const offset = startDate
  111. endDate = endDate - offset
  112. currentDate = currentDate - offset
  113. return currentDate / endDate
  114. }
  115.  
  116. function getDate(regex, selector = 'div') {
  117. return new Date(getValue(regex, selector)).getTime()
  118. }
  119.  
  120. function getValue(regex, selector) {
  121. return replaceString(getText(first(contains(selector, regex))), regex, '')
  122. }
  123.  
  124. function replaceString(string, searchString, newString) {
  125. if (!string) {
  126. console.warn('Received bad params', { string, searchString, newString })
  127. return string
  128. }
  129.  
  130. return string.replace(searchString, newString)
  131. }
  132.  
  133. function contains(selector, text) {
  134. var elements = document.querySelectorAll(selector)
  135. return Array.prototype.filter.call(elements, function (element) {
  136. return RegExp(text).test(getText(element))
  137. })
  138. }
  139.  
  140. function getText(element) {
  141. return element.innerText
  142. }
  143.  
  144. function first(arr) {
  145. return arr[0]
  146. }
  147.  
  148. function waitUntilTrue(checkFn, cb = (x) => x, timeout = 250) {
  149. const intervalId = setInterval(checkInterval, timeout)
  150. function checkInterval() {
  151. if (checkFn()) {
  152. clearInterval(intervalId)
  153. cb()
  154. }
  155. }
  156. }