info_model.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. """
  2. /***************************************************************************
  3. Name : DB Manager
  4. Description : Database manager plugin for QGIS
  5. Date : May 23, 2011
  6. copyright : (C) 2011 by Giuseppe Sucameli
  7. email : brush.tyler@gmail.com
  8. ***************************************************************************/
  9. /***************************************************************************
  10. * *
  11. * This program is free software; you can redistribute it and/or modify *
  12. * it under the terms of the GNU General Public License as published by *
  13. * the Free Software Foundation; either version 2 of the License, or *
  14. * (at your option) any later version. *
  15. * *
  16. ***************************************************************************/
  17. """
  18. from qgis.PyQt.QtWidgets import QApplication
  19. from .html_elems import HtmlContent, HtmlSection, HtmlParagraph, HtmlList, HtmlTable, HtmlTableHeader, HtmlTableCol
  20. class DatabaseInfo:
  21. def __init__(self, db):
  22. self.db = db
  23. def __del__(self):
  24. self.db = None
  25. def generalInfo(self):
  26. info = self.db.connector.getInfo()
  27. tbl = [
  28. (QApplication.translate("DBManagerPlugin", "Server version: "), info[0])
  29. ]
  30. return HtmlTable(tbl)
  31. def connectionDetails(self):
  32. tbl = [
  33. (QApplication.translate("DBManagerPlugin", "Host:"), self.db.connector.host),
  34. (QApplication.translate("DBManagerPlugin", "User:"), self.db.connector.user)
  35. ]
  36. return HtmlTable(tbl)
  37. def spatialInfo(self):
  38. ret = []
  39. info = self.db.connector.getSpatialInfo()
  40. if info is None:
  41. return
  42. tbl = [
  43. (QApplication.translate("DBManagerPlugin", "Library:"), info[0]),
  44. ("GEOS:", info[1]),
  45. ("Proj:", info[2])
  46. ]
  47. ret.append(HtmlTable(tbl))
  48. if not self.db.connector.has_geometry_columns:
  49. ret.append(HtmlParagraph(
  50. QApplication.translate("DBManagerPlugin", "<warning> geometry_columns table doesn't exist!\n"
  51. "This table is essential for many GIS applications for enumeration of tables.")))
  52. return ret
  53. def privilegesDetails(self):
  54. details = self.db.connector.getDatabasePrivileges()
  55. lst = []
  56. if details[0]:
  57. lst.append(QApplication.translate("DBManagerPlugin", "create new schemas"))
  58. if details[1]:
  59. lst.append(QApplication.translate("DBManagerPlugin", "create temporary tables"))
  60. return HtmlList(lst)
  61. def toHtml(self):
  62. if self.db is None:
  63. return HtmlSection(QApplication.translate("DBManagerPlugin", 'Not connected')).toHtml()
  64. ret = []
  65. # connection details
  66. conn_details = self.connectionDetails()
  67. if conn_details is None:
  68. pass
  69. else:
  70. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Connection details'), conn_details))
  71. # database information
  72. general_info = self.generalInfo()
  73. if general_info is None:
  74. pass
  75. else:
  76. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'General info'), general_info))
  77. # has spatial enabled?
  78. spatial_info = self.spatialInfo()
  79. if spatial_info is None:
  80. pass
  81. else:
  82. typename = self.db.connection().typeNameString()
  83. spatial_info = HtmlContent(spatial_info)
  84. if not spatial_info.hasContents():
  85. spatial_info = QApplication.translate("DBManagerPlugin", '<warning> {0} support not enabled!').format(typename)
  86. ret.append(HtmlSection(typename, spatial_info))
  87. # privileges
  88. priv_details = self.privilegesDetails()
  89. if priv_details is None:
  90. pass
  91. else:
  92. priv_details = HtmlContent(priv_details)
  93. if not priv_details.hasContents():
  94. priv_details = QApplication.translate("DBManagerPlugin", '<warning> This user has no privileges!')
  95. else:
  96. priv_details = [QApplication.translate("DBManagerPlugin", "User has privileges:"), priv_details]
  97. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Privileges'), priv_details))
  98. return HtmlContent(ret).toHtml()
  99. class SchemaInfo:
  100. def __init__(self, schema):
  101. self.schema = schema
  102. def __del__(self):
  103. self.schema = None
  104. def generalInfo(self):
  105. tbl = [
  106. # ("Tables:", self.schema.tableCount)
  107. ]
  108. if self.schema.owner:
  109. tbl.append((QApplication.translate("DBManagerPlugin", "Owner:"), self.schema.owner))
  110. if self.schema.comment:
  111. tbl.append((QApplication.translate("DBManagerPlugin", "Comment:"), self.schema.comment))
  112. return HtmlTable(tbl)
  113. def privilegesDetails(self):
  114. details = self.schema.database().connector.getSchemaPrivileges(self.schema.name)
  115. lst = []
  116. if details[0]:
  117. lst.append(QApplication.translate("DBManagerPlugin", "create new objects"))
  118. if details[1]:
  119. lst.append(QApplication.translate("DBManagerPlugin", "access objects"))
  120. return HtmlList(lst)
  121. def toHtml(self):
  122. ret = []
  123. general_info = self.generalInfo()
  124. if general_info is None:
  125. pass
  126. else:
  127. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Schema details'), general_info))
  128. priv_details = self.privilegesDetails()
  129. if priv_details is None:
  130. pass
  131. else:
  132. priv_details = HtmlContent(priv_details)
  133. if not priv_details.hasContents():
  134. priv_details = QApplication.translate("DBManagerPlugin",
  135. '<warning> This user has no privileges to access this schema!')
  136. else:
  137. priv_details = [QApplication.translate("DBManagerPlugin", "User has privileges:"), priv_details]
  138. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Privileges'), priv_details))
  139. return HtmlContent(ret).toHtml()
  140. class TableInfo:
  141. def __init__(self, table):
  142. self.table = table
  143. def __del__(self):
  144. self.table = None
  145. def generalInfo(self):
  146. if self.table.rowCount is None:
  147. # row count information is not displayed yet, so just block
  148. # table signals to avoid double refreshing (infoViewer->refreshRowCount->tableChanged->infoViewer)
  149. self.table.blockSignals(True)
  150. self.table.refreshRowCount()
  151. self.table.blockSignals(False)
  152. tbl = [
  153. (QApplication.translate("DBManagerPlugin", "Relation type:"),
  154. QApplication.translate("DBManagerPlugin", "View") if self.table.isView else QApplication.translate(
  155. "DBManagerPlugin", "Table")),
  156. (QApplication.translate("DBManagerPlugin", "Rows:"),
  157. self.table.rowCount if self.table.rowCount is not None else QApplication.translate("DBManagerPlugin",
  158. 'Unknown (<a href="action:rows/count">find out</a>)'))
  159. ]
  160. if self.table.comment:
  161. tbl.append((QApplication.translate("DBManagerPlugin", "Comment:"), self.table.comment))
  162. return HtmlTable(tbl)
  163. def spatialInfo(self): # implemented in subclasses
  164. return None
  165. def fieldsDetails(self):
  166. tbl = []
  167. # define the table header
  168. header = (
  169. "#", QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Type"),
  170. QApplication.translate("DBManagerPlugin", "Null"), QApplication.translate("DBManagerPlugin", "Default"))
  171. tbl.append(HtmlTableHeader(header))
  172. # add table contents
  173. for fld in self.table.fields():
  174. is_null_txt = "N" if fld.notNull else "Y"
  175. # make primary key field underlined
  176. attrs = {"class": "underline"} if fld.primaryKey else None
  177. name = HtmlTableCol(fld.name, attrs)
  178. tbl.append((fld.num, name, fld.type2String(), is_null_txt, fld.default2String()))
  179. return HtmlTable(tbl, {"class": "header"})
  180. def constraintsDetails(self):
  181. if self.table.constraints() is None or len(self.table.constraints()) <= 0:
  182. return None
  183. tbl = []
  184. # define the table header
  185. header = (QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Type"),
  186. QApplication.translate("DBManagerPlugin", "Column(s)"))
  187. tbl.append(HtmlTableHeader(header))
  188. # add table contents
  189. for con in self.table.constraints():
  190. # get the fields the constraint is defined on
  191. cols = [p[1].name if p[1] is not None else "??? (#%d)" % p[0] for p in iter(list(con.fields().items()))]
  192. tbl.append((con.name, con.type2String(), '\n'.join(cols)))
  193. return HtmlTable(tbl, {"class": "header"})
  194. def indexesDetails(self):
  195. if self.table.indexes() is None or len(self.table.indexes()) <= 0:
  196. return None
  197. tbl = []
  198. # define the table header
  199. header = (
  200. QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Column(s)"))
  201. tbl.append(HtmlTableHeader(header))
  202. # add table contents
  203. for idx in self.table.indexes():
  204. # get the fields the index is defined on
  205. cols = [p[1].name if p[1] is not None else "??? (#%d)" % p[0] for p in iter(list(idx.fields().items()))]
  206. tbl.append((idx.name, '\n'.join(cols)))
  207. return HtmlTable(tbl, {"class": "header"})
  208. def triggersDetails(self):
  209. if self.table.triggers() is None or len(self.table.triggers()) <= 0:
  210. return None
  211. tbl = []
  212. # define the table header
  213. header = (
  214. QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Function"))
  215. tbl.append(HtmlTableHeader(header))
  216. # add table contents
  217. for trig in self.table.triggers():
  218. name = '%(name)s (<a href="action:trigger/%(name)s/%(action)s">%(action)s</a>)' % {"name": trig.name,
  219. "action": "delete"}
  220. tbl.append((name, trig.function.replace('<', '&lt;')))
  221. return HtmlTable(tbl, {"class": "header"})
  222. def getViewDefinition(self):
  223. if not self.table.isView:
  224. return None
  225. return self.table.database().connector.getViewDefinition((self.table.schemaName(), self.table.name))
  226. def getTableInfo(self):
  227. ret = []
  228. general_info = self.generalInfo()
  229. if general_info is None:
  230. pass
  231. else:
  232. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'General info'), general_info))
  233. # spatial info
  234. spatial_info = self.spatialInfo()
  235. if spatial_info is None:
  236. pass
  237. else:
  238. spatial_info = HtmlContent(spatial_info)
  239. if not spatial_info.hasContents():
  240. spatial_info = QApplication.translate("DBManagerPlugin", '<warning> This is not a spatial table.')
  241. ret.append(HtmlSection(self.table.database().connection().typeNameString(), spatial_info))
  242. # fields
  243. fields_details = self.fieldsDetails()
  244. if fields_details is None:
  245. pass
  246. else:
  247. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Fields'), fields_details))
  248. # constraints
  249. constraints_details = self.constraintsDetails()
  250. if constraints_details is None:
  251. pass
  252. else:
  253. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Constraints'), constraints_details))
  254. # indexes
  255. indexes_details = self.indexesDetails()
  256. if indexes_details is None:
  257. pass
  258. else:
  259. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Indexes'), indexes_details))
  260. # triggers
  261. triggers_details = self.triggersDetails()
  262. if triggers_details is None:
  263. pass
  264. else:
  265. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Triggers'), triggers_details))
  266. return ret
  267. def getViewInfo(self):
  268. if not self.table.isView:
  269. return []
  270. ret = self.getTableInfo()
  271. # view definition
  272. view_def = self.getViewDefinition()
  273. if view_def is None:
  274. pass
  275. else:
  276. ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'View definition'), view_def))
  277. return ret
  278. def toHtml(self):
  279. if self.table.isView:
  280. ret = self.getViewInfo()
  281. else:
  282. ret = self.getTableInfo()
  283. return HtmlContent(ret).toHtml()
  284. class VectorTableInfo(TableInfo):
  285. def __init__(self, table):
  286. TableInfo.__init__(self, table)
  287. def spatialInfo(self):
  288. ret = []
  289. if self.table.geomType is None:
  290. return ret
  291. tbl = [
  292. (QApplication.translate("DBManagerPlugin", "Column:"), self.table.geomColumn),
  293. (QApplication.translate("DBManagerPlugin", "Geometry:"), self.table.geomType)
  294. ]
  295. # only if we have info from geometry_columns
  296. if self.table.geomDim:
  297. tbl.append((QApplication.translate("DBManagerPlugin", "Dimension:"), self.table.geomDim))
  298. srid = self.table.srid if self.table.srid not in (None, 0) else -1
  299. sr_info = self.table.database().connector.getSpatialRefInfo(srid) if srid != -1 else QApplication.translate(
  300. "DBManagerPlugin", "Undefined")
  301. if sr_info:
  302. tbl.append((QApplication.translate("DBManagerPlugin", "Spatial ref:"), "%s (%d)" % (sr_info, srid)))
  303. # estimated extent
  304. if not self.table.isView:
  305. if self.table.estimatedExtent is None:
  306. # estimated extent information is not displayed yet, so just block
  307. # table signals to avoid double refreshing (infoViewer->refreshEstimatedExtent->tableChanged->infoViewer)
  308. self.table.blockSignals(True)
  309. self.table.refreshTableEstimatedExtent()
  310. self.table.blockSignals(False)
  311. if self.table.estimatedExtent is not None and self.table.estimatedExtent[0] is not None:
  312. if isinstance(self.table.estimatedExtent, list):
  313. estimated_extent_str = ', '.join('%.5f' % e for e in self.table.estimatedExtent)
  314. else:
  315. estimated_extent_str = '%.5f, %.5f - %.5f, %.5f' % self.table.estimatedExtent
  316. tbl.append((QApplication.translate("DBManagerPlugin", "Estimated extent:"), estimated_extent_str))
  317. # extent
  318. if self.table.extent is not None and self.table.extent[0] is not None:
  319. if isinstance(self.table.extent, list):
  320. extent_str = ', '.join('%.5f' % e for e in self.table.extent)
  321. else:
  322. extent_str = '%.5f, %.5f - %.5f, %.5f' % self.table.extent
  323. else:
  324. extent_str = QApplication.translate("DBManagerPlugin",
  325. '(unknown) (<a href="action:extent/get">find out</a>)')
  326. tbl.append((QApplication.translate("DBManagerPlugin", "Extent:"), extent_str))
  327. ret.append(HtmlTable(tbl))
  328. # is there an entry in geometry_columns?
  329. if self.table.geomType.lower() == 'geometry':
  330. ret.append(HtmlParagraph(
  331. QApplication.translate("DBManagerPlugin", "<warning> There is no entry in geometry_columns!")))
  332. # find out whether the geometry column has spatial index on it
  333. if not self.table.isView:
  334. if not self.table.hasSpatialIndex():
  335. ret.append(HtmlParagraph(QApplication.translate("DBManagerPlugin",
  336. '<warning> No spatial index defined (<a href="action:spatialindex/create">create it</a>)')))
  337. return ret
  338. class RasterTableInfo(TableInfo):
  339. def __init__(self, table):
  340. TableInfo.__init__(self, table)
  341. def spatialInfo(self):
  342. ret = []
  343. if self.table.geomType is None:
  344. return ret
  345. tbl = [
  346. (QApplication.translate("DBManagerPlugin", "Column:"), self.table.geomColumn),
  347. (QApplication.translate("DBManagerPlugin", "Geometry:"), self.table.geomType)
  348. ]
  349. # only if we have info from geometry_columns
  350. srid = self.table.srid if self.table.srid is not None else -1
  351. sr_info = self.table.database().connector.getSpatialRefInfo(srid) if srid != -1 else QApplication.translate(
  352. "DBManagerPlugin", "Undefined")
  353. if sr_info:
  354. tbl.append((QApplication.translate("DBManagerPlugin", "Spatial ref:"), "%s (%d)" % (sr_info, srid)))
  355. # extent
  356. if self.table.extent is not None and self.table.extent[0] is not None:
  357. extent_str = '%.5f, %.5f - %.5f, %.5f' % self.table.extent
  358. else:
  359. extent_str = QApplication.translate("DBManagerPlugin",
  360. '(unknown) (<a href="action:extent/get">find out</a>)')
  361. tbl.append((QApplication.translate("DBManagerPlugin", "Extent:"), extent_str))
  362. ret.append(HtmlTable(tbl))
  363. return ret