You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
5.4 KiB

  1. var bodyParser = require('body-parser')
  2. var express = require('express')
  3. const got = require('got')
  4. var MIXMAX_API_KEY
  5. var app = express()
  6. var input
  7. app.use(function (req, res, next) {
  8. res.header('Access-Control-Allow-Origin', '*')
  9. res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
  10. next()
  11. })
  12. app.use(bodyParser.json({
  13. limit: '100mb',
  14. type: 'application/json'
  15. }))
  16. app.get('/', function (req, res) {
  17. res.redirect('https://binhonglee.github.io/Breakups/')
  18. })
  19. app.get('/help', function (req, res) {
  20. res.redirect('https://github.com/binhonglee/Breakups')
  21. })
  22. app.get('/webapp', function (req, res) {
  23. res.redirect('https://breakups-webapp.herokuapp.com/')
  24. })
  25. app.post('/total', function (req, res) {
  26. input = req.body
  27. var toReturn = { 'total': 0 }
  28. toReturn.total = total(input.users)
  29. res.json(toReturn)
  30. })
  31. app.post('/perPerson', function (req, res) {
  32. input = req.body
  33. var toReturn = { 'perPerson': 0 }
  34. toReturn.perPerson = (total(input.users)) / input.users.length
  35. res.json(toReturn)
  36. })
  37. app.post('/oweChart', function (req, res) {
  38. input = req.body
  39. input.users = oweChart(input.users)
  40. res.send(input)
  41. })
  42. app.post('/sortedOweChart', function (req, res) {
  43. input = req.body
  44. input.users = mergeSort(oweChart(input.users))
  45. res.send(input)
  46. })
  47. app.post('/paymentChain', function (req, res) {
  48. input = req.body
  49. var sorted1 = mergeSort(oweChart(input.users))
  50. var sorted2 = mergeSort(oweChart(input.users))
  51. res.json(paymentNetwork(sorted1, sorted2))
  52. })
  53. app.post('/emailPaymentChain', function (req, res) {
  54. input = req.body
  55. MIXMAX_API_KEY = input['mixmax-api']
  56. var sorted = mergeSort(oweChart(input.users))
  57. var chain = paymentChain(sorted)
  58. var emails = []
  59. for (var i = 0; i < chain.length; i++) {
  60. var message = messageCreation(sorted[i], sorted[i + 1], chain[i].amount)
  61. send(message)
  62. emails.push('Email sent to ' + sorted[i].email)
  63. }
  64. res.json(emails)
  65. })
  66. // app.listen(5000)
  67. app.listen(process.env.PORT)
  68. function total (input) {
  69. var total = 0
  70. for (var i = 0; i < input.length; i++) {
  71. total += input[i].amount
  72. }
  73. return total
  74. }
  75. function oweChart (users) {
  76. var perPerson = total(users) / users.length
  77. for (var i = 0; i < users.length; i++) {
  78. users[i].amount -= perPerson
  79. }
  80. return users
  81. }
  82. function mergeSort (users) {
  83. if (users.length < 2) {
  84. return users
  85. }
  86. var middle = Math.floor(users.length / 2)
  87. var left = users.slice(0, middle)
  88. var right = users.slice(middle)
  89. return merge(mergeSort(left), mergeSort(right))
  90. }
  91. function merge (left, right) {
  92. var result = []
  93. var il = 0
  94. var ir = 0
  95. while (il < left.length && ir < right.length) {
  96. if (left[il].amount < 0 && right[ir].amount < 0) {
  97. if (left[il].amount > right[ir].amount) {
  98. result.push(left[il++])
  99. } else {
  100. result.push(right[ir++])
  101. }
  102. } else {
  103. if (left[il].amount < right[ir].amount) {
  104. result.push(left[il++])
  105. } else {
  106. result.push(right[ir++])
  107. }
  108. }
  109. }
  110. return result.concat(left.slice(il)).concat(right.slice(ir))
  111. }
  112. function paymentChain (users) {
  113. var chain = []
  114. var stack = 0
  115. for (var i = 0; i < users.length - 1; i++) {
  116. while (users[i + 1].amount === 0) {
  117. users.splice((i + 1), 1)
  118. }
  119. var item = {'from': '', 'to': '', 'amount': 0}
  120. item['from'] = users[i].name
  121. item['to'] = users[i + 1].name
  122. var amount = users[i].amount * -1
  123. stack += amount
  124. item['amount'] = stack
  125. chain.push(item)
  126. }
  127. return chain
  128. }
  129. function paymentNetwork (users, backup) {
  130. var chain = []
  131. while (chain.length < users.length && backup.length > 1) {
  132. if (backup.length > 0 && backup[0].amount === 0) {
  133. backup.splice(0, 1)
  134. } else if ((0 !== (backup.length - 1)) && (backup[0].amount === (backup[backup.length - 1].amount * -1))) {
  135. chain.push(createItem(backup[0].name, backup[backup.length - 1].name, backup[backup.length - 1].amount))
  136. backup.splice((0), 1)
  137. backup.splice((backup.length - 1), 1)
  138. } else if (backup[0].amount * -1 < (backup[backup.length - 1].amount)) {
  139. chain.push(createItem(backup[0].name, backup[backup.length - 1].name, backup[0].amount * -1))
  140. backup[backup.length - 1].amount += backup[0].amount
  141. backup.splice((0), 1)
  142. } else {
  143. chain.push(createItem(backup[0].name, backup[backup.length - 1].name, backup[backup.length - 1].amount))
  144. backup[0].amount += backup[backup.length - 1].amount
  145. backup.splice((backup.length - 1), 1)
  146. }
  147. }
  148. if (backup.length > 1) {
  149. return paymentChain(users)
  150. } else {
  151. return chain
  152. }
  153. }
  154. function createItem (from, to, amount) {
  155. console.log('from: ' + from + ' to: ' + to + ' amount: ' + amount)
  156. var item = {'from': '', 'to': '', 'amount': 0}
  157. item['from'] = from
  158. item['to'] = to
  159. item['amount'] = amount
  160. return item
  161. }
  162. function messageCreation (user1, user2, amount) {
  163. var signature = '<br><br><p>Sincerely,<br><a href="https://github.com/binhonglee/Breakups">BreakupBills</a></p>'
  164. var message = {}
  165. message.to = user1.email
  166. message.subject = 'Pay ' + user2.name + ' $' + amount
  167. message.html = '<p>Please pay ' + user2.name + ' $' + amount + '</p>' + signature
  168. return message
  169. }
  170. function send (message) {
  171. got.post('https://api.mixmax.com/v1/send', {
  172. body: JSON.stringify({
  173. message
  174. }),
  175. headers: {
  176. 'Content-Type': 'application/json',
  177. 'X-API-Token': MIXMAX_API_KEY
  178. }
  179. })
  180. }