Project.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. from contextlib import closing
  2. from os.path import join
  3. from time import time
  4. from typing import List, Optional, Tuple
  5. from pycs.database.File import File
  6. from pycs.database.Label import Label
  7. from pycs.database.LabelProvider import LabelProvider
  8. from pycs.database.Model import Model
  9. class Project:
  10. """
  11. database class for projects
  12. """
  13. def __init__(self, database, row):
  14. self.database = database
  15. self.identifier = row[0]
  16. self.name = row[1]
  17. self.description = row[2]
  18. self.created = row[3]
  19. self.model_id = row[4]
  20. self.label_provider_id = row[5]
  21. self.root_folder = row[6]
  22. self.external_data = bool(row[7])
  23. self.data_folder = row[8]
  24. def model(self) -> Model:
  25. """
  26. get the model this project is associated with
  27. :return: model
  28. """
  29. return self.database.model(self.model_id)
  30. def label_provider(self) -> Optional[LabelProvider]:
  31. """
  32. get the label provider this project is associated with
  33. :return: label provider
  34. """
  35. if self.label_provider_id is not None:
  36. return self.database.label_provider(self.label_provider_id)
  37. return None
  38. def labels(self) -> List[Label]:
  39. """
  40. get a list of labels associated with this project
  41. :return: list of labels
  42. """
  43. with closing(self.database.con.cursor()) as cursor:
  44. cursor.execute('SELECT * FROM labels WHERE project = ?', [self.identifier])
  45. return list(map(
  46. lambda row: Label(self.database, row),
  47. cursor.fetchall()
  48. ))
  49. def label(self, identifier: int) -> Optional[Label]:
  50. """
  51. get a label using its unique identifier
  52. :param identifier: unique identifier
  53. :return: label
  54. """
  55. with closing(self.database.con.cursor()) as cursor:
  56. cursor.execute('SELECT * FROM labels WHERE id = ? AND project = ?',
  57. (identifier, self.identifier))
  58. row = cursor.fetchone()
  59. if row is not None:
  60. return Label(self.database, row)
  61. return None
  62. def create_label(self, name: str, reference: str = None) -> Tuple[Optional[Label], bool]:
  63. """
  64. create a label for this project. If there is already a label with the same reference
  65. in the database its name is updated.
  66. :param name: label name
  67. :param reference: label reference
  68. :return: created or edited label, insert
  69. """
  70. created = int(time())
  71. with closing(self.database.con.cursor()) as cursor:
  72. cursor.execute('''
  73. INSERT INTO labels (project, created, reference, name)
  74. VALUES (?, ?, ?, ?)
  75. ON CONFLICT (project, reference) DO
  76. UPDATE SET name = ?
  77. ''', (self.identifier, created, reference, name, name))
  78. # lastrowid is 0 if on conflict clause applies.
  79. # If this is the case we do an extra query to receive the row id.
  80. if cursor.lastrowid > 0:
  81. row_id = cursor.lastrowid
  82. insert = True
  83. else:
  84. cursor.execute('SELECT id FROM labels WHERE project = ? AND reference = ?',
  85. (self.identifier, reference))
  86. row_id = cursor.fetchone()[0]
  87. insert = False
  88. return self.label(row_id), insert
  89. def remove(self) -> None:
  90. """
  91. remove this project from the database
  92. :return:
  93. """
  94. with closing(self.database.con.cursor()) as cursor:
  95. cursor.execute('DELETE FROM projects WHERE id = ?', [self.identifier])
  96. def set_name(self, name: str) -> None:
  97. """
  98. set this projects name
  99. :param name: new name
  100. :return:
  101. """
  102. with closing(self.database.con.cursor()) as cursor:
  103. cursor.execute('UPDATE projects SET name = ? WHERE id = ?', (name, self.identifier))
  104. self.name = name
  105. def set_description(self, description: str) -> None:
  106. """
  107. set this projects description
  108. :param description: new description
  109. :return:
  110. """
  111. with closing(self.database.con.cursor()) as cursor:
  112. cursor.execute('UPDATE projects SET description = ? WHERE id = ?',
  113. (description, self.identifier))
  114. self.description = description
  115. def count_files(self) -> int:
  116. """
  117. count files associated with this project
  118. :return: count
  119. """
  120. with closing(self.database.con.cursor()) as cursor:
  121. cursor.execute('SELECT COUNT(*) FROM files WHERE project = ?', [self.identifier])
  122. return cursor.fetchone()[0]
  123. def files(self, offset=0, limit=-1) -> List[File]:
  124. """
  125. get a list of files associated with this project
  126. :param offset: file offset
  127. :param limit: file limit
  128. :return: list of files
  129. """
  130. with closing(self.database.con.cursor()) as cursor:
  131. cursor.execute('SELECT * FROM files WHERE project = ? ORDER BY id ASC LIMIT ? OFFSET ?',
  132. (self.identifier, limit, offset))
  133. return list(map(
  134. lambda row: File(self.database, row),
  135. cursor.fetchall()
  136. ))
  137. def files_without_results(self) -> List[File]:
  138. """
  139. get a list of files without associated results
  140. :return: list of files
  141. """
  142. with closing(self.database.con.cursor()) as cursor:
  143. cursor.execute('''
  144. SELECT files.*
  145. FROM files
  146. LEFT JOIN results ON files.id = results.file
  147. WHERE files.project = ? AND results.id IS NULL
  148. ORDER BY id ASC
  149. ''', [self.identifier])
  150. return list(map(
  151. lambda row: File(self.database, row),
  152. cursor.fetchall()
  153. ))
  154. def file(self, identifier) -> Optional[File]:
  155. """
  156. get a file using its unique identifier
  157. :param identifier: unique identifier
  158. :return: file
  159. """
  160. with closing(self.database.con.cursor()) as cursor:
  161. cursor.execute('SELECT * FROM files WHERE id = ? AND project = ?',
  162. (identifier, self.identifier))
  163. row = cursor.fetchone()
  164. if row is not None:
  165. return File(self.database, row)
  166. return None
  167. def add_file(self, uuid: str, file_type: str, name: str, extension: str, size: int,
  168. filename: str, frames: int = None, fps: float = None) -> Tuple[File, bool]:
  169. """
  170. add a file to this project
  171. :param uuid: unique identifier which is used for temporary files
  172. :param file_type: file type (either image or video)
  173. :param name: file name
  174. :param extension: file extension
  175. :param size: file size
  176. :param filename: actual name in filesystem
  177. :param frames: frame count
  178. :param fps: frames per second
  179. :return: file
  180. """
  181. created = int(time())
  182. path = join(self.data_folder, filename + extension)
  183. with closing(self.database.con.cursor()) as cursor:
  184. cursor.execute('''
  185. INSERT INTO files (
  186. uuid, project, type, name, extension, size, created, path, frames, fps
  187. )
  188. VALUES (
  189. ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
  190. )
  191. ON CONFLICT (project, path) DO
  192. UPDATE SET type = ?, name = ?, extension = ?, size = ?, frames = ?, fps = ?
  193. ''', (uuid, self.identifier, file_type, name, extension, size, created, path, frames,
  194. fps, file_type, name, extension, size, frames, fps))
  195. # lastrowid is 0 if on conflict clause applies.
  196. # If this is the case we do an extra query to receive the row id.
  197. if cursor.lastrowid > 0:
  198. row_id = cursor.lastrowid
  199. insert = True
  200. else:
  201. cursor.execute('SELECT id FROM files WHERE project = ? AND path = ?',
  202. (self.identifier, path))
  203. row_id = cursor.fetchone()[0]
  204. insert = False
  205. return self.file(row_id), insert