from typing import List

from summer.compute import ComputedValueProcessor

from autumn.models.covid_19.constants import (
    INFECT_DEATH, INFECTION, Compartment, NOTIFICATIONS, HISTORY_STRATA, INFECTION_DEATHS,
    COMPARTMENTS, Vaccination, PROGRESS, Clinical, History, Tracing, NOTIFICATION_CLINICAL_STRATA,
    HOSTPIALISED_CLINICAL_STRATA,
)
from autumn.models.covid_19.parameters import Sojourn, VaccinationRisk
from autumn.settings import COVID_BASE_AGEGROUPS
from autumn.models.covid_19.stratifications.clinical import CLINICAL_STRATA
from autumn.models.covid_19.constants import INCIDENCE
from autumn.models.covid_19.stratifications.strains import Strain
from autumn.core.utils.utils import get_complement_prop
from autumn.model_features.outputs import OutputsBuilder

class TimeProcess(ComputedValueProcessor):
    """
    This is just sitting here ready to go, in case anyone wants to produce any outputs that are dependent on modelled
    time.

    """

    def process(self, compartment_values, computed_values, time):
        return time

class CovidOutputsBuilder(OutputsBuilder):
    """
    The object responsible for collating and requesting all the derived outputs for the model.

    Attributes:
        is_contact_tracing: Whether contact tracing is active in this model

    """

    def __init__(self, model, compartments, is_contact_tracing):
        OutputsBuilder.__init__(self, model, compartments)
        self.is_contact_tracing = is_contact_tracing
        self.untraced_stratum = {"tracing": Tracing.UNTRACED} if is_contact_tracing else {}

    def request_incidence(self):
        """
        Incidence is the transition from late exposed to early active - i.e. the rate of onset of an "episode".

        Generates the following derived outputs both overall and by age group:
            incidence: Rate of onset of new episodes

        """

        # Unstratified
        self.model.request_output_for_flow(name=INCIDENCE, flow_name=INCIDENCE)

        # Stratified by age group
        self.request_stratified_output_for_flow(INCIDENCE, COVID_BASE_AGEGROUPS, "agegroup")

        # Stratified by age group and by clinical stratum
        self.request_double_stratified_output_for_flow(
            INCIDENCE, COVID_BASE_AGEGROUPS, "agegroup", CLINICAL_STRATA, "clinical"
        )

    def request_infection(self):
        """
        Track the rate at which people are newly infected.
        Infection is the transition from susceptible to early exposed, of course.

        Generates the following derived outputs both overall and by age group:
            infection: Rate of new infections by time

        """

        self.model.request_output_for_flow("infection", INFECTION)

    def request_notifications(self, cumul_inc_start_time: float, hospital_reporting: float):
        """
        Calculate the rate of notifications over time.

        Args:
            cumul_inc_start_time: The starting time to start the cumulative calculations
            hospital_reporting: The proportion of hospitalisations notified (defaults to one)

        Generates the following derived outputs:
            notifications overall: Progressions to active for those in the last three strata or any strata if traced
            notifications by age group: As previous, split by age

        """

        # Age-specific notifications
        for agegroup in COVID_BASE_AGEGROUPS:
            notification_pathways = []

            # First track all traced cases (regardless of clinical stratum, on the assumption that all traced patients
            # are identified as they are in quarantine)
            if self.is_contact_tracing:
                traced_dest_strata = {"agegroup": agegroup, "tracing": Tracing.TRACED}
                name = f"progress_{Tracing.TRACED}Xagegroup_{agegroup}"
                notification_pathways.append(name)
                self.model.request_output_for_flow(
                    name=name, flow_name=PROGRESS, dest_strata=traced_dest_strata, save_results=False,
                )

            # Then track untraced cases that are passively detected - either regardless of tracing status,
            # (empty dictionary if tracing not active) or just in the untraced group otherwise
            for clinical in NOTIFICATION_CLINICAL_STRATA:

                # Include reporting adjustment if requested
                reporting = hospital_reporting if clinical in HOSTPIALISED_CLINICAL_STRATA else 1.
                untraced_dest_strata = {"clinical": clinical, "agegroup": agegroup}
                untraced_dest_strata.update(self.untraced_stratum)
                notified_hospitalisations = f"_progress_{Tracing.UNTRACED}Xagegroup_{agegroup}Xclinical_{clinical}"
                self.model.request_output_for_flow(
                    name=notified_hospitalisations, flow_name=PROGRESS, dest_strata=untraced_dest_strata,
                    save_results=False,
                )

                # Has to have a different name to the flow output to avoid summer error
                name = notified_hospitalisations[1:]
                notification_pathways.append(name)
                self.model.request_function_output(
                    name=name, func=lambda rate: rate * reporting, sources=(notified_hospitalisations,),
                    save_results=False,
                )

            final_name = f"{NOTIFICATIONS}Xagegroup_{agegroup}"
            self.model.request_aggregate_output(name=final_name, sources=notification_pathways)

        notifications_by_agegroup = [f"{NOTIFICATIONS}Xagegroup_{i_age}" for i_age in COVID_BASE_AGEGROUPS]
        self.model.request_aggregate_output(name=NOTIFICATIONS, sources=notifications_by_agegroup)

        # Cumulative unstratified notifications
        if cumul_inc_start_time:
            self.model.request_cumulative_output(
                name=f"accum_{NOTIFICATIONS}", source=NOTIFICATIONS, start_time=cumul_inc_start_time,
            )

    def request_non_hosp_notifications(self):
        """
        Calculate the rate of non-hospitalised notifications over time and
        the prevalence of the non-hospitalised notifications to reflect the number of quarantined persons.

        Generates the following derived outputs:
            non hospitalised notifications: Age specific notifications in Symptomatic ambulatory ever detected
            prevalence non hospitalised notifications: Age specific outputs. For the traced everyone in notification
            clinical strata and infectious compartments. For the untraced symptomatic detected in active compartments.

        """

        # Age-specific non-hospitalised notifications
        for agegroup in COVID_BASE_AGEGROUPS:
            age_notification_pathways = []

            # First track all traced cases (in Symptomatic ambulatory ever detected)
            if self.is_contact_tracing:
                name = f"progress_traced_non_hospitalisedXagegroup_{agegroup}"
                age_notification_pathways.append(name)
                self.model.request_output_for_flow(
                    name=name,
                    flow_name=PROGRESS,
                    dest_strata={"clinical": Clinical.SYMPT_ISOLATE, "tracing": Tracing.TRACED, "agegroup": agegroup},
                    save_results=False,
                )

            # Then track untraced cases in Symptomatic ambulatory ever detected
            name = f"progress_non_hosp_Xclinical_{Clinical.SYMPT_ISOLATE}Xagegroup_{agegroup}Xtraced_{Tracing.UNTRACED}"
            dest_strata = {"clinical": Clinical.SYMPT_ISOLATE, "agegroup": agegroup}.update(self.untraced_stratum)
            age_notification_pathways.append(name)
            self.model.request_output_for_flow(
                name=name,
                flow_name=PROGRESS,
                dest_strata=dest_strata,
                save_results=False,
            )
            agg_name = f"non_hospitalised_notificationsXagegroup_{agegroup}"
            self.model.request_aggregate_output(name=agg_name, sources=age_notification_pathways)

        # calculating the prevalence of the non hospitalised notifications by age group
        for agegroup in COVID_BASE_AGEGROUPS:
            age_notification_pathways = []

            # First track traced cases in all clinical strata except hospitalisations
            if self.is_contact_tracing:
                for clinical in NOTIFICATION_CLINICAL_STRATA:
                    name = f"progress_prevalence_traced_X{agegroup}X{clinical}"
                    age_notification_pathways.append(name)
                    self.model.request_output_for_compartments(
                        name=name,
                        compartments=[Compartment.LATE_ACTIVE],
                        strata={"clinical": clinical, "agegroup": agegroup, "tracing": Tracing.TRACED},
                    )

            # Then track untraced cases (everyone in notified clinical stratum)
            dest_strata = {"clinical": Clinical.SYMPT_ISOLATE, "agegroup": agegroup}.update(self.untraced_stratum)
            name = f"progress_prevalence_{Tracing.UNTRACED}Xagegroup_{agegroup}Xclinical_{Clinical.SYMPT_ISOLATE}"
            age_notification_pathways.append(name)
            self.model.request_output_for_compartments(
                name=name,
                compartments=[Compartment.LATE_ACTIVE],
                strata=dest_strata,
            )

            self.model.request_aggregate_output(
                name=f"prevalence_non_hospitalised_notificationsXagegroup_{agegroup}", sources=age_notification_pathways
            )

    def request_adult_paeds_notifications(self):
        """
        Split the age-specific notifications previously generated into paediatric and adult.

        Generates the following derived outputs:
            notificationsXpaediatric: Notifications for those aged under 15
            notificationsXadult: Notifications for those aged 15 and above

        """

        # Split by child and adult
        paed_notifications = [f"notificationsXagegroup_{agegroup}" for agegroup in COVID_BASE_AGEGROUPS[:3]]
        adult_notifications = [f"notificationsXagegroup_{agegroup}" for agegroup in COVID_BASE_AGEGROUPS[3:]]
        self.model.request_aggregate_output(name="notificationsXpaediatric", sources=paed_notifications)
        self.model.request_aggregate_output(name="notificationsXadult", sources=adult_notifications)

    def request_cdr(self):
        """
        Just make the computed value CDR (case detection rate) available as a derived output.

        """

        self.model.request_computed_value_output("cdr")

    def request_deaths(self):
        """
        Track COVID-19-related deaths.

        Generates the following derived outputs both overall and by age group:
            infection_deaths: Rate of deaths over time
            accum_deaths: Cumulative deaths that have accrued by that point in time

        """

        # Unstratified
        self.model.request_output_for_flow(name=INFECTION_DEATHS, flow_name=INFECT_DEATH)
        self.model.request_cumulative_output(name="accum_deaths", source="infection_deaths")

        # Stratified by age
        self.request_stratified_output_for_flow(
            INFECT_DEATH, COVID_BASE_AGEGROUPS, "agegroup", name_stem=INFECTION_DEATHS, filter_on="source"
        )
        for agegroup in COVID_BASE_AGEGROUPS:
            self.model.request_cumulative_output(
                name=f"accum_deathsXagegroup_{agegroup}",
                source=f"infection_deathsXagegroup_{agegroup}",
            )

        # Stratified by age and clinical stratum
        self.request_double_stratified_output_for_flow(
            INFECT_DEATH, COVID_BASE_AGEGROUPS, "agegroup", CLINICAL_STRATA, "clinical", name_stem=INFECTION_DEATHS,
            filter_on="source"
        )

    def request_admissions(self):
        """
        Track COVID-19-attributable admissions to hospital and to ICU.

        Generates the following derived outputs both overall and by age group:
            icu_admissions: Only those being admitted to ICU
            hospital_admissions: All those being admitted to hospital

        """

        # Track non-ICU hospital admissions (transition from early to late active in hospital, non-ICU stratum)
        self.model.request_output_for_flow(
            name="non_icu_admissions",
            flow_name=PROGRESS,
            source_strata={"clinical": Clinical.HOSPITAL_NON_ICU},
            dest_strata={"clinical": Clinical.HOSPITAL_NON_ICU},
            save_results=False,
        )

        # Track ICU admissions (transition from early to late active in ICU stratum)
        self.model.request_output_for_flow(
            name="icu_admissions",
            flow_name=PROGRESS,
            source_strata={"clinical": Clinical.ICU},
            dest_strata={"clinical": Clinical.ICU},
        )

        # Track all hospitalisations as the sum of hospital non-ICU and ICU
        self.model.request_aggregate_output("hospital_admissions", sources=["icu_admissions", "non_icu_admissions"])

        for agegroup in COVID_BASE_AGEGROUPS:
            self.model.request_output_for_flow(
                name=f"non_icu_admissionsXagegroup_{agegroup}",
                flow_name=PROGRESS,
                source_strata={"clinical": Clinical.HOSPITAL_NON_ICU, "agegroup": agegroup},
                dest_strata={"clinical": Clinical.HOSPITAL_NON_ICU, "agegroup": agegroup},
                save_results=False,
            )
            self.model.request_output_for_flow(
                name=f"icu_admissionsXagegroup_{agegroup}",
                flow_name=PROGRESS,
                source_strata={"clinical": Clinical.ICU, "agegroup": agegroup},
                dest_strata={"clinical": Clinical.ICU, "agegroup": agegroup},
                save_results=False,
            )
            self.model.request_aggregate_output(
                f"hospital_admissionsXagegroup_{agegroup}",
                sources=[f"icu_admissionsXagegroup_{agegroup}", f"non_icu_admissionsXagegroup_{agegroup}"]
            )

    def request_occupancy(self, sojourn_periods: Sojourn):
        """
        Track the number of people in hospital or in ICU over time.

        Args:
            sojourn_periods: The sojourn periods for the hospitalised compartments

        Generates the following derived outputs both overall and by age group:
            icu_occupancy: Only those currently in ICU
            hospital_occupancy: All those currently in hospital

        """

        # Hospital occupancy is represented as all ICU, all hospital late active, and some early active ICU cases
        compartment_periods = sojourn_periods
        icu_early_period = compartment_periods["icu_early"]
        hospital_early_period = compartment_periods["hospital_early"]
        period_icu_patients_in_hospital = max(icu_early_period - hospital_early_period, 0.)
        proportion_icu_patients_in_hospital = period_icu_patients_in_hospital / icu_early_period

        # Unstratified calculations
        self.model.request_output_for_compartments(
            "icu_occupancy",
            compartments=[Compartment.LATE_ACTIVE],
            strata={"clinical": Clinical.ICU},
        )
        self.model.request_output_for_compartments(
            "_late_active_hospital",
            compartments=[Compartment.LATE_ACTIVE],
            strata={"clinical": Clinical.HOSPITAL_NON_ICU},
            save_results=False,
        )
        self.model.request_output_for_compartments(
            "_early_active_icu",
            compartments=[Compartment.EARLY_ACTIVE],
            strata={"clinical": Clinical.ICU},
            save_results=False,
        )
        self.model.request_function_output(
            name="_early_active_icu_proportion",
            func=lambda patients: patients * proportion_icu_patients_in_hospital,
            sources=["_early_active_icu"],
            save_results=False,
        )
        self.model.request_aggregate_output(
            name="hospital_occupancy",
            sources=[
                "_late_active_hospital",
                "icu_occupancy",
                "_early_active_icu_proportion",
            ],
        )

        # Stratified by age group
        for agegroup in COVID_BASE_AGEGROUPS:

            age_icu_name = f"icu_occupancyXagegroup_{agegroup}"
            self.model.request_output_for_compartments(
                name=age_icu_name,
                compartments=[Compartment.LATE_ACTIVE],
                strata={"clinical": Clinical.ICU, "agegroup": agegroup},
                save_results=True,
            )
            age_late_hospital_name = f"late_hospitalXagegroup_{agegroup}"
            self.model.request_output_for_compartments(
                name=age_late_hospital_name,
                compartments=[Compartment.LATE_ACTIVE],
                strata={"clinical": Clinical.HOSPITAL_NON_ICU, "agegroup": agegroup},
                save_results=False,
            )
            age_icu_ealy_name = f"early_active_icuXagegroup_{agegroup}"
            self.model.request_output_for_compartments(
                name=age_icu_ealy_name,
                compartments=[Compartment.EARLY_ACTIVE],
                strata={"clinical": Clinical.ICU, "agegroup": agegroup},
                save_results=False,
            )
            age_icu_early_in_hospital_name = f"early_active_icu_in_hospitalXagegroup_{agegroup}"
            self.model.request_function_output(
                name=age_icu_early_in_hospital_name,
                func=lambda patients: patients * proportion_icu_patients_in_hospital,
                sources=[age_icu_ealy_name],
                save_results=False,
            )
            self.model.request_aggregate_output(
                name=f"hospital_occupancyXagegroup_{agegroup}",
                sources=[
                    age_icu_name,
                    age_late_hospital_name,
                    age_icu_early_in_hospital_name,
                ],
            )

    def request_tracing(self):
        """
        Collate up all the computed values used during the process of working out the effect of contact tracing.

        Additionally generates the following derived outputs:
            prop_contacts_quarantined: The proportion of all contacts (including those of undetected cases) identified

        """

        # Standard calculations always computed when contact tracing requested
        self.model.request_computed_value_output("prevalence")
        self.model.request_computed_value_output("prop_detected_traced")
        self.model.request_computed_value_output("prop_contacts_with_detected_index")
        self.model.request_computed_value_output("traced_flow_rate")

        # Proportion of quarantined contacts among all contacts
        self.model.request_function_output(
            name="prop_contacts_quarantined",
            func=lambda prop_detected_traced, prop_detected_index: prop_detected_traced * prop_detected_index,
            sources=["prop_detected_traced", "prop_contacts_with_detected_index"],
        )

    def request_strains(self, voc_names: List[str]):
        """
        VoC-related outputs.

        Args:
            voc_names: The names of all the VoCs being implemented in the model

        Generates the following derived outputs:
            incidence (by strain): See above for definition
            prop_incidence (by strain): Proportion of incidence attributable to that strain

        """

        # Incidence rate for each strain implemented
        all_strains = [Strain.WILD_TYPE] + voc_names
        self.request_stratified_output_for_flow(INCIDENCE, all_strains, "strain")

        # Convert to a proportion
        for strain in all_strains:
            self.model.request_function_output(
                name=f"prop_{INCIDENCE}_strain_{strain}",
                func=lambda strain_inc, total_inc: strain_inc / total_inc,
                sources=[f"{INCIDENCE}Xstrain_{strain}", INCIDENCE]
            )

    def request_vaccination(self, is_dosing_active: bool, vacc_strata: List[str]):
        """
        Vaccination-related outputs

        Args:
            is_dosing_active: Are we including an additional model stratum for those who have been fully vaccinated
            vacc_strata: All the vaccination strata being implemented

        Generates the following derived outputs:
            proportions of people (by vaccination status): See the population distribution by vaccination status
            at_least_one_dose_prop: To facilitate calculating total vaccination coverage, regardless of model structure

        """

        # Track proportions vaccinated by vaccination status (depends on total_population previously being requested)
        for vacc_stratum in vacc_strata:
            self.model.request_output_for_compartments(
                name=f"_{vacc_stratum}",
                compartments=COMPARTMENTS,
                strata={"vaccination": vacc_stratum},
                save_results=False,
            )
            self.model.request_function_output(
                name=f"proportion_{vacc_stratum}",
                sources=[f"_{vacc_stratum}", "total_population"],
                func=lambda vaccinated, total: vaccinated / total,
            )

        if is_dosing_active:
            self.model.request_function_output(
                name="at_least_one_dose_prop",
                func=lambda unvacc, pop: 1. - unvacc / pop,
                sources=[f"_{Vaccination.UNVACCINATED}", "total_population"]
            )

    def request_vacc_aefis(self, vacc_risk_params: VaccinationRisk):
        """
        Calculate the risk of TTS and myocarditis for the vaccines predominantly responsible for these.

        Args:
            vacc_risk_params: The parameters defining vaccine-related risk (i.e. AEFIs)

        """

        risk_multiplier = vacc_risk_params.risk_multiplier

        # Track the rate of adverse events and hospitalisations by age, if adverse events calculations are requested
        self.request_stratified_output_for_flow("vaccination", COVID_BASE_AGEGROUPS, "agegroup", filter_on="source")

        for agegroup in COVID_BASE_AGEGROUPS:
            agegroup_string = f"agegroup_{agegroup}"

            # TTS for AstraZeneca vaccines
            self.model.request_function_output(
                name=f"tts_casesX{agegroup_string}",
                sources=[f"vaccinationX{agegroup_string}"],
                func=lambda vaccinated:
                vaccinated * vacc_risk_params.tts_rate[agegroup] * vacc_risk_params.prop_astrazeneca * risk_multiplier
            )
            self.model.request_function_output(
                name=f"tts_deathsX{agegroup_string}",
                sources=[f"tts_casesX{agegroup_string}"],
                func=lambda tts_cases:
                tts_cases * vacc_risk_params.tts_fatality_ratio[agegroup]
            )

            # Myocarditis for mRNA vaccines
            self.model.request_function_output(
                name=f"myocarditis_casesX{agegroup_string}",
                sources=[f"vaccinationX{agegroup_string}"],
                func=lambda vaccinated:
                vaccinated * vacc_risk_params.myocarditis_rate[agegroup] * vacc_risk_params.prop_mrna * risk_multiplier
            )

        # Aggregate using larger age-groups
        aggregated_age_groups = {"15_19": ["15"]}
        for age_min in [20 + i * 10 for i in range(5)]:
            age_max = age_min + 9
            aggregated_age_groups[f"{age_min}_{age_max}"] = [str(age_min), str(age_min + 5)]
        aggregated_age_groups["70_plus"] = ["70", "75"]

        cumul_start_time = None if not vacc_risk_params.cumul_start_time else vacc_risk_params.cumul_start_time
        for output_type in ["tts_cases", "tts_deaths", "myocarditis_cases", "hospital_admissions"]:
            for aggregated_age_group, agegroups in aggregated_age_groups.items():
                agg_output = f"{output_type}Xagg_age_{aggregated_age_group}"
                agg_output_sources = [f"{output_type}Xagegroup_{agegroup}" for agegroup in agegroups]
                self.model.request_aggregate_output(name=agg_output, sources=agg_output_sources)

                # Cumulative output calculation
                self.model.request_cumulative_output(
                    name=f"cumulative_{agg_output}",
                    source=agg_output,
                    start_time=cumul_start_time
                )

    def request_experienced(self):
        """
        History status-related outputs.
        Arguably the naive group should exclude the people with active infection - this is not currently being done.

        Generates the following derived outputs:
            proportions of people (by history status)
            prop_ever_infected: Everyone who is not infection-naive

        """

        self.request_stratified_output_for_compartment("prop", COMPARTMENTS, HISTORY_STRATA, "history")
        self.model.request_function_output(
            name="prop_ever_infected",
            func=get_complement_prop,
            sources=[f"propXhistory_{History.NAIVE}", "total_population"],
        )

        # Stratified by age group
        self.request_stratified_output_for_compartment(
            "total_population", COMPARTMENTS, COVID_BASE_AGEGROUPS, "agegroup", save_results=False
        )
        for agegroup in COVID_BASE_AGEGROUPS:
            for stratum in HISTORY_STRATA:
                name = f"prop_{stratum}Xagegroup{agegroup}"
                self.model.request_output_for_compartments(
                    name=name,
                    compartments=COMPARTMENTS,
                    strata={"history": stratum, "agegroup": agegroup},
                )
            self.model.request_function_output(
                name=f"prop_ever_infectedXagegroup_{agegroup}",
                func=get_complement_prop,
                sources=[f"prop_{History.NAIVE}Xagegroup{agegroup}", f"total_populationXagegroup_{agegroup}"],
            )

    def request_time(self):
        """
        Currently not used, but available if needed.

        """

        self.model.request_computed_value_output("time_process")


"""
O programa recebe uma frase qualquer e retorna vários dados a respeito dela,
como a quantidade de letras da frase, o número de vogais e consoantes, a quantidade
de palavras da frase, a quantidade de vogais que se repetem e a frase em forma
cifrada.
"""
print("Informe uma frase qualquer: ")
nome = str(input())

def funcoesSimples():
    print("A frase informada foi:", nome)
    qtdchar = len(nome)  # quantidade de caracteres

    frase = nome
    frase.split(" ")
    qtdpalavras = 1  # quantidade de palavras
    nespacos = 0  # numero de espaços
    for i in range(len(nome)):
        if frase[i].isspace():
            qtdpalavras = qtdpalavras + 1
            nespacos = nespacos + 1

    qtdchar = qtdchar - nespacos
    nvogais = 0  # número de vogais
    nconsoantes = 0  # número de consoantes
    qta, qte, qti, qto, qtu = 0, 0, 0, 0, 0  # qts são variáveis para armazenar a quantidade de vogais

    alphabet = 'abcdefghijklmnopqrstuvwxyz'
    alphabet.split(" ")
    frasecifrada = []

    for i in range(len(nome)):
        if nome[i] == 'a' or nome[i] == 'e' or nome[i] == 'i' or nome[i] == 'o' or nome[i] == 'u':
            letra = nome[i]
            if letra in alphabet:
                pos = alphabet.index(letra)
                frasecifrada.append(alphabet[pos + 3])

        elif nome[i] == ' ':
            frasecifrada.append(' ')

        elif nome[i] == 'b' or nome[i] == 'c' or nome[i] == 'd' or nome[i] == 'f':
            letra = nome[i]
            if letra in alphabet:
                pos = alphabet.index(letra)  # pos se refere ao índice da letra no alfabeto
                frasecifrada.append(alphabet[pos - 6 + 26])
        else:
            letra = nome[i]
            if letra in alphabet:
                pos = alphabet.index(letra)
                frasecifrada.append(alphabet[pos - 6])

    for i in range(len(nome)):
        if frase[i] == 'A' or frase[i] == 'a':
            nvogais = nvogais + 1
            qta = qta + 1
        elif frase[i] == 'E' or frase[i] == 'e':
            nvogais = nvogais + 1
            qte = qte + 1
        elif frase[i] == 'I' or frase[i] == 'i':
            nvogais = nvogais + 1
            qti = qti + 1
        elif frase[i] == 'O' or frase[i] == 'o':
            nvogais = nvogais + 1
            qto = qto + 1
        elif frase[i] == 'U' or frase[i] == 'u':
            nvogais = nvogais + 1
            qtu = qtu + 1
        else:
            nconsoantes = nconsoantes + 1

    nconsoantes = nconsoantes - nespacos
    print("A qtd de letras é %i" % qtdchar)
    print("O numero de vogais eh: ", nvogais)
    print("O numero de consoantes eh: ", nconsoantes)
    print("A quantidade de palavras eh: ", qtdpalavras)
    print(f"A quantidade de vogais que se repetem eh: A={qta} E={qte} I={qti} O={qto} U={qtu}")
    print(f"A frase normal eh: '{''.join(nome)}' e a frase cifrada eh: '{''.join(frasecifrada)}'")

funcoesSimples()

Data structure/Manipulacao completa de frases.py


from django.db import models
from django.utils import timezone
from uuid import uuid4
import os

def rename_imagefile_to_uuid(instance, filename):
    upload_to = f"uploads"
    ext = filename.split(".")[-1]
    uuid = uuid4().hex
    filename = "{}.{}".format(uuid, ext)

    return os.path.join(upload_to, filename)

class AttachFile(models.Model):
    file_id = models.AutoField(primary_key=True)
    file = models.FileField(upload_to=rename_imagefile_to_uuid, default="")
    use_yn = models.BooleanField(default=True)
    del_yn = models.BooleanField(default=False)
    write_date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    board = models.ForeignKey('board', on_delete=models.DO_NOTHING, null=True)

    class Meta:
        db_table = "attachFile"

class Topic(models.Model):
    topic_id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=30)
    use_yn = models.BooleanField(blank=True, default=True)

    def get_all_topics():
        return Topic.objects.all()

    class Meta:
        db_table = "topic"

class Board(models.Model):
    board_id = models.AutoField(primary_key=True)
    title = models.TextField()
    content = models.TextField()
    use_yn = models.BooleanField(blank=True, default=True)
    del_yn = models.BooleanField(blank=True, default=False)
    write_date = models.DateTimeField(default=timezone.now)
    modif_date = models.DateTimeField(default=timezone.now)
    viewcount = models.IntegerField(default=0)
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE, null=True)

    def update_board(_board):
        board = Board.objects.get(pk=board_id)
        if board:
            board.title = _board.title
            board.content = _board.content
            board.topic_id = _board.topic
            board.use_yn = _board.use_yn
            board.topic = _board.topic
            board.save()
            return True
        return False

    # def save(self, *args, **kwargs):
    #     current_datetime = timezone.now()
    #     self.write_date = current_datetime
    #     super(Board, self).save(*args, **kwargs)

    class Meta:
        db_table = "board"


"""
    Problem Statement:
        1. Find the a average / mean of elements of a list

    Concept:
        1. sum of all / no of element
"""
# code below
def avg_or_mean_of_list(numbers):
    sum = 0
    for number in numbers:
        sum += number

    n = len(numbers)
    return sum/n

def avg_or_mean_of_list_v2(numbers):
    return sum(numbers)/len(numbers)

if __name__ == "__main__":
    input_str = input("Enter numbers separated by a space: ")
    # Split the input by space and convert to integers
    int_list = list(map(int, input_str.split()))

    print(avg_or_mean_of_list(int_list))
    print(avg_or_mean_of_list_v2(int_list))


# %%
# https://www.usreligioncensus.org/node/1639
import requests
import tempfile
import polars as pl
import pandas as pd

url = "https://www.usreligioncensus.org/sites/default/files/2023-06/2020_USRC_Group_Detail.xlsx"
response = requests.get(url)

with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as temp_file:
    temp_file.write(response.content)
    temp_file_path = temp_file.name

# %%
# blank line and totals on the last two lines
rel = pl.from_pandas(pd.read_excel(temp_file_path, sheet_name = "2020 Group by County")).slice(0, -2)
# %%
people = rel.select("FIPS", "State Name", "County Name", "Group Code", "Group Name", "Adherents")
congregations = rel.select("FIPS", "State Name", "County Name", "Group Code", "Group Name", "Adherents")
# %%
keep_churches = people.group_by("Group Name").count().sort("count", descending=True).slice(0, 12).select("Group Name").to_series().to_list()

top13 = people.filter(pl.col("Group Name").is_in(keep_churches))\
    .with_columns(
        pl.col("FIPS").str.slice(0, 2).alias("STATEFP"),
        pl.col("FIPS").str.slice(2).alias("COUNTYFP"))\
    .pivot(values="Adherents",
           index = ["FIPS","STATEFP", "COUNTYFP", "State Name", "County Name"],
           columns="Group Name",
           aggregate_function="first")\
    .fill_null(0)

# %%
lslice = 500
values = list(range(0, top13.shape[0], lslice))

previous = 0
for i in values:
    print(str(i))
    top13.slice(i, lslice).write_parquet("religion_census/religion_" + str(i) +".parquet")

# %%


num_q = int(input())

# 3
while num_q:

    first_line = input()  # 6 10
    x, y = first_line.split()  # ["6", "10"]
    x = int(x)
    y = int(y)

    smaller, bigger = min(x, y), max(x, y)
    dividers = []
    to_divide = 1

    # A,B의 최소공배수 * 최대공약수 = A*B

    for each in range(1, smaller+1):
        if smaller % each == 0:
            dividers.append(each)
    #print("dividers:", dividers)  # [1, 2, 3, 6]

    for d_each in reversed(dividers):
        if bigger % d_each == 0:
            gcd = d_each  # 2
            #print("lastly...:", gcd)
            break

    # 6 * 10 / 2
    print(int(smaller/gcd*bigger))

    num_q -= 1


from django.db.transaction import atomic
from django.forms import model_to_dict
from django.utils import timezone
from django.utils.translation import ugettext as _

from apps.api import TransferApi
from apps.log_databus.constants import (
    RESTORE_INDEX_SET_PREFIX,
    ArchiveExpireTime,
    ArchiveInstanceType,
)
from apps.log_databus.exceptions import (
    ArchiveIndexSetInfoNotFound,
    ArchiveIndexSetStatusError,
    ArchiveNotFound,
    CollectorActiveException,
    CollectorConfigNotExistException,
    RestoreExpired,
    RestoreNotFound,
)
from apps.log_databus.models import ArchiveConfig, CollectorConfig, RestoreConfig
from apps.log_search.constants import (
    DEFAULT_TIME_FIELD,
    InnerTag,
    TimeFieldTypeEnum,
    TimeFieldUnitEnum,
)
from apps.log_search.handlers.index_set import IndexSetHandler
from apps.log_search.models import LogIndexSetData, Scenario
from apps.utils.db import array_group, array_hash
from apps.utils.function import ignored
from apps.utils.local import get_local_param, get_request_username
from apps.utils.thread import MultiExecuteFunc
from apps.utils.time_handler import (
    format_user_time_zone,
    format_user_time_zone_humanize,
)
from bkm_space.utils import bk_biz_id_to_space_uid

class ArchiveHandler:
    def __init__(self, archive_config_id=None):
        self.archive = None
        if archive_config_id is not None:
            try:
                self.archive: ArchiveConfig = ArchiveConfig.objects.get(archive_config_id=archive_config_id)
            except ArchiveConfig.DoesNotExist:
                raise ArchiveNotFound

    @classmethod
    def to_user_time_format(cls, time):
        return format_user_time_zone(time, get_local_param("time_zone"))

    @classmethod
    def list(cls, archives):
        """
        list
        @param archives:
        @return:
        """
        archive_group = array_group(archives, "archive_config_id", True)
        archive_config_ids = list(archive_group.keys())
        archive_objs_all = ArchiveConfig.objects.filter(archive_config_id__in=archive_config_ids)
        table_ids = list()
        index_set_ids = list()
        for _obj in archive_objs_all:
            if _obj.instance_type != ArchiveInstanceType.INDEX_SET.value:
                table_ids.append(_obj.table_id)
            else:
                index_set_ids.append(_obj.instance_id)
        log_index_set_data_objs = LogIndexSetData.objects.filter(index_set_id__in=index_set_ids)
        index_set_table_ids = [_obj.result_table_id for _obj in log_index_set_data_objs]

        table_ids.extend(index_set_table_ids)
        archive_detail = array_group(TransferApi.list_result_table_snapshot({"table_ids": table_ids}), "table_id", True)
        index_set_table_id_mapping = dict()
        for _obj in log_index_set_data_objs:
            if _obj.index_set_id not in index_set_table_id_mapping:
                index_set_table_id_mapping[_obj.index_set_id] = list()
            index_set_table_id_mapping[_obj.index_set_id].append(_obj.result_table_id)
        for archive in archive_objs_all:
            if archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
                archive_group[archive.archive_config_id]["instance_name"] = archive.instance_name
                archive_group[archive.archive_config_id]["_index_set_id"] = archive.instance_id
                table_ids = index_set_table_id_mapping.get(archive.instance_id, [])
                if not table_ids:
                    for field in ["doc_count", "store_size", "index_count"]:
                        archive_group[archive.archive_config_id][field] = 0
                for table_id in table_ids:
                    for field in ["doc_count", "store_size", "index_count"]:
                        if field not in archive_group.get(archive.archive_config_id, {}):
                            archive_group[archive.archive_config_id][field] = 0
                        _num = int(archive_detail.get(table_id, {}).get(field, 0))
                        archive_group[archive.archive_config_id][field] += _num
            else:
                archive_group[archive.archive_config_id]["instance_name"] = archive.instance_name
                archive_group[archive.archive_config_id]["_collector_config_id"] = archive.collector_config_id
                for field in ["doc_count", "store_size", "index_count"]:
                    _num = int(archive_detail.get(archive.table_id, {}).get(field, 0))
                    archive_group[archive.archive_config_id][field] = _num
        return archives

    def retrieve(self, page, pagesize):
        """
        retrieve
        @param page:
        @param pagesize:
        @return:
        """
        if self.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
            table_ids = list(
                LogIndexSetData.objects.filter(index_set_id=self.archive.instance_id).values_list(
                    "result_table_id", flat=True
                )
            )
        else:
            table_ids = [self.archive.table_id]
        snapshot_info = TransferApi.list_result_table_snapshot_indices({"table_ids": table_ids})
        archive = model_to_dict(self.archive)
        indices = []
        for snapshot in snapshot_info:
            for _snapshot in snapshot:
                expired_time = (
                    ArchiveExpireTime.PERMANENT
                    if self.archive.snapshot_days == 0
                    else format_user_time_zone_humanize(_snapshot.get("expired_time"), get_local_param("time_zone"))
                )
                indices.extend(
                    [
                        {
                            **indice,
                            "start_time": self.to_user_time_format(indice.get("start_time")),
                            "end_time": self.to_user_time_format(indice.get("end_time")),
                            "expired_time": expired_time,
                            "state": _snapshot.get("state"),
                        }
                        for indice in _snapshot.get("indices", [])
                    ]
                )
        archive["indices"] = indices[page * pagesize : (page + 1) * pagesize]
        return archive

    @atomic
    def create_or_update(self, params):
        """
        create_or_update
        @param params:
        @return:
        """
        if self.archive:
            self.archive.snapshot_days = params.get("snapshot_days")
            self.archive.save()
            if self.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
                # 索引集归档需要查询当前索引集所关联的所有table_id
                index_set_data_objs = LogIndexSetData.objects.filter(index_set_id=self.archive.instance_id)
                if not index_set_data_objs:
                    raise ArchiveIndexSetInfoNotFound
                table_ids = list()
                for _obj in index_set_data_objs:
                    if _obj.apply_status != LogIndexSetData.Status.NORMAL:
                        raise ArchiveIndexSetStatusError
                    table_ids.append(_obj.result_table_id)

                multi_execute_func = MultiExecuteFunc()
                for table_id in table_ids:
                    multi_params = {"table_id": table_id, "snapshot_days": self.archive.snapshot_days}
                    multi_execute_func.append(
                        result_key=f"modify_result_table_snapshot_{table_id}",
                        func=TransferApi.modify_result_table_snapshot,
                        params=multi_params,
                    )

                multi_execute_func.run()

            else:
                meta_update_params = {"table_id": self.archive.table_id, "snapshot_days": self.archive.snapshot_days}
                TransferApi.modify_result_table_snapshot(meta_update_params)
            return model_to_dict(self.archive)
        # 只有采集项类型需要确认结果表状态
        if params["instance_type"] == ArchiveInstanceType.COLLECTOR_CONFIG.value:
            try:
                collector: CollectorConfig = CollectorConfig.objects.get(collector_config_id=params["instance_id"])
            except CollectorConfig.DoesNotExist:
                raise CollectorConfigNotExistException
            if not collector.is_active:
                raise CollectorActiveException

        if params["instance_type"] == ArchiveInstanceType.INDEX_SET.value:
            index_set_data_objs = LogIndexSetData.objects.filter(index_set_id=int(params["instance_id"]))
            if not index_set_data_objs:
                raise ArchiveIndexSetInfoNotFound
            table_ids = list()
            for _obj in index_set_data_objs:
                if _obj.apply_status != LogIndexSetData.Status.NORMAL:
                    raise ArchiveIndexSetStatusError
                table_ids.append(_obj.result_table_id)
            create_obj = ArchiveConfig.objects.create(**params)

            multi_execute_func = MultiExecuteFunc()
            for table_id in table_ids:
                multi_params = {
                    "table_id": table_id,
                    "target_snapshot_repository_name": create_obj.target_snapshot_repository_name,
                    "snapshot_days": create_obj.snapshot_days,
                }
                multi_execute_func.append(
                    result_key=f"create_result_table_snapshot_{table_id}",
                    func=TransferApi.create_result_table_snapshot,
                    params=multi_params,
                )

            multi_execute_func.run()
        else:
            create_obj = ArchiveConfig.objects.create(**params)
            meta_create_params = {
                "table_id": create_obj.table_id,
                "target_snapshot_repository_name": create_obj.target_snapshot_repository_name,
                "snapshot_days": create_obj.snapshot_days,
            }
            TransferApi.create_result_table_snapshot(meta_create_params)
        return model_to_dict(create_obj)

    @atomic
    def delete(self):
        self.archive.delete()
        if self.archive.instance_type != ArchiveInstanceType.INDEX_SET.value:
            TransferApi.delete_result_table_snapshot({"table_id": self.archive.table_id})
        else:
            table_ids = list(
                LogIndexSetData.objects.filter(index_set_id=self.archive.instance_id).values_list(
                    "result_table_id", flat=True
                )
            )
            multi_execute_func = MultiExecuteFunc()
            for table_id in table_ids:
                params = {"table_id": table_id}
                multi_execute_func.append(
                    result_key=f"delete_result_table_snapshot_{table_id}",
                    func=TransferApi.delete_result_table_snapshot,
                    params=params,
                )

            multi_execute_func.run()

    @atomic
    def restore(self, bk_biz_id, index_set_name, start_time, end_time, expired_time, notice_user):
        """
        restore
        @param bk_biz_id:
        @param index_set_name:
        @param start_time:
        @param end_time:
        @param expired_time:
        @param notice_user:
        @return:
        """
        index_set = self._create_index_set(index_set_name)
        result_errors = list()
        if self.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
            table_ids = list(
                LogIndexSetData.objects.filter(index_set_id=self.archive.instance_id).values_list(
                    "result_table_id", flat=True
                )
            )
            multi_execute_func = MultiExecuteFunc()
            for table_id in table_ids:
                params = {
                    "table_id": table_id,
                    "start_time": start_time,
                    "end_time": end_time,
                    "expired_time": expired_time,
                }
                multi_execute_func.append(
                    result_key=f"restore_{table_id}",
                    func=TransferApi.restore_result_table_snapshot,
                    params=params,
                )

            multi_result = multi_execute_func.run(return_exception=True)
            # 构建批量创建参数列表
            bulk_create_params = list()
            for table_id in table_ids:
                meta_restore_result = multi_result.get(f"restore_{table_id}", {})
                if isinstance(meta_restore_result, Exception):
                    result_errors.append({"table_id": table_id, "reason": str(meta_restore_result)})
                    continue
                params = {
                    "bk_biz_id": bk_biz_id,
                    "archive_config_id": self.archive.archive_config_id,
                    "start_time": start_time,
                    "end_time": end_time,
                    "expired_time": expired_time,
                    "index_set_name": index_set_name,
                    "notice_user": ",".join(notice_user),
                    "index_set_id": index_set.index_set_id,
                    "meta_restore_id": meta_restore_result.get("restore_id"),
                    "total_store_size": meta_restore_result.get("total_store_size"),
                    "total_doc_count": meta_restore_result.get("total_doc_count"),
                    "created_at": timezone.now(),
                    "created_by": get_request_username(),
                }
                bulk_create_params.append(RestoreConfig(**params))
            if bulk_create_params:
                RestoreConfig.objects.bulk_create(bulk_create_params)
                objs = RestoreConfig.objects.filter(
                    archive_config_id=self.archive.archive_config_id, index_set_name=index_set_name
                )
                restore_config_info = [model_to_dict(obj) for obj in objs]
            else:
                restore_config_info = list()
        else:
            create_restore_config = RestoreConfig.objects.create(
                **{
                    "bk_biz_id": bk_biz_id,
                    "archive_config_id": self.archive.archive_config_id,
                    "start_time": start_time,
                    "end_time": end_time,
                    "expired_time": expired_time,
                    "index_set_name": index_set_name,
                    "notice_user": ",".join(notice_user),
                }
            )

            meta_params = {
                "table_id": self.archive.table_id,
                "start_time": start_time,
                "end_time": end_time,
                "expired_time": expired_time,
            }
            meta_restore_result = TransferApi.restore_result_table_snapshot(meta_params)

            create_restore_config.index_set_id = index_set.index_set_id
            create_restore_config.meta_restore_id = meta_restore_result["restore_id"]
            create_restore_config.total_store_size = meta_restore_result["total_store_size"]
            create_restore_config.total_doc_count = meta_restore_result["total_doc_count"]
            create_restore_config.save()
            restore_config_info = [model_to_dict(create_restore_config)]

        res = {
            "index_set_id": index_set.index_set_id,
            "index_set_name": index_set.index_set_name,
            "restore_config_info": restore_config_info,
            "errors": result_errors,
        }

        return res

    def _create_index_set(self, index_set_name):
        index_set_name = _("[回溯]") + index_set_name
        if self.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
            table_ids = list(
                LogIndexSetData.objects.filter(index_set_id=self.archive.instance_id).values_list(
                    "result_table_id", flat=True
                )
            )
            indexes = [
                {
                    "bk_biz_id": self.archive.bk_biz_id,
                    "result_table_id": f"{RESTORE_INDEX_SET_PREFIX}*{table_id.replace('.', '_')}_*",
                    "result_table_name": self.archive.instance_name,
                    "time_field": DEFAULT_TIME_FIELD,
                }
                for table_id in table_ids
            ]
            # 索引集下所包含的物理索引一定存在一个集群 所以取第一个result_table_id去获取集群信息
            cluster_infos = TransferApi.get_result_table_storage(
                {"result_table_list": table_ids[0], "storage_type": "elasticsearch"}
            )
            cluster_info = cluster_infos.get(table_ids[0])
        else:
            indexes = [
                {
                    "bk_biz_id": self.archive.bk_biz_id,
                    "result_table_id": f"{RESTORE_INDEX_SET_PREFIX}*{self.archive.table_id.replace('.', '_')}_*",
                    "result_table_name": self.archive.instance_name,
                    "time_field": DEFAULT_TIME_FIELD,
                }
            ]

            cluster_infos = TransferApi.get_result_table_storage(
                {"result_table_list": self.archive.table_id, "storage_type": "elasticsearch"}
            )
            cluster_info = cluster_infos.get(self.archive.table_id)
        storage_cluster_id = cluster_info["cluster_config"]["cluster_id"]
        index_set = IndexSetHandler.create(
            index_set_name=index_set_name,
            space_uid=bk_biz_id_to_space_uid(self.archive.bk_biz_id),
            storage_cluster_id=storage_cluster_id,
            scenario_id=Scenario.ES,
            view_roles=[],
            indexes=indexes,
            category_id=self.archive.instance.category_id,
            collector_config_id=self.archive.collector_config_id,
            time_field=DEFAULT_TIME_FIELD,
            time_field_type=TimeFieldTypeEnum.DATE.value,
            time_field_unit=TimeFieldUnitEnum.MILLISECOND.value,
        )
        index_set.set_tag(index_set.index_set_id, InnerTag.RESTORING.value)
        return index_set

    @classmethod
    def list_archive(cls, bk_biz_id):
        """
        list_archive
        @param bk_biz_id:
        @return:
        """
        return [
            {
                "archive_config_id": archive.archive_config_id,
                "instance_name": archive.instance_name,
                "instance_id": archive.instance_id,
                "_collector_config_id": archive.collector_config_id,
            }
            for archive in ArchiveConfig.objects.filter(bk_biz_id=bk_biz_id)
        ]

    @classmethod
    def list_restore(cls, restore_list):
        """
        list_restore
        @param restore_list:
        @return:
        """
        ret = []
        instances = restore_list.serializer.instance
        for instance in instances:
            # archive config maybe delete so not show restore
            with ignored(ArchiveConfig.DoesNotExist):
                ret.append(
                    {
                        "restore_config_id": instance.restore_config_id,
                        "index_set_name": instance.index_set_name,
                        "index_set_id": instance.index_set_id,
                        "start_time": cls.to_user_time_format(instance.start_time),
                        "end_time": cls.to_user_time_format(instance.end_time),
                        "expired_time": cls.to_user_time_format(instance.expired_time),
                        "total_store_size": instance.total_store_size,
                        "instance_id": instance.archive.instance_id,
                        "instance_name": instance.archive.instance_name,
                        "instance_type": instance.archive.instance_type,
                        "_collector_config_id": instance.archive.collector_config_id,
                        "archive_config_id": instance.archive.archive_config_id,
                        "notice_user": instance.notice_user.split(","),
                        "is_expired": instance.is_expired(),
                    }
                )
        return ret

    @classmethod
    @atomic
    def update_restore(cls, restore_config_id, expired_time):
        """
        update_restore
        @param restore_config_id:
        @param expired_time:
        @return:
        """
        try:
            restore: RestoreConfig = RestoreConfig.objects.get(restore_config_id=restore_config_id)
        except RestoreConfig.DoesNotExist:
            raise RestoreNotFound
        if restore.is_expired():
            raise RestoreExpired

        # 归档任务是索引集的场景下 会存在多个归档回溯
        result_errors = list()
        if restore.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
            restore_objs = RestoreConfig.objects.filter(index_set_id=restore.index_set_id)
            restore_objs.update(expired_time=expired_time)
            meta_restore_ids = list(restore_objs.values_list("meta_restore_ids", flat=True))
            multi_execute_func = MultiExecuteFunc()
            for meta_restore_id in meta_restore_ids:
                params = {"restore_id": meta_restore_id, "expired_time": expired_time}
                multi_execute_func.append(
                    result_key=f"modify_restore_result_table_snapshot_{meta_restore_id}",
                    func=TransferApi.modify_restore_result_table_snapshot,
                    params=params,
                )
            multi_execute_result = multi_execute_func.run(return_exception=True)
            for meta_restore_id in meta_restore_ids:
                multi_result = multi_execute_result.get(f"modify_restore_result_table_snapshot_{meta_restore_id}", {})
                if isinstance(multi_result, Exception):
                    result_errors.append({"meta_restore_id": meta_restore_id, "reason": str(multi_result)})

            restore_config_info = [model_to_dict(obj) for obj in restore_objs]
        else:
            restore.expired_time = expired_time
            restore.save()
            TransferApi.modify_restore_result_table_snapshot(
                {"restore_id": restore.meta_restore_id, "expired_time": expired_time}
            )

            restore_config_info = [model_to_dict(restore)]

        return {"restore_config_info": restore_config_info, "errors": result_errors}

    @classmethod
    @atomic
    def delete_restore(cls, restore_config_id):
        """
        delete_restore
        @param restore_config_id:
        @return:
        """
        try:
            restore: RestoreConfig = RestoreConfig.objects.get(restore_config_id=restore_config_id)
        except RestoreConfig.DoesNotExist:
            raise RestoreNotFound
        index_set_handler = IndexSetHandler(restore.index_set_id)
        index_set_handler.stop()
        # 归档任务是索引集的场景下 会存在多个归档回溯
        if restore.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
            restore_objs = RestoreConfig.objects.filter(index_set_id=restore.index_set_id)
            meta_restore_ids = list(restore_objs.values_list("meta_restore_id", flat=True))
            restore_objs.delete()
            multi_execute_func = MultiExecuteFunc()
            for meta_restore_id in meta_restore_ids:
                params = {"restore_id": meta_restore_id}
                multi_execute_func.append(
                    result_key=f"delete_restore_result_table_snapshot_{meta_restore_id}",
                    func=TransferApi.delete_restore_result_table_snapshot,
                    params=params,
                )
            multi_execute_func.run()
        else:
            restore.delete()
            TransferApi.delete_restore_result_table_snapshot({"restore_id": restore.meta_restore_id})

    def archive_state(self):
        if self.archive.instance_type == ArchiveInstanceType.INDEX_SET.value:
            table_ids = list(
                LogIndexSetData.objects.filter(index_set_id=self.archive.instance_id).values_list(
                    "result_table_id", flat=True
                )
            )
        else:
            table_ids = [self.archive.table_id]
        return TransferApi.get_result_table_snapshot_state({"table_ids": table_ids})

    @staticmethod
    def batch_get_restore_state(restore_config_ids: list):
        """
        batch_get_restore_state
        @param restore_config_ids:
        @return:
        """
        restores = RestoreConfig.objects.filter(restore_config_id__in=restore_config_ids).values(
            "meta_restore_id", "restore_config_id"
        )
        meta_restore_ids = [restore["meta_restore_id"] for restore in restores]
        restore_hash = array_hash(restores, "meta_restore_id", "restore_config_id")
        meta_restore_states = TransferApi.get_restore_result_table_snapshot_state({"restore_ids": meta_restore_ids})
        for meta_restore_state in meta_restore_states:
            meta_restore_state["restore_config_id"] = restore_hash[meta_restore_state["restore_id"]]
        return meta_restore_states


from pypy.interpreter.error import oefmt
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app
from rpython.rlib import jit

from pypy.module._cffi_backend.cdataobj import W_CData
from pypy.module._cffi_backend.cdataobj import W_CDataPtrToStructOrUnion
from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion

class W_FunctionWrapper(W_Root):
    """A wrapper around a real W_CData which points to a function
    generated in the C code.  The real W_CData has got no struct/union
    argument (only pointers to it), and no struct/union return type
    (it is replaced by a hidden pointer as first argument).  This
    wrapper is callable, and the arguments it expects and returns
    are directly the struct/union.  Calling ffi.typeof(wrapper)
    also returns the original struct/union signature.
    """
    _immutable_ = True

    def __init__(self, space, fnptr, directfnptr, ctype,
                 locs, rawfunctype, fnname):
        assert isinstance(ctype, W_CTypeFunc)
        assert ctype.cif_descr is not None     # not for '...' functions
        assert locs is None or len(ctype.fargs) == len(locs)
        #
        self.space = space
        self.fnptr = fnptr
        self.directfnptr = directfnptr
        self.ctype = ctype
        self.locs = locs
        self.rawfunctype = rawfunctype
        self.fnname = fnname
        self.nargs_expected = len(ctype.fargs) - (locs is not None and
                                                  locs[0] == 'R')

    def typeof(self, ffi):
        return self.rawfunctype.unwrap_as_fnptr(ffi)

    @jit.unroll_safe
    def _prepare(self, args_w, start_index):
        # replaces struct/union arguments with ptr-to-struct/union arguments
        space = self.space
        locs = self.locs
        fargs = self.ctype.fargs
        for i in range(start_index, len(locs)):
            if locs[i] != 'A':
                continue
            w_arg = args_w[i]
            farg = fargs[i]      # <ptr to struct/union>
            assert isinstance(farg, W_CTypePtrOrArray)
            if isinstance(w_arg, W_CData) and w_arg.ctype is farg.ctitem:
                # fast way: we are given a W_CData "struct", so just make
                # a new W_CData "ptr-to-struct" which points to the same
                # raw memory.  We use unsafe_escaping_ptr(), so we have to
                # make sure the original 'w_arg' stays alive; the easiest
                # is to build an instance of W_CDataPtrToStructOrUnion.
                w_arg = W_CDataPtrToStructOrUnion(
                    space, w_arg.unsafe_escaping_ptr(), farg, w_arg)
            else:
                # slow way: build a new "ptr to struct" W_CData by calling
                # the equivalent of ffi.new()
                if space.is_w(w_arg, space.w_None):
                    continue
                w_arg = farg.newp(w_arg)
            args_w[i] = w_arg

    def descr_call(self, args_w):
        if len(args_w) != self.nargs_expected:
            space = self.space
            if self.nargs_expected == 0:
                raise oefmt(space.w_TypeError,
                            "%s() takes no arguments (%d given)",
                            self.fnname, len(args_w))
            elif self.nargs_expected == 1:
                raise oefmt(space.w_TypeError,
                            "%s() takes exactly one argument (%d given)",
                            self.fnname, len(args_w))
            else:
                raise oefmt(space.w_TypeError,
                            "%s() takes exactly %d arguments (%d given)",
                            self.fnname, self.nargs_expected, len(args_w))
        #
        if self.locs is not None:
            # This case is if there are structs as arguments or return values.
            # If the result we want to present to the user is "returns struct",
            # then internally allocate the struct and pass a pointer to it as
            # a first argument.
            if self.locs[0] == 'R':
                w_result_cdata = self.ctype.fargs[0].newp(self.space.w_None)
                args_w = [w_result_cdata] + args_w
                self._prepare(args_w, 1)
                self.ctype._call(self.fnptr, args_w)    # returns w_None
                assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
                return w_result_cdata.structobj
            else:
                args_w = args_w[:]
                self._prepare(args_w, 0)
        #
        return self.ctype._call(self.fnptr, args_w)

    def descr_repr(self, space):
        return space.wrap("<FFIFunctionWrapper for %s()>" % (self.fnname,))

W_FunctionWrapper.typedef = TypeDef(
        'FFIFunctionWrapper',
        __repr__ = interp2app(W_FunctionWrapper.descr_repr),
        __call__ = interp2app(W_FunctionWrapper.descr_call),
        )
W_FunctionWrapper.typedef.acceptable_as_base_class = False


import pygame
import pygame.freetype

import client.eventHandler
import client.fieldRender
import client.gameState
import client.inputProcessing
import library.constants

class Game:
    quit_game: bool
    status: library.constants.GameStatus
    window: pygame.Surface
    field_renderer: client.fieldRender.PlayingFieldRenderer

    def __init__(self):
        pygame.init()
        pygame.freetype.init()
        self.window = pygame.display.set_mode(
            library.constants.DEFAULT_RESOLUTION,
            pygame.RESIZABLE
        )
        self.field_renderer = client.fieldRender.PlayingFieldRenderer(
            self.window
        )
        self.clock = pygame.time.Clock()
        self.quit_game = False
        self.status = library.constants.GameStatus.RUNNING

        self.game_state = client.gameState.GameState()

        self.event_processor = client.eventHandler.EventHandler()
        self.mouse_processor = client.inputProcessing.MouseInput(
            self.field_renderer,
            self.game_state.player_brick
        )

        # Controls
        event_dict = self.event_processor.event_func_dict
        press_dict = self.event_processor.keydown_func_dict
        release_dict = self.event_processor.keyup_func_dict

        press_dict[pygame.K_UP] = print
        event_dict[pygame.MOUSEMOTION] = self.mouse_processor.player_mouse_input

    def process_input(self):
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                self.quit_game = True
            elif event.type != pygame.NOEVENT:
                self.event_processor.handle_event(event)

        pygame.event.clear()

    def update_state(self):
        self.clock.tick(library.constants.TICK_RATE_LIMIT)
        if self.status == library.constants.GameStatus.RUNNING:
            self.game_state.tick_state(self.clock.get_time())

    def render(self):
        if self.status == library.constants.GameStatus.RUNNING:
            # Draw field
            self.field_renderer.draw_background()
            self.field_renderer.draw_score(
                self.game_state.player_score,
                self.game_state.enemy_score
            )

            # Draw players
            self.game_state.player_brick.draw(self.field_renderer)
            self.game_state.enemy_brick.draw(self.field_renderer)

            # Draw the ball
            self.game_state.ball.draw(self.field_renderer)
        pygame.display.update()

    def run(self):
        while not self.quit_game:
            self.process_input()
            self.update_state()
            self.render()
        pygame.quit()


import sys
import urllib.error
import urllib.request
import urllib.response
import pafy
import os
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUiType
from PyQt5.uic.properties import QtGui, QtCore

from overlay import *
import humanize

ui, _ = loadUiType('main.ui')

class MainApp(QMainWindow, ui):
    def __init__(self, parent=None):
        super(MainApp, self).__init__(parent)
        QMainWindow.__init__(self)
        self.setupUi(self)
        self.initUi()
        self.handelButtons()
        ## Youtube :- one video

        ## Youtube :- playlist

    def initUi(self):

        pass

    def handelButtons(self):
        self.pushButton_6.clicked.connect(self.download)
        self.pushButton_10.clicked.connect(self.handelBrowse)
        ## Youtube
        self.pushButton_5.clicked.connect(self.getVideoData)
        self.pushButton_9.clicked.connect(self.saveBrowse)
        self.pushButton_4.clicked.connect(self.downloadVideo)
        self.pushButton_8.clicked.connect(self.save_playlist_browse)
        self.pushButton_7.clicked.connect(self.download_playlist)

        pass

    def handelProgress(self, blockNum, blockSize, totalSize):
        readedData = blockNum * blockSize
        if totalSize > 0:
            downloadPercetage = readedData * 100 / totalSize
            self.progressBar.setValue(downloadPercetage)
            QApplication.processEvents()

        pass

    def handelBrowse(self):

        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        path = QFileDialog.getSaveFileName(self, caption='Save as', directory='.', filter='All Files(*.*)',
                                           initialFilter='', options=options)
        self.lineEdit_2.setText(str(path[0]))

    def download(self):
        url = self.lineEdit.text()
        pathToSave = self.lineEdit_2.text()

        if url == '':
            QMessageBox.warning(self, "Data Error", 'Please add valid Url')
            return
        elif pathToSave == '':
            QMessageBox.warning(self, "Data Error", 'Please add valid path to save')
            return
        else:
            try:
                urllib.request.urlretrieve(url, pathToSave, self.handelProgress)
            except urllib.error.URLError as e:
                QMessageBox.warning(self, "Download Error", e.reason)
                return
        QMessageBox.information(self, 'Download Completed', 'Download Completed')
        self.lineEdit.setText('')
        self.lineEdit_2.setText('')
        self.progressBar.setValue(0)

    ###################################
    ######### Youtube one video #######
    ###################################
    def saveBrowse(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        path = QFileDialog.getSaveFileName(self, caption='Save as', directory='.', filter='All Files(*.*)',
                                           initialFilter='', options=options)
        self.lineEdit_4.setText(str(path[0]))

    def getVideoData(self):

        videoLink = self.lineEdit_3.text()
        if (videoLink == ''):

            QMessageBox.warning(self, "Data Error",
                                "Please add valid Youtube URL like this 'https://www.youtube.com/watch?v=xxxxx'")
        else:
            try:
                data = pafy.new(videoLink)
                all_streams = data.videostreams
                for stream in all_streams:
                    size = humanize.naturalsize(stream.get_filesize())
                    data = "{} - {} - {} ".format(stream.extension, stream.quality, size)
                    self.comboBox.addItem(data)
            except Exception as error:
                QMessageBox.warning(self, 'Fetch Data Error', str(error))
                return

        pass

    def downloadVideo(self):
        video_link = self.lineEdit_3.text()
        save_path = self.lineEdit_4.text()

        if video_link == '':
            QMessageBox.warning(self, "Data Error",
                                "Please add valid Youtube URL like this 'https://www.youtube.com/watch?v=xxxxx'")
            return
        elif save_path == '':
            QMessageBox.warning(self, "Data Error", 'Please add valid path to save')
            return
        else:
            video = pafy.new(video_link)
            video_stream = video.videostreams
            video_quality = self.comboBox.currentIndex()
            try:
                download = video_stream[video_quality].download(filepath=save_path, callback=self.handel_video_progress)
                QMessageBox.information(self, 'Download Completed', 'Download Completed')
            except Exception as error:
                QMessageBox.warning(self, 'Download video Error', str(error))
                return
            self.lineEdit_3.setText('')
            self.lineEdit_4.setText('')
            self.progressBar_2.setValue(0)

    def handel_video_progress(self, total, recvd, ratio, rate, eta):
        readedData = recvd
        if total > 0:
            downloadPercetage = readedData * 100 / total
            self.progressBar_2.setValue(downloadPercetage)
            QApplication.processEvents()

    ##playlist

    def download_playlist(self):
        paylist_url = self.lineEdit_6.text()
        paylist_save_path = self.lineEdit_5.text()
        if paylist_url == '':
            QMessageBox.warning(self, "Data Error",
                                "Please add valid Playlist URL like this 'https://www.youtube.com/playlist?list=xxxxx'")
            return
        elif paylist_save_path == '':
            QMessageBox.warning(self, "Data Error", 'Please add valid path to save')
            return
        else:
            try:
                playlist = pafy.get_playlist(paylist_url)
                videos = playlist['items']
                self.lcdNumber_2.display(len(playlist['items']))
                os.chdir(paylist_save_path)
                if os.path.exists(str(playlist['title'])):
                    os.chdir(str(playlist['title']))
                else:
                    os.mkdir(str(playlist['title']))
                    os.chdir(str(playlist['title']))
                current_video_in_download = 1
                # quality = self.comboBox_2.currentIndex()
                QApplication.processEvents()

                for video in playlist['items']:
                    self.lcdNumber.display(current_video_in_download)
                    current_video = video['pafy'].getbest()
                    print(current_video)
                    playlist_meta = video['playlist_meta']
                    title = playlist_meta['title']
                    thumbnail = playlist_meta['thumbnail']
                    length_seconds = round(playlist_meta['length_seconds']/60, 2)
                    self.label_4.setText(str('Now Downloading {} Length {} '.format(title, length_seconds)))

                    download = current_video.download(callback=self.handel_playlist_progress)
                    current_video_in_download += 1
                    QApplication.processEvents()
            except Exception as error:
                QMessageBox.warning(self, 'Download video Error', str(error))
                return

    def handel_playlist_progress(self, total, recvd, ratio, rate, eta):
        readedData = recvd
        if total > 0:
            rate = humanize.naturalsize(rate)
            self.label_11.setText(str(' {} '.format(rate)))
            download_percentage = readedData * 100 / total
            self.progressBar_3.setValue(download_percentage)
            remaining_time = round(eta/60, 2)
            QApplication.processEvents()

    def save_playlist_browse(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        options |= QFileDialog.DontUseCustomDirectoryIcons
        dialog = QFileDialog()
        dialog.setOptions(options)
        dialog.setFileMode(QFileDialog.DirectoryOnly)
        dialog.setDirectory('.')
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.exec_()
        path = dialog.selectedFiles()[0]
        self.lineEdit_5.setText(path)

def main():
    app = QApplication(sys.argv)
    window = MainApp()
    window.show()
    app.exec_()

if __name__ == '__main__':
    main()