A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

211 строки
7.4 KiB

  1. # Copyright (C) 2009-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. # SOFTWARE.
  20. import urllib.parse
  21. from threading import Event
  22. from time import time
  23. from earwigbot.wiki.copyvios.markov import EMPTY, EMPTY_INTERSECTION
  24. __all__ = ["CopyvioSource", "CopyvioCheckResult"]
  25. class CopyvioSource:
  26. """
  27. **EarwigBot: Wiki Toolset: Copyvio Source**
  28. A class that represents a single possible source of a copyright violation,
  29. i.e., a URL.
  30. *Attributes:*
  31. - :py:attr:`url`: the URL of the source
  32. - :py:attr:`confidence`: the confidence of a violation, between 0 and 1
  33. - :py:attr:`chains`: a 2-tuple of the source chain and the delta chain
  34. - :py:attr:`skipped`: whether this URL was skipped during the check
  35. - :py:attr:`excluded`: whether this URL was in the exclusions list
  36. """
  37. def __init__(
  38. self,
  39. workspace,
  40. url,
  41. headers=None,
  42. timeout=5,
  43. parser_args=None,
  44. search_config=None,
  45. ):
  46. self.workspace = workspace
  47. self.url = url
  48. self.headers = headers
  49. self.timeout = timeout
  50. self.parser_args = parser_args
  51. self.search_config = search_config
  52. self.confidence = 0.0
  53. self.chains = (EMPTY, EMPTY_INTERSECTION)
  54. self.skipped = False
  55. self.excluded = False
  56. self._event1 = Event()
  57. self._event2 = Event()
  58. self._event2.set()
  59. def __repr__(self):
  60. """Return the canonical string representation of the source."""
  61. res = (
  62. "CopyvioSource(url={0!r}, confidence={1!r}, skipped={2!r}, "
  63. "excluded={3!r})"
  64. )
  65. return res.format(self.url, self.confidence, self.skipped, self.excluded)
  66. def __str__(self):
  67. """Return a nice string representation of the source."""
  68. if self.excluded:
  69. return f"<CopyvioSource ({self.url}, excluded)>"
  70. if self.skipped:
  71. return f"<CopyvioSource ({self.url}, skipped)>"
  72. res = "<CopyvioSource ({0} with {1} conf)>"
  73. return res.format(self.url, self.confidence)
  74. @property
  75. def domain(self):
  76. """The source URL's domain name, or None."""
  77. return urllib.parse.urlparse(self.url).netloc or None
  78. def start_work(self):
  79. """Mark this source as being worked on right now."""
  80. self._event2.clear()
  81. self._event1.set()
  82. def update(self, confidence, source_chain, delta_chain):
  83. """Fill out the confidence and chain information inside this source."""
  84. self.confidence = confidence
  85. self.chains = (source_chain, delta_chain)
  86. def finish_work(self):
  87. """Mark this source as finished."""
  88. self._event2.set()
  89. def skip(self):
  90. """Deactivate this source without filling in the relevant data."""
  91. if self._event1.is_set():
  92. return
  93. self.skipped = True
  94. self._event1.set()
  95. def join(self, until):
  96. """Block until this violation result is filled out."""
  97. for event in [self._event1, self._event2]:
  98. if until:
  99. timeout = until - time()
  100. if timeout <= 0:
  101. return
  102. event.wait(timeout)
  103. else:
  104. event.wait()
  105. class CopyvioCheckResult:
  106. """
  107. **EarwigBot: Wiki Toolset: Copyvio Check Result**
  108. A class holding information about the results of a copyvio check.
  109. *Attributes:*
  110. - :py:attr:`violation`: ``True`` if this is a violation, else ``False``
  111. - :py:attr:`sources`: a list of CopyvioSources, sorted by confidence
  112. - :py:attr:`best`: the best matching CopyvioSource, or ``None``
  113. - :py:attr:`confidence`: the best matching source's confidence, or 0
  114. - :py:attr:`url`: the best matching source's URL, or ``None``
  115. - :py:attr:`queries`: the number of queries used to reach a result
  116. - :py:attr:`time`: the amount of time the check took to complete
  117. - :py:attr:`article_chain`: the MarkovChain of the article text
  118. - :py:attr:`possible_miss`: whether some URLs might have been missed
  119. """
  120. def __init__(
  121. self,
  122. violation,
  123. sources,
  124. queries,
  125. check_time,
  126. article_chain,
  127. possible_miss,
  128. included_sources=None,
  129. unified_confidence=None,
  130. ):
  131. assert isinstance(sources, list)
  132. self.violation = violation
  133. self.sources = sources
  134. self.queries = queries
  135. self.time = check_time
  136. self.article_chain = article_chain
  137. self.possible_miss = possible_miss
  138. self.included_sources = included_sources if included_sources else []
  139. self.unified_confidence = unified_confidence
  140. def __repr__(self):
  141. """Return the canonical string representation of the result."""
  142. res = "CopyvioCheckResult(violation={0!r}, sources={1!r}, queries={2!r}, time={3!r})"
  143. return res.format(self.violation, self.sources, self.queries, self.time)
  144. def __str__(self):
  145. """Return a nice string representation of the result."""
  146. res = "<CopyvioCheckResult ({0} with best {1})>"
  147. return res.format(self.violation, self.best)
  148. @property
  149. def best(self):
  150. """The best known source, or None if no sources exist."""
  151. return self.sources[0] if self.sources else None
  152. @property
  153. def confidence(self):
  154. """The confidence of the best source, or 0 if no sources exist."""
  155. if self.unified_confidence is not None:
  156. return self.unified_confidence
  157. if self.best:
  158. return self.best.confidence
  159. return 0.0
  160. @property
  161. def url(self):
  162. """The URL of the best source, or None if no sources exist."""
  163. return self.best.url if self.best else None
  164. def get_log_message(self, title):
  165. """Build a relevant log message for this copyvio check result."""
  166. if not self.sources:
  167. log = "No violation for [[{0}]] (no sources; {1} queries; {2} seconds)"
  168. return log.format(title, self.queries, self.time)
  169. log = "{0} for [[{1}]] (best: {2} ({3} confidence); {4} sources; {5} queries; {6} seconds)"
  170. is_vio = "Violation detected" if self.violation else "No violation"
  171. return log.format(
  172. is_vio,
  173. title,
  174. self.url,
  175. self.confidence,
  176. len(self.sources),
  177. self.queries,
  178. self.time,
  179. )