dashboard 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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. databack = JSON::pretty_generate(Dir.glob('*/').sort_by{|x| x.downcase});
  34. Dir.chdir( __dir__)
  35. Riddl::Parameter::Complex.new('list','application/json',databack)
  36. end
  37. end
  38. class GetAllVisus < Riddl::Implementation
  39. def response
  40. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r[-2],'visus.html')))
  41. end
  42. end
  43. class Get < Riddl::Implementation
  44. def response
  45. #Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  46. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r.last,'index.html')))
  47. end
  48. end
  49. class GetAllData < Riddl::Implementation
  50. def response
  51. fname = File.join('data',@r[-2],'data.db')
  52. db = SQLite3::Database.open fname
  53. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  54. #db.execute "CREATE TABLE IF NOT EXISTS Entries(seriennummer TEXT PRIMARY KEY, produktcode TEXT, activity TEXT, cpeeInstance INTEGER)"
  55. db.results_as_hash = true
  56. alldata = {};
  57. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC")
  58. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  59. else
  60. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  61. end
  62. end
  63. end
  64. class GetNData < Riddl::Implementation
  65. def response
  66. fname = File.join('data',@r[-2],'data.db')
  67. db = SQLite3::Database.open fname
  68. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  69. db.results_as_hash = true
  70. alldata = {};
  71. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
  72. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  73. else
  74. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  75. end
  76. end
  77. end
  78. class DeleteNData < Riddl::Implementation
  79. def response
  80. fname = File.join('data',@r[-2],'data.db')
  81. db = SQLite3::Database.open fname
  82. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  83. db.results_as_hash = true
  84. alldata = {};
  85. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
  86. if alldata["data"] != []
  87. id = alldata["data"][0]["__orderID__"].to_s
  88. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id + "'")
  89. end
  90. else
  91. nil
  92. end
  93. @a[0].send("reset")
  94. end
  95. end
  96. # used when executed from e.g. website, in such a case no cpee instance is given
  97. class AddEntry < Riddl::Implementation
  98. def response
  99. newEntry = JSON.parse(@p[0].value.read)
  100. fname = File.join('data',@r.last,'data.db')
  101. db = SQLite3::Database.open fname
  102. # in case primary key is given
  103. # PRIMARY KEY Momentan noch nicht implementiert
  104. if newEntry.has_key?("pkvalue")
  105. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT, cpeeInstance INTEGER)"
  106. db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",JSON.dump(newEntry["alldata"]), newEntry["pkvalue"], "Direkt")
  107. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), pkvalue: newEntry["pkvalue"], cpeeInstance: "Direkt"};
  108. else
  109. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  110. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",JSON.dump(newEntry["alldata"]), "Direkt")
  111. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), cpeeInstance: "Direkt"};
  112. end
  113. @a[0].send(JSON.dump(hash))
  114. nil
  115. end
  116. end
  117. class Put < Riddl::Implementation
  118. def self::putentry(r, p, h)
  119. Dir.mkdir(File.join('data',r.last)) rescue nil
  120. Dir.mkdir(File.join('data',r.last, "js")) rescue nil
  121. Dir.mkdir(File.join('data',r.last, "visus")) rescue nil
  122. #prevent overriding ui and index in case user changed something there
  123. if !File.file?(File.join('data',r.last,'js/ui.js'))
  124. File.write(File.join('data',r.last,'js/ui.js'),File.open("defaultContent/js/ui.js").read)
  125. end
  126. if !File.file?(File.join('data',r.last,'index.html'))
  127. file = File.open("defaultContent/index.html").read
  128. newfile = file.gsub("!replaceThisString!", r.last)
  129. File.write(File.join('data',r.last,'index.html'), newfile)
  130. end
  131. #prevent overriding visualization
  132. if !File.file?(File.join('data',r.last,'visus.html'))
  133. File.write(File.join('data',r.last,'visus.html'),File.open("defaultContent/visus.html").read)
  134. end
  135. fname = File.join('data',r.last,'data.db')
  136. db = SQLite3::Database.open fname
  137. #in case primary key is given
  138. if p[1] != nil
  139. keyfunction = ""
  140. if p[2] != nil
  141. keyfunction = p[2].value
  142. end
  143. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT " + keyfunction + ", cpeeInstance INTEGER)"
  144. db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",p[0].value, p[1].value, h['CPEE_INSTANCE'])
  145. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, pkvalue: p[1].value, cpeeInstance: h['CPEE_INSTANCE']};
  146. else
  147. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  148. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",p[0].value, h['CPEE_INSTANCE'])
  149. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, cpeeInstance: h['CPEE_INSTANCE']};
  150. end
  151. hash;
  152. end
  153. def response
  154. hash = Put::putentry(@r, @p, @h)
  155. @a[0].send(JSON.dump(hash))
  156. nil
  157. end
  158. end
  159. class PutPrio < Riddl::Implementation
  160. def response
  161. hash = Put::putentry(@r, @p, @h) #add entry
  162. hash = Move::moveentry(hash[:__orderID__][0], 0,@r) #move to front
  163. @a[0].send("reset")
  164. nil
  165. end
  166. end
  167. class PutPrioN < Riddl::Implementation
  168. def response
  169. #move all back
  170. fname = File.join('data',@r.last,'data.db')
  171. db = SQLite3::Database.open fname
  172. #Updating entries directly + X would result in unique constraint failed
  173. #Therefore updates first to negative values then *-1
  174. db.execute "UPDATE Entries SET __orderID__= 0 - (__orderID__ + " + @p[1].value + ")"
  175. db.execute "UPDATE Entries SET __orderID__= __orderID__ *-1"
  176. #Add entries
  177. i = 0
  178. while i < @p[1].value.to_i do
  179. #db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  180. #in case primary key is given
  181. if @p[2] != nil
  182. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, pkvalue, cpeeInstance) VALUES (?,?,?,?)",i, @p[0].value, @p[2].value, @h['CPEE_INSTANCE'])
  183. else
  184. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  185. end
  186. i +=1
  187. end
  188. @a[0].send("reset")
  189. nil
  190. end
  191. end
  192. class Move < Riddl::Implementation
  193. def self::moveentry(from, to,r)
  194. fname = File.join('data',r.last,'data.db')
  195. db = SQLite3::Database.open fname
  196. #getMaxID
  197. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  198. if(result[0][0] == nil)
  199. maxImgId = 0
  200. else
  201. maxImgId = result[0][0] +1
  202. end
  203. i = 0
  204. if to < from
  205. while to < from do
  206. #nutze max ID zum tauschen
  207. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from - 1])
  208. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from - 1, from])
  209. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  210. from -=1
  211. end
  212. elsif from < to
  213. while from < to do
  214. #nutze max ID zum tauschen
  215. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from + 1])
  216. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from + 1, from])
  217. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  218. from +=1
  219. end
  220. end
  221. nil
  222. end
  223. def response
  224. movement = JSON.parse(@p[0].value.read)
  225. from = movement["From"]
  226. to = movement["To"]
  227. hash = Move::moveentry(from, to,@r)
  228. nil
  229. end
  230. end
  231. class Update < Riddl::Implementation
  232. def response
  233. fname = File.join('data',@r.last,'data.db')
  234. db = SQLite3::Database.open fname
  235. updateData = JSON.parse(@p[0].value.read)
  236. orderID = updateData["ID"]
  237. alldata = JSON.generate(updateData["alldata"])
  238. db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", [alldata, orderID])
  239. nil
  240. end
  241. end
  242. class Storesortedtable < Riddl::Implementation
  243. def response
  244. fname = File.join('data',@r.last,'data.db')
  245. db = SQLite3::Database.open fname
  246. #invert keys from + to - to prevent duplicate primary keys when reordering
  247. db.execute("UPDATE Entries SET __orderID__ = __orderID__ *-1 -1")
  248. updateData = JSON.parse(@p[0].value.read)
  249. updateData.each_with_index do |child, idx|
  250. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [idx, child.to_i*-1 -1])
  251. end
  252. #security
  253. #in case there are negative entries remaining add them with positive value to the end
  254. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  255. if(result[0][0] == nil)
  256. maxorderID = 0
  257. else
  258. maxorderID = result[0][0] +1
  259. end
  260. resultpattern = db.execute "SELECT * FROM Entries WHERE __orderID__ < 0"
  261. resultpattern.each do |row2|
  262. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxorderID, row2[0]]);
  263. maxorderID = maxorderID +1;
  264. end
  265. @a[0].send("reset")
  266. nil
  267. end
  268. end
  269. class Delete < Riddl::Implementation
  270. def response
  271. fname = File.join('data',@r.last,'data.db')
  272. db = SQLite3::Database.open fname
  273. alldata = @p[0].value.read
  274. db.execute("DELETE FROM Entries WHERE alldata = '" + alldata + "'")
  275. nil
  276. end
  277. end
  278. class DeleteID < Riddl::Implementation
  279. def self::remove(dbfolder, id)
  280. fname = File.join('data',dbfolder,'data.db')
  281. db = SQLite3::Database.open fname
  282. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id.to_s + "'")
  283. end
  284. def response
  285. DeleteID::remove(@r.last, @p[0].value.read)
  286. nil
  287. end
  288. end
  289. class DeleteIDDirect < Riddl::Implementation
  290. def response
  291. DeleteID::remove(@r.last, @p[0].value)
  292. nil
  293. end
  294. end
  295. class DeletePK < Riddl::Implementation
  296. def response
  297. fname = File.join('data',@r.last,'data.db')
  298. db = SQLite3::Database.open fname
  299. pkvalue = @p[0].value
  300. db.execute("DELETE FROM Entries WHERE pkvalue = '" + pkvalue + "'")
  301. nil
  302. end
  303. end
  304. class SearchPK < Riddl::Implementation
  305. def response
  306. fname = File.join('data',@r[-4],'data.db')
  307. db = SQLite3::Database.open fname
  308. pkvalue = @r.last
  309. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  310. db.results_as_hash = true
  311. alldata = {};
  312. alldata["data"] = (db.execute "Select * from Entries WHERE pkvalue = '" + pkvalue + "'")
  313. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  314. else
  315. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  316. end
  317. end
  318. end
  319. class GetNext < Riddl::Implementation
  320. def response
  321. fname = File.join('data',@r[-2],'data.db')
  322. db = SQLite3::Database.open fname
  323. #get value
  324. db.results_as_hash = true
  325. alldata = {};
  326. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  327. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  328. end
  329. end
  330. class DeleteNext < Riddl::Implementation
  331. def response
  332. fname = File.join('data',@r[-2],'data.db')
  333. db = SQLite3::Database.open fname
  334. #get value
  335. db.results_as_hash = true
  336. alldata = {};
  337. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  338. #remove from DB
  339. DeleteID::remove(@r[-2], alldata["data"]["__orderID__"])
  340. @a[0].send("reset")
  341. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  342. end
  343. end
  344. class DeleteAll < Riddl::Implementation
  345. def response
  346. File.unlink(File.join('data',@r.last,'data.db')) rescue nil
  347. nil
  348. end
  349. end
  350. class SSE < Riddl::SSEImplementation #{{{
  351. def onopen
  352. signals = @a[0]
  353. signals.add self
  354. send 'started'
  355. true
  356. end
  357. def onclose
  358. signals = @a[0]
  359. signals.remove self
  360. nil
  361. end
  362. end #}}}
  363. class Signaling # {{{
  364. def initialize
  365. @binding = []
  366. end
  367. def add(binding)
  368. @binding << binding
  369. end
  370. def remove(binding)
  371. @binding.delete(binding)
  372. end
  373. def length
  374. @binding.length
  375. end
  376. def send(value)
  377. @binding.each do |b|
  378. b.send(value)
  379. end
  380. end
  381. end #}}}
  382. server = Riddl::Server.new(File.join(__dir__,'/dashboard.xml'), :host => 'localhost') do |opts|
  383. accessible_description true
  384. cross_site_xhr true
  385. opts[:signals] = {}
  386. parallel do
  387. loop do
  388. opts[:signals].each do |k,v|
  389. v.send('keepalive')
  390. end
  391. sleep 5
  392. end
  393. end
  394. on resource do
  395. run ConfigSites if get
  396. on resource 'getconfigs' do
  397. run GetAllConfigs if get
  398. end
  399. on resource do |r|
  400. idx = r[:r][0]
  401. opts[:signals][idx] ||= Signaling.new
  402. run Get if get
  403. run Put, opts[:signals][idx] if put 'input'
  404. run PutPrio, opts[:signals][idx] if put 'inputprio'
  405. run PutPrioN, opts[:signals][idx] if put 'inputNprio'
  406. run Move if put 'move'
  407. run Update if put 'update'
  408. run Storesortedtable, opts[:signals][idx] if put 'storesortedtable'
  409. run AddEntry, opts[:signals][idx] if post 'directADD'
  410. run Delete if delete 'deleteMsg'
  411. run DeleteID if delete 'deleteByID'
  412. run DeletePK if delete 'deletePK'
  413. run DeleteAll if delete 'deleteAll'
  414. run DeleteIDDirect if delete
  415. on resource '\d+' do
  416. run GetNData, opts if get
  417. run DeleteNData, opts[:signals][idx] if delete
  418. end
  419. on resource 'sse' do
  420. run SSE, opts[:signals][idx] if sse
  421. end
  422. on resource 'data.db' do
  423. run GetAllData if get
  424. end
  425. on resource 'search' do
  426. on resource 'PK' do
  427. on resource '.*' do
  428. run SearchPK if get
  429. end
  430. end
  431. end
  432. on resource 'visus' do
  433. run GetAllVisus if get
  434. end
  435. on resource 'next' do
  436. run GetNext, opts[:signals][idx] if get
  437. run DeleteNext, opts[:signals][idx] if delete
  438. end
  439. end
  440. end
  441. end.loop!