db.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QDir, QDirIterator, QObject, QVariant
  2. from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel, QSqlField, QSqlRecord
  3. import hashlib
  4. import os
  5. from RBK.Model.DataType import DataType
  6. from RBK.Model.Field import Field
  7. from RBK.Model.Functions import runQuery, createModel, isError
  8. import RBK.Model.Functions as Func
  9. class Database(QObject):
  10. errored = pyqtSignal(str)
  11. relayAddField = pyqtSignal(str, str)
  12. relayRemoveField = pyqtSignal(str, str)
  13. relayAddDataType = pyqtSignal(str)
  14. relayRemoveDataType = pyqtSignal(str)
  15. def __init__(self):
  16. super().__init__()
  17. # Create a db w/ a list of the Games, versions and available Core Rulebooks
  18. self.models = {}
  19. self.db = QSqlDatabase().addDatabase('QSQLITE', 'db')
  20. self.dbcore = QSqlDatabase().addDatabase('QSQLITE', 'dbcore')
  21. self.rulebooks = QSqlDatabase().addDatabase('QSQLITE', 'rulebooks')
  22. self.rulebooks.setDatabaseName(':memory:')
  23. self.rulebooks.open()
  24. runQuery(self.rulebooks, "CREATE TABLE Rulebooks(GameName TEXT NOT NULL, GameVersion REAL NOT NULL, RulebookName TEXT PRIMARY KEY NOT NULL, Hash TEXT NOT NULL, Core TEXT NOT NULL, CoreRulebook TEXT, CoreRulebookHash TEXT)")
  25. runQuery(self.rulebooks, "CREATE TABLE ChangeLog(Table TEXT NOT NULL, Field TEXT NOT NULL, OldValue TEXT NOT NULL, NewValue TEXT NOT NULL)")
  26. iter = QDirIterator('Rulebooks/', ['*.rbk'], QDir.NoFilter, QDirIterator.Subdirectories)
  27. while(iter.hasNext()):
  28. rbk = iter.next()
  29. self.db.setDatabaseName(rbk)
  30. if not self.db.open():
  31. errored.emit('Error: Cannot open database: {0}'.format(self.db.lastError().text()))
  32. else:
  33. hasher = hashlib.sha256()
  34. with open(rbk, 'rb') as dbfile:
  35. buffer = dbfile.read()
  36. hasher.update(buffer)
  37. hash = hasher.hexdigest()
  38. print('{0}\t{1}'.format(rbk, hash))
  39. dbfile.close()
  40. rbkquery = runQuery(self.db, 'SELECT * FROM Rulebook LIMIT 1')
  41. rbkinfo = rbkquery.getValue()[0]
  42. result = runQuery(self.rulebooks, ('INSERT INTO Rulebooks VALUES ("{0}", "{1}", "{2}", "{3}", "{4}", "{5}", "{6}")'.format(rbkinfo.value('GameName'), rbkinfo.value('GameVersion'), rbkinfo.value('RulebookName'), hash, rbkinfo.value('Core'), rbkinfo.value('CoreRulebook'), rbkinfo.value('CoreRulebookHash'))))
  43. self.db.close()
  44. self.models['rulebooks'] = QSqlTableModel(None, self.rulebooks)
  45. self.models['rulebooks'].setTable('Rulebooks')
  46. self.models['rulebooks'].select()
  47. def newRulebook(self, game, version, rbk, core, corerbk, corehash):
  48. # Create directory structures if they don't already exist
  49. if not os.path.exists('Rulebooks/{0}'.format(game)):
  50. os.makedirs('Rulebooks/{0}'.format(game))
  51. os.makedirs('Rulebooks/{0}/{1}'.format(game, version))
  52. elif not os.path.exists('Rulebooks/{0}/{1}'.format(game, version)):
  53. os.makedirs('Rulebooks/{0}/{1}'.format(game, version))
  54. filename = 'Rulebooks/{0}/{1}/{2}.rbk'.format(game, version, rbk)
  55. self.db.setDatabaseName(filename)
  56. if not self.db.open():
  57. errored.emit('Error: Cannot open database: {0}'.format(self.lastError().text()))
  58. return False
  59. self.dataTypeModel = DataType(self)
  60. self.relayAddDataType.connect(self.dataTypeModel.addDataType)
  61. self.relayRemoveDataType.connect(self.dataTypeModel.removeDataType)
  62. self.fieldModel = Field(self)
  63. self.relayAddField.connect(self.fieldModel.addField)
  64. self.relayRemoveField.connect(self.fieldModel.removeField)
  65. runQuery(self.db, "CREATE TABLE Rulebook(GameName TEXT NOT NULL, GameVersion TEXT NOT NULL, RulebookName TEXT PRIMARY KEY NOT NULL, Core TEXT NOT NULL, CoreRulebook TEXT, CoreRulebookHash TEXT)")
  66. result = runQuery(self.db, 'INSERT INTO Rulebook (GameName, GameVersion, RulebookName, Core, CoreRulebook, CoreRulebookHash) VALUES ("{0}", "{1}", "{2}", "{3}", "{4}", "{5}")'.format(game, version, rbk, core, corerbk, corehash))
  67. if core=='True':
  68. self.addDataType('DataTypes')
  69. self.addDataType('Actions')
  70. self.addDataType('Currencies')
  71. self.addDataType('Character')
  72. for table in self.db.tables():
  73. createModel(self, table)
  74. if core=='False':
  75. coreFilename = 'Rulebooks/{0}/{1}/{2}.rbk'.format(game, version, corerbk)
  76. self.openCoreRulebook(coreFilename)
  77. return True
  78. def openRulebook(self, filename):
  79. self.db.setDatabaseName(filename)
  80. if not self.db.open():
  81. self.errored.emit('Error: Cannot open database: {0}'.format(self.db.lastError().text()))
  82. return False
  83. for table in self.db.tables():
  84. createModel(self, table)
  85. if not self.isCore(self.db):
  86. result = runQuery(self.db, 'SELECT GameName, GameVersion, CoreRulebook FROM Rulebook LIMIT 1')
  87. if not isError(result):
  88. rbk = result.getValue()[0]
  89. game = rbk.value('GameName')
  90. version = rbk.value('GameVersion')
  91. corerbk = rbk.value('CoreRulebook')
  92. coreFilename = 'Rulebooks/{0}/{1}/{2}.rbk'.format(game, version, corerbk)
  93. self.openCoreRulebook(coreFilename)
  94. else:
  95. message = result.getValue()
  96. self.errored.emit(message)
  97. return False
  98. self.dataTypeModel = DataType(self)
  99. self.relayAddDataType.connect(self.dataTypeModel.addDataType)
  100. self.relayRemoveDataType.connect(self.dataTypeModel.removeDataType)
  101. self.dataTypeModel.errored.connect(self.errorReceived)
  102. self.fieldModel = Field(self)
  103. self.relayAddField.connect(self.fieldModel.addField)
  104. self.relayRemoveField.connect(self.fieldModel.removeField)
  105. self.fieldModel.errored.connect(self.errorReceived)
  106. return True
  107. def openCoreRulebook(self, filename):
  108. self.dbcore.setDatabaseName(filename)
  109. if not self.dbcore.open():
  110. self.errored.emit('Error: Cannot open core database: {0}'.format(self.dbcore.lastError().text()))
  111. return False
  112. createModel(self, 'DataTypes')
  113. dtr = runQuery(self.dbcore, 'SELECT * FROM DataTypes')
  114. if not isError(dtr):
  115. datatypes = dtr.getValue()
  116. for dt in datatypes:
  117. s = 'Struct ' + dt.value(0)
  118. createModel(self, s)
  119. self.addDataType(dt.value(0))
  120. for r in range(self.models[s].rowCount()):
  121. f = self.models[s].record(r).value(0)
  122. if not ((f=='Name' or f=='Rulebook PDF Destination' or f=='Character Sheet Field')):
  123. self.addField(dt.value(0), f)
  124. return True
  125. else:
  126. return False
  127. def closeRulebook(self):
  128. self.db.close()
  129. # store a copy of the rulebooks model, clear the models, and restore the rulebook model
  130. rbk = self.models['rulebooks']
  131. self.models.clear()
  132. self.models['rulebooks'] = rbk
  133. def getModels(db):
  134. return db.models
  135. def isCore(self, db):
  136. return Func.isCore(db)
  137. #return RBK.Model.Functions.isCore(db)
  138. @pyqtSlot(str, str)
  139. def addField(self, datatype, newfield):
  140. self.relayAddField.emit(datatype, newfield)
  141. @pyqtSlot(str, str)
  142. def removeField(self, datatype, fieldrow):
  143. self.relayRemoveField.emit(datatype, fieldrow)
  144. @pyqtSlot(str)
  145. def addDataType(self, text):
  146. self.relayAddDataType.emit(text)
  147. @pyqtSlot(str)
  148. def removeDataType(self, dt):
  149. self.relayRemoveDataType.emit(dt)
  150. @pyqtSlot(str)
  151. def errorReceived(self, message):
  152. self.errored.emit(message)
  153. def getGameList(self):
  154. gameList = []
  155. gameList.append('New Game...')
  156. gamesresult = runQuery(self.rulebooks, 'SELECT DISTINCT GameName From Rulebooks ORDER BY GameName ASC')
  157. if not isError(gamesresult):
  158. games = gamesresult.getValue()
  159. print('# Games: {0}'.format(str(len(games))))
  160. for game in games:
  161. print(game.value(0))
  162. gameList.append(game.value(0))
  163. print(gameList)
  164. return gameList
  165. def getVersionList(self, gamename):
  166. versionList = []
  167. versionList.append('New Version...')
  168. versionsresult = runQuery(self.rulebooks, 'SELECT DISTINCT GameVersion FROM Rulebooks WHERE GameName = "{0}" ORDER BY GameVersion DESC'.format(gamename))
  169. if not isError(versionsresult):
  170. versions = versionsresult.getValue()
  171. for version in versions:
  172. versionList.append(str(version.value(0)))
  173. return versionList
  174. def getCoreRulebooksList(self, gamename, version):
  175. coreBookList = []
  176. if not ((gamename=='New Game...') or (version=='New Version...')):
  177. coreBookResults = runQuery(self.rulebooks, 'SELECT DISTINCT RulebookName FROM Rulebooks WHERE GameName = "{0}" AND GameVersion = "{1}" And Core = "True" ORDER BY RulebookName ASC'.format(gamename, version))
  178. if not isError(coreBookResults):
  179. corebooks = coreBookResults.getValue()
  180. for corebook in corebooks:
  181. coreBookList.append(str(corebook.value(0)))
  182. return coreBookList
  183. def getCoreRulebookHash(self, gamename, version, coreRulebook):
  184. corehash = None
  185. hashresult = runQuery(self.rulebooks, 'SELECT CoreRulebookHash FROM Rulebooks WHERE GameName = "{0}" AND GameVersion = "{1}" And RulebookName = "{2}"'.format(gamename, version, coreRulebook))
  186. print(hashresult.getValue())
  187. if not isError(hashresult):
  188. #corehash = hashresult.getValue()[0].value('CoreRulebookHash')
  189. print(corehash)
  190. return corehash