6
0

Database.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. import sqlite3
  2. from contextlib import closing
  3. from time import time
  4. from typing import Optional, Iterator
  5. from pycs.database.Collection import Collection
  6. from pycs.database.File import File
  7. from pycs.database.LabelProvider import LabelProvider
  8. from pycs.database.Model import Model
  9. from pycs.database.Project import Project
  10. from pycs.database.Result import Result
  11. from pycs.database.discovery.LabelProviderDiscovery import discover as discover_label_providers
  12. from pycs.database.discovery.ModelDiscovery import discover as discover_models
  13. class Database:
  14. """
  15. opens an sqlite database and allows to access several objects
  16. """
  17. def __init__(self, path: str = ':memory:', initialization=True, discovery=True):
  18. """
  19. opens or creates a given sqlite database and creates all required tables
  20. :param path: path to sqlite database
  21. """
  22. # save properties
  23. self.path = path
  24. # initialize database connection
  25. self.con = sqlite3.connect(path)
  26. self.con.execute("PRAGMA foreign_keys = ON")
  27. if initialization:
  28. # create tables
  29. with self:
  30. with closing(self.con.cursor()) as cursor:
  31. cursor.execute('''
  32. CREATE TABLE IF NOT EXISTS models (
  33. id INTEGER PRIMARY KEY,
  34. name TEXT NOT NULL,
  35. description TEXT,
  36. root_folder TEXT NOT NULL UNIQUE,
  37. supports TEXT NOT NULL
  38. )
  39. ''')
  40. cursor.execute('''
  41. CREATE TABLE IF NOT EXISTS label_providers (
  42. id INTEGER PRIMARY KEY,
  43. name TEXT NOT NULL,
  44. description TEXT,
  45. root_folder TEXT NOT NULL UNIQUE
  46. )
  47. ''')
  48. cursor.execute('''
  49. CREATE TABLE IF NOT EXISTS projects (
  50. id INTEGER PRIMARY KEY,
  51. name TEXT NOT NULL,
  52. description TEXT,
  53. created INTEGER NOT NULL,
  54. model INTEGER,
  55. label_provider INTEGER,
  56. root_folder TEXT NOT NULL UNIQUE,
  57. external_data BOOL NOT NULL,
  58. data_folder TEXT NOT NULL,
  59. FOREIGN KEY (model) REFERENCES models(id)
  60. ON UPDATE CASCADE ON DELETE SET NULL,
  61. FOREIGN KEY (label_provider) REFERENCES label_providers(id)
  62. ON UPDATE CASCADE ON DELETE SET NULL
  63. )
  64. ''')
  65. cursor.execute('''
  66. CREATE TABLE IF NOT EXISTS labels (
  67. id INTEGER PRIMARY KEY,
  68. project INTEGER NOT NULL,
  69. parent INTEGER,
  70. created INTEGER NOT NULL,
  71. reference TEXT,
  72. name TEXT NOT NULL,
  73. FOREIGN KEY (project) REFERENCES projects(id)
  74. ON UPDATE CASCADE ON DELETE CASCADE,
  75. FOREIGN KEY (parent) REFERENCES labels(id)
  76. ON UPDATE CASCADE ON DELETE SET NULL,
  77. UNIQUE(project, reference)
  78. )
  79. ''')
  80. cursor.execute('''
  81. CREATE TABLE IF NOT EXISTS collections (
  82. id INTEGER PRIMARY KEY,
  83. project INTEGER NOT NULL,
  84. reference TEXT NOT NULL,
  85. name TEXT NOT NULL,
  86. description TEXT,
  87. position INTEGER NOT NULL,
  88. autoselect INTEGER NOT NULL,
  89. FOREIGN KEY (project) REFERENCES projects(id)
  90. ON UPDATE CASCADE ON DELETE CASCADE,
  91. UNIQUE(project, reference)
  92. )
  93. ''')
  94. cursor.execute('''
  95. CREATE TABLE IF NOT EXISTS files (
  96. id INTEGER PRIMARY KEY,
  97. uuid TEXT NOT NULL,
  98. project INTEGER NOT NULL,
  99. collection INTEGER,
  100. type TEXT NOT NULL,
  101. name TEXT NOT NULL,
  102. extension TEXT NOT NULL,
  103. size INTEGER NOT NULL,
  104. created INTEGER NOT NULL,
  105. path TEXT NOT NULL,
  106. frames INTEGER,
  107. fps FLOAT,
  108. FOREIGN KEY (project) REFERENCES projects(id)
  109. ON UPDATE CASCADE ON DELETE CASCADE,
  110. FOREIGN KEY (collection) REFERENCES collections(id)
  111. ON UPDATE CASCADE ON DELETE SET NULL,
  112. UNIQUE(project, path)
  113. )
  114. ''')
  115. cursor.execute('''
  116. CREATE TABLE IF NOT EXISTS results (
  117. id INTEGER PRIMARY KEY,
  118. file INTEGER NOT NULL,
  119. origin TEXT NOT NULL,
  120. type TEXT NOT NULL,
  121. label INTEGER,
  122. data TEXT,
  123. FOREIGN KEY (file) REFERENCES files(id)
  124. ON UPDATE CASCADE ON DELETE CASCADE
  125. )
  126. ''')
  127. if discovery:
  128. # run discovery modules
  129. with self:
  130. discover_models(self.con)
  131. discover_label_providers(self.con)
  132. def close(self):
  133. self.con.close()
  134. def copy(self):
  135. return Database(self.path, initialization=False, discovery=False)
  136. def commit(self):
  137. self.con.commit()
  138. def __enter__(self):
  139. self.con.__enter__()
  140. return self
  141. def __exit__(self, exc_type, exc_val, exc_tb):
  142. self.con.__exit__(exc_type, exc_val, exc_tb)
  143. def models(self) -> Iterator[Model]:
  144. """
  145. get a list of all available models
  146. :return: iterator of models
  147. """
  148. with closing(self.con.cursor()) as cursor:
  149. cursor.execute('SELECT * FROM models')
  150. for row in cursor:
  151. yield Model(self, row)
  152. def model(self, identifier: int) -> Optional[Model]:
  153. """
  154. get a model using its unique identifier
  155. :param identifier: unique identifier
  156. :return: model
  157. """
  158. with closing(self.con.cursor()) as cursor:
  159. cursor.execute('SELECT * FROM models WHERE id = ?', [identifier])
  160. row = cursor.fetchone()
  161. if row is not None:
  162. return Model(self, row)
  163. return None
  164. def label_providers(self) -> Iterator[LabelProvider]:
  165. """
  166. get a list of all available label providers
  167. :return: iterator over label providers
  168. """
  169. with closing(self.con.cursor()) as cursor:
  170. cursor.execute('SELECT * FROM label_providers')
  171. for row in cursor:
  172. yield LabelProvider(self, row)
  173. def label_provider(self, identifier: int) -> Optional[LabelProvider]:
  174. """
  175. get a label provider using its unique identifier
  176. :param identifier: unique identifier
  177. :return: label provider
  178. """
  179. with closing(self.con.cursor()) as cursor:
  180. cursor.execute('SELECT * FROM label_providers WHERE id = ?', [identifier])
  181. row = cursor.fetchone()
  182. if row is not None:
  183. return LabelProvider(self, row)
  184. return None
  185. def projects(self) -> Iterator[Project]:
  186. """
  187. get a list of all available projects
  188. :return: iterator over projects
  189. """
  190. with closing(self.con.cursor()) as cursor:
  191. cursor.execute('SELECT * FROM projects')
  192. for row in cursor:
  193. yield Project(self, row)
  194. def project(self, identifier: int) -> Optional[Project]:
  195. """
  196. get a project using its unique identifier
  197. :param identifier: unique identifier
  198. :return: project
  199. """
  200. with closing(self.con.cursor()) as cursor:
  201. cursor.execute('SELECT * FROM projects WHERE id = ?', [identifier])
  202. row = cursor.fetchone()
  203. if row is not None:
  204. return Project(self, row)
  205. return None
  206. def create_project(self,
  207. name: str,
  208. description: str,
  209. model: Model,
  210. label_provider: Optional[LabelProvider],
  211. root_folder: str,
  212. external_data: bool,
  213. data_folder: str):
  214. """
  215. insert a project into the database
  216. :param name: project name
  217. :param description: project description
  218. :param model: used model
  219. :param label_provider: used label provider (optional)
  220. :param root_folder: path to project folder
  221. :param external_data: whether an external data directory is used
  222. :param data_folder: path to data folder
  223. :return: created project
  224. """
  225. # prepare some values
  226. created = int(time())
  227. label_provider_id = label_provider.identifier if label_provider is not None else None
  228. # insert statement
  229. with closing(self.con.cursor()) as cursor:
  230. cursor.execute('''
  231. INSERT INTO projects (
  232. name, description, created, model, label_provider, root_folder, external_data,
  233. data_folder
  234. )
  235. VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  236. ''', (name, description, created, model.identifier, label_provider_id, root_folder,
  237. external_data, data_folder))
  238. return self.project(cursor.lastrowid)
  239. def collection(self, identifier: int) -> Optional[Collection]:
  240. """
  241. get a collection using its unique identifier
  242. :param identifier: unique identifier
  243. :return: collection
  244. """
  245. with closing(self.con.cursor()) as cursor:
  246. cursor.execute('SELECT * FROM collections WHERE id = ?', [identifier])
  247. row = cursor.fetchone()
  248. if row is not None:
  249. return Collection(self, row)
  250. return None
  251. def file(self, identifier) -> Optional[File]:
  252. """
  253. get a file using its unique identifier
  254. :param identifier: unique identifier
  255. :return: file
  256. """
  257. with closing(self.con.cursor()) as cursor:
  258. cursor.execute('SELECT * FROM files WHERE id = ?', [identifier])
  259. row = cursor.fetchone()
  260. if row is not None:
  261. return File(self, row)
  262. return None
  263. def result(self, identifier) -> Optional[Result]:
  264. """
  265. get a result using its unique identifier
  266. :param identifier: unique identifier
  267. :return: result
  268. """
  269. with closing(self.con.cursor()) as cursor:
  270. cursor.execute('SELECT * FROM results WHERE id = ?', [identifier])
  271. row = cursor.fetchone()
  272. if row is not None:
  273. return Result(self, row)
  274. return None