import json
import json
import re
from datetime import datetime
from dateutil.parser import parse as parse_datetime
from pathlib import Path
from typing import List, NoReturn, Optional
from pywikibot import Site, Page, Category
# globals
site = Site('ca', 'wikipedia', 'TronaBot')
class Article(Page):
def __init__(self, title, user: 'User'):
Page.__init__(self, site, title)
self.user = user
self.size = 0
def growth(self):
return self.size
class User:
def __init__(self):
self.name = ''
self.growth = 0
class Setup:
"""
Classe per obtenir la configuració del Viquiprojecte.
La pàgina de configuració és un wikitext prou interpretable tant per a un humà com per al bot. Es pretén que
qualsevol puga comprendre la configuració que se li dóna al bot.
"""
def __init__(self, title):
self.page = Page(site, title)
self.content = self.page.text
self.categories: List[str] = []
self.requested_articles: List[Page] = []
self.candidates: List[User] = []
self.start_dt: Optional[datetime] = None
self.end_dt: Optional[datetime] = None
self.debug = False
def load(self, topic) -> NoReturn:
"""
Obtenció de dades emmagatzemades a un fitxer.
Només s'hauria de recòrrer a este mètode per a agilitzar l'obtenció d'estes dades quan
estem fent proves o en mode de depuració (debug = True).
:param topic: és el attribut de classe que hem emmagatzemat
:return:
"""
with open(f'resources/vp_comics_{topic}.json', 'r') as fp:
data = json.load(fp)
if hasattr(self, topic):
setattr(self, topic, data)
def save(self, topic) -> NoReturn:
"""
Emmagatzemament de dades per agilitzar la càrrega en successives execucions.
:param topic:
:return:
"""
data = getattr(self, topic)
with open(f'resources/vp_comics_{topic}.json', 'w') as fp:
json.dump(data, fp)
def set_datetimes(self) -> NoReturn:
"""
Obtenim dates d'inici i de finalització del Viquiprojecte.
"""
datetimes=[]
for match in re.finditer(
r'(?P<datetimezone>\d{4}(?P<datesep>[/-])\d{1,2}(?P=datesep)\d{1,2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2})', self.content):
datetimes.append(parse_datetime(match.group('datetimezone')))
self.start_dt = min(datetimes)
self.end_dt = max(datetimes)
print(f'start datetime: {self.start_dt:%Y-%m-%d %H:%M:%S}\nend datetime: {self.end_dt:%Y-%m-%d %H:%M:%S}')
def load_categories(self) -> NoReturn:
"""
Per agilitzar la càrrega de categories, llegim de fitxer o recorrem recursivament les supercategories.
"""
if self.debug and Path(f'resources/vp_comics_categories.json').exists():
self.load('categories')
print(f'{len(self.categories)} categories has been loaded:\n{self.categories}')
else:
self.set_categories()
def set_categories(self) -> NoReturn:
"""
Recorrem les supercategories i les inserim en un fitxer per facilitar la càrrega més ràpida.
"""
for item in re.finditer(r'\*{2}\s*(?P<category>Categoria:.+)', self.content):
category = Category(site, item.group('category'))
self.categories.append(category.title(with_ns=False, without_brackets=False))
for cat in category.subcategories(recurse=True):
self.categories.append(cat.title(with_ns=False, without_brackets=False))
self.categories = list(set(self.categories))
self.categories.sort()
self.save('categories')
def set_requested_articles(self) -> NoReturn:
"""
Obtenim els articles sol·licitats.
"""
match = re.search(r'\*\* (?P<title>Portal:+[^/]+/Sol·licitats)', self.content)
if match:
page = Page(site, match.group('title'))
for match in re.finditer(r'\*\s*\[\[(?P<title>[^\]]+)\]\]', page.text):
self.requested_articles.append(match.group('title'))
print(f'{len(self.requested_articles)} articles found')
def run(self):
self.set_datetimes()
self.load_categories()
self.set_requested_articles()
class Wikiproject:
"""
Classe pensada per a poder fer recompte de qualsevol Viquiprojecte.
En esta primera entrega ens centrem amb el Viquiprojecte:Còmics 2023
Carreguem la configuració a la pàgina Viquiprojecte:*/config
Els resultats en pengen a Viquiprojecte:*/Seguiment
Els participants els obtenim de cada secció de Viquiprojecte:*/Seguiment.
Analitzem cada contribució que fan dins de les dates assignades a l'espai principal
només si pertany a alguna de les supercategories o llurs subcategories.
Aleshores ens interessa saber:
- el nombre d'octets afegits
- si és article nou
[cal tenir en compte que no ens interessa saber el creador si l'article fou creat anteriorment a les dates]
- si s'ha extret alguna plantilla de manteniment
[sugg.: el bot no hauria de comptar els octets que ocupen estes plantilles, algorisme complex!]
"""
def __init__(self, project_name='Còmics 2023'):
self.init_time = datetime.now()
site.login()
self.main_page = Page(site, f'Viquiprojecte:{project_name}')
self.setup_title = f'Viquiprojecte:{project_name}/config'
self.contestant_title = f'Viquiprojecte:{project_name}/Seguiment'
self.result_title = f'Viquiprojecte:{project_name}/resultats automatitzats'
self.debug = False
def run(self):
print(f'[{self.init_time:%H:%M:%S}] started')
setup = Setup(self.setup_title)
setup.debug = self.debug
setup.run()
if __name__ == '__main__':
viquiprojecte = Wikiproject()
viquiprojecte.debug = True
viquiprojecte.run()