comment_sheet.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from __future__ import absolute_import
  2. # Copyright (c) 2010-2019 openpyxl
  3. ## Incomplete!
  4. from openpyxl.descriptors.serialisable import Serialisable
  5. from openpyxl.descriptors import (
  6. Typed,
  7. Float,
  8. Integer,
  9. Set,
  10. String,
  11. Bool,
  12. )
  13. from openpyxl.descriptors.excel import Guid, ExtensionList
  14. from openpyxl.descriptors.sequence import NestedSequence
  15. from openpyxl.utils.indexed_list import IndexedList
  16. from openpyxl.xml.constants import SHEET_MAIN_NS
  17. from openpyxl.xml.functions import tostring
  18. from openpyxl.cell.text import Text
  19. #from openpyxl.worksheet.ole import ObjectAnchor
  20. from .author import AuthorList
  21. from .comments import Comment
  22. from .shape_writer import ShapeWriter
  23. class Properties(Serialisable):
  24. locked = Bool(allow_none=True)
  25. defaultSize = Bool(allow_none=True)
  26. _print = Bool(allow_none=True)
  27. disabled = Bool(allow_none=True)
  28. uiObject = Bool(allow_none=True)
  29. autoFill = Bool(allow_none=True)
  30. autoLine = Bool(allow_none=True)
  31. altText = String(allow_none=True)
  32. textHAlign = Set(values=(['left', 'center', 'right', 'justify', 'distributed']))
  33. textVAlign = Set(values=(['top', 'center', 'bottom', 'justify', 'distributed']))
  34. lockText = Bool(allow_none=True)
  35. justLastX = Bool(allow_none=True)
  36. autoScale = Bool(allow_none=True)
  37. rowHidden = Bool(allow_none=True)
  38. colHidden = Bool(allow_none=True)
  39. #anchor = Typed(expected_type=ObjectAnchor, )
  40. __elements__ = ('anchor',)
  41. def __init__(self,
  42. locked=None,
  43. defaultSize=None,
  44. _print=None,
  45. disabled=None,
  46. uiObject=None,
  47. autoFill=None,
  48. autoLine=None,
  49. altText=None,
  50. textHAlign=None,
  51. textVAlign=None,
  52. lockText=None,
  53. justLastX=None,
  54. autoScale=None,
  55. rowHidden=None,
  56. colHidden=None,
  57. anchor=None,
  58. ):
  59. self.locked = locked
  60. self.defaultSize = defaultSize
  61. self._print = _print
  62. self.disabled = disabled
  63. self.uiObject = uiObject
  64. self.autoFill = autoFill
  65. self.autoLine = autoLine
  66. self.altText = altText
  67. self.textHAlign = textHAlign
  68. self.textVAlign = textVAlign
  69. self.lockText = lockText
  70. self.justLastX = justLastX
  71. self.autoScale = autoScale
  72. self.rowHidden = rowHidden
  73. self.colHidden = colHidden
  74. self.anchor = anchor
  75. class CommentRecord(Serialisable):
  76. tagname = "comment"
  77. ref = String()
  78. authorId = Integer()
  79. guid = Guid(allow_none=True)
  80. shapeId = Integer(allow_none=True)
  81. text = Typed(expected_type=Text)
  82. commentPr = Typed(expected_type=Properties, allow_none=True)
  83. author = String(allow_none=True)
  84. __elements__ = ('text', 'commentPr')
  85. __attrs__ = ('ref', 'authorId', 'guid', 'shapeId')
  86. def __init__(self,
  87. ref="",
  88. authorId=0,
  89. guid=None,
  90. shapeId=0,
  91. text=None,
  92. commentPr=None,
  93. author=None,
  94. height=79,
  95. width=144
  96. ):
  97. self.ref = ref
  98. self.authorId = authorId
  99. self.guid = guid
  100. self.shapeId = shapeId
  101. if text is None:
  102. text = Text()
  103. self.text = text
  104. self.commentPr = commentPr
  105. self.author = author
  106. self.height = height
  107. self.width = width
  108. @classmethod
  109. def from_cell(cls, cell):
  110. """
  111. Class method to convert cell comment
  112. """
  113. comment = cell._comment
  114. ref = cell.coordinate
  115. self = cls(ref=ref, author=comment.author)
  116. self.text.t = comment.content
  117. self.height = comment.height
  118. self.width = comment.width
  119. return self
  120. @property
  121. def content(self):
  122. """
  123. Remove all inline formatting and stuff
  124. """
  125. return self.text.content
  126. class CommentSheet(Serialisable):
  127. tagname = "comments"
  128. authors = Typed(expected_type=AuthorList)
  129. commentList = NestedSequence(expected_type=CommentRecord, count=0)
  130. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  131. _id = None
  132. _path = "/xl/comments/comment{0}.xml"
  133. mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
  134. _rel_type = "comments"
  135. _rel_id = None
  136. __elements__ = ('authors', 'commentList')
  137. def __init__(self,
  138. authors=None,
  139. commentList=None,
  140. extLst=None,
  141. ):
  142. self.authors = authors
  143. self.commentList = commentList
  144. def to_tree(self):
  145. tree = super(CommentSheet, self).to_tree()
  146. tree.set("xmlns", SHEET_MAIN_NS)
  147. return tree
  148. @property
  149. def comments(self):
  150. """
  151. Return a dictionary of comments keyed by coord
  152. """
  153. authors = self.authors.author
  154. for c in self.commentList:
  155. yield c.ref, Comment(c.content, authors[c.authorId], c.height, c.width)
  156. @classmethod
  157. def from_comments(cls, comments):
  158. """
  159. Create a comment sheet from a list of comments for a particular worksheet
  160. """
  161. authors = IndexedList()
  162. # dedupe authors and get indexes
  163. for comment in comments:
  164. comment.authorId = authors.add(comment.author)
  165. return cls(authors=AuthorList(authors), commentList=comments)
  166. def write_shapes(self, vml=None):
  167. """
  168. Create the VML for comments
  169. """
  170. sw = ShapeWriter(self.comments)
  171. return sw.write(vml)
  172. @property
  173. def path(self):
  174. """
  175. Return path within the archive
  176. """
  177. return self._path.format(self._id)