dashboard 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. #!/usr/bin/ruby
  2. #
  3. # This file is part of centurio.work/ing/commands.
  4. #
  5. # centurio.work/ing/commands is free software: you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or (at your
  8. # option) any later version.
  9. #
  10. # centurio.work/ing/commands is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  13. # Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # centurio.work/ing/commands (file COPYING in the main directory). If not, see
  17. # <http://www.gnu.org/licenses/>.
  18. require 'rubygems'
  19. require 'json'
  20. require 'xml/smart'
  21. require 'riddl/server'
  22. require 'fileutils'
  23. require 'typhoeus'
  24. require 'sqlite3'
  25. class ConfigSites < Riddl::Implementation
  26. def response
  27. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','index.html')))
  28. end
  29. end
  30. class GetAllConfigs < Riddl::Implementation
  31. def response
  32. Dir.chdir( __dir__ + '/data')
  33. Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(Dir.glob('*/').sort_by{|x| x.downcase}))
  34. end
  35. end
  36. class GetAllVisus < Riddl::Implementation
  37. def response
  38. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r[-2],'visus.html')))
  39. end
  40. end
  41. class Get < Riddl::Implementation
  42. def response
  43. #Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  44. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r.last,'index.html')))
  45. end
  46. end
  47. class GetAllData < Riddl::Implementation
  48. def response
  49. fname = File.join('data',@r[-2],'data.db')
  50. db = SQLite3::Database.open fname
  51. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  52. #db.execute "CREATE TABLE IF NOT EXISTS Entries(seriennummer TEXT PRIMARY KEY, produktcode TEXT, activity TEXT, cpeeInstance INTEGER)"
  53. db.results_as_hash = true
  54. alldata = {};
  55. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC")
  56. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  57. else
  58. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  59. end
  60. end
  61. end
  62. class GetNData < Riddl::Implementation
  63. def response
  64. fname = File.join('data',@r[-2],'data.db')
  65. db = SQLite3::Database.open fname
  66. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  67. db.results_as_hash = true
  68. alldata = {};
  69. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
  70. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  71. else
  72. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  73. end
  74. end
  75. end
  76. class DeleteNData < Riddl::Implementation
  77. def response
  78. fname = File.join('data',@r[-2],'data.db')
  79. db = SQLite3::Database.open fname
  80. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  81. db.results_as_hash = true
  82. alldata = {};
  83. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
  84. if alldata["data"] != []
  85. id = alldata["data"][0]["__orderID__"].to_s
  86. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id + "'")
  87. end
  88. else
  89. nil
  90. end
  91. @a[0].send("reset")
  92. end
  93. end
  94. # used when executed from e.g. website, in such a case no cpee instance is given
  95. class AddEntry < Riddl::Implementation
  96. def response
  97. newEntry = JSON.parse(@p[0].value.read)
  98. fname = File.join('data',@r.last,'data.db')
  99. db = SQLite3::Database.open fname
  100. # in case primary key is given
  101. # PRIMARY KEY Momentan noch nicht implementiert
  102. if newEntry.has_key?("pkvalue")
  103. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT, cpeeInstance INTEGER)"
  104. db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",JSON.dump(newEntry["alldata"]), newEntry["pkvalue"], "Direkt")
  105. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), pkvalue: newEntry["pkvalue"], cpeeInstance: "Direkt"};
  106. else
  107. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  108. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",JSON.dump(newEntry["alldata"]), "Direkt")
  109. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), cpeeInstance: "Direkt"};
  110. end
  111. @a[0].send(JSON.dump(hash))
  112. nil
  113. end
  114. end
  115. class Put < Riddl::Implementation
  116. def self::putentry(r, p, h)
  117. Dir.mkdir(File.join('data',r.last)) rescue nil
  118. Dir.mkdir(File.join('data',r.last, "js")) rescue nil
  119. Dir.mkdir(File.join('data',r.last, "visus")) rescue nil
  120. #prevent overriding ui and index in case user changed something there
  121. if !File.file?(File.join('data',r.last,'js/ui.js'))
  122. File.write(File.join('data',r.last,'js/ui.js'),File.open("defaultContent/js/ui.js").read)
  123. end
  124. if !File.file?(File.join('data',r.last,'index.html'))
  125. file = File.open("defaultContent/index.html").read
  126. newfile = file.gsub("!replaceThisString!", r.last)
  127. File.write(File.join('data',r.last,'index.html'), newfile)
  128. end
  129. #prevent overriding visualization
  130. if !File.file?(File.join('data',r.last,'visus.html'))
  131. File.write(File.join('data',r.last,'visus.html'),File.open("defaultContent/visus.html").read)
  132. end
  133. fname = File.join('data',r.last,'data.db')
  134. db = SQLite3::Database.open fname
  135. #in case primary key is given
  136. if p[1] != nil
  137. keyfunction = ""
  138. if p[2] != nil
  139. keyfunction = p[2].value
  140. end
  141. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT " + keyfunction + ", cpeeInstance INTEGER)"
  142. db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",p[0].value, p[1].value, h['CPEE_INSTANCE'])
  143. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, pkvalue: p[1].value, cpeeInstance: h['CPEE_INSTANCE']};
  144. else
  145. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  146. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",p[0].value, h['CPEE_INSTANCE'])
  147. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, cpeeInstance: h['CPEE_INSTANCE']};
  148. end
  149. hash;
  150. end
  151. def response
  152. hash = Put::putentry(@r, @p, @h)
  153. @a[0].send(JSON.dump(hash))
  154. nil
  155. end
  156. end
  157. class PutPrio < Riddl::Implementation
  158. def response
  159. hash = Put::putentry(@r, @p, @h) #add entry
  160. hash = Move::moveentry(hash[:__orderID__][0], 0,@r) #move to front
  161. @a[0].send("reset")
  162. nil
  163. end
  164. end
  165. class PutPrioN < Riddl::Implementation
  166. def response
  167. #move all back
  168. fname = File.join('data',@r.last,'data.db')
  169. db = SQLite3::Database.open fname
  170. #Updating entries directly + X would result in unique constraint failed
  171. #Therefore updates first to negative values then *-1
  172. db.execute "UPDATE Entries SET __orderID__= 0 - (__orderID__ + " + @p[1].value + ")"
  173. db.execute "UPDATE Entries SET __orderID__= __orderID__ *-1"
  174. #Add entries
  175. i = 0
  176. while i < @p[1].value.to_i do
  177. #db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  178. #in case primary key is given
  179. if @p[2] != nil
  180. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, pkvalue, cpeeInstance) VALUES (?,?,?,?)",i, @p[0].value, @p[2].value, @h['CPEE_INSTANCE'])
  181. else
  182. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  183. end
  184. i +=1
  185. end
  186. @a[0].send("reset")
  187. nil
  188. end
  189. end
  190. class Move < Riddl::Implementation
  191. def self::moveentry(from, to,r)
  192. fname = File.join('data',r.last,'data.db')
  193. db = SQLite3::Database.open fname
  194. #getMaxID
  195. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  196. if(result[0][0] == nil)
  197. maxImgId = 0
  198. else
  199. maxImgId = result[0][0] +1
  200. end
  201. i = 0
  202. if to < from
  203. while to < from do
  204. #nutze max ID zum tauschen
  205. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from - 1])
  206. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from - 1, from])
  207. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  208. from -=1
  209. end
  210. elsif from < to
  211. while from < to do
  212. #nutze max ID zum tauschen
  213. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from + 1])
  214. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from + 1, from])
  215. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  216. from +=1
  217. end
  218. end
  219. nil
  220. end
  221. def response
  222. movement = JSON.parse(@p[0].value.read)
  223. from = movement["From"]
  224. to = movement["To"]
  225. hash = Move::moveentry(from, to,@r)
  226. nil
  227. end
  228. end
  229. class Update < Riddl::Implementation
  230. def response
  231. fname = File.join('data',@r.last,'data.db')
  232. db = SQLite3::Database.open fname
  233. updateData = JSON.parse(@p[0].value.read)
  234. orderID = updateData["ID"]
  235. alldata = JSON.generate(updateData["alldata"])
  236. db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", [alldata, orderID])
  237. nil
  238. end
  239. end
  240. class Delete < Riddl::Implementation
  241. def response
  242. fname = File.join('data',@r.last,'data.db')
  243. db = SQLite3::Database.open fname
  244. alldata = @p[0].value.read
  245. db.execute("DELETE FROM Entries WHERE alldata = '" + alldata + "'")
  246. nil
  247. end
  248. end
  249. class DeleteID < Riddl::Implementation
  250. def self::remove(dbfolder, id)
  251. fname = File.join('data',dbfolder,'data.db')
  252. db = SQLite3::Database.open fname
  253. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id.to_s + "'")
  254. end
  255. def response
  256. DeleteID::remove(@r.last, @p[0].value.read)
  257. nil
  258. end
  259. end
  260. class DeleteIDDirect < Riddl::Implementation
  261. def response
  262. DeleteID::remove(@r.last, @p[0].value)
  263. nil
  264. end
  265. end
  266. class DeletePK < Riddl::Implementation
  267. def response
  268. fname = File.join('data',@r.last,'data.db')
  269. db = SQLite3::Database.open fname
  270. pkvalue = @p[0].value
  271. db.execute("DELETE FROM Entries WHERE pkvalue = '" + pkvalue + "'")
  272. nil
  273. end
  274. end
  275. class SearchPK < Riddl::Implementation
  276. def response
  277. fname = File.join('data',@r[-4],'data.db')
  278. db = SQLite3::Database.open fname
  279. pkvalue = @r.last
  280. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  281. db.results_as_hash = true
  282. alldata = {};
  283. alldata["data"] = (db.execute "Select * from Entries WHERE pkvalue = '" + pkvalue + "'")
  284. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  285. else
  286. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  287. end
  288. end
  289. end
  290. class GetNext < Riddl::Implementation
  291. def response
  292. fname = File.join('data',@r[-2],'data.db')
  293. db = SQLite3::Database.open fname
  294. #get value
  295. db.results_as_hash = true
  296. alldata = {};
  297. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  298. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  299. end
  300. end
  301. class DeleteNext < Riddl::Implementation
  302. def response
  303. fname = File.join('data',@r[-2],'data.db')
  304. db = SQLite3::Database.open fname
  305. #get value
  306. db.results_as_hash = true
  307. alldata = {};
  308. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  309. #remove from DB
  310. DeleteID::remove(@r[-2], alldata["data"]["__orderID__"])
  311. @a[0].send("reset")
  312. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  313. end
  314. end
  315. class SSE < Riddl::SSEImplementation #{{{
  316. def onopen
  317. signals = @a[0]
  318. signals.add self
  319. send 'started'
  320. true
  321. end
  322. def onclose
  323. signals = @a[0]
  324. signals.remove self
  325. nil
  326. end
  327. end #}}}
  328. class Signaling # {{{
  329. def initialize
  330. @binding = []
  331. end
  332. def add(binding)
  333. @binding << binding
  334. end
  335. def remove(binding)
  336. @binding.delete(binding)
  337. end
  338. def length
  339. @binding.length
  340. end
  341. def send(value)
  342. @binding.each do |b|
  343. b.send(value)
  344. end
  345. end
  346. end #}}}
  347. server = Riddl::Server.new(File.join(__dir__,'/dashboard.xml'), :host => 'localhost') do |opts|
  348. accessible_description true
  349. cross_site_xhr true
  350. opts[:signals] = {}
  351. parallel do
  352. loop do
  353. opts[:signals].each do |k,v|
  354. v.send('keepalive')
  355. end
  356. sleep 5
  357. end
  358. end
  359. on resource do
  360. run ConfigSites if get
  361. on resource 'getconfigs' do
  362. run GetAllConfigs if get
  363. end
  364. on resource do |r|
  365. idx = r[:r][0]
  366. opts[:signals][idx] ||= Signaling.new
  367. run Get if get
  368. run Put, opts[:signals][idx] if put 'input'
  369. run PutPrio, opts[:signals][idx] if put 'inputprio'
  370. run PutPrioN, opts[:signals][idx] if put 'inputNprio'
  371. run Move if put 'move'
  372. run Update if put 'update'
  373. run AddEntry, opts[:signals][idx] if post 'directADD'
  374. run Delete if delete 'deleteMsg'
  375. run DeleteID if delete 'deleteByID'
  376. run DeletePK if delete 'deletePK'
  377. run DeleteIDDirect if delete
  378. on resource '\d+' do
  379. run GetNData, opts if get
  380. run DeleteNData, opts[:signals][idx] if delete
  381. end
  382. on resource 'sse' do
  383. run SSE, opts[:signals][idx] if sse
  384. end
  385. on resource 'data.db' do
  386. run GetAllData if get
  387. end
  388. on resource 'search' do
  389. on resource 'PK' do
  390. on resource '.*' do
  391. run SearchPK if get
  392. end
  393. end
  394. end
  395. on resource 'visus' do
  396. run GetAllVisus if get
  397. end
  398. on resource 'next' do
  399. run GetNext, opts[:signals][idx] if get
  400. run DeleteNext, opts[:signals][idx] if delete
  401. end
  402. end
  403. end
  404. end.loop!