Bikarhêner:Balyozxane/skrîpt/py/addwikiproje.py

#!/usr/bin/env python3
"""
Şablonên Wîkîprojeyan ji kategoriya [[Kategorî:Şablonên Wîkîprojeyan yên bi nirxandina kalîteyê]] derdixe û li rûpelê zêde dike eger eynî şablon tê de tinebe.
Tenê serê xwe wisa dixebite:
python pwb.py addwikiproje -recentchanges (li ser guhartinên dawî dixebite)
python pwb.py addwikiproje -page:"Navê rûpelê" (tenê li ser rûpelekê dixebite)
python pwb.py addwikiproje -file:listeyawikiprojeyan.txt (li serrûpelên di dosyeyê de ne dixebite)

Lîste ji aliyê [[Bikarhêner:Balyozxane/skrîpt/py/getwikiproje.py]] ve tê çêkirin.  
"""
#
# (C) w:ku:User:Balyozxane :)
#
# Distributed under the terms of the MIT license.
#
import pywikibot
from pywikibot.bot import SingleSiteBot, ConfigParserBot, AutomaticTWSummaryBot
from pywikibot import pagegenerators
import requests
import re
import mwparserfromhell

VERBOSE = False
TESTING = False


def get_category_members(category_title):
    base_url = f'https://ku.wikipedia.org/w/api.php'
    params = {
        "action": "query",
        "format": "json",
        "list": "categorymembers",
        "cmtitle": category_title,
        "cmlimit": "max"  # Retrieve all members of the category
    }
    response = requests.get(base_url, params=params)
    data = response.json()
    if 'error' in data:
        if VERBOSE:
            print(f"Error: {data['error']['info']}")
        return None
    else:
        return [member['title'] for member in data['query']['categorymembers']]


def get_enwiki_title(kuwiki_title):
    url = f"https://www.wikidata.org/w/api.php?action=wbgetentities&sites=kuwiki&titles={kuwiki_title}&props=sitelinks&format=json"
    response = requests.get(url)
    data = response.json()
    entity = next(iter(data["entities"].values()))  # Get the first (and only) entity
    if "sitelinks" in entity and "enwiki" in entity["sitelinks"]:
        enwiki_title = entity["sitelinks"]["enwiki"]["title"]
        return enwiki_title
    else:
        return None


def get_assessment_info(page_title, projects):
    base_url = 'https://en.wikipedia.org/w/api.php'
    params = {
        'action': 'query',
        'prop': 'pageassessments',
        'titles': page_title,
        'format': 'json'
    }
    response = requests.get(base_url, params=params)
    data = response.json()

    try:
        pages = data['query']['pages']
    except KeyError:
        if VERBOSE:
            print(f"Error: 'query' key not found for {page_title} in API response.")
        return {}

    assessment_info = {}
    for page_id, page_data in pages.items():
        assessments = page_data.get('pageassessments', {})

        if VERBOSE:
            print("pageassessments:")
            print(assessments)

        for project in projects:
            project_info = {}
            project_found = False
            for assessment_project, project_assessment in assessments.items():
                if assessment_project.lower() == project.lower():
                    project_info['class'] = project_assessment.get('class', 'Unknown')
                    project_info['importance'] = project_assessment.get('importance', 'Unknown')
                    project_found = True
                    break
            if not project_found:
                project_info = None
            assessment_info[project] = project_info

    if VERBOSE:
        print(f"Assessment Info for {page_title}:")
        print(assessment_info)

    return assessment_info


def get_class(page):
    categories_list = list(page.categories())

    sitil_to_check = "Kategorî:Hemû şitil"
    liste_to_check = "Kategorî:Liste"

    if sitil_to_check in [cat.title() for cat in categories_list]:
        if VERBOSE:
            print(f"The page is in the category: {sitil_to_check}")
        class_value = "Şitil"
    elif liste_to_check in [cat.title() for cat in categories_list]:
        if VERBOSE:
            print(f"The page is in the category: {liste_to_check}")
        class_value = "Lîste"

    else:
        if VERBOSE:
            print(f"The page is NOT in the given categories")
        class_value = ""

    return class_value


class WikiprojeBot(
    SingleSiteBot,
    ConfigParserBot,
    AutomaticTWSummaryBot,
):
    summary_key = 'basic-changing'
    use_redirects = False

    update_options = {
        'summary': None,
        'text': '',
        'top': False,
    }

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bot_name = "Bikarhêner:Balyozxane/skrîpt/py/addwikiproje.py"
        if not TESTING:
            self.wikiproje = {}
            self.create_wikiproje_dictionary()
        else:
            self.wikiproje = {
                'Wîkîproje Almanya': {
                    'ku_proje': 'Almanya',
                    'en_proje': 'Germany',
                    'redirects': ['WikiProject Germany']
                },
                'Wîkîproje Fransa': {
                    'ku_proje': 'Fransa',
                    'en_proje': 'France',
                    'redirects': ['WikiProject France', 'WPFR', 'WPFRANCE']
                },
                'Wîkîproje Kurdistan': {
                    'ku_proje': 'Kurdistan',
                    'en_proje': 'Kurdistan',
                    'redirects': ['WPKU', 'WPKURDISTAN']
                },
                'Wîkîproje Mîtolojî': {
                    'ku_proje': 'Mîtolojî',
                    'en_proje': 'Mythology',
                    'redirects': ['WikiProject Mythology']
                }
            }

        self.kalik_templates = ['Kalika wîkîprojeyê']
        if not TESTING:
            kalik_redirects = self.get_template_redirects("Şablon:Kalika wîkîprojeyê")
        else:
            kalik_redirects = ['WPB', 'WikiProject banner shell', 'WikiProject Shell', 'Bannershell', 'WPBS',
                               'WikiProject banner', 'Kalika Wîkîprojeyê']

        if kalik_redirects:
            self.kalik_templates.extend(kalik_redirects)

        if VERBOSE:
            print(self.kalik_templates)

    def get_template_redirects(self, template_title):
        template_page = pywikibot.Page(self.site, template_title)
        redirects = template_page.backlinks(filter_redirects=True, namespaces=[10])
        redirect_titles = [redirect.title(with_ns=False) for redirect in redirects]
        if VERBOSE:
            print(f"template_redirects:\n{redirect_titles}")
        return redirect_titles

    def create_wikiproje_dictionary(self):
        self.wikiproje = {}

        category_title = 'Kategorî:Şablonên Wîkîprojeyan yên bi nirxandina kalîteyê'
        category_members = get_category_members(category_title)

        for member in category_members:
            ku_title = member
            en_title = get_enwiki_title(member)
            if en_title:
                ku_proje = ku_title.replace("Şablon:Wîkîproje ", "")
                en_proje = en_title.replace("Template:WikiProject ", "")
                ku_template = ku_title.replace("Şablon:", "")
                ku_template_key = ku_template.replace("Şablon:", "")  # Use ku_template as the key
                redirects = self.get_template_redirects(member)
                self.wikiproje[ku_template_key] = {
                    'ku_proje': ku_proje,
                    'en_proje': en_proje,
                    'redirects': redirects  # Store the redirects under each ku_template
                }
        if VERBOSE:
            print(self.wikiproje)

    def check_assessment_info(self, page_title):
        """
        Check if the page requires modification based on assessment information.
        Return a dictionary containing the found templates and their associated assessment_info, if any, else return None.
        """
        en_page_title = get_enwiki_title(page_title)
        # Get the en_proje keys from self.wikiproje as the projects list
        projects = [info['en_proje'] for info in self.wikiproje.values()]

        assessment_info = get_assessment_info(en_page_title, projects)

        if not assessment_info:
            if VERBOSE:
                print(f"assessment_info not found for {page_title}")
            return False

        importance_translation = {
            "Top": "Herî zêde",
            "High": "Zêde",
            "Mid": "Navîn",
            "Low": "Kêm",
            "Bottom": "Herî kêm",
            "Unknown": "Nayê zanîn",
            "NA": "NA"
        }

        # Check if any of the en_proje keys from self.wikiproje match any of the assessment_info keys
        matching_projects = {}
        for key, value in self.wikiproje.items():
            if value['en_proje'] in assessment_info and assessment_info[value['en_proje']] is not None:
                importance_en = assessment_info[value['en_proje']]['importance']
                importance_ku = importance_translation.get(importance_en, 'Unknown')
                matching_projects[key] = importance_ku

        if matching_projects:
            if VERBOSE:
                print(f"Found matching_projects:\n{matching_projects}")
            return matching_projects
        else:
            return False

    @staticmethod
    def reorder_templates(new_template, wikicode):
        # Define the desired template order
        template_order = [
            "Kalika wîkîprojeyê",
            "Dîtinên salane",
            "Kopîkirî",
            "Serê gotûbêjê",
            "Talk header",
            "Portal dîrok",
        ]
        # Find the index of the first non-template node
        idx = next(
            (i for i, node in enumerate(wikicode.nodes) if
             not isinstance(node, mwparserfromhell.nodes.template.Template)), 0)
        wikicode.insert(idx, new_template)  # Insert the new template at the index

        # Insert a line break before adding the new template
        wikicode.insert(idx, "\n")

        # Reorder the remaining templates
        for template_name in template_order:
            templates = wikicode.filter_templates(matches=lambda template: template.name.matches(template_name))
            if templates:
                template = templates[0]
                print("Reordering template:", template)
                wikicode.remove(template)
                wikicode.insert(idx, "\n")  # Insert a line break after each template
                wikicode.insert(idx, template)  # Insert at the appropriate index
                wikicode.insert(idx, "\n")  # Insert a line break after each template

        # Save the page with the modified text
        updated_text = str(wikicode)
        return updated_text

    def add_new_wikiproje(self, talk_page, matching_projects):
        """
        :param talk_page:
        :param matching_projects: example {'Wîkîproje Mîtolojî': 'Herî zêde'} there might be more than one key
        :return: this should add the new wikiproject to the existing talk page and save the page.
        """
        if VERBOSE:
            print(f"adding each wikiproject in {matching_projects} to existing {talk_page}")

        # Create a list to store all wikiprojects added
        added_wikiprojects = []
        page_text = talk_page.text
        wikicode = mwparserfromhell.parse(page_text)

        for wikiproje, importance in matching_projects.items():
            # Check if the WikiProject template or its redirects are already present on the talk page
            existing_templates = [template.name.strip() for template in
                                  wikicode.filter_templates()]
            wikiproje_templates = [wikiproje] + self.wikiproje[wikiproje]['redirects']

            if any(wikiproje_template in existing_templates for wikiproje_template in wikiproje_templates):
                if VERBOSE:
                    print(f"WikiProject '{wikiproje}' or its redirects already exist on the talk page. Skipping.")
                continue

            # Check if there are Kalik templates present on the talk page
            has_kalik_template = any(kalik_template in existing_templates for kalik_template in self.kalik_templates)

            new_wikiproje = "\n{{"
            new_wikiproje += f"{wikiproje}"
            if importance:
                if importance == "Nayê zanîn":
                    new_wikiproje += f"|muhîmî="
                elif importance != "NA":
                    new_wikiproje += f"|muhîmî={importance}"
            new_wikiproje += "}}\n"

            # If there are Kalik templates, find and update the first empty parameter with the new WikiProject
            if has_kalik_template:
                if VERBOSE:
                    print(f"has_kalik_template: {has_kalik_template}")
                for template in wikicode.filter_templates():
                    if template.name.strip() in self.kalik_templates:
                        if not template.has(1):
                            template.add(1, new_wikiproje, showkey=True)
                        else:
                            param_val = template.get(1).value.strip()
                            if param_val:  # If parameter has value, append the new WikiProject
                                template.add(1, f"{param_val}{new_wikiproje}", showkey=True)
                            else:
                                template.add(1, new_wikiproje, showkey=True)
                page_text = str(wikicode)
            else:
                # If there are no Kalik templates, create a new one with the new WikiProject

                new_template = "{{Kalika wîkîprojeyê"

                if self.current_page.namespace() == 0:
                    sinif = get_class(self.current_page)
                    new_template += f"|sinif={sinif}"

                new_template += f"|1=\n{new_wikiproje}}}}}"
                page_text = self.reorder_templates(new_template, wikicode)

            if VERBOSE:
                print(f"Added WikiProject '{wikiproje}' to the talk page.")

            page_text = re.sub(r'1=\n\n\{', '1=\n{', page_text)
            page_text = re.sub(r'\n\n\n*', '\n\n', page_text)
            page_text = re.sub(r'^\n+', '', page_text)

            # Add the added wikiproject to the list
            added_wikiprojects.append(f"[[WP:{wikiproje}|{wikiproje}]]")

        self.save_page(talk_page, page_text, added_wikiprojects)

    def create_new_talk(self, page, matching_projects):
        talk_page = page.toggleTalkPage()

        added_wikiprojects = []  # Create a list to store added wikiprojects
        new_template = "{{Serê gotûbêjê}}\n{{Kalika wîkîprojeyê"
        if page.namespace() == 0:
            sinif = get_class(page)
            new_template += f"|sinif={sinif}"

        new_template += "|1=\n"

        for wikiproje, importance in matching_projects.items():
            new_wikiproje = "\n{{"
            new_wikiproje += f"{wikiproje}"
            if importance:
                if importance == "Nayê zanîn":
                    new_wikiproje += "|muhîmî="
                elif importance != "NA":
                    new_wikiproje += f"|muhîmî={importance}"
            new_wikiproje += "}}\n"

            new_template += f"{new_wikiproje}"
            added_wikiprojects.append(f"[[WP:{wikiproje}|{wikiproje}]]")  # Append the added wikiproject to the list

        new_template += "}}"

        page_text = new_template
        updated_text = re.sub(r'1=\n\n\{', '1=\n{', page_text)

        self.save_page(talk_page, updated_text, added_wikiprojects)

    def save_page(self, page, text, added_wikiprojects) -> None:
        # Construct the summary including all added wikiprojects
        summary = (f'[[{self.bot_name}|Bot]]: Wîkîproje lê '
                   f'{"hatin" if len(added_wikiprojects) > 1 else "hat"}'
                   f' zêdekirin: '
                   f'{", ".join(added_wikiprojects)}')

        if page.text != text:
            # Save the talk page
            try:
                if VERBOSE:
                    print(text)
                    print(summary)
                page.text = text
                page.save(summary=summary)
            except Exception as e:
                print(f"Error occurred while saving the talk page: {e}")
        else:
            print(f"No changes detected for {page.title()}")

    def treat_page(self) -> None:
        current_page = self.current_page
        page_title = self.current_page.title()

        # Get matching projects and check if modification needed
        matching_projects = self.check_assessment_info(page_title)
        if not matching_projects:
            if VERBOSE:
                print(f"Skipping {page_title} because no matching projects found.")
            return

        talk_page = current_page.toggleTalkPage()

        if not talk_page.exists():
            if VERBOSE:
                print(f"{talk_page} doesn't exist creating for {matching_projects}")
            self.create_new_talk(current_page, matching_projects)
        else:
            self.add_new_wikiproje(talk_page, matching_projects)


def main(*args: str) -> None:
    local_args = pywikibot.handle_args(args)
    gen_factory = pagegenerators.GeneratorFactory()
    local_args = gen_factory.handle_args(local_args)

    options = {'text': ''}

    for arg in local_args:
        option, _, value = arg.partition(':')
        if option in ('summary', 'text'):
            if not value:
                pywikibot.input(f'Please enter a value for {option}')
            options[option] = value
        else:
            options[option] = True

    gen = gen_factory.getCombinedGenerator(preload=True)

    if not pywikibot.bot.suggest_help(missing_generator=not gen):
        bot = WikiprojeBot(generator=gen, **options)
        bot.run()


if __name__ == '__main__':
    main()