You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

summarypage.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. #
  4. # summarypage.py
  5. #
  6. # Copyright 2017 Jonathan Golder <jonathan@golderweb.de>
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  21. # MA 02110-1301, USA.
  22. #
  23. #
  24. """
  25. Provides classes for handling Charts summary page
  26. """
  27. from datetime import datetime, timedelta
  28. # import pywikibot
  29. import mwparserfromhell as mwparser
  30. import jogobot
  31. from countrylist import CountryList, CountryListError
  32. class SummaryPage():
  33. """
  34. Handles summary page related actions
  35. """
  36. def __init__( self, text, force_reload=False ):
  37. """
  38. Create Instance
  39. @param text: Page Text of summarypage
  40. @type text: str
  41. @param force-reload: If given, countrylists will be always parsed
  42. regardless if needed or not
  43. @type force-reload: bool
  44. """
  45. # Parse Text with mwparser
  46. self.wikicode = mwparser.parse( text )
  47. # Force parsing of countrylist
  48. self.force_reload = force_reload
  49. def treat( self ):
  50. """
  51. Handles parsing/editing of text
  52. """
  53. # Get mwparser.template objects for Template "/Eintrag"
  54. for entry in self.wikicode.filter_templates( matches="/Eintrag" ):
  55. # Instantiate SummaryPageEntry-object
  56. summarypageentry = SummaryPageEntry(entry,
  57. force_reload=self.force_reload)
  58. # Treat SummaryPageEntry-object
  59. summarypageentry.treat()
  60. # Get result
  61. # We need to replace origninal entry since objectid changes due to
  62. # recreation of template object and reassignment won't be reflected
  63. self.wikicode.replace(entry, summarypageentry.get_entry().template)
  64. def get_new_text( self ):
  65. """
  66. If writing page is needed, return new text, otherwise false
  67. """
  68. # Get information wether writing is needed from class attribute
  69. if SummaryPageEntry.write_needed:
  70. # Convert wikicode back to string and return
  71. return str( self.wikicode )
  72. return False
  73. class SummaryPageEntry():
  74. """
  75. Provides a generic wrapper for summary page entry template
  76. """
  77. write_needed = False
  78. def __init__( self, entry, force_reload=False ):
  79. """
  80. Constructor
  81. @param entry: Entry template of summarypage entry
  82. @type text: mwparser.template
  83. @param force-reload: If given, countrylists will be always parsed
  84. regardless if needed or not
  85. @type force-reload: bool
  86. """
  87. self.old_entry = SummaryPageEntryTemplate( entry )
  88. self.new_entry = SummaryPageEntryTemplate( )
  89. # Force parsing of countrylist
  90. self.force_reload = force_reload
  91. def treat( self ):
  92. """
  93. Controls parsing/update-sequence of entry
  94. """
  95. # Get CountryList-Object
  96. self.get_countrylist()
  97. # Check if parsing country list is needed
  98. if( self.countrylist.parsed):
  99. self.correct_chartein()
  100. self.update_params()
  101. self.is_write_needed()
  102. def get_countrylist( self ):
  103. """
  104. Get the CountryList-Object for current entry
  105. """
  106. # Get wikilink to related countrylist
  107. self.get_countrylist_wikilink()
  108. # Get saved revision of related countrylist
  109. self.get_countrylist_saved_revid()
  110. # Get current year
  111. current_year = datetime.now().year
  112. # If list is from last year, replace year
  113. if (current_year - 1) in self.countrylist_wikilink.title:
  114. jogobot.output( "Trying to use new years list for [[{page}]]"
  115. .format( page=self.countrylist_wikilink.title ) )
  116. self.countrylist_wikilink.title.replace( (current_year - 1),
  117. current_year )
  118. # Try to get current years list
  119. try:
  120. self.countrylist = CountryList( self.countrylist_wikilink )
  121. self.maybe_parse_countrylist()
  122. # Maybe fallback to last years list
  123. except CountryListError:
  124. # If list is from last year, replace year
  125. if (current_year ) in self.countrylist_wikilink.title:
  126. jogobot.output( "New years list for [[{page}]] does not " +
  127. "exist, fall back to old list!".format(
  128. page=self.countrylist_wikilink.title ) )
  129. self.countrylist_wikilink.title.replace( current_year,
  130. (current_year - 1) )
  131. self.countrylist = CountryList( self.countrylist_wikilink )
  132. self.maybe_parse_countrylist()
  133. if not self.countrylist:
  134. raise SummaryPageEntryError( "CountryList does not exists!" )
  135. def maybe_parse_countrylist( self ):
  136. """
  137. Parse countrylist if page-object exists and if parsing is needed or
  138. param -force-reload is set
  139. """
  140. # Fast return if no countrylist-object
  141. if not self.countrylist:
  142. return
  143. # Parse if needed or forced
  144. if( self.countrylist.is_parsing_needed( self.countrylist_revid ) or
  145. self.force_reload ):
  146. self.countrylist.parse()
  147. def get_countrylist_wikilink( self ):
  148. """
  149. Load wikilink to related countrylist
  150. """
  151. if self.old_entry.Liste:
  152. try:
  153. self.countrylist_wikilink = next(
  154. self.old_entry.Liste.ifilter_wikilinks() )
  155. except StopIteration:
  156. raise SummaryPageEntryError(
  157. "Parameter Liste does not contain valid wikilink!" )
  158. else:
  159. raise SummaryPageEntryError( "Parameter Liste is not present!")
  160. def get_countrylist_saved_revid( self ):
  161. """
  162. Load saved revid of related countrylist if Param is present
  163. """
  164. if self.old_entry.Liste_Revision:
  165. self.countrylist_revid = int(self.old_entry.Liste_Revision.strip())
  166. else:
  167. self.countrylist_revid = 0
  168. def update_params( self ):
  169. """
  170. Updates values of Parameters of template
  171. """
  172. self.new_entry.Liste = self.countrylist_wikilink
  173. self.new_entry.Liste_Revision = \
  174. self.countrylist.page.latest_revision_id
  175. self.new_entry.Interpret = self.countrylist.interpret
  176. self.new_entry.Titel = self.countrylist.titel
  177. self.new_entry.Chartein = self._corrected_chartein
  178. if self.old_entry.Korrektur:
  179. self.new_entry.Korrektur = self.old_entry.Korrektur
  180. else:
  181. self.new_entry.Korrektur = ""
  182. if self.old_entry.Hervor:
  183. self.new_entry.Hervor = self.old_entry.Hervor
  184. else:
  185. self.new_entry.Hervor = ""
  186. def correct_chartein( self ):
  187. """
  188. Calulates the correct value of chartein, based on the chartein value
  189. from countrylist entry and param Korrektur of summarypage entry
  190. """
  191. # If param Korrektur is present extract the value
  192. if self.old_entry.Korrektur:
  193. # If Korrektur is (after striping) castable to int use it
  194. try:
  195. days = int( str( self.old_entry.Korrektur ).strip() )
  196. # Otherwise, if casting fails, ignore it
  197. except ValueError:
  198. days = 0
  199. else:
  200. days = 0
  201. corrected = self.countrylist.chartein + timedelta( days=days )
  202. self._corrected_chartein = corrected.strftime( "%d. %B" ).lstrip( "0" )
  203. def is_write_needed( self ):
  204. """
  205. Detects wether writing of entry is needed and stores information in
  206. Class-Attribute
  207. """
  208. type( self ).write_needed = ( ( self.old_entry != self.new_entry ) and
  209. self.countrylist.parsed or
  210. type( self ).write_needed )
  211. def get_entry( self ):
  212. """
  213. Returns the new entry if CountryList was parsed otherwise returns the
  214. old one
  215. """
  216. if( self.countrylist.parsed):
  217. return self.new_entry
  218. else:
  219. return self.old_entry
  220. class SummaryPageEntryTemplate():
  221. """
  222. Interface class for mwparser.template to simply use template params as
  223. Properties
  224. """
  225. # Classatribute
  226. params = ( "Liste", "Liste_Revision", "Interpret", "Titel", "Chartein",
  227. "Korrektur", "Hervor" )
  228. def __init__( self, template_obj=None ):
  229. """
  230. Creates Instance of Class for given mwparser.template object of
  231. SummmaryPageEntry Template. If no object was given create empty one.
  232. @param template_obj Object of SummmaryPageEntry Template
  233. @type template_obj: mwparser.template
  234. """
  235. # Check if object was given
  236. if( template_obj ):
  237. # Check if object has correct type
  238. if isinstance( template_obj,
  239. mwparser.nodes.template.Template ):
  240. self.template = template_obj
  241. self.__initial = False
  242. # Otherwise raise error
  243. else:
  244. raise SummaryPageEntryTemplateError( "Wrong type given" )
  245. # Otherwise initialise template
  246. else:
  247. self.__initial_template()
  248. self.__initial = True
  249. def __initial_template( self ):
  250. """
  251. Builds the initial template
  252. """
  253. self.template = next( mwparser.parse( "{{Portal:Charts und Popmusik/\
  254. Aktuelle Nummer-eins-Hits/Eintrag|Liste=|Liste_Revision=|Interpret=|Titel=NN\
  255. |Chartein=|Korrektur=|Hervor=}}" ).ifilter_templates() )
  256. def __getattr__( self, name ):
  257. """
  258. Special getter for template params
  259. """
  260. if name in type(self).params:
  261. if( self.template.has( name ) ):
  262. return self.template.get( name ).value
  263. else:
  264. return False
  265. else:
  266. raise AttributeError
  267. def __setattr__( self, name, value ):
  268. """
  269. Special setter for template params
  270. """
  271. if name in type(self).params:
  272. self.__dict__[ 'template' ].add( name, value )
  273. else:
  274. object.__setattr__(self, name, value)
  275. def __ne__( self, other ):
  276. """
  277. Checks wether all Template param values except for Liste_Revision are
  278. equal
  279. """
  280. # Detect which of the two was initialised (without)
  281. # If none raise error
  282. if( self.__initial ):
  283. initial = self
  284. cmpto = other
  285. elif( other.__initial ):
  286. initial = other
  287. cmpto = self
  288. else:
  289. raise SummaryPageEntryTemplateError(
  290. "One of the compared instances must have been initial!" )
  291. # Iterate over each param
  292. for param in initial.template.params:
  293. # Slice out only Param.name
  294. param = param[:param.find("=")].strip()
  295. # If param is missing, writing is needed
  296. if not cmpto.template.has( param ):
  297. return True
  298. # Do not compare List Revisions (not just write about Revids)
  299. if param == "Liste_Revision":
  300. continue
  301. # Compare other param values, if one unequal write is needed
  302. if( initial.template.get( param ).value.strip() !=
  303. cmpto.template.get( param ).value.strip() ):
  304. return True
  305. # If not returned True until now
  306. return False
  307. class SummaryPageError( Exception ):
  308. """
  309. Handles errors occuring in class SummaryPage
  310. """
  311. pass
  312. class SummaryPageEntryError( SummaryPageError ):
  313. """
  314. Handles errors occuring in class SummaryPageEntry
  315. """
  316. pass
  317. class SummaryPageEntryTemplateError( SummaryPageError ):
  318. """
  319. Handles errors occuring in class SummaryPageEntryTemplate
  320. """
  321. pass