Vés al contingut

Usuari:CobainBot/scripts/cawiki/AdQ.py

De la Viquipèdia, l'enciclopèdia lliure
##############################################################################################################
# 4a versió                                                                                                  #
# Programa que arxiva les propostes de distinció de qualitat i classifica els articles com AdQ o bo          #
# IMPORTANT!!! ---> No està provat amb articles que ja hagin passat una avaluació. Si ho feu, reviseu-ho tot #
#                                                                                                            #
# Autor:Gerardduenas                                                                                         #
# Uniformitzat per Coet, a 14-ag-2015 (versió anterior a museum/AdQ.py)                                      #
# Py2 > Py3 by Coet, at 2023-05-20.                                                                          #
##############################################################################################################
import html.parser as html_parser
import locale
import re
import sys
import time

from argparse import ArgumentParser

import pywikibot
from dateutil.relativedelta import relativedelta
from datetime import date, datetime
from dataclasses import dataclass
from typing import Dict, List, Literal, NoReturn, Optional

from pywikibot import Page, Site, bot
from pywikibot.data import api

"""
Automatització de l'arxivament de propostes a Article de Qualitat/Bo [20/05/2023]

El bot es dirigix a [[Viquipèdia:Articles de qualitat]] i ha d'obtenir aquelles propostes que continguen la plantilla
{{AdQ per arxivar}}. La plantilla conté quatre paràmetres:
 - Categoria
 - Subapartat
 - Resultat proposta
 - és una traducció.

De la proposta ha d'extreure:
 - nom de l'article
 - proponent
 - avaluació prèvia
 - data
 - plantilla {{AdQ per arxivar|...}} o {{ABo per arxivar|...}}
 
El bot haurà de modificar:
 - La pàgina de l'article
 - La pàgina de discussió de l'article
 - La pàgina principal [[Viquipèdia:Proposta de distinció d'alta qualitat d'un article]]
 - La subpàgina [[Viquipèdia:Articles <de qualitat|bons>/<Categoria>]]
 - La subpàgina de la proposta [[Viquipèdia:Proposta de distinció d'alta qualitat d'un article/<Article>]]
 - La subpàgina de la proposta anual [[Viquipèdia:Proposta de distinció d'alta qualitat d'un article/<Any>]]
 - La subpàgina [[Viquipèdia:Articles <de qualitat|bons>/Llista cronològica]]
 - La plantilla de les darreres propostes resoltes [[Plantilla:Darreres propostes resoltes]]
 
El bot accedirà a:
 - [[Viquipèdia:Articles de qualitat]] per obtenir les categories dels AdQ
 - [[Viquipèdia:Articles bons]] per obtenir les categories dels ABons
 - [[Plantilla:Darreres propostes resoltes]] per obtenir el nombre de propostes recents a mostrar.
 
Possibles errors:
    - la plantilla {{AdQ per arxivar}} no inclou la categoria
    - la categoria de la plantilla {{AdQ per arxivar}}
      - inclou una categoria que no existeix
      - inclou un subapartat que no existeix
    - el resultat no coincideix amb el recompte de vots
    - no s'indica si és una traducció.
    
Normes:
    Les trobem a [[Viquipèdia:Proposta_de_distinció_d'alta_qualitat_d'un_article]]
    Un article ha d'haver estat un mínim de dues setmanes en avaluació per a ser candidat a una distinció. 
    - A [[Viquipèdia:Avaluació d'articles/<Article>]] Hi trobem la data d'avaluació
    
    vots posibles: {{votqualitat}}, {{votbo}}, {{objecció}}, {{nsnc}}.
    vot modificat: <s[trike]>{{vot}}</s[trike]>
    usuari actiu amb una antiguitat mínima d'un mes i 100 contribucions.
    
    Assoliment:
     - QUALITAT: 
        - 8 vots sense cap en contra [votbo, objecció]
        - 10 vots dels quals 8 són favorables [votqualitat]
     - BONS:
        - 5 vots, cap en contra
        - 6 vots, un en contra
        - 10 vots, 2 objeccions
        
    Periode:
        A banda de les dues setmanes d'avaluació no hi ha periode mínim
        No hi ha nombre de dies. Es recomana 3 o 4 dies si el mínim de vots s'assolira abans.
        Quorum:
            Si porta 3 mesos només necessita 7 vots favorables, cap en contra
            Si porta 4 mesos només necessita 6 vots favorables, cap en contra
            Si porta 5 mesos només necessita 5 vots favorables, cap en contra
            Si porta 6 mesos amb algun vot negatiu, no es pot aprovar.
"""


class Months:
    def __init__(self):
        self._wiki_months = [short for long, short in site.months_names]
        self._sys_months = [time.strftime("%b", time.strptime(f"01-{_:>02}-2023", "%d-%m-%Y")) for _ in range(1, 13)]
        self.month_dict = dict(zip(self._wiki_months, self._sys_months))

    def get(self, wiki_month) -> str:
        return self.month_dict.get(wiki_month)

    def from_wiki_text(self, text) -> Optional[date]:
        match = re.search(r'(?P<date>\d{2}:\d{2}, \d{1,2} (?P<month>\w{3,4}) \d{4}) \(CES?T\)', text)
        if match:
            wiki_month = match.group('month')
            date_str = match.group('date').replace(wiki_month, self.get(wiki_month))
            return datetime.strptime(date_str, '%H:%M, %d %b %Y')


@dataclass
class Vote:
    award: str = ''
    date: datetime = None
    user: 'User' = None
    valid: bool = False


class Display:
    line_sep = "####################################################################\n"
    BLACK = (0, 0, 0)
    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    YELLOW = (255, 255, 0)

    @classmethod
    def _color_text(cls, text, rgb):
        r, g, b = rgb
        return f"\033[38;2;{r};{g};{b}m{text}\033[0m"

    @classmethod
    def show(cls, new_text, header, location=None):
        print(cls._color_text(cls.line_sep, cls.GREEN))
        print(cls._color_text(header, cls.GREEN))
        if location:
            match = re.search('(?P<begin>.*)<<(?P<title>[^>]+)>>(?P<end>.*)', location)
            if match:
                location_begin = cls._color_text(match.group('begin'), cls.YELLOW)
                title = cls._color_text(f'[[{match.group("title")}]]', cls.RED)
                location_end = '' if not match.group('end') else cls._color_text(match.group('end'), cls.YELLOW)
                print(f'{location_begin}{title}{location_end}')
            else:
                print(cls._color_text(location, cls.YELLOW))
        print(new_text)
        print(cls._color_text(cls.line_sep, cls.RED))

    @classmethod
    def begin(cls, proposal: 'Proposal'):
        ln = cls._color_text(cls.line_sep[:-1], cls.BLUE)
        txt = cls._color_text(f"ARXIVANT LA PROPOSTA de l'article", cls.GREEN)
        art = cls._color_text(f"[[{proposal.title}]]", cls.RED)
        print(f"\n{ln} {txt} {art} {ln}\n")


class User:
    def __init__(self, name):
        self.id = 0
        self.name: str = name
        self.edit_count: int = 0
        self.last_contrib: Optional[datetime] = None
        self.run()

    def __repr__(self):
        return f"<User {self.id}, name: {self.name}, editcount: {self.edit_count}, " \
               f"last_edit: {self.last_contrib:%Y-%m-%d %H:%M}>"

    @staticmethod
    def _query(params) -> dict:
        qry = api.Request(site, parameters=params)
        return qry.submit()

    def set_last_contrib(self):
        params = {
            'action': 'query',
            'list': 'usercontribs',
            'ucuser': self.name,
            'ucprop': 'timestamp',
            'uclimit': 1
        }
        data = self._query(params)
        user_dict = data['query']['usercontribs'][0]
        self.last_contrib = datetime.strptime(user_dict['timestamp'], '%Y-%m-%dT%H:%M:%SZ')
        return data['query']['usercontribs'][0]['timestamp']

    def get_last_contrib_before_dt(self, dt: datetime):
        ts = datetime.strftime(dt, '%Y-%m-%dT%H:%M:%SZ')
        params = {
            'action': 'query',
            'list': 'usercontribs',
            'ucuser': self.name,
            'ucprop': 'timestamp',
            'ucend': ts,
            'uclimit': 1
        }
        data = self._query(params)
        user_dict = data['query']['usercontribs'][0]
        last_contrib = user_dict['timestamp']
        return datetime.strptime(last_contrib, '%Y-%m-%dT%H:%M:%SZ')

    def set_contribs(self):
        # action=query&list=users&ususers=Coet&usprop=editcount
        params = {
            'action': 'query',
            'list': 'users',
            'usprop': ('editcount',),
            'ususers': self.name
        }
        data = self._query(params)
        user_dict = data['query']['users'][0]
        self.id = user_dict['userid']
        self.edit_count = user_dict['editcount']

    def run(self):
        self.set_contribs()
        self.set_last_contrib()


class Proposal:
    """
    Classe per a emmagatzemar les dades d'una proposta.
    """
    def __init__(self, title: str, author: str, date_str: str, votes: str):
        self.page = Page(site, title)
        self.title: str = title
        self.nominator: Optional[str] = author
        self.nomination_page: Page = Page(site, f"Viquipèdia:Avaluació d'articles/{title}")
        self.award: Optional[Literal['qualitat', 'bo']] = None
        self.category: str = ''
        self.proposal_date: date = datetime.strptime(date_str, '%d-%m-%Y')
        self.nomination_date: Optional[date] = None
        self._votes: str = votes
        self.votes: List[Vote] = []
        self.first_tpl = r"#{{ArxiuAdQ|"
        self.approved: Optional[bool] = None
        self.translated: Optional[bool] = None
        self.pattern = re.compile(
            r"#\s*(?P<strike><s(?:trike)?>)?{{\s*(?P<vote>[Vv]ot(?P<award>bo|qualitat)?|objecció|nsnc)}}.*"
            r"(?P<date>\d{2}:\d{2}, \d{1,2} (?P<month>\w{4}|\w{2,3}\.?) \d{4}) \(CES?T\)\s*(?P<ekirts></s(?:trike)?>)?",
            re.UNICODE
        )
        self.parse_votes()
        self.set_nomination_date()

    @property
    def title_without_html_entities(self):
        return html_parser.unescape(self.title)

    def __repr__(self):
        return f"<Proposal [[{self.title}]], nominator: {self.nominator}, date: {self.proposal_date:%Y-%#m-%#d}, " \
               f"award: {self.award} {len(self.votes)} votes>"

    @property
    def award_tpl(self):
        return f"{{{{vot{self.award}}}}}"

    @property
    def color(self):
        return 'green' if self.approved else 'red'

    @property
    def result(self):
        return f"'''{'Aprovat' if self.approved else 'No aprovat'}'''"

    @property
    def day(self) -> int:
        return self.proposal_date.day

    @property
    def month(self) -> str:
        return self.proposal_date.strftime("%B")

    @property
    def year(self):
        return self.proposal_date.strftime("%y")

    @property
    def full_year(self) -> str:
        return self.proposal_date.strftime("%Y")

    @property
    def of(self) -> str:
        return "d'" if self.month.startswith('a') or self.month.startswith('o') else 'de '

    @property
    def exposure_period(self):
        return relativedelta(self.proposal_date, self.nomination_date)

    @property
    def exposure_period_is_valid(self):
        days = self.exposure_period.days
        months = self.exposure_period.months
        return months > 0 or (months == 0 and days >= 14)

    def parse_votes(self):
        counter = {
            'qualitat': 0,
            'bo': 0,
            'objecció': 0,
            'nsnc': 0
        }
        for match in self.pattern.finditer(self._votes):
            award = match.group('award')
            wiki_month = match.group('month')
            date_str = match.group('date').replace(wiki_month, months.get(wiki_month))
            vote = Vote()
            vote.award = award
            vote.date = datetime.strptime(date_str, '%H:%M, %d %b %Y')
            self.votes.append(vote)
            counter[award] += 1
        self.award = 'qualitat' if counter['qualitat'] > counter['bo'] else 'bo'

    def set_nomination_date(self):
        text = self.nomination_page.text
        self.nomination_date = months.from_wiki_text(text)


class ProposalProvider:
    """
    Classe per a emmagatzemar les diferents propostes.
    """
    def __init__(self):
        self.debug: bool = debug
        self.proposal_list: List[Proposal] = []
        self.page = Page(site, "Viquipèdia:Proposta de distinció d'alta qualitat d'un article")
        self.page_pattern = re.compile("== Propostes de distinció actuals ==(?P<proposals>.*)==", re.DOTALL)
        self.subpage_pattern = re.compile(
            r"=== \[\[[^]]+]] ===\s*\* '''Article''': {{DadesArticle\|(?P<title>[^}]*)}}\s*\* "
            r"'''Proponent''': \[\[Usuari:(?P<nominator>[^]]+)]]\s*\* "
            r"'''Avaluació prèvia:''' \[\[Viquipèdia:Avaluació d'articles/[^]]+]]\s*\* "
            r"'''Data''': (?P<date>\d{1,2}-\d{1,2}-\d{4}).+"
            r"'''Valoracions'''(?P<votes>.*)\[\[Categoria:Propostes d'articles de qualitat\|",
            re.DOTALL
        )
        self.proposal_pattern = re.compile(r"----.*?{{/(?P<title>[^}]+)}}", re.DOTALL)
        self.dispatch()

    def __iter__(self):
        return self.proposal_list.__iter__()

    def get_by_name(self, title) -> Optional[Proposal]:
        filtered_proposals = [p for p in self.proposal_list if p.title == title]
        return filtered_proposals[0] if filtered_proposals else None

    def dispatch(self):
        text = self.page.text
        match = self.page_pattern.search(text)
        if match:
            for proposal in self.proposal_pattern.finditer(match.group('proposals')):
                title = proposal.group('title')
                page = Page(site, f"Viquipèdia:Proposta de distinció d'alta qualitat d'un article/{title}")
                match = self.subpage_pattern.search(page.text)
                if match:
                    title = match.group('title')
                    nominator = match.group('nominator')
                    date_str = match.group('date')
                    votes = match.group('votes')
                    new_proposal = Proposal(title, nominator, date_str, votes)
                    self.proposal_list.append(new_proposal)
        if self.debug and self.proposal_list:
            for proposal in self.proposal_list:
                print(proposal)


class ProposalSubpage:
    """
    Classe mare per a les propostes.
    Les subpàgines contenen les propostes.
    """
    def __init__(self):
        self.debug: bool = debug
        self.main_title = "Viquipèdia:Proposta de distinció d'alta qualitat d'un article"
        self.page: Optional[Page] = None
        self.proposal: Optional[Proposal] = None

    def show(self, new_text: str, header: str = '', location: str = ''):
        Display.show(new_text, header, location)


class ArticleProposalSubpage(ProposalSubpage):
    """
    Subpàgina de la classe mare que conté la proposta.
    """
    def __init__(self):
        ProposalSubpage.__init__(self)

    def run(self, prop: Proposal):
        # Categories
        self.proposal = prop
        self.page = Page(site, f"{self.main_title}/{prop.title}")
        old_text = self.page.text
        old_cat = "Categoria:Propostes d'articles de qualitat"
        new_cat = "Categoria:Propostes d'AdQ ({{subst:CURRENTYEAR}})"
        new_text = old_text.replace(old_cat, new_cat)

        if self.debug:
            self.show(
                new_text,
                "Les categories han quedat així",
                f'... a la subpàgina de la proposta <<{self.page.title(without_brackets=True)}>>'
            )
            return

        bot.output('Canviant categories...')
        self.page.text = new_text
        self.page.save("Bot arxivant la proposta")


class ArchiveProposalSubpage(ProposalSubpage):
    """
    Subpàgina de la classe mare que conté l'arxivament de la proposta.
    """
    def __init__(self):
        ProposalSubpage.__init__(self)

    def run(self, prop: Proposal):
        # Archive page list
        self.proposal = prop
        self.page = Page(site, f"{self.main_title}/{prop.full_year}")
        old_text = self.page.text
        award = f' {prop.award_tpl}' if prop.approved else ''
        appended_text = f'{prop.first_tpl}{prop.title}|{prop.nominator}}}}}' \
                        f'<font color="{prop.color}">{prop.result}</font> ' \
                        f'el dia {prop.day} {prop.of}{prop.month} de {prop.year}.{award}\n{prop.first_tpl}'
        new_text = old_text.replace(prop.first_tpl, appended_text, 1)

        if self.debug:
            self.show(new_text, "L'arxiu ha quedat així", f"... a l'arxiu anual <<{self.page.title(without_brackets=True)}>>")
            return

        self.page.text = new_text
        self.page.save()


class ProposalTemplate:
    """
    Plantilla que conté les darreres propostes resoltes.
    """
    def __init__(self):
        self.debug: bool = debug
        self.title = "Plantilla:Darreres propostes resoltes"
        self.page = Page(site, self.title)
        self.proposal: Optional[Proposal] = None
        self.pattern = re.compile(r'#\{\{ArxiuAdQ.*')
        self.tpl_pattern = re.compile(r"(?P<comment><!-- màx\s+(?P<number>\d+)\s*-->)")
        self.number_of_templates = 0
        self.comment = ''

    def set_number_of_templates(self, text):
        match = self.tpl_pattern.search(text)
        if match:
            self.number_of_templates = int(match.group('number'))
            self.comment = match.group('comment')

    def run(self, prop: Proposal):
        # Recent proposal list
        self.proposal = prop
        old_text = self.page.text
        self.set_number_of_templates(old_text)
        award = f' {prop.award_tpl}' if prop.approved else ''
        prepended_text = f'{self.comment}\n{prop.first_tpl}{prop.title}|{prop.nominator}}}}}' \
                         f'<font color="{prop.color}">{prop.result}</font> ' \
                         f'el dia {prop.day} {prop.of}{prop.month} de {prop.full_year}.{award}'
        new_text = old_text.replace(self.comment, prepended_text)
        found = self.pattern.findall(new_text)
        for item in found[self.number_of_templates:]:
            new_text = new_text.replace(item, '')

        if self.debug:
            Display.show(
                new_text,
                "La llista ha quedat així",
                f'... a la plantilla de propostes recents <<{self.page.title(without_brackets=True)}>>'
            )
            return

        # Esborrar proposta
        self.page.text = new_text
        self.page.save(u"Bot esborrant proposta aprovada")


class ProposalsMainPage(ProposalSubpage):
    """
    Classe filla per a la pàgina principal.
    """
    def __init__(self):
        ProposalSubpage.__init__(self)

    def run(self, prop: Proposal):
        self.page = Page(site, self.main_title)
        # Remove proposal
        old_text = self.page.text
        search_term = f'----\n{{{{/{prop.title_without_html_entities}}}}}\n'
        new_text = old_text.replace(search_term, '')

        if self.debug:
            self.show(
                new_text,
                f"La proposta per a l'article [[{prop.title}]] ha quedat així",
                f"... a la pàgina principal (<<{self.page.title(without_brackets=True)}>>)"
            )
            return

        self.page.text = new_text
        self.page.save("Bot esborrant proposta aprovada")


class Article:
    """
    Classe que representa un article.
    """
    def __init__(self):
        self.debug: bool = debug
        self.proposal: Optional[Proposal] = None
        self.title: Optional[str] = None
        self.keep_title_case = ('terra',)
        self.categories: List[str] = []

    @property
    def category_award(self):
        return 'de qualitat' if self.proposal.award == 'qualitat' else 'bons'

    @property
    def template_award(self):
        return 'de qualitat' if self.proposal.award == 'qualitat' else 'bo'

    def fix_capital_letters(self, category):
        pattern = re.compile("(?P<before>de la |del |dels |de les |de l')(?P<topic>.*)")
        match = pattern.search(category)
        if match:
            old_topic = match.group('topic')
            if old_topic in self.keep_title_case:
                new_topic = old_topic.title()
                category = category.replace(old_topic, new_topic)
        return category

    def run(self, prop: Proposal):
        self.proposal = prop
        self.categories = main.categories.get_categories(prop)
        self.title = prop.title
        bot.output(u"Fent canvis a l'article...")
        category = prop.category.lower()
        page = Page(site, self.title)
        text = page.text
        lower_cats = [cat.lower() for cat in self.categories]
        prep = "d'" if category[0].lower() in "aeiouàèéíòóú" else 'de '
        category = self.fix_capital_letters(category)
        category = f"\n[[Categoria:Articles {self.category_award} {prep}{category}]]"
        if prop.translated:
            category += f"\n[[Categoria:Traduccions que són articles {self.category_award}]]"
        text += f"{category}\n{{{{Article {self.template_award}}}"

        if self.debug:
            self.show(text)
            return

    def show(self, text):
        Display.show(text, "L'article ha quedat així", f'... a <<{self.title}>>')


class ArticleTalk:
    """
    Classe que representa la pàgina de discussió.
    """
    def __init__(self):
        # Actualitza l'historial d'avaluacions
        self.debug: bool = debug
        self.proposal: Optional[Proposal] = None
        self.history_pattern = re.compile(r"\{\{Historial d'avaluacions\|.*?\}\}")
        self.award_pattern = re.compile(r"\{\{Proposta distinció\|(?P<title>[\w\d]+)\|")

    @property
    def title(self):
        return f"Discussió:{self.proposal.title}"

    def run(self, prop: Proposal):
        self.proposal = prop
        bot.output("Fent canvis a la pàgina de discussió...")
        talk_page = Page(site, self.title)
        text = talk_page.text
        approved = 'aprovat' if prop.approved else 'no aprovat'
        text = self.history_pattern.sub(rf"{{Historial d'avaluacions|{approved}}}", text, 1)
        match = self.award_pattern.search(text)
        if match:
            award = match.group('title')
            text = self.award_pattern.sub(award, text, 1)
        if self.debug:
            self.show(text)
            return

        bot.output(u"Desant la pàgina de discussió...")
        talk_page.text = text
        talk_page.save(u"Bot actualitzant historial d'avalucions")

    def show(self, text):
        Display.show(text, "La discussió ha quedat així", f"... a <<{self.title}>>")


class GrantedArticlesCategories:
    """
    Recull de categories dels AdQ i ABons.
    """

    def __init__(self):
        self.debug: bool = debug
        self.proposal: Optional[Proposal] = None
        self.fd_pattern = re.compile(
            r"{\|[^|]+\|\+ \s*style=\"\"\s+\|\s+'''Categories d'Articles de Qualitat'''.*?\|(?P<categories>.*)\|}",
            re.DOTALL | re.MULTILINE
        )
        self.gd_pattern = re.compile(r'<div style="text-align:center;">(?P<categories>.*)\|}', re.DOTALL | re.MULTILINE)
        self.cat_pattern = re.compile(r"\[\[#(?P<category>[^|]+)\|[^\]]+]]")
        self.good_pattern = re.compile(r"\{\{Infralliga\|(?P<category>[^}]+)}}")
        self.fa_categories: List[str] = []
        self.ga_categories: List[str] = []
        self.dispatch()

    @staticmethod
    def title(award):
        return f"Viquipèdia:Articles {f'de {award}' if award == 'qualitat' else 'bons/Introducció'}"

    def get_categories(self, prop: Proposal):
        return self.fa_categories if prop.award == 'qualitat' else self.ga_categories

    def dispatch(self):
        # Get featured articles categories
        title = self.title('qualitat')
        page = Page(site, title)
        old_text = page.text

        categories_text = self.fd_pattern.search(old_text)
        if categories_text:
            for match in self.cat_pattern.finditer(categories_text.group('categories')):
                self.fa_categories.append(match.group('category'))

        # Get good articles categories
        title = self.title('bons')
        page = Page(site, title)
        old_text = page.text

        categories_text = self.gd_pattern.search(old_text)
        if categories_text:
            for match in self.good_pattern.finditer(categories_text.group('categories')):
                self.ga_categories.append(match.group('category'))

        if self.debug:
            if self.fa_categories:
                print(self.fa_categories)
            if self.ga_categories:
                print(self.ga_categories)


class GrantedArticlesProject:
    """
    Classe que representa la subpàgina de la categoria a la que pertany un article bo o de qualitat.
    """
    def __init__(self):
        self.debug: bool = debug
        self.award = ''
        self.category = ''
        self.page: Optional[Page] = None

    @property
    def title(self):
        return f"Viquipèdia:Articles {self.award}/{self.category}"

    def sort_category(self):
        pass

    def run(self, prop: Proposal):
        self.award = 'de qualitat' if prop.award == 'qualitat' else 'bons'
        self.category = prop.category
        self.page = Page(site, self.title)
        self.sort_category()


class Main:
    """
    Classe principal per engegar els diversos procediments.
    """
    def __init__(self):
        self.debug: bool = debug
        self.categories: Optional[GrantedArticlesCategories] = None
        self.users: Dict[str, User] = {}

    def start(self, prop: Proposal):
        bot.output('Arxivant proposta...')

        # Categories
        subpage = ArticleProposalSubpage()
        subpage.run(prop)

        # Llista de l'axiu
        archive = ArchiveProposalSubpage()
        archive.run(prop)

        # Llista recentment propostes
        recent_proposals = ProposalTemplate()
        recent_proposals.run(prop)

        # Esborrar proposta
        main_page = ProposalsMainPage()
        main_page.run(prop)

    def collect(self):
        provider = ProposalProvider()
        self.categories = GrantedArticlesCategories()
        for proposal in provider:
            Display.begin(proposal)
            self.start(proposal)


if __name__ == '__main__':
    arg_parser = ArgumentParser()
    arg_parser.add_argument('-D', '--debug', default=False, dest='debug', action='store_true')
    args = arg_parser.parse_args()

    debug = args.debug
    on_win = sys.platform.startswith("win")
    encoding = 'Catalan_Andorra.1252' if on_win else "ca_ES.utf8"
    locale.setlocale(locale.LC_ALL, encoding)
    site = Site('ca', 'wikipedia', 'ArxivaBot')
    site.login()
    months = Months()
    main = Main()
    main.collect()