interactions.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. -- INTERACTIONS.LUA (CLI)
  2. -- User interaction functions for Modpol
  3. -- Called by modpol.lua
  4. modpol.interactions = {}
  5. -- DASHBOARDS
  6. -- ==========
  7. -- Function: modpol.interactions.dashboard(user)
  8. -- Params: user (string)
  9. -- Q: Should this return a menu of commands relevant to the specific user?
  10. -- Output: Displays a menu of commands to the user
  11. -- TKTK currently just prints all of modpol---needs major improvement
  12. function modpol.interactions.dashboard(user)
  13. -- adds user to root org if not already in it
  14. if not modpol.instance:has_member(user) then
  15. modpol.instance:add_member(user)
  16. end
  17. local all_users = modpol.instance:list_members()
  18. print('\n-=< MODPOL DASHBOARD >=-')
  19. print('All orgs: (user orgs indicated by *)')
  20. for id, org in ipairs(modpol.orgs.array) do
  21. if type(org) == "table" then
  22. local indicator = ""
  23. if org:has_member(user) then indicator = "*" end
  24. print('['..id..'] '..indicator..org.name)
  25. end
  26. end
  27. -- pending list
  28. local user_pending = {}
  29. local user_pending_count = 0
  30. for k,v in ipairs(modpol.orgs.array) do
  31. if v.pending and v.pending[user] then
  32. if modpol.util.num_pairs(v.pending[user]) ~= 0 then
  33. table.insert(user_pending, v.name)
  34. user_pending_count = user_pending_count + 1
  35. end
  36. end
  37. end
  38. print('Pending actions in: '..table.concat(user_pending,', '))
  39. print('All users: ' .. table.concat(all_users, ', '))
  40. print()
  41. print("Commands: (O)rg, (U)ser, (R)eset, (Q)uit")
  42. local sel = io.read()
  43. if sel == "O" or sel == "o" then
  44. print('Access which org id?')
  45. sel = io.read()
  46. print()
  47. if modpol.orgs.array[tonumber(sel)] then
  48. local sel_org = modpol.orgs.array[tonumber(sel)].name
  49. modpol.interactions.org_dashboard(user, sel_org)
  50. else
  51. print("Org id not found")
  52. modpol.interactions.dashboard(user)
  53. end
  54. elseif sel == "U" or sel == "u" then
  55. print("Access which user?")
  56. sel = io.read()
  57. print()
  58. if modpol.instance:has_member(sel) then
  59. modpol.interactions.user_dashboard(
  60. user, sel,
  61. function()
  62. modpol.interactions.dashboard(user)
  63. end
  64. )
  65. else
  66. print("User name not found")
  67. modpol.interactions.dashboard(user)
  68. end
  69. elseif sel == "R" or sel == "r" then
  70. modpol.instance.members = {}
  71. modpol.orgs.reset()
  72. print("Orgs and users reset")
  73. modpol.interactions.dashboard(user)
  74. elseif sel == "Q" or "q" then
  75. return
  76. else
  77. print("Invalid input, try again")
  78. modpol.interactions.dashboard(user)
  79. end
  80. end
  81. -- Function: modpol.interactions.org_dashboard
  82. -- Params: user (string), org_string (string or id)
  83. -- Output: Displays a menu of org-specific commands to the user
  84. function modpol.interactions.org_dashboard(user, org_string)
  85. local org = modpol.orgs.get_org(org_string)
  86. if not org then return nil end
  87. -- identify parent
  88. local parent = ""
  89. if org.id == 1 then
  90. parent = "none"
  91. else
  92. parent = modpol.orgs.get_org(org.parent).name
  93. end
  94. -- identify children
  95. local children = {}
  96. for k,v in ipairs(org.children) do
  97. local this_child = modpol.orgs.get_org(v)
  98. table.insert(children, this_child.name)
  99. end
  100. -- prepare modules menu
  101. local modules = {}
  102. if org.modules then
  103. for k,v in pairs(org.modules) do
  104. if not v.hide then -- hide utility modules
  105. local module_entry = v.slug
  106. table.insert(modules, module_entry)
  107. end
  108. end
  109. end
  110. table.sort(modules)
  111. -- list pending
  112. local process_msg = #org.processes .. " total processes"
  113. if org.pending[user] then
  114. process_msg = process_msg .. " (" ..
  115. modpol.util.num_pairs(org.pending[user]) .. " pending)"
  116. else
  117. process_msg = process_msg .. " (0 pending)"
  118. end
  119. -- set up output
  120. print('\n-=< ORG DASHBOARD >=-')
  121. print("Org: " .. org.name)
  122. print("Parent: " .. parent)
  123. print("Members: " .. table.concat(org.members, ", "))
  124. print("Child orgs: " .. table.concat(children, ", "))
  125. print("Modules: " .. table.concat(modules, ", "))
  126. print("Pending: " .. process_msg)
  127. print()
  128. print("Commands: (M)odules, (P)ending, (B)ack")
  129. local sel = io.read()
  130. print()
  131. if sel == 'm' or sel == 'M' then
  132. print("Type module name: ")
  133. local module_sel = io.read()
  134. print()
  135. local module_result = false
  136. for k,v in ipairs(modules) do
  137. if v == module_sel then
  138. module_result = true
  139. end
  140. end
  141. if module_result then
  142. org:call_module(module_sel, user)
  143. else
  144. print("Error: Module not found.")
  145. modpol.interactions.org_dashboard(user, org.id)
  146. end
  147. elseif sel == 'p' or sel == 'P' then
  148. local processes = {}
  149. print("All processes: (* indicates pending)")
  150. for i,v in ipairs(org.processes) do
  151. local active = ''
  152. if org.pending[user] then
  153. if org.pending[user][v.id] then
  154. active = '*'
  155. end
  156. end
  157. print("["..v.id.."] "..v.slug..active)
  158. end
  159. print()
  160. print("Interact with which one (use [id] number)?")
  161. local to_interact = io.read()
  162. local process = org.processes[tonumber(to_interact)]
  163. if not process then return end
  164. if org:has_pending_actions(user) then
  165. if org.pending[user][process.id] then
  166. org:interact(process.id, user)
  167. end
  168. end
  169. elseif sel == 'b' or sel == 'B' then
  170. modpol.interactions.dashboard(user)
  171. else
  172. print("Command not found")
  173. modpol.interactions.org_dashboard(user, org.name)
  174. end
  175. end
  176. --- Function: modpol.interactions.user_dashboard
  177. -- Displays a dashboard about a particular user
  178. -- @param viewer Name of user viewing the dashboard (string)
  179. -- @param user Name of user being viewed (string)
  180. -- @param completion Optional function to call on Done button
  181. function modpol.interactions.user_dashboard(viewer, user, completion)
  182. local user_orgs = {}
  183. local user_modules = {}
  184. print("\n-=< USER DASHBOARD: "..user.." >=-")
  185. print("User's orgs:")
  186. for id, org in ipairs(modpol.orgs.array) do
  187. if type(org) == "table" then
  188. if org:has_member(user) then
  189. print(org.name)
  190. end
  191. end
  192. end
  193. print()
  194. print("Commands: (M)essage user, Enter when done")
  195. local sel = io.read()
  196. if sel == "M" or sel == "m" then
  197. modpol.interactions.message_user(
  198. viewer, user)
  199. completion()
  200. else
  201. completion()
  202. end
  203. end
  204. -- buttons: message, done
  205. -- INTERACTION PRIMITIVES
  206. -- ======================
  207. -- Function: modpol.interactions.message
  208. -- Produces a brief message to a user
  209. -- input: user (string), message (string)
  210. -- output: prints message to CLI
  211. function modpol.interactions.message(user, message)
  212. print(user .. ": " .. message)
  213. end
  214. --- Function: modpol.interactions.message_user
  215. -- Gets and sends a message from one user to another
  216. -- @param sender Name of user sending (string)
  217. -- @param recipient Name of user receiving (string)
  218. function modpol.interactions.message_user(sender, recipient)
  219. print("Enter your message for "..recipient..":")
  220. local sel = io.read()
  221. modpol.interactions.message(
  222. recipient,
  223. sel.." [from "..sender.."]")
  224. end
  225. --- Function: modpol.interactions.display
  226. -- Displays complex data to a user
  227. -- @param user Name of target user (string)
  228. -- @param title Title of display (string)
  229. -- @param message Content of message (string or table of strings)
  230. -- @param done Optional function for what happens when user is done
  231. function modpol.interactions.display(user, title, message, completion)
  232. local output = ""
  233. output = "\n-=< "..title.." >=-\n\n"
  234. if type(message) == "table" then
  235. output = table.concat(message,"\n")
  236. elseif type(message) == "string" then
  237. output = message
  238. elseif type(message) == "number" then
  239. output = message
  240. else
  241. modpol.interactions.message(
  242. self.initiator, "Error: message not typed for display")
  243. modpol.interactions.message(
  244. self.initiator, "Error: input not typed for display")
  245. if completion then completion() else
  246. modpol.intereactions.dashboard(user)
  247. end
  248. end
  249. print(message)
  250. print("\nEnter to continue")
  251. io.read()
  252. if completion then completion() else
  253. modpol.intereactions.dashboard(user)
  254. end
  255. end
  256. -- Function: modpol.interactions.text_query
  257. -- input: User (string), Query (string), func (function)
  258. -- func input: user input (string)
  259. -- output: Applies "func" to user input
  260. function modpol.interactions.text_query(user, query, func)
  261. print(user .. ": " .. query)
  262. answer = io.read()
  263. func(answer)
  264. end
  265. -- Function: dropdown_query
  266. -- input: user (string), label (string), options (table of strings), func(choice) (function)
  267. -- func input: choice (string)
  268. -- output: calls func on choice
  269. function modpol.interactions.dropdown_query(user, label, options, func)
  270. -- set up options
  271. local options_display = ""
  272. local options_number = 0
  273. for k,v in ipairs(options) do
  274. options_display = options_display .. k .. ". " ..
  275. options[k] .. "\n"
  276. options_number = options_number + 1
  277. end
  278. options_display = options_display .. "Select number:"
  279. if options_number == 0 then
  280. print("Error: No options given for dropdown")
  281. return nil
  282. end
  283. -- begin displaying
  284. print(user .. ": " .. label)
  285. print(options_display)
  286. -- read input and produce output
  287. local answer
  288. answer = io.read()
  289. answer = tonumber(answer)
  290. if answer then
  291. if answer >= 1 and answer <= options_number then
  292. print("Selection: " .. options[answer])
  293. func(options[answer])
  294. else
  295. print("Error: Not in dropdown range")
  296. return nil
  297. end
  298. else
  299. print("Error: Must be a number")
  300. return nil
  301. end
  302. end
  303. -- Function: modpol.binary_poll_user(user, question)
  304. -- Params: user (string), question (string), func (function)
  305. -- func input: user input (string: y/n)
  306. -- Output: Applies "func" to user input
  307. function modpol.interactions.binary_poll_user(user, question, func)
  308. local query = "Poll for " .. user .. " (y/n): ".. question
  309. local answer
  310. repeat
  311. print(query)
  312. answer = io.read()
  313. until answer == "y" or answer == "n"
  314. if answer == "y" then
  315. modpol.interactions.message(user, "Response recorded")
  316. func("Yes")
  317. elseif answer == "n" then
  318. modpol.interactions.message(user, "Response recorded")
  319. func("No")
  320. else
  321. modpol.interactions.message(user, "Error: invalid response")
  322. end
  323. end
  324. -- COMPLEX INTERACTIONS
  325. -- ====================
  326. -- Function: modpol.interactions.message_org
  327. -- input: initiator (string), org (number or string), message (string)
  328. -- output: broadcasts message to all org members
  329. function modpol.interactions.message_org(initiator, org, message)
  330. local this_org = modpol.orgs.get_org(org)
  331. local users = this_org:list_members()
  332. for k,v in ipairs(users) do
  333. modpol.interactions.message(v, message)
  334. end
  335. end
  336. -- Function: modpol.interactions.binary_poll_org
  337. -- input: initator (user string), org_id (number)
  338. -- output: gets question from initiator, asks all org members, broadcasts answers
  339. -- TESTING
  340. --testing command
  341. function modpol.msg(text)
  342. modpol.interactions.message("TEST MSG: ",text)
  343. end