dashboard 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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("reset")
  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("reset")
  156. nil
  157. end
  158. end
  159. class PostHtmlVals < Riddl::Implementation
  160. def response
  161. Dir.mkdir(File.join('data',@r[-2])) rescue nil
  162. Dir.mkdir(File.join('data',@r[-2], "js")) rescue nil
  163. Dir.mkdir(File.join('data',@r[-2], "visus")) rescue nil
  164. #prevent overriding ui and index in case user changed something there
  165. if !File.file?(File.join('data',@r[-2],'js/ui.js'))
  166. File.write(File.join('data',@r[-2],'js/ui.js'),File.open("defaultContent/js/ui.js").read)
  167. end
  168. if !File.file?(File.join('data',@r[-2],'index.html'))
  169. file = File.open("defaultContent/index.html").read
  170. newfile = file.gsub("!replaceThisString!", @r[-2])
  171. File.write(File.join('data',@r[-2],'index.html'), newfile)
  172. end
  173. #prevent overriding visualization
  174. if !File.file?(File.join('data',@r[-2],'visus.html'))
  175. File.write(File.join('data',@r[-2],'visus.html'),File.open("defaultContent/visus.html").read)
  176. end
  177. file = JSON['{}']
  178. @p.each_with_index do |child, idx|
  179. file[child.name] = child.value
  180. end
  181. fname = File.join('data',@r[-2],'data.db')
  182. db = SQLite3::Database.open fname
  183. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  184. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)", JSON[file], "none")
  185. @a[0].send("reset")
  186. Riddl::Parameter::Complex.new('ui','text/html', '<html><head><meta http-equiv="refresh" content="0; url =' +@h["REFERER"]+'"/></head>')
  187. end
  188. end
  189. class PutPrio < Riddl::Implementation
  190. def response
  191. hash = Put::putentry(@r, @p, @h) #add entry
  192. hash = Move::moveentry(hash[:__orderID__][0], 0,@r) #move to front
  193. @a[0].send("reset")
  194. nil
  195. end
  196. end
  197. class PutPrioN < Riddl::Implementation
  198. def response
  199. #move all back
  200. fname = File.join('data',@r.last,'data.db')
  201. db = SQLite3::Database.open fname
  202. #Updating entries directly + X would result in unique constraint failed
  203. #Therefore updates first to negative values then *-1
  204. db.execute "UPDATE Entries SET __orderID__= 0 - (__orderID__ + " + @p[1].value + ")"
  205. db.execute "UPDATE Entries SET __orderID__= __orderID__ *-1"
  206. #Add entries
  207. i = 0
  208. while i < @p[1].value.to_i do
  209. #db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  210. #in case primary key is given
  211. if @p[2] != nil
  212. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, pkvalue, cpeeInstance) VALUES (?,?,?,?)",i, @p[0].value, @p[2].value, @h['CPEE_INSTANCE'])
  213. else
  214. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  215. end
  216. i +=1
  217. end
  218. @a[0].send("reset")
  219. nil
  220. end
  221. end
  222. class Move < Riddl::Implementation
  223. def self::moveentry(from, to,r)
  224. fname = File.join('data',r.last,'data.db')
  225. db = SQLite3::Database.open fname
  226. #getMaxID
  227. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  228. if(result[0][0] == nil)
  229. maxImgId = 0
  230. else
  231. maxImgId = result[0][0] +1
  232. end
  233. i = 0
  234. if to < from
  235. while to < from do
  236. #nutze max ID zum tauschen
  237. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from - 1])
  238. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from - 1, from])
  239. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  240. from -=1
  241. end
  242. elsif from < to
  243. while from < to do
  244. #nutze max ID zum tauschen
  245. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from + 1])
  246. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from + 1, from])
  247. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  248. from +=1
  249. end
  250. end
  251. nil
  252. end
  253. def response
  254. movement = JSON.parse(@p[0].value.read)
  255. from = movement["From"]
  256. to = movement["To"]
  257. hash = Move::moveentry(from, to,@r)
  258. nil
  259. end
  260. end
  261. class Update < Riddl::Implementation
  262. def response
  263. fname = File.join('data',@r.last,'data.db')
  264. db = SQLite3::Database.open fname
  265. updateData = JSON.parse(@p[0].value.read)
  266. orderID = updateData["ID"]
  267. alldata = JSON.generate(updateData["alldata"])
  268. db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", [alldata, orderID])
  269. nil
  270. end
  271. end
  272. class Storesortedtable < Riddl::Implementation
  273. def response
  274. fname = File.join('data',@r.last,'data.db')
  275. db = SQLite3::Database.open fname
  276. #invert keys from + to - to prevent duplicate primary keys when reordering
  277. db.execute("UPDATE Entries SET __orderID__ = __orderID__ *-1 -1")
  278. updateData = JSON.parse(@p[0].value.read)
  279. updateData.each_with_index do |child, idx|
  280. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [idx, child.to_i*-1 -1])
  281. end
  282. #security
  283. #in case there are negative entries remaining add them with positive value to the end
  284. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  285. if(result[0][0] == nil)
  286. maxorderID = 0
  287. else
  288. maxorderID = result[0][0] +1
  289. end
  290. resultpattern = db.execute "SELECT * FROM Entries WHERE __orderID__ < 0"
  291. resultpattern.each do |row2|
  292. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxorderID, row2[0]]);
  293. maxorderID = maxorderID +1;
  294. end
  295. @a[0].send("reset")
  296. nil
  297. end
  298. end
  299. class Delete < Riddl::Implementation
  300. def response
  301. fname = File.join('data',@r.last,'data.db')
  302. db = SQLite3::Database.open fname
  303. alldata = @p[0].value.read
  304. db.execute("DELETE FROM Entries WHERE alldata = '" + alldata + "'")
  305. @a[0].send("reset")
  306. nil
  307. end
  308. end
  309. class DeleteID < Riddl::Implementation
  310. def self::remove(dbfolder, id)
  311. fname = File.join('data',dbfolder,'data.db')
  312. db = SQLite3::Database.open fname
  313. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id.to_s + "'")
  314. end
  315. def response
  316. DeleteID::remove(@r.last, @p[0].value.read)
  317. @a[0].send("reset")
  318. nil
  319. end
  320. end
  321. class DeleteIDDirect < Riddl::Implementation
  322. def response
  323. DeleteID::remove(@r.last, @p[0].value)
  324. @a[0].send("reset")
  325. nil
  326. end
  327. end
  328. class DeletePK < Riddl::Implementation
  329. def response
  330. fname = File.join('data',@r.last,'data.db')
  331. db = SQLite3::Database.open fname
  332. pkvalue = @p[0].value
  333. db.execute("DELETE FROM Entries WHERE pkvalue = '" + pkvalue + "'")
  334. @a[0].send("reset")
  335. nil
  336. end
  337. end
  338. class SearchPK < Riddl::Implementation
  339. def response
  340. fname = File.join('data',@r[-4],'data.db')
  341. db = SQLite3::Database.open fname
  342. pkvalue = @r.last
  343. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  344. db.results_as_hash = true
  345. alldata = {};
  346. alldata["data"] = (db.execute "Select * from Entries WHERE pkvalue = '" + pkvalue + "'")
  347. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  348. else
  349. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  350. end
  351. end
  352. end
  353. class GetNext < Riddl::Implementation
  354. def response
  355. fname = File.join('data',@r[-2],'data.db')
  356. db = SQLite3::Database.open fname
  357. #get value
  358. db.results_as_hash = true
  359. alldata = {};
  360. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  361. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  362. end
  363. end
  364. class DeleteNext < Riddl::Implementation
  365. def response
  366. fname = File.join('data',@r[-2],'data.db')
  367. db = SQLite3::Database.open fname
  368. #get value
  369. db.results_as_hash = true
  370. alldata = {};
  371. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  372. #remove from DB
  373. DeleteID::remove(@r[-2], alldata["data"]["__orderID__"])
  374. @a[0].send("reset")
  375. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  376. end
  377. end
  378. class DeleteAll < Riddl::Implementation
  379. def response
  380. File.unlink(File.join('data',@r.last,'data.db')) rescue nil
  381. nil
  382. end
  383. end
  384. class SSE < Riddl::SSEImplementation #{{{
  385. def onopen
  386. signals = @a[0]
  387. signals.add self
  388. send 'started'
  389. true
  390. end
  391. def onclose
  392. signals = @a[0]
  393. signals.remove self
  394. nil
  395. end
  396. end #}}}
  397. class Signaling # {{{
  398. def initialize
  399. @binding = []
  400. end
  401. def add(binding)
  402. @binding << binding
  403. end
  404. def remove(binding)
  405. @binding.delete(binding)
  406. end
  407. def length
  408. @binding.length
  409. end
  410. def send(value)
  411. @binding.each do |b|
  412. b.send(value)
  413. end
  414. end
  415. end #}}}
  416. server = Riddl::Server.new(File.join(__dir__,'/dashboard.xml'), :host => 'localhost') do |opts|
  417. accessible_description true
  418. cross_site_xhr true
  419. opts[:signals] = {}
  420. parallel do
  421. loop do
  422. opts[:signals].each do |k,v|
  423. v.send('keepalive')
  424. end
  425. sleep 5
  426. end
  427. end
  428. on resource do
  429. run ConfigSites if get
  430. on resource 'getconfigs' do
  431. run GetAllConfigs if get
  432. end
  433. on resource do |r|
  434. idx = r[:r][0]
  435. opts[:signals][idx] ||= Signaling.new
  436. run Get if get
  437. run Put, opts[:signals][idx] if put 'input'
  438. run PutPrio, opts[:signals][idx] if put 'inputprio'
  439. run PutPrioN, opts[:signals][idx] if put 'inputNprio'
  440. run Move if put 'move'
  441. run Update if put 'update'
  442. run Storesortedtable, opts[:signals][idx] if put 'storesortedtable'
  443. run AddEntry, opts[:signals][idx] if post 'directADD'
  444. run Delete, opts[:signals][idx] if delete 'deleteMsg'
  445. run DeleteID, opts[:signals][idx] if delete 'deleteByID'
  446. run DeletePK, opts[:signals][idx] if delete 'deletePK'
  447. run DeleteAll, opts[:signals][idx] if delete 'deleteAll'
  448. run DeleteIDDirect, opts[:signals][idx] if delete
  449. on resource '\d+' do
  450. run GetNData, opts if get
  451. run DeleteNData, opts[:signals][idx] if delete
  452. end
  453. on resource 'sse' do
  454. run SSE, opts[:signals][idx] if sse
  455. end
  456. on resource 'data.db' do
  457. run GetAllData if get
  458. end
  459. on resource 'htmlform' do
  460. run PostHtmlVals, opts[:signals][idx] if post
  461. end
  462. on resource 'search' do
  463. on resource 'PK' do
  464. on resource '.*' do
  465. run SearchPK if get
  466. end
  467. end
  468. end
  469. on resource 'visus' do
  470. run GetAllVisus if get
  471. end
  472. on resource 'next' do
  473. run GetNext, opts[:signals][idx] if get
  474. run DeleteNext, opts[:signals][idx] if delete
  475. end
  476. end
  477. end
  478. end.loop!