import pandas as pd
import numpy as np
from similarity_learning.loading_pipeline import PairsDataFrame
from ..feature_hierarchy_encoder import FeatureHierarchyEncoder
from ..language import LanguageFeatureHierarchyEncoder
from typing import Tuple, List, Union
import logging
from .language_features_order import LanguageFeaturesOrder
from sklearn.preprocessing import MultiLabelBinarizer
from similarity_learning.exceptions import NotSetAttributeError, NotFittedError
logger = logging.getLogger("similarity_learning")

class BaselineFeatureExtractor:
    """
    Order of subtraction is chosen to increase intuitiveness (more means better, negatives means insufficient).
    Language mapping order is introduced to account for some bias towards certain languages.
    """
    def __init__(self,
                 seniority_hierarchy_mapping: List[str],
                 degree_hierarchy_mapping: List[str],
                 language_rating_hierarchy_mapping: List[str],
                 language_must_have_hierarchy_mapping: List[str],
                 language_mapping_order: LanguageFeaturesOrder = LanguageFeaturesOrder.TALENTS):

        self.language_mapping_order = language_mapping_order

        self.seniority_hierarchy_encoder = FeatureHierarchyEncoder(feature_hierarchy_mapping=seniority_hierarchy_mapping)
        self.degree_hierarchy_encoder = FeatureHierarchyEncoder(feature_hierarchy_mapping=degree_hierarchy_mapping)
        self.language_rating_hierarchy_encoder = LanguageFeatureHierarchyEncoder(feature_hierarchy_mapping=language_rating_hierarchy_mapping)
        self.language_must_have_hierarchy_encoder = LanguageFeatureHierarchyEncoder(feature_hierarchy_mapping=language_must_have_hierarchy_mapping)
        self.role_encoder = MultiLabelBinarizer()

        self._seniority_features = None
        self._salary_features = None
        self._degree_features = None
        self._language_features = None
        self._role_features = None
        self.feature_names = []

        self.__fitted = False

    def fit_transform(self,
                data: PairsDataFrame,
                )->np.ndarray:
        data = data.copy()
        if not data.add_language_feature_suffix_flag: #only this format is ok
            raise NotSetAttributeError("The language_features should be preprocessed by appyling the"
                                       " add_language_feature_suffix preprocessing step on the PairsDataFrame!")

        # N x dim
        self._seniority_features = self.compute_seniority_features(data)
        self._salary_features = self.compute_salary_features(data)
        self._degree_features = self.compute_degree_features(data)
        self._language_features = self.compute_language_features(data)
        self._role_features = self.compute_role_features(data)

        self.__fitted = True
        return np.hstack((self._seniority_features,
                         self._salary_features,
                         self._degree_features,
                         self._language_features,
                         self._role_features))

    def transform(self,
                data: PairsDataFrame,
                )->np.ndarray:
        if not self.__fitted:
            raise NotFittedError("Algorithm not yet fitted in fit_transform")
        return self.fit_transform(data)

    def compute_role_features(self, data: PairsDataFrame)->np.ndarray:
        logger.info("Computing role features: difference, overall intersection.")

        encoded_roles_talent, encoded_roles_job = self.feature_hierarchy_encoding(self.role_encoder,
                                                                                data.talents.job_roles,
                                                                                data.jobs.job_roles)

        differences = self.binary_difference_encoding(encoded_roles_job, encoded_roles_talent)
        intersection = np.array(encoded_roles_talent*encoded_roles_job).sum(axis = 1)

        feature_names = self.feature_names_from_label_encoder("job_role_diff_", self.role_encoder) + ["job_role_intersection"]
        self.feature_names.extend(feature_names)

        return np.hstack((differences, intersection[:, None]))

    def compute_language_features(self, data: PairsDataFrame) -> np.ndarray:
        logger.info("Computing language features: rating difference and must have difference.")
        # N x L
        encoded_rating_talents, encoded_rating_jobs = self.encode_language_rating(data)

        # instead of doing redundant fitting simply reuse the classes fit by the rating encoder
        self.language_must_have_hierarchy_encoder.classes_ = self.language_rating_hierarchy_encoder.classes_
        encoded_must_have_jobs = self.language_rating_hierarchy_encoder.transform(data.jobs.must_have_languages)
        encoded_title_talents = (encoded_rating_talents>0).astype(int)

        language_rating_difference_feature = np.array(encoded_rating_talents - encoded_rating_jobs)
        language_must_have_difference_feature = self.binary_difference_encoding(encoded_must_have_jobs, encoded_title_talents)

        rating_feature_names = self.feature_names_from_label_encoder("language_rating_diff_",
                                                                     self.language_rating_hierarchy_encoder)
        must_have_feature_names = self.feature_names_from_label_encoder("language_must_have_",
                                                                     self.language_rating_hierarchy_encoder)

        self.feature_names.extend(rating_feature_names + must_have_feature_names)

        return np.hstack((language_rating_difference_feature, language_must_have_difference_feature))

    def compute_degree_features(self, data: PairsDataFrame)->np.ndarray:
        logger.info("Computing seniority features: required, available, difference.")
        encoded_degree_jobs, encoded_degree_talents = self.feature_hierarchy_encoding(self.degree_hierarchy_encoder,
                                                                                data.jobs.min_degree,
                                                                                data.talents.degree)
        degree_difference = encoded_degree_jobs - encoded_degree_talents

        self.feature_names.extend(['min_degree_hierarchic_job', 'degree_hierarchic_talent', "degree_diff"])
        return np.hstack((encoded_degree_jobs.values[:, None],
                         encoded_degree_talents.values[:, None],
                         degree_difference.values[:, None]))

    def compute_salary_features(self, data: PairsDataFrame)->np.ndarray:
        logger.info("Computing salary features: difference.")
        salary_difference_feature = data.jobs.max_salary.values - data.talents.salary_expectation.values

        self.feature_names.append("salary_diff")
        return salary_difference_feature[:, None]

    def compute_seniority_features(self, data: PairsDataFrame)->np.ndarray:
        logger.info("Computing seniority features:min required, max required, actual,"
                    " difference from min required, and difference from max required.")
        encoded_seniority_jobs, encoded_seniority_talents = self.feature_hierarchy_encoding(self.seniority_hierarchy_encoder,
                                                                                data.jobs.seniorities,
                                                                                data.talents.seniority)
        max_required_seniority = encoded_seniority_jobs.apply(max).values
        min_required_seniority = encoded_seniority_jobs.apply(min).values
        encoded_seniority_talents = encoded_seniority_talents.values

        seniority_difference_feature_max = encoded_seniority_talents - max_required_seniority
        seniority_difference_feature_min = encoded_seniority_talents - min_required_seniority

        self.feature_names.extend(["min_required_seniority", "max_required_seniority", "seniority_talents", "seniority_diff_min", "seniority_diff_max"])
        return np.hstack((min_required_seniority[:, None],
                          max_required_seniority[:, None],
                          encoded_seniority_talents[:, None],
                          seniority_difference_feature_min[:, None],
                          seniority_difference_feature_max[:, None]))

    def feature_names_from_label_encoder(self, feature_prefix: str,
                                         label_encoder: Union[FeatureHierarchyEncoder, MultiLabelBinarizer])->List[str]:
        return [feature_prefix + role for role in label_encoder.classes_]

    def binary_difference_encoding(self, a: Union[np.ndarray, pd.Series], b: Union[np.ndarray, pd.Series]):
        """
        a and b are assumed to have binary values
        :param a:
        :param b:
        :return: 0 neither required nor present, 1 present but not required, -1 required but not present, 3 required and present

        """
        binary_difference = np.array(a*2 + b)
        binary_difference[binary_difference == 2] = -1
        return binary_difference

    def feature_hierarchy_encoding(self,
                                   feature_hierarchy_encoder: Union[FeatureHierarchyEncoder, MultiLabelBinarizer],
                                   features_fit_transform: pd.Series,
                                   features_transform: pd.Series) -> Tuple[Union[pd.Series, np.ndarray],...]:
        if not self.__fitted:
            encoded_a = feature_hierarchy_encoder.fit_transform(features_fit_transform)
        else:
            encoded_a = feature_hierarchy_encoder.transform(features_fit_transform)
        encoded_b = feature_hierarchy_encoder.transform(features_transform)
        return encoded_a, encoded_b

    def encode_language_rating(self, data: PairsDataFrame)->Tuple[np.ndarray,...]:
        if self.language_mapping_order == LanguageFeaturesOrder.TALENTS:
            encoded_rating_talents, encoded_rating_jobs = self.feature_hierarchy_encoding(self.language_rating_hierarchy_encoder,
                                                                                data.talents.rating_languages,
                                                                                data.jobs.rating_languages)

        elif self.language_mapping_order == LanguageFeaturesOrder.JOBS:
            encoded_rating_jobs, encoded_rating_talents = self.feature_hierarchy_encoding(self.language_rating_hierarchy_encoder,
                                                                                data.jobs.rating_languages,
                                                                                data.talents.rating_languages)

        else:
            raise ValueError(f"The value of language_mapping_order: {self.language_mapping_order}, is not supported.")
        return encoded_rating_talents, encoded_rating_jobs


"""Transform raw New York City, NY data to prepared CSV files."""

import pandas as pd
import numpy as np
import geopandas as gpd
import logging
# from tqdm import tqdm
import click
import yaml
from functools import reduce
import datetime
import re
import json

pd.options.mode.chained_assignment = None

def get_month_as_str_col(df, date_col):
    """Generate a pandas series of the month as a string in YYYY-MM format from the date column.

    Parameters
    ----------
    df : pandas df
        Pandas dataframe
    date_col : str
        Name of the column in the dataframe containing the timestamp

    Returns
    -------
    pandas series
        Pandas series with the month as string in YYYY-MM format
    """
    return df[date_col].dt.to_period("M").astype(str)

def load_evictions_data(data_path, date_col, min_year, max_year, create_geoid = True):
    """Load the evictions data, filter for the correct years, and add a column that
    contains the month.

    Parameters
    ----------
    data_path : str
        Path to raw mortgage foreclosures data in CSV format
    date_col : str
        The eviction date column from which to extract the month and year
    min_year : int
        Earliest year to include in analysis
    max_year : int
        Latest year to include in analysis

    Returns
    -------
    pandas df
        Dataframe containing evictions data
    """
    ev_raw = (
        pd.read_csv(
            data_path,
            parse_dates=[date_col],
            infer_datetime_format=True,
        )
        .dropna(how="all")
        .drop_duplicates()
    )
    ev_raw["year"] = ev_raw[date_col].dt.year
    ev_df = ev_raw[(ev_raw.year >= min_year) & (ev_raw.year <= max_year)]
    ev_df["year"] = ev_df["year"].astype(int)
    ev_df["month"] = get_month_as_str_col(ev_df, date_col)

    if create_geoid == True:
        # Convert to strings
        ev_df["state_code"] = ev_df["state_code"].astype(str).replace(r'\.0', '', regex = True)
        ev_df["county_code"] = ev_df["county_code"].astype(str).replace(r'\.0', '', regex = True)
        ev_df["tract_code"] = ev_df["tract_code"].astype(str).replace(r'\.0', '', regex = True)
        # Add zeroes as necessary
        ev_df["state_code"] = ev_df["state_code"].apply(lambda x: (((2-len(x)) * "0") + x) if len(x) < 2 else x)
        ev_df["county_code"] = ev_df["county_code"].apply(lambda x: (((3-len(x)) * "0") + x) if len(x) < 3 else x)
        ev_df["tract_code"] = ev_df["tract_code"].apply(lambda x: (((6-len(x)) * "0") + x) if len(x) < 6 else x)
        # Finally, concat everything
        ev_df["GEOID"] = (
            ev_df["state_code"] + ev_df["county_code"] + ev_df["tract_code"]
        )

    return ev_df

def aggregate_evictions_using_rate_estimates(data_frame, eviction_rates_path, estimate_var = "evictions", time_group = "year"):

    if estimate_var == "evictions":
        # Aggregate all rows to count eviction filings
        filings_df = data_frame.groupby(by=["GEOID", time_group]).size().to_frame('eviction-filings').reset_index()

        # Loading eviction rates by tract, using Eviction Lab data for 2014-2016
        eviction_rates_by_tract = pd.read_csv(eviction_rates_path)
        overall_eviction_rate = sum(eviction_rates_by_tract["Total_Evictions"])/sum(eviction_rates_by_tract["Total_Filings"])

        eviction_rates_by_tract = eviction_rates_by_tract[["GEOID", "eviction-rate"]]
        eviction_rates_by_tract["GEOID"] = eviction_rates_by_tract["GEOID"].astype(str)

        # Returning final data frame
        evictions_final = filings_df.merge(eviction_rates_by_tract, how='left', on='GEOID')
        evictions_final["eviction-rate"].fillna(overall_eviction_rate, inplace=True)
        evictions_final["evictions"] = evictions_final["eviction-filings"] * evictions_final["eviction-rate"]
        evictions_final = evictions_final[["GEOID", time_group, "eviction-filings", "evictions", "eviction-rate"]]

        # Filtering to desired county's GEOIDs only
        # evictions_final = evictions_final[evictions_final.GEOID.str.contains('^12057')]

    if estimate_var == "filings":
        # Aggregate all rows to count eviction filings
        filings_df = data_frame.groupby(by=["GEOID", time_group]).size().to_frame('evictions').reset_index()

        # Loading eviction rates by tract, using Eviction Lab data for 2014-2016
        eviction_rates_by_tract = pd.read_csv(eviction_rates_path)
        overall_eviction_rate = sum(eviction_rates_by_tract["Total_Evictions"])/sum(eviction_rates_by_tract["Total_Filings"])

        eviction_rates_by_tract = eviction_rates_by_tract[["GEOID", "eviction-rate"]]
        eviction_rates_by_tract["GEOID"] = eviction_rates_by_tract["GEOID"].astype(str)

        # Returning final data frame
        evictions_final = filings_df.merge(eviction_rates_by_tract, how='left', on='GEOID')
        evictions_final["eviction-rate"].fillna(overall_eviction_rate, inplace=True)
        evictions_final["eviction-filings"] = evictions_final["evictions"] / evictions_final["eviction-rate"]
        evictions_final = evictions_final[["GEOID", time_group, "eviction-filings", "evictions", "eviction-rate"]]

        # Filtering to desired county's GEOIDs only
        # evictions_final = evictions_final[evictions_final.GEOID.str.contains('^12057')]
    return evictions_final

def load_census_data(data_path):
    """Load the ACS data and generate relevant columns.

    Parameters
    ----------
    data_path : str
        Path to ACS data in CSV format

    Returns
    -------
    pandas df
        Dataframe containing ACS data
    """
    census_cols = {
        "DP03_0051E": "total-households",
        "DP04_0047E": "total-renter-occupied-households",
        "DP04_0046E": "total-owner-occupied-households",
        "S2506_C01_001E": "total-owner-occupied-households-mortgage",
        "B25064_001E": "median-gross-rent",
        "DP03_0062E": "median-household-income",
        "B25077_001E": "median-property-value",
        "S2506_C01_039E": "median-monthly-housing-cost",
        "S2502_C01_002E": "pct-white",
        "S2502_C01_003E": "pct-af-am",
        "S2502_C01_009E": "pct-hispanic",
        "S2502_C01_004E": "pct-am-indian",
        "S2502_C01_005E": "pct-asian",
        "S2502_C01_006E": "pct-nh-pi",
        "S2502_C01_008E": "pct-multiple",
        "S2502_C01_007E": "pct-other",
        "DP03_0119E": "pct-below-poverty-level",
        "DP03_0099E": "without-health-insurance",
        "DP03_0096E": "with-health-insurance",
        "DP02_0003E": "households-children",
        "DP02_0009E": "single-parent-household",
        "DP02_0012E": "older-adult-alone",
        "DP02_0058E": "level-of-education",
        "DP02_0095E": "immigrant-status",
        "DP02_0112E": "english-fluency",
        "DP03_0019E": "drive-to-work",
        "DP03_0021E": "public-transport-to-work",
        "DP04_0003E": "vacant-properties",
        "DP04_0014E": "live-in-mobile-home",
        "B25035_001E": "median-year-structure-built",
    }

    census_df = pd.read_csv(data_path, dtype={"GEOID": str})[
        ["GEOID"] + list(census_cols.keys())
    ].rename(columns=census_cols)

    census_df["pct-renter-occupied"] = (
        census_df["total-renter-occupied-households"] / census_df["total-households"]
    ) * 100
    census_df["pct-owner-occupied"] = (
        census_df["total-owner-occupied-households"] / census_df["total-households"]
    ) * 100
    census_df["pct-owner-occupied-mortgage"] = (
        census_df["total-owner-occupied-households-mortgage"]
        / census_df["total-households"]
    ) * 100
    census_df["pct-owner-occupied-without-mortgage"] = (
        (
            census_df["total-owner-occupied-households"]
            - census_df["total-owner-occupied-households-mortgage"]
        )
        / census_df["total-households"]
        * 100
    )
    census_df["median-house-age"] = (
        datetime.datetime.now().year - census_df["median-year-structure-built"]
    )
    census_df["pct-non-white"] = 100 - census_df["pct-white"]
    census_df["pct-without-health-insurance"] = (
        census_df["without-health-insurance"]
        / (census_df["without-health-insurance"] + census_df["with-health-insurance"])
        * 100
    )
    return census_df.drop(
        [
            "without-health-insurance",
            "with-health-insurance",
            "median-year-structure-built",
        ],
        axis=1,
    )

def get_totals_across_years(df, tract_col, data_col):
    """Get the sum and average of a datapoint across all years for a given geography.

    Parameters
    ----------
    df : pandas df
        Dataframe where each row contains a datapoint by year by geography, e.g. columns =
        ['year', 'tract', 'num_evictions']
    tract_col : str
        Name of the column that contains the tract/geography identifier
    data_col : str
        Name of the column that contains the data point to be aggregated

    Returns
    -------
     : pandas df
        Dataframe containing the sum and average of the data point across all years for each
        geography
    """
    return df.groupby(tract_col)[data_col].agg(["sum", "mean"]).reset_index()

def create_year_cols_from_df(df, data_cols, data_col_prefix_mapper, year_col, geo_col):
    """Generate the year columns from the dataframe containing yearly data in separate rows. The
    resulting dataframe will be reduced to only one row for each geography, with the yearly data
    included in separate columns with the suffix of that year. For example, "evictions" will become
    "evictions-2014".

    Parameters
    ----------
    df : pandas df
        Dataframe with yearly data, with columns containing [year, geo_col, data_col]
    data_cols : list of str
        List of data column names to retain
    data_col_prefix_mapper : dict
        Dictionary mapping original data columns to new prefixes, e.g. {'evictions': 'total-evictions'}
    year_col : str
        Name of the column containing the year information
    geo_col : str
        Name of the geographic identifier column. This will be used as the primary key

    Returns
    -------
    df
        Dataframe with the geo_col column and len(data_cols) * len(years) columns (one column per
        year per data point)
    """
    # Get the unique list of years that are present in the dataframe
    years_list = set(df[year_col].tolist())
    year_dfs = []

    # For each year, generate a dataframe containing only that year's information. Add the year to
    # the end of the data column name and rename as necessary
    for year in years_list:
        year_df = df[df[year_col] == year][[geo_col] + data_cols].rename(
            columns={
                data_col: f"{data_col_prefix_mapper.get(data_col, data_col)}-{str(year)}"
                for data_col in data_cols
            }
        )
        year_dfs.append(year_df)

    # Combine all the year dataframes
    # result = reduce(lambda left, right: pd.merge(left, right, on=geo_col), year_dfs)
    result = reduce(lambda left, right: pd.merge(left, right, on=geo_col, how="outer"), year_dfs)
    result = result.fillna(0)
    return result

def build_rename_mapper_from_df(df, chunk_name_mapper):
    """Build a dictionary where the keys are the current column names and the values are new
    column names based on an input dictionary that contains substrings to rename. For example,
    if the dataframe contains the columns 'evictions-2016' and 'evictions-2017', and the
    chunk_name_mapper contains {'evictions': 'total-evictions'}, this function would generate a new
    dictionary containing {'evictions-2016': 'total-evictions-2016', 'evictions-2017',
    'total-evictions-2017'}. This allows us to generate a dictionary to rename columns that contain
    yearly data without having to manually specify the year.

    Parameters
    ----------
    df : pandas dataframe
        Dataframe containing columns to rename
    chunk_name_mapper : dict of str
        Dict mapping the old substring to the new substring, e.g. {'evictions': 'total-evictions'}

    Returns
    -------
    dict of str
        Dict mapping the old column names to new column names
    """
    rename_mapper = {}
    for column_name_chunk in chunk_name_mapper:
        for c in df.columns:
            if column_name_chunk in c:
                rename_mapper[c] = c.replace(
                    column_name_chunk, chunk_name_mapper[column_name_chunk]
                )
    return rename_mapper

def add_yearly_rates_to_merged_df(
    merged_df, counts_to_rate_name_mapper, rate_denom_col
):
    """Add the yearly rates columns to the merged dataframe by dividing the yearly counts column by
    the rate denominator column.

    Parameters
    ----------
    merged_df : pandas df
        Dataframe containing the year counts and rate denominator columns
    counts_to_rate_name_mapper : dict
        Dictionary that maps the count column name to the rate column name, e.g.
        {'eviction-filings': 'eviction-filing-rate'}
    rate_denom_col : str
        Column containing the data to use as the denominator

    Returns
    -------
    pandas df
        Dataframe containing the yearly rates
    """
    rename_mapper = build_rename_mapper_from_df(merged_df, counts_to_rate_name_mapper)
    rates_df = (
        merged_df[rename_mapper.keys()]
        .div(merged_df[rate_denom_col], axis=0)
        .multiply(100)
        .rename(columns=rename_mapper)
    )
    return pd.concat([merged_df, rates_df,], axis=1,)

def get_counts_by_tract(mf_with_tract, output_col_name):
    """Return a dataframe containing the the mortgage foreclosure counts by Census Tract by year.

    Parameters
    ----------
    mf_with_parcels : pandas df
        Mortgage foreclosure data, where each row represents a foreclosed property and its parcel
        ID

    Returns
    -------
    pandas df
        Dataframe with columns ['year', 'GEOID', 'num_mortgage_foreclosures']
    """

    # Get the counts by census tract by year
    counts_by_ct_by_year = mf_with_tract.groupby(["year", "GEOID"]).size().to_frame('num').reset_index()

    # Reset index to add year column & clean up
    mf_counts_by_ct = (
        counts_by_ct_by_year
        .rename(columns={"num": output_col_name},) # "num_mortgage_foreclosures" or "lien-foreclosures"
    )
    return mf_counts_by_ct

def get_counts_by_month(df, month_col, counts_col_name):
    """Get the count of rows by a given column and rename the counts column.

    Parameters
    ----------
    df : pandas df
        Dataframe containing rows to count and the month column to group by
    month_col : str
        Name of the column containing the month; will be grouped by this column
    counts_col_name : str
        Name to give the column with the counts by month

    Returns
    -------
    pandas df
        Dataframe with columns = ['month', counts_col_name]
    """
    return (
        df.groupby(month_col)
        .count()[df.columns[0]]
        .reset_index()
        .rename(columns={df.columns[0]: counts_col_name})
    )

def generate_time_series_df(eviction_df):
    """Generate the dataframe containing the count of housing loss events by month. Only includes
    mortgage and tax foreclosures since we do not have the evictions by month.

    Parameters
    ----------
    mortgage_df : pandas df
        Dataframe containing the mortgage data; must include "month" column
    tax_df : pandas df
        Dataframe containing the tax data; must include "month" column

    Returns
    -------
    pandas df
        Dataframe containing the mortgage and tax foreclosures counts by month,
        columns = ['month', 'total-foreclosures', 'total-lien-foreclosures']
    """
    evictions_by_month = get_counts_by_month(eviction_df, "month", "total-eviction-filings")
    timeseries_df = evictions_by_month
    return timeseries_df

def reformat_tract_code(tract: str, state_code: str, county_code: str) -> str:
    """Helper function to return GEOIDs compatible with those in deep-dive data files.

    Parameters
    ----------
    tract : A string
        An unformatted tract code, e.g. '53.38'
    state_code : A string
        The FIPS code for the state that we want to append.
    county_code : A string
        The FIPS code for the county that we want to append.

    Returns
    -------
    str
    """

    # If the tract code contains a period, remove it, then prepend zeroes until length is 6
    if "." in tract:
        tract = tract.replace(".", "")
        num_zeroes = 6 - len(tract)
        tract = ("0" * num_zeroes) + tract
    # Else prepend zeroes until the length is 4, then add 2 zeroes to the end
    else:
        num_zeroes = 4 - len(tract)
        tract = ("0" * num_zeroes) + tract + "00"

    # Prepend state and county FIPS codes
    geoid = state_code + county_code + tract

    return geoid

@click.command()
@click.option(
    "--config_path",
    type=click.File("r"),
    help="Path to yaml containing config parameters.",
)
def main(config_path):
    """Process raw Marion, IN housing loss data and write timeseries (counts by month) data and
    aggregate data (by census tract) to CSV.

    Parameters
    ----------
    config_path : str
        Path to the yaml config file containing the input and output file paths
    """
    logging.info("Loading raw data files. This may take a few minutes.")
    config = yaml.load(config_path, yaml.SafeLoader)
    min_year = config["min_year"]
    max_year = config["max_year"]

    # The Eviction Lab data came in two files (2014-2016 and 2017-2018) with different formats, so
    # we need to read them in separately and merge them later.
    eviction_df = load_evictions_data(
        config["eviction_data_path"],
        "EXECUTED_DATE",
        min_year,
        max_year,
        create_geoid = True
    )
    census_df = load_census_data(config["acs_data_path"])

    # Generate the time series data and save output
    logging.info("Generating time series data (monthly counts of housing loss events).")

    # Calculating eviction totals separately because number of evictions does not always equal number of rows in the df
    evictions_monthly = aggregate_evictions_using_rate_estimates(
        eviction_df,
        config["path_to_eviction_filing_rates"],
        estimate_var = "filings",
        time_group = "month"
    )
    evictions_monthly = evictions_monthly[["eviction-filings", "month"]].groupby("month").sum().reset_index()

    # Creating timeseries df and merging on separate eviction counts
    timeseries_df = generate_time_series_df(eviction_df)
    timeseries_df = timeseries_df.merge(evictions_monthly, how="outer")
    timeseries_df.drop_duplicates().to_csv(
        config["timeseries_output_csv_path"], index=False
    )
    logging.info(
        "Output timeseries CSV saved to %s." % config["timeseries_output_csv_path"]
    )

    # Process evictions data--get totals/rates across the analysis period & totals/rates
    # by year
    eviction_df = aggregate_evictions_using_rate_estimates(
        eviction_df,
        config["path_to_eviction_filing_rates"],
        estimate_var = "filings",
        time_group = "year"
    )
    eviction_years_df = create_year_cols_from_df(
        eviction_df,
        ["evictions", "eviction-filings", "eviction-rate"], # "eviction-filing-rate"
        {"evictions": "total-evictions"},
        "year",
        "GEOID",
    )

    eviction_totals = get_totals_across_years(eviction_df, "GEOID", "evictions").rename(
        columns={"sum": "total-evictions", "mean": "avg-evictions"}
    )
    eviction_filing_totals = get_totals_across_years(
        eviction_df, "GEOID", "eviction-filings",
    ).rename(columns={"sum": "total-eviction-filings", "mean": "avg-eviction-filings"})

    # Join evictions, mortgage, tax, and ACS data together into a single dataframe
    merged = (
        census_df
        .merge(eviction_totals, on="GEOID", how="left")
        .merge(eviction_df[["GEOID"]].dropna().drop_duplicates(),on="GEOID",how="left",)
        .merge(eviction_years_df, on="GEOID", how="left")
        )

    merged["overall-city-eviction-rate"] = ((np.sum(merged["avg-evictions"][np.isfinite(merged["avg-evictions"])]))/sum(merged["total-renter-occupied-households"]))*100

    merged["avg-eviction-rate"] = (
        merged["avg-evictions"] / merged["total-renter-occupied-households"]
    ) * 100

    merged["ratio-to-mean-eviction-rate"] = (
        merged["avg-eviction-rate"] / merged["overall-city-eviction-rate"]
    )

    # Add geographic identifier columns
    merged = merged.rename(columns={"GEOID": "census_tract_GEOID"})
    merged["county_GEOID"] = merged["census_tract_GEOID"].apply(lambda x: x[:5])
    merged.loc[merged["county_GEOID"] == "36005", "county"] = "Bronx"
    merged.loc[merged["county_GEOID"] == "36047", "county"] = "Brooklyn"
    merged.loc[merged["county_GEOID"] == "36061", "county"] = "Manhattan"
    merged.loc[merged["county_GEOID"] == "36081", "county"] = "Queens"
    merged.loc[merged["county_GEOID"] == "36085", "county"] = "Staten Island"
    merged["state"] = "New York"

    # Write main output file to CSV
    merged.drop_duplicates().to_csv(config["output_csv_path"], index=False)
    logging.info("Output CSV saved to %s." % config["output_csv_path"])

if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO, format="%(asctime)s - %(levelname)s: %(message)s"
    )
    main()


# -*- coding: utf-8 -*-

####
#
# setuptools likes to see a name for the package,
# and it's best-practices to have the __version__
# present, too:
#
name = 'vunits'
__version__ = '0.0.4'

import os

def run_tests(python_command='python', buffer=False, failfast=False,
              verbose=False):
    """Run unit tests.

    Parameters
    ----------
        python_command : str
            Command used by terminal to run python. Default is 'python'.
        buffer : bool, optional
            The standard output and standard error streams are buffered during
            the test run. Output during a passing test is discarded. Output is
            echoed normally on test fail or error and is added to the failure
            messages. Default is False.
        failfast : bool, optional
            Stop the test run on the first error or failure. Default is False.
        verbose : bool, optional
            Verbose output. Default is False.
    """
    # Swich to test directory and run the tests
    base_path = os.getcwd()
    pmutt_path = os.path.dirname(__file__)
    test_path = os.path.join(pmutt_path, 'tests')
    os.chdir(test_path)
    test_command = '{} -m unittest'.format(python_command)
    if buffer:
        test_command += ' -b'
    if failfast:
        test_command += ' -f'
    if verbose:
        test_command += ' -v'
    os.system(test_command)
    os.chdir(base_path)


import cv2
import matplotlib
import numpy
from My_View import My_View
from My_Model import My_Model
import sys
import os
sys.path.insert(0, '/classes')

from classes.image import My_Image
from sklearn.ensemble import RandomForestClassifier

class My_Controller:
	def __init__(self):
		self.model = My_Model()
		self.view  = My_View()

#------------------Aquisição dos dados-----------------
	def readDataTrain(self, local, name_files):
		for name in name_files:
			band = name[15:17]
			img = self.readImage((local + name))
			self.saveDataTrain(band, img)
			print("Banda: "+ band + " carregado com sucesso!")

	def readDataTarget(self, local, name_file):
		band = name_file[15:17]
		data = self.readImage((local + name_file))
		self.saveDataTarget(band, data)
		print("Banda: ", band)
		print("Data target carregado com sucesso!")

	def readImage(self, path):
		img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
		return img

	def saveDataTrain(self, band, img):
		self.model.__setCollection__(band, img)

	def saveDataTarget(self, band, img):
		self.model.__setEarthTruth__(band, img)

#-----------------Fim Aquisição de Dados-----------------

#-----------------Tratamento dos Dados-----------------\
	def standardizeDataTrain(self):
		data = self.model.__getCollection__()
		final_data = []

		for image in data:
			aux_data = []
			for line in data[image]:
				for column in line:
					aux_data.append(column)

			final_data.append(aux_data)

		self.model.data_train = final_data

	def standardizeDataTarget(self):
		target = self.model.__getEarthTruth__()
		final_data = []

		for line in target['B1']:
			for column in line:
				if column != -99.0:
					final_data.append(column)

		self.model.data_target = final_data

#-----------------Fim Tratamento dos dados-----------------

#-----------------Executando RF-----------------
	def runRandomForest(self):
		rf = RandomForestClassifier(n_estimators=5, oob_score=False, random_state=0, n_jobs=-1)

		X = self.model.__getDataTrain__()
		y = self.model.__getDataTarget__()

		x_train = [X[0][:100]]
		y_train = [y[:100]]
		print("X_train:", x_train)
		print("Y_Train:", y_train)
		print(len(x_train))
		print("Iniciando função Fit")
		rf.fit(x_train, y_train)
		print("Modelo contruído!")
		print("Iniciando Predição de B2")
		print(rf.predict([X[1][:100]]))
		print(rf.predict_proba([X[1][:100]]))
		print(rf.score([X[1][:100]], y_train))

#-----------------Fim Execução RF-----------------

#-----------------Iteração com usuário-----------------
	def __RunMenu__(self):
		while True:
			option = self.view.__ShowOptions__()
			self.handleOptions(option)
			os.system("pause")
			self.clearScreen()

	def handleOptions(self, option):
		if option == 1:
			self.readDataTrain(self.model.path, self.model.files_name)
		elif option == 2:
			self.readDataTarget(self.model.path_target, self.model.file_target)
		elif option == 3:
			self.standardizeDataTrain()
			print("Imagens convertidas para Arrays 1D")
		elif option == 4:
			self.standardizeDataTarget()
			print("Imagem convertida para Array 1D")
		elif option == 5:
			self.runRandomForest()
			print("RF Executado com sucesso!")
		else:
			print("Opção não existe: reentre!")

	def clearScreen(self):
		if os.name == "posix":
			os.system("clear")
		elif os.name in ("nt", "dos", "ce"):
			os.system("CLS")
		else:
			print("Não rolou")
#-----------------Fim da Iteração com usuário-----------------


import cv2
#from matplotlib import pyplot as plt
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("IP")
parser.add_argument("MODE")
args = parser.parse_args()
if args.MODE == 'MOBILE':
    link = 'http://'+ args.IP + ':8080/video'
elif args.MODE == 'PI':
    link = 'http://' + args.IP + ':80/html/cam_pic.php'

while(True):
    # Capture frame-by-frame
    cap = cv2.VideoCapture(link)
    ret, frame = cap.read()

    # Our operations on the frame come here
    #gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Display the resulting frame
    cv2.imshow('frame',frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()


from filenames import *
import numpy as np
from gensim.models import word2vec
from math import sqrt

#################################################
# word2vec functions are defined here.
#################################################

def root_mean_square(arr):
    """
    The function calculates the root mean square of a list.

    Parameters
    ---------------
    arr: list
        The list of numbers.

    Returns
    ---------------
    rms: number
        The root mean square of the given list.
    """
    mean_square = 0
    for x in arr:
        mean_square += x*x
    mean_square /= len(arr)
    return sqrt(mean_square)

def extract_word2vec_feature(word_count, vec_size):
    """
    The function gets the feature based on the vector of each word
    acquired from word2vec model.

    Parameters
    ---------------
    word_count: int
        The number of genes to be considered as a word.
    vec_size: int
        The size of the output vector.
    """

    input_file = data_path + sentence_file + "_" + str(word_count) + ".csv"

    # load text from transcript data
    corpus = word2vec.Text8Corpus(input_file)

    # train word2vec model
    model = word2vec.Word2Vec(corpus, size=vec_size, window=5, min_count=5)

    # translate each line in transcript to a vector
    features = []
    with open(input_file) as infile:
        for line in infile:
            words = line.split()
            feature = np.zeros(vec_size)
            for word in words:
                feature += model.wv[word]
            rms = root_mean_square(feature)
            feature = [x / rms for x in feature]
            features.append(feature)

    # save features to file
    output_file = feature_path + word2vec_feature_file + "_" + str(word_count) + ".feature"
    with open(output_file, 'w+') as outfile:
        for feature in features:
            for item in feature:
                outfile.write("%f " % item)
            outfile.write("\n")

#################################################
# Main entry starts here.
#################################################

if __name__ == "__main__":
    for i in range(2, 7):
        extract_word2vec_feature(i, 256)


import opensilexClientToolsPython
from opensilexClientToolsPython.rest import ApiException

def create_experiment(pythonClient, name, start_date, objective, description, species, organisations, projects,
                      scientific_supervisors):
    """Create an experiment in Phis.

            :param pythonClient: current client in phis
            :type pythonClient: TokenGetDTO
            :param name: name of the experiment
            :type name: str
            :param start_date: start_date of the experiment
            :type start_date: str
            :param objective: objective of the experiment
            :type objective: str
            :param description: description of the experiment
            :type description: str
            :param species: species on which the experiment is/was done (list of uris)
            :type species: list[str]
            :param organisations: list of organisations related to the experiment (list of uris)
            :type organisations: list[str]
            :param projects: list of projects related to the experiment (list of uris)
            :type projects: list[str]
            :param scientific_supervisors: list of the scientific supervisors of the experiment (list of uris)
            :type scientific_supervisors: list[str]
            :return: uri corresponding to the experiment created in Phis
            :rtype: str
    """
    api_instance = opensilexClientToolsPython.ExperimentsApi(pythonClient)
    body = opensilexClientToolsPython.ExperimentCreationDTO(name=name, start_date=start_date, objective=objective,
                                                            description=description, species=species,
                                                            organisations=organisations, projects=projects,
                                                            scientific_supervisors=scientific_supervisors)
    try:
        # Add an experiment
        api_response = api_instance.create_experiment(body=body, )
        return api_response['result'][0]
    except ApiException as e:
        print("Exception when calling ExperimentsApi->create_experiment: %s\n" % e)

def find_experiment_by_name(pythonClient, name):
    """Find an experiment in Phis by its name (we suppose that experiments dont't have similar names so only
        one result will be found)

                   :param pythonClient: Current client in Phis
                   :type pythonClient: TokenGetDTO
                   :param name: Name of the experiment
                   :type name: str
                   :return: uri of the experiment
                   :rtype: list[str]
    """
    api_instance = opensilexClientToolsPython.ExperimentsApi(pythonClient)
    try:
        api_response = api_instance.search_experiments(name=name)
        return api_response['result'][0].uri
    except ApiException as e:
        print("Exception when calling ExperimentsApi->search_experiments: %s\n" % e)

def find_experiment_by_uri(pythonClient, uri):
    """Find an experiment in Phis by its uri (useful to check the validity of a provided uri for an experiment).

            :param pythonClient: current client in phis
            :type pythonClient: TokenGetDTO
            :param uri: uri of the experiment
            :type uri: str
            :return: experiment attributes
            :rtype: ExperimentGetDTO
    """
    api_instance = opensilexClientToolsPython.ExperimentsApi(pythonClient)
    try:
        # Get an experiment
        api_response = api_instance.get_experiment(uri, )
        return api_response['result']
    except ApiException as e:
        print("Exception when calling ExperimentsApi->get_experiment: %s\n" % e)

def get_experiment_name(pythonClient, experiment_uri):
    """Get the name of an experiment.

                :param pythonClient: current client in phis
                :type pythonClient: TokenGetDTO
                :param experiment_uri: uri of the experiment
                :type experiment_uri: str
                :return: name of the experimentF
                :rtype: str

    """
    api_instance = opensilexClientToolsPython.ExperimentsApi(pythonClient)
    try:
        # Get an experiment
        api_response = api_instance.get_experiment(experiment_uri, )
        return api_response['result'].name
    except ApiException as e:
        print("Exception when calling ExperimentsApi->get_experiment: %s\n" % e)

def get_experiment_period(pythonClient, experiment_uri):
    """Get the start and end date of an experiment

                :param pythonClient: current client in phis
                :type pythonClient: TokenGetDTO
                :param experiment_uri: uri of the experiment
                :type experiment_uri: str
                :return: start date and end date
                :rtype: list[datetime]

    """
    api_instance = opensilexClientToolsPython.ExperimentsApi(pythonClient)
    try:
        # Get an experiment
        api_response = api_instance.get_experiment(experiment_uri, )
        start_date = api_response['result'].start_date
        end_date = api_response['result'].end_date
        return [start_date, end_date]
    except ApiException as e:
        print("Exception when calling ExperimentsApi->get_experiment: %s\n" % e)


"""
VIEW DICTIONARY
ADD WORD AND MEANING
UPDATE WORD AND MEANING
DELETE WORD FROM RECORD
CLEAR THE ENTRY
EXIT
"""

"""
--
-- Database: `dictionary`
--

-- --------------------------------------------------------

--
-- Table structure for table `english`
--

CREATE TABLE `english` (
  `sno` int(11) NOT NULL,
  `word` varchar(25) NOT NULL,
  `definition` varchar(100) NOT NULL DEFAULT ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

--
-- Dumping data for table `english`
--

INSERT INTO `english` (`sno`, `word`, `definition`) VALUES
(1, 'table', 'to keep things'),
(2, 'rose', 'a flower in garden'),
(3, 'apple', 'a fruit'),
(4, 'head', 'a part of body'),
(5, 'pen', 'a tool to write'),
(6, 'bottle', 'to hold a quantity of water or liquid'),
(7, 'note', 'to note things'),
(8, 'pencil', 'to write something'),
(9, 'cold', 'to feel cool and freezed'),
(10, 'eraser', 'to eraser the writen things'),
(11, 'window', 'for the purpose of air and light'),
(12, 'water', 'to provide energy');
"""

from tkinter import *
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk,Image
from tkinter import messagebox
import pyttsx3

root=Tk()
import mysql.connector

root.title("Dictionary")
root.geometry("1366x768+0+0")  #resolution of ur screen
root.config(bg="#90EE90")
root.state("zoomed") #to get screen in full screen

leftFrame=Frame(root,bg="#87CEEB")   #for radiobuttons
leftFrame.pack(side=TOP,fill=X)

bottomFrame=Frame(root,bg="#90EE90",width=1988,height=520)   #for fill details
bottomFrame.pack(side=TOP,fill=X)

rightFrame = Frame(root, width=550)    #for treeview
rightFrame.place(relx = 1, x =-590, y = 30)

#****************************************************************************************

def getData(event):             #to get values when the record is clicked
    selected_row = tv.focus()
    data = tv.item(selected_row)
    global row
    row = data["values"]
    #print(row)
    i_sno.set(row[0])
    i_name.set(row[1])
    i_meaning.set(row[2])
def save():         #add the record
    global c
    db = mysql.connector.connect(host="localhost",user="root",passwd="",database="dictionary")
    con = db.cursor()
    a=i_name.get()         #to get the values from entry box
    b=i_meaning.get()
    if a=="" or b=="" :
        messagebox.showerror("Error in Input", "Please Fill all the details")
        return
    con.execute("insert into english(word, definition) values ('"+a+"','"+b+"')")
    engine = pyttsx3.init()
    engine.say("Word added successfully!")
    engine.runAndWait()
    messagebox.showinfo("Add word", "Word added successfully!")
    db.commit()
    db.close()
    aa.grid_forget()                #to vanish the label and entry box
    name.grid_forget()
    name_1.grid_forget()
    meaning_1.grid_forget()
    meaning.grid_forget()
    submit.grid_forget()
    lab1.grid_forget()
    lab2.grid_forget()
    clear()
def update():
    global c
    db = mysql.connector.connect(host="localhost",user="root",passwd="",database="dictionary")
    con = db.cursor()
    a=i_name.get()
    b=i_meaning.get()
    d=i_sno.get()
    if a=="" or b=="" or d=="":
        messagebox.showerror("Error in Input", "Please Fill all the details")
        return
    con.execute("update english set word='"+a+"', definition='"+b+"' where sno='%s'"%(d))
    db.commit()
    db.close()
    engine = pyttsx3.init()
    engine.say("Word updated successfully, click on to view dictionary to see the changes made ")
    engine.runAndWait()
    messagebox.showinfo("Update word", "Updation successful!")
    bb.grid_forget()
    name.grid_forget()
    name_1.grid_forget()
    meaning_1.grid_forget()
    meaning.grid_forget()
    sno.grid_forget()
    submit.grid_forget()
    sno_1.grid_forget()
    lab1.grid_forget()
    lab2.grid_forget()
    clear()
def delete():
    global c
    db = mysql.connector.connect(host="localhost",user="root",passwd="",database="dictionary")
    con = db.cursor()
    e=i_sno.get()
    if e=="":
        messagebox.showerror("Error in Input", "Please Fill all the details")
        return
    con.execute("delete from english where sno='%s'"%(e))
    db.commit()
    db.commit()
    db.close()
    engine = pyttsx3.init()
    engine.say("Word deleted")
    engine.runAndWait()
    messagebox.showinfo("Delete word", "Word deleted successfully!")
    clear()
    sno.grid_forget()
    sno_1.grid_forget()
    d.grid_forget()
    submit.grid_forget()

def clear():
    if i_sno != ""  :
        i_sno.set("")                 #to clear the entry box
        i_name.set("")
        i_meaning.set("")

    else:
        i_name.set("")
        i_meaning.set("")

def search():                     #like query
    db = mysql.connector.connect(host="localhost",user="root",passwd="",database="dictionary")
    con = db.cursor()
    c=i_name.get()
    if c=="":
        messagebox.showerror("Error in Input", "Please Fill all the details")
        return
    sql="select sno,word,definition from english where word like '%{a}%';".format(a=c)
    con.execute(sql)
    records = con.fetchall()

    tv.delete(*tv.get_children())       #it clear the treeview and re-enter the records
    for row in records:
            tv.insert("",END,values=row)
    db.commit()
    db.close()
    clear()

def define():               #to view meaning of a word typed
    db = mysql.connector.connect(host="localhost",user="root",passwd="",database="dictionary")
    con = db.cursor()
    c=i_name.get()
    if c=="":
        messagebox.showerror("Error in Input", "Please Fill all the details")
        return
    sql="select definition from english where word='%s'"%(c);
    con.execute(sql)
    records = con.fetchone()
    if records==None:
        messagebox.showinfo("Word not found", "Word not found!! \t Enter a valid word!")

    engine = pyttsx3.init()
    engine.say("The meaning of "+c+ " is " + " " +records[0])
    engine.runAndWait()
    messagebox.showinfo("Meaning", "The meaning of "+c+ " is " + " " +records[0])

    db.commit()
    db.close()
    clear()
    sno.grid_forget()
    d.grid_forget()
    submit.grid_forget()
    submit1.grid_forget()

img=Image.open("mic1.jpg")
re=img.resize((30,30),Image.ANTIALIAS)
new=ImageTk.PhotoImage(re)
img1=Image.open("mic1.jpg")
re1=img1.resize((30,30),Image.ANTIALIAS)
new1=ImageTk.PhotoImage(re1)

i_sno=IntVar()
i_name = StringVar()
i_meaning = StringVar()
def radio():
    select=int(var.get())
    if(select==1):
        tv.delete(*tv.get_children())   #it clear the treeview and re-enter the records
        db = mysql.connector.connect(host="localhost",user="root",passwd="",database="dictionary")
        con = db.cursor()
        sql = "select * from english order by sno"
        con.execute(sql)
        records = con.fetchall()

        for row in records:
            tv.insert("",END,values=row)
        db.commit()
        db.close()

    if(select==2):
        global name,name_1,aa,meaning,meaning_1,submit,lab,lab1,lab2,submit1

        aa=Label(bottomFrame,text = "Add Word",font=("Arial Bold", 20),fg="black",bg="#90EE90")
        aa.grid(row=2,column=4,padx=10,pady=10,columnspan=2)
        name=Label(bottomFrame,text="Word",font=("Arial Bold", 14),fg="black",bg="#90EE90")
        name.grid(row=4,column=3,padx=5,pady=10,sticky=W)
        name_1=Entry(bottomFrame,textvariable=i_name,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        name_1.grid(row=4,column=4,padx=5,sticky=W)
        lab1=Button(bottomFrame,text="hii",image=new,bd=0,bg="#90EE90")
        lab1.grid(row=4,column=6)

        meaning=Label(bottomFrame,text="Meaning",font=("Arial Bold", 14),fg="black",bg="#90EE90")
        meaning.grid(row=6,column=3,padx=5,sticky=W)
        meaning_1=Entry(bottomFrame,textvariable=i_meaning,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        meaning_1.grid(row=6,column=4,padx=5,sticky=W)
        lab2=Button(bottomFrame,text="hii",image=new,bd=0,bg="#90EE90")
        lab2.grid(row=6,column=6)
        submit=Button(bottomFrame,text="ADD",command=save,width=10,font=("Arial Bold", 14),fg="black",bg="#87CEEB")
        submit.grid(row=7,column=4,padx=5,pady=10,columnspan=2)

    if(select==3):
        global bb,sno,sno_1
        bb=Label(bottomFrame,text = "Update word",font=("Arial Bold", 20),fg="black",bg="#90EE90")
        bb.grid(row=2,column=4,padx=10,pady=10,columnspan=2)
        sno=Label(bottomFrame,text="sno",font=("Arial Bold", 14),fg="black",bg="#90EE90")
        sno.grid(row=4,column=3,padx=5,pady=10,sticky=W)
        sno_1=Entry(bottomFrame,textvariable=i_sno,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        sno_1.grid(row=4,column=4,padx=5,pady=10,sticky=W)

        name=Label(bottomFrame,text="Word",font=("Arial Bold", 14),fg="black",bg="#90EE90")
        name.grid(row=6,column=3,padx=5,pady=10,sticky=W)
        name_1=Entry(bottomFrame,textvariable=i_name,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        name_1.grid(row=6,column=4,padx=5,sticky=W)
        lab1=Button(bottomFrame,text="hii",image=new,bd=0,bg="#90EE90")
        lab1.grid(row=6,column=6)

        meaning=Label(bottomFrame,text="Meaning",font=("Arial Bold", 14),fg="black",bg="#90EE90")
        meaning.grid(row=8,column=3,padx=5,sticky=W)
        meaning_1=Entry(bottomFrame,textvariable=i_meaning,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        meaning_1.grid(row=8,column=4,padx=5,sticky=W)
        lab2=Button(bottomFrame,text="hii",image=new,bd=0,bg="#90EE90")
        lab2.grid(row=8,column=6)
        submit=Button(bottomFrame,text="UPDATE",command=update,width=10,font=("Arial Bold", 14),fg="black",bg="#87CEEB")
        submit.grid(row=9,column=4,padx=5,pady=10,columnspan=2)

    if(select==4):
        global d
        d=Label(bottomFrame,text = "Delete word",font=("Arial Bold", 20),fg="black",bg="#90EE90")
        d.grid(row=2,column=4,padx=10,pady=10,columnspan=2)
        sno=Label(bottomFrame,text="sno",font=("Arial Bold", 14),fg="black",bg="#90EE90")
        sno.grid(row=4,column=3,padx=5,pady=10,sticky=W)
        sno_1=Entry(bottomFrame,textvariable=i_sno,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        sno_1.grid(row=4,column=4,padx=5,pady=10,sticky=W)
        submit=Button(bottomFrame,text="DELETE",command=delete,width=10,font=("Arial Bold", 14),fg="black",bg="#87CEEB")
        submit.grid(row=5,column=4,padx=5,pady=10,columnspan=2)

    if (select==5):
        d=Label(bottomFrame,text = "Enter a part of word you remember!!",font=("Arial Bold", 20),fg="black",bg="#90EE90")
        d.grid(row=2,column=4,padx=10,pady=10,columnspan=2)
        sno=Entry(bottomFrame,textvariable=i_name,width=30,font=("Arial Bold", 14),fg="black",bg="white")
        sno.grid(row=4,column=4,padx=5,pady=10,sticky=W)
        submit=Button(bottomFrame,text="SEARCH",command=search,width=10,font=("Arial Bold", 14),fg="black",bg="#87CEEB")
        submit.grid(row=4,column=5,padx=5,pady=10,columnspan=2)
        submit1=Button(bottomFrame,text="DEFINITION",command=define,width=10,font=("Arial Bold", 14),fg="black",bg="#87CEEB")
        submit1.grid(row=5,column=5,padx=5,pady=10,columnspan=2)

    if(select==6):
        clear()

var = IntVar()
name=Label(leftFrame,text="DICTIONARY",font=("Arial Bold", 30),fg="black",bg="#87CEEB")
name.grid(row=0,column=3,sticky=W,padx=10,pady=20)

R1 = Radiobutton(leftFrame, text="View Dictionary",variable=var, value=1,command=radio,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R1.grid(row=2,column=2,sticky=W,padx=10)
R2 = Radiobutton(leftFrame, text="Add word",variable=var, value=2,command=radio,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R2.grid(row=3,column=2,sticky=W,padx=10)
R3 = Radiobutton(leftFrame, text="Update word",variable=var, value=3,command=radio,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R3.grid(row=4,column=2,sticky=W,padx=10)
R4 = Radiobutton(leftFrame, text="Delete word",variable=var, value=4,command=radio,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R4.grid(row=5,column=2,sticky=W,padx=10)
R7=Radiobutton(leftFrame,text="Search",variable=var,command=radio,value=5,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R7.grid(row=6,column=2,sticky=W,padx=10)
R5 = Radiobutton(leftFrame, text="Clear",variable=var, value=6,command=radio,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R5.grid(row=7,column=2,sticky=W,padx=10)
R6=Radiobutton(leftFrame,text="Exit",variable=var,command=root.destroy,value=7,bg="#87CEEB",font=("Arial Bold",14,"bold"))
R6.grid(row=8,column=2,sticky=W,padx=10)

style=ttk.Style()
#pick theme for treeview
style.theme_use("alt")

"""
style.configure("Treeview",background="silver",foreground="black",font=("Calibri",16,"bold"),rowheight=25,fieldbackground="silver")
#change selected color
style.map('Treeview',background=[('selected','green')])

tv=ttk.Treeview(rightFrame,column=(1,2,3))
tv.heading("1",text='ID')
tv.column(1,width=10)
tv.heading("2",text='WORD')
tv.column(1,width=30)
tv.heading("3",text="MEANING")
tv.column(1,width=140)

tv['show']='headings'
tv.bind("<ButtonRelease-1>",getData)
tv.pack(fill=X)
"""

#rightFrame scrollbar for treeview
tree_scroll = Scrollbar(rightFrame)
tree_scroll.pack(side=RIGHT,fill=Y)

#creating tree view
tv=ttk.Treeview(rightFrame,yscrollcommand=tree_scroll.set)
tree_scroll.config(command=tv.yview)   #configure scrollbar for treeview

tv['columns']=("ID","WORD","MEANING")
tv.column("#0",width=0,stretch=NO)
tv.column("ID",anchor=CENTER,width=50)
tv.column("WORD",anchor=W,width=140)
tv.column("MEANING",anchor=W,width=250)

tv.heading("#0",text="",anchor=W)
tv.heading("ID",text="ID",anchor=CENTER)
tv.heading("WORD",text="WORD",anchor=CENTER)
tv.heading("MEANING",text="MEANING",anchor=CENTER)
tv['show']='headings'
tv.bind("<ButtonRelease-1>",getData)    #butt-1 is for leftclick of mouse
tv.pack(fill=X)

root.mainloop()     #to display in screen


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov  9 22:41:30 2022

@author: jp735
"""
possible_roles = ['villager', 'village idiot', 'werewolf',
                  'bodyguard', 'seer', 'prostitute',
                  'witch', 'necromancer']

# Making the classes ---------------------------------------------------

class Villager:
    """
    The parent class for all player types. Also acts as the village idiot.

    :param: str player_name: The name of the player, only the first name please.
    :param: str character: The character they are playing, eg. werewolf, bodyguard etc.
    :param: bool alive: Set to True if character is alive.
    :param: bool visited: Set to True if the character has been visited by the prositute.
    :param: bool guarded: Set to True f the character has been guarded by the bodyguard.
    """
    def __init__(self, player_name: str, character: str, alive: bool, visited: bool, guarded: bool) -> None:
        self.player_name = player_name
        self.character_type = character
        self.life_status = alive
        self.visited_status = visited
        self.guarded_status = guarded

class Werewolf(Villager):
    """Werewolf subclass."""

    def __init__(self, player_name: str, character: str, alive: bool, visited: bool, guarded: bool) -> None:

        # Call the init from Villager
        super().__init__(player_name, character, alive, visited, guarded)

    def murder(self,chosen_player: str) -> None:
        '''
        Controlling the murder of a non werewolf by a werewolf.

        :param: str chosen_player: the name of the player the werewolf intends to murder.
        '''
        # Player dict is a dictionary of the player name and their instance
        if not player_dict[chosen_player].guarded_status and not self.visited_status: # Only proceeding if they are not guarded
            player_dict[chosen_player].life_status = False
            print(f'{chosen_player} has been killed')
            if player_dict[chosen_player].visited_status: # If the prostitute is at the chosen players house, they also die
                prostitute_player = role_finder('prostitute') # Finding name of the player with the prostitute role
                player_dict[prostitute_player].life_status = False
                print(f'{prostitute_player} has been killed')

class Bodyguard(Villager):
    """Bodyguard subclass."""

    def __init__(self, player_name: str, character: str, alive: bool, visited: bool, guarded: bool) -> None:

        # Call the init from Villager
        super().__init__(player_name, character, alive, visited, guarded)

    def guard(self,chosen_player) -> None:
        '''
        Controlling guarding of a player by the bodyguard.

        :param str chosen_player: the name of the player the bodyguard intends to guard.
        '''
        if not self.visited_status : # Only doing the function if they havent been visited
            # Player dict is a dictionary of the player name and their instance
            player_dict[chosen_player].guarded_status = True
            print(f'{chosen_player} has been guarded')

class Seer(Villager):
    """Seer subclass."""
    def __init__(self, player_name: str, character: str, alive: bool, visited: bool, guarded: bool) -> None:

        # Call the init from Villager
        super().__init__(player_name, character, alive, visited, guarded)

    def crystal_ball(self,chosen_player: str) -> None:
        '''
        Revealing to the seer if the person they are investigating is a werewolf.

        :param str chosen_player: the name of the player the seer intends to investigate.
        '''
        if not self.visited_status : # Only doing the function if they havent been visited
            # Player dict is a dictionary of the player name and their instance
            if player_dict[chosen_player].character == 'werewolf':
                print(f' Tell the seer that {chosen_player} is a werewolf')
            else:
                print(f' Tell the seer that {chosen_player} is not werewolf')

class Prostitute(Villager):
      """Prostitute subclass."""
      def __init__(self, player_name: str, character: str, alive: bool, visited: bool, guarded: bool) -> None:

        # Call the init from Villager
        super().__init__(player_name, character, alive, visited, guarded)

      def visit(self,chosen_player: str) -> None:
        '''
        Controlling the prostitute visiting someone.

        :param str chosen_player: the name of the player the prostitute intends to visit.
        '''
        # Player dict is a dictionary of the player name and their instance
        player_dict[chosen_player].visited_status = True
        print(f'{chosen_player} has been visited by the prostitute')

class Witch(Villager):
    """Witch subclass."""
    def __init__(self, player_name: str, character: str,
                 alive: bool, visited: bool, guarded: bool, apothecary: tuple(bool, bool)) -> None:

      # Call the init from Villager
      super().__init__(player_name, character, alive, visited, guarded)
      self.apothecary = (True, True)

# Defining all the necessary functions -----------------------------------------------------------

def clean_input(input_str: str) -> str:
    """
    A wrapper function to safely take inputs from the user without erroring
    and ending the game.

    :param str input_str: input string obviously
    :return: cleaned string in correct format so it doesn't break the code later'
    :rtype: str
    """

    # Get the response from the user
    output_str = input(input_str).lower().strip()

    # Check it is a valid input
    if output_str in player_dict or output_str == "skip":  # is it a player name
        return output_str
    elif output_str in possible_roles:  # is it a valid role
        return output_str
    else:
        print("Invalid input!")
        return clean_input(input_str)

def alive_people_checker() -> None:
    '''
    Checking which people are still alive and adding them to a dictionary.

    :return: dictionary where keys are the people who are still alive, and values are their character types
    :rtype: dict
    '''

    alive_player_dict = {} # Creating the empty dictionary

    for player in players: # Looping over all players, where players is defined later by the user input
        if player_dict[player].life_status == True:   # Collecting the players who are still alive
            # Adding them and their roles to the dictionary
            alive_player_dict[player_dict[player].player_name] = player_dict[player].character_type
    return alive_player_dict

def werewolf_information(alive_player_dict: dict) -> tuple(int, int, list):
    '''
    Checking which people are still alive and adding them to a dictionary.

    :param dict alive_player_dict: dictionary with alive players as keys and roles as values
    :return werewolf_counter: number of werewolves left alive in the game
    :rtype: int
    :return: number of werewolves left alive in the game, number of non werewolves left alive in the game,
        name of one of/ the werewolf
    :rtype: tuple(int, int, list )
    '''
    werewolf_counter = 0
    non_wolf_counter = 0
    name_of_werewolf = []

    for key in alive_player_dict.keys(): # Going through each player who is still alive
        if alive_player_dict[key] == 'werewolf':
            werewolf_counter += 1 # Adding one to werewolf_counter if they are a werewolf
            name_of_werewolf.append(key) # Saving name of the werewolf
        else:
            non_wolf_counter += 1

    return werewolf_counter, non_wolf_counter, name_of_werewolf

def game_status_checker() -> bool:
    '''
    Return True if the game is over

    :return: whether the game is over
    :rtype: bool
    '''
    alive_player_dict = alive_people_checker() # Keys are the player names, values are their character types
    werewolf_counter, non_wolf_counter, name_of_werewolf = werewolf_information(alive_player_dict)

    if werewolf_counter == non_wolf_counter: # Checking if the werewolves have won
        print('Game Over, the village has been lost to the wolves.')
        return True
    elif werewolf_counter == 0: # Checking if non wolves have won
         print('Game Over, the werewolves have lost')
         return True
    else:
        return False

def role_finder(role: str) -> str:
    '''
    Finding a players name from their role/character

    :param str role: name of the role/character you want to find the player name for
    :return person[0]: the name of the person with that role
    :rtype: str
    '''
    # Cycles through all the values in player dict (which are the instances)
    # If the role matches the role we are looking for, it appends the corresponding key of the player dict
    person = [k for k, item in player_dict.items() if item.character_type == role]
    return person[0]

def reset_values() -> None:
    '''
    Reseting the guarded and visited status for all players so that another round can begin.
    '''
    for player in player_dict:
        player_dict[player].guarded_status = False
        player_dict[player].visited_status = False

def Night() -> None:
    '''
    Controlling all the happenings that occur in the night.
    '''
    print('We enter the night')
    print('Everyone close their eyes')

    if 'prostitute' in roles_in_game: # Seeing if there is a prostitute in the game
        print('Prostitute, wake up')
        visited_player = clean_input('Who is the prostitute visiting: ')
        prostitute_player = role_finder('prostitute')  # Name of the player who is a prostitute
        player_dict[prostitute_player].visit(visited_player) # Changing the visited player's status to True
        print('Prostitute, go back to sleep')

    if 'bodyguard' in roles_in_game: # Seeing if there is a bodyguard in the game
        # The bodyguard protocol
        print('Bodyguard, wake up')
        guarded_player = clean_input('Who is the bodyguard guarding: ')
        body_guard_player = role_finder('bodyguard')  # Name of the player who is a bodyguard
        player_dict[body_guard_player].guard(guarded_player) # Changing the guarded player's status to True
        print('Bodyguard, go back to sleep')

    if 'seer' in roles_in_game: # Seeing if there is a seer in the game
        # The seer protocol
        print('Seer, wake up')
        investigated_player = clean_input('Who is the Seer investigating: ')
        seer_player = role_finder('seer') # Name of the player who is a seer
        player_dict[seer_player].crystal_ball(investigated_player) # Investigating the person
        print('Seer, go back to sleep')

    # Gathering information needed to proceed with the werewolf protocol
    alive_player_dict = alive_people_checker()
    werewolf_counter, non_wolf_counter,name_of_werewolf = werewolf_information(alive_player_dict)

    # Deciding who the werewolves are killing
    print('Werewolves, wake up')
    if werewolf_counter < 1: # If there are more than one wolves use the plural
        chosen_player = input('Who are the werewolves killing: ')
        for wolf in name_of_werewolf:
            player_dict[wolf].murder(chosen_player)
        print('Werewolves, go back to sleep')
        Voting() # go to the day
    elif werewolf_counter == 1: # Changing what the string says so it is gramatticaly correct for one wolf
        chosen_player = input('Who is the werewolf killing: ')
        player_dict[name_of_werewolf].murder(chosen_player)
        print('Werewolves, go back to sleep')
        Voting() # go to the day

def Voting() -> None:
    '''
    Controlling all the happenings that occur in the day, mainly voting.
    '''
    reset_values() # resetting everyones guarded and visited statuses to False

    alive_player_dict = alive_people_checker() # Keys are the player names, values are their character types
    alive_players = alive_player_dict.keys() # Collecting names of players who are still alive
    werewolf_counter, non_wolf_counter, name_of_werewolf = werewolf_information(alive_player_dict)

    # Has anyone won
    if game_status_checker():
        return

    vote_dict = {}  # Keys are the player names, # values are how many votes they have recieved
    # Adding the players who are still alive to a dictionary to count votes later
    for player in alive_players:
        if player_dict[player].life_status == True:
            vote_dict[player_dict[player].player_name] = 0

    # Asking each player who is still alive who they are voting for
    for player in alive_players:
        voted_player = clean_input(f'{player} is voting for (type skip if they are not voting this round): ')
        if voted_player == 'skip':
            pass
        else:
            vote_dict[voted_player] += 1

    # Deciding the outcome of the vote
    max_value = max(vote_dict.values()) # Value of the most votes
    max_keys = [k for k, v in vote_dict.items() if v == max_value] # Who has this many votes
    print(max_keys[0])

    if len(max_keys) > 1:  # Seeing if it is a draw
        Night() # Then go straight into the night
    elif max_value > (len(alive_player_dict)/2): # If it is a majority
        player_dict[max_keys[0]].life_status = False
        print(f'{max_keys[0]} has been voted out')
        if game_status_checker():
            return
        Night()
    else: # If is isn't a majority
        Night() # Then go straight into the night

# GAME BEGINS ---------------------------------------------------------------------------

# Number of players
# Getting input from the gamemaster
player_number = input('Enter the number of players: ')
player_number = int(player_number.strip())

# Creating an array of the players names
players = []
for i in range(0,player_number):
    player_name = input(f'Player {i+1} ').lower().strip()
    players.append(player_name)

# Create dictionary for players
player_dict = {}
roles_in_game = []

# Assigning a class to each player
for player in players:
    class_name_str = clean_input(f'{player} is a:')

    # Create the right class
    if class_name_str == "werewolf":
        player_dict[player] = Werewolf(player, class_name_str,
                                       True, False, False)
        roles_in_game.append('werewolf')
    elif class_name_str == "villager":
        player_dict[player] = Villager(player, class_name_str,
                                       True, False, False)
        roles_in_game.append('villager')
    elif class_name_str == "bodyguard":
        player_dict[player] = Bodyguard(player, class_name_str,
                                        True, False, False)
        roles_in_game.append('bodyguard')
    elif class_name_str == "seer":
        player_dict[player] = Seer(player, class_name_str,
                                        True, False, False)
        roles_in_game.append('seer')
    elif class_name_str == "village idiot":
        player_dict[player] = Villager(player, class_name_str,
                                        True, False, False)
        roles_in_game.append('villager')
    elif class_name_str == "prostitute":
        player_dict[player] = Prostitute(player, class_name_str,
                                        True, False, False)
        roles_in_game.append('prostitute')

Night()

#    # Converting string into class
#    class_name = globals()[class_name_str.capitalize()]
#    # Assigning the class to the player
#    globals()[player] = class_name(player, class_name_str, True, False, False)


import time
import praw
import config as cfg, rants

class Comments:
    """ Instance and actions for each comment visited. """

    def __init__(self, db=None, r=None, post=None) -> None:
        self.db = db
        self.r = r
        if type(post) is not praw.models.Submission:
            post.refresh()
        self.post = post

    def getParent(self) -> str:
        """ Returns the parent of post. """

        if type(self.post) is praw.models.Submission:
            return(self.post)
        elif type(self.post) is praw.models.Comment:
            return(self.post.parent())

    def markVisited(self) -> None:
        """ Mark a post as visited if not duplicate. """

        id = str(self.post.id)
        if type(self.post) is praw.models.Submission:
            id = "sub " + id

        if id not in cfg.already_visited:
            cfg.already_visited.append(id)

    def alreadyVisited(self) -> bool:
        """ Checks if a post had already been visited. """

        id = str(self.post.id)
        if type(self.post) is praw.models.Submission:
            id = "sub " + id

        if id in cfg.already_visited:
            if cfg.DEBUG: print("Already visited, skipping.")
            return True
        return False

    def postReply(self, reply) -> None:
        """ Add the post to list of visited, post reply. """

        if cfg.DEBUG:
            print("Posting reply to "+ str(self.post.id) +":\n"+ reply)
        elif not cfg.HOSTED: print("X", end="")

        parent = self.getParent()
        if parent.author is None or parent.author == "[deleted]":
            if cfg.DEBUG: print("Post was deleted, skipping.")
            return None
        elif parent.author == cfg.ZENBOT_USERNAME:
            if cfg.DEBUG: print("Reply is to bot, skipping.")
            return None
        elif parent.author == cfg.AUTHOR:
            if cfg.DEBUG: print("Replying to poster instead.")
            parent = self.post

        try:
            parent.reply(reply + cfg.sig)
            time.sleep(cfg.RATELIMIT)
            self.db.writeVisited()
        except praw.exceptions.APIException as err:
            raise cfg.ExitException(err)
        except:
            raise cfg.ExitException("ERROR: Post reply error.")

    def checkComment(self) -> None:
        """ Post replies to random submissions and trigger replies. """

        if cfg.DEBUG: print("comment: "+ format(self.post))
        elif not cfg.HOSTED: print(".", end="", flush=True)

        replies = self.post.replies
        for reply in replies:
            newc = Comments(self.db, self.r, reply)
            newc.checkComment()

        if self.alreadyVisited():
            return
        self.markVisited()

        if (cfg.TRIGGER in self.post.body):
            if (cfg.CMD_KOAN in self.post.body):
                self.postReply(self.db.readRandom(cfg.KOAN_STORE))
            elif (cfg.CMD_RANT in self.post.body):
                self.postReply(rants.Rants(self.db).getTemplate())
            elif (cfg.CMD_HAIKU in self.post.body):
                self.postReply(self.db.readRandom(cfg.HAIKU_STORE))
            elif (cfg.CMD_REPLY in self.post.body):
                self.postReply(cfg.botReply(self.db.readRandom(cfg.REPLY_STORE)))
            cfg.already_visited.append(str(self.post.id))


from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.gui import *

import resources
import os.path

class Qcadastre:

    def __init__(self, iface, version):
        self.iface = iface
        self.version = version
        self.settings = QSettings("CatAIS","Qcadastre")

        self.plugin_dir = os.path.dirname(__file__)
        locale = QSettings().value("locale/userLocale")[0:2]
        localePath = os.path.join(self.plugin_dir, 'i18n', 'qcadastre_{}.qm'.format(locale))

        if os.path.exists(localePath):
            self.translator = QTranslator()
            self.translator.load(localePath)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

    def initGui(self):
        self.action = QAction(
            QIcon(":/plugins/qcadastre/icon.png"),
            u"Qcadastre", self.iface.mainWindow())
        self.action.triggered.connect(self.run)

        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu(u"&Qcadastre", self.action)

        # main toolbar
        self.toolBar = self.iface.addToolBar("Qcadastre Main Toolbar")
        self.toolBar.setObjectName("QcadastreMainToolBar")
        self.toolBar.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))

        # projects
        self.menuBarProjects = QMenuBar()
        self.menuBarProjects.setObjectName("Qcadastre.Main.ProjectsMenuBar")
        self.menuBarProjects.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred))
        self.menuProjects = QMenu()
        self.menuProjects.setTitle(QCoreApplication.translate( "Qcadastre","Projects"))
        self.menuBarProjects.addMenu(self.menuProjects)

        # files
        self.menuBarFile= QMenuBar()
        self.menuBarFile.setObjectName("Qcadastre.Main.FileMenuBar")
        self.menuBarFile.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        self.menuFile = QMenu()
        self.menuFile.setTitle(QCoreApplication.translate( "Qcadastre","File"))

        self.createproject = QAction(QCoreApplication.translate("Qcadastre", "Create empty project"), self.iface.mainWindow())
        #QObject.connect(self.createproject, SIGNAL("triggered()"), self.doCreateProject)
        self.importproject = QAction(QCoreApplication.translate("Qcadastre", "Import project"), self.iface.mainWindow())
        QObject.connect(self.importproject, SIGNAL("triggered()"), self.doImportProject)
        self.deleteproject = QAction(QCoreApplication.translate("Qcadastre", "Delete project"), self.iface.mainWindow())
        QObject.connect(self.deleteproject, SIGNAL("triggered()"), self.doDeleteProject)
        self.menuFile.addActions([self.createproject, self.importproject, self.deleteproject])
        self.menuBarFile.addMenu(self.menuFile)

        # settings
        self.menuBarSettings = QMenuBar()
        self.menuBarSettings.setObjectName("Qcadastre.Main.SettingsMenuBar")
        self.menuBarSettings.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        self.menuSettings = QMenu()
        self.menuSettings.setTitle(QCoreApplication.translate( "Qcadastre","Settings"))

        self.options = QAction(QCoreApplication.translate("QGeoApp", "Options"), self.iface.mainWindow())
        QObject.connect(self.options, SIGNAL("triggered()"), self.doOptions)
        self.menuSettings.addActions([self.options])
        self.menuBarSettings.addMenu(self.menuSettings)

        # help
        self.menuBarHelp = QMenuBar()
        self.menuBarHelp.setObjectName("Qcadastre.Main.HelpMenuBar")
        self.menuBarHelp.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        self.menuHelp = QMenu()
        self.menuHelp.setTitle(QCoreApplication.translate("Qcadastre","Help"))

        self.about = QAction(QCoreApplication.translate("Qcadastre", "About"), self.iface.mainWindow())
        QObject.connect(self.about, SIGNAL("triggered()"), self.doAbout)

        self.menuHelp.addActions([self.about])
        self.menuBarHelp.addMenu(self.menuHelp)

        # add menus to toolbar
        self.toolBar.addWidget(self.menuBarProjects)
        self.toolBar.addWidget(self.menuBarFile)
        self.toolBar.addWidget(self.menuBarSettings)
        self.toolBar.addWidget(self.menuBarHelp)

        # initial load of project menu entries
        self.doLoadProjectsDatabase()

    def doAbout(self):
        from base.help.doAbout import AboutDialog
        self.about_dlg = AboutDialog(self.iface.mainWindow(), self.version)
        self.about_dlg.show()

    def doOptions(self):
        from base.settings.doOptions import OptionsDialog
        self.options_dlg = OptionsDialog(self.iface.mainWindow())
        self.options_dlg.initGui()
        self.options_dlg.show()
        QObject.connect(self.options_dlg, SIGNAL("projectsDatabaseHasChanged()"), self.doLoadProjectsDatabase)

    def doImportProject(self):
        from base.file.doImportProject import ImportProjectDialog
        self.import_dlg = ImportProjectDialog(self.iface.mainWindow())
        self.import_dlg.initGui()
        self.import_dlg.show()
        QObject.connect(self.import_dlg, SIGNAL("projectsDatabaseHasChanged()"), self.doLoadProjectsDatabase)

    def doDeleteProject(self):
        from base.file.doDeleteProject import DeleteProjectDialog
        self.delete_dlg = DeleteProjectDialog(self.iface.mainWindow())
        self.delete_dlg.initGui()
        self.delete_dlg.show()
        QObject.connect(self.delete_dlg, SIGNAL("projectsDatabaseHasChanged()"), self.doLoadProjectsDatabase)

    def doLoadProjectsDatabase(self):
        from base.projects.doLoadProjectsDatabase import LoadProjectsDatabase
        d = LoadProjectsDatabase(self.iface.messageBar())
        projects = d.read()

        if projects != None:
            groupedProjects = {}
            for project in projects:
                moduleName = project["appmodulename"]
                try:
                    moduleList = groupedProjects[moduleName]
                except KeyError:
                    moduleList = []

                moduleList.append(project)
                groupedProjects[moduleName] = moduleList

            self.menuProjects.clear()
            for key in sorted(groupedProjects.iterkeys()):
                modules = groupedProjects[key]
                groupMenu = self.menuProjects.addMenu(QCoreApplication.translate("Qcadastre", unicode(key)))
                sortedProjectsList = sorted(modules, key=lambda k: k['displayname'])
                for project in sortedProjectsList:
                    action = QAction(QCoreApplication.translate("QGeoApp", unicode(project["displayname"])), self.iface.mainWindow())
                    groupMenu.addAction(action)
                    QObject.connect(action, SIGNAL( "triggered()"), lambda activeProject=project: self.doLoadProject(activeProject))

    def doLoadProject(self, project):
        self.settings.setValue("project/id", str(project["id"]))
        self.settings.setValue("project/displayname", str(project["displayname"]))
        self.settings.setValue("project/appmodule", str(project["appmodule"]))
        self.settings.setValue("project/appmodulename", unicode(project["appmodulename"]))
        self.settings.setValue("project/ilimodelname", str(project["ilimodelname"]))
        self.settings.setValue("project/epsg", str(project["epsg"]))
        self.settings.setValue("project/provider", str(project["provider"]))
        self.settings.setValue("project/dbhost", str(project["dbhost"]))
        self.settings.setValue("project/dbport", str(project["dbport"]))
        self.settings.setValue("project/dbname", str(project["dbname"]))
        self.settings.setValue("project/dbschema", str(project["dbschema"]))
        self.settings.setValue("project/dbuser", str(project["dbuser"]))
        self.settings.setValue("project/dbpwd", str(project["dbpwd"]))
        self.settings.setValue("project/dbadmin", str(project["dbadmin"]))
        self.settings.setValue("project/dbadminpwd", str(project["dbadminpwd"]))
        self.settings.setValue("project/projectdir", str(project["projectdir"]))

        moduleName = str(project["appmodule"]).lower()

#        try:
        _temp = __import__("modules." + moduleName + ".applicationmodule", globals(), locals(), ['ApplicationModule'])
        c = _temp.ApplicationModule(self.iface, self.toolBar)
        c.initGui()
#        except Exception, e:
#            print "Couldn't do it: %s" % e
#            self.iface.messageBar().pushMessage("Error",  QCoreApplication.translate("Qcadastre", str(e)), level=QgsMessageBar.CRITICAL, duration=5)

    def unload(self):
        self.iface.removePluginMenu(u"&Qcadastre", self.action)
        self.iface.removeToolBarIcon(self.action)
        self.iface.mainWindow().removeToolBar(self.toolBar)

    def run(self):
        pass


import csv
import os

import numpy as np
import torch
from pytorch_lightning import LightningDataModule
from torch.utils.data import Dataset, DataLoader, Subset
from torch.utils.data.dataset import random_split
from tqdm import tqdm

class DensityContrastiveDataset(Dataset):

    def __init__(self, dataset_dir, seed=42, subset=None):
        self.dataset_dir = dataset_dir
        self.seed = seed
        self.subset = int(subset * 5) if subset is not None else None

        raw_data, self.density_file, self.scale_data = self.__loadfile()

        torch.manual_seed(seed)
        np.random.seed(seed)
        indices = torch.randperm(self.scale_data.shape[0])

        self.density_file = [self.density_file[index] for index in indices]
        self.scale_data = self.scale_data[indices]

    def __len__(self):
        return len(self.density_file)

    def __getitem__(self, index):
        f1, f2 = self.density_file[index]
        d1 = torch.load(os.path.join(self.dataset_dir, f1))
        d2 = torch.load(os.path.join(self.dataset_dir, f2))
        scale = self.scale_data[index]
        mol = '_'.join(f1.split('_')[:2]) + '.xyz'

        _, xidx, yidx, zidx = torch.where(d2 > 0.001)
        xmin, xmax = xidx.aminmax()
        ymin, ymax = yidx.aminmax()
        zmin, zmax = zidx.aminmax()

        xshift = torch.randint(0 - xmin, 65 - xmax, (1,))
        yshift = torch.randint(0 - ymin, 65 - ymax, (1,))
        zshift = torch.randint(0 - zmin, 65 - zmax, (1,))

        d2 = d2.roll((xshift, yshift, zshift), (1, 2, 3))

        return d1, d2, scale, mol

    def __loadfile(self):
        mol_energy_file = os.path.join(self.dataset_dir, 'mol_energy_nwchem.csv')
        assert os.path.exists(mol_energy_file), f'mol_energy file does not exist!'

        n_data = 0
        raw_data = {}
        with open(mol_energy_file, 'r') as f:
            reader = csv.reader(f)
            for mol_scale, energy in reader:

                if self.subset is not None and n_data == self.subset:
                    break

                _, mol_id, scale = mol_scale.split('_')
                if mol_id not in raw_data.keys():
                    raw_data[mol_id] = {scale: energy}
                else:
                    raw_data[mol_id][scale] = energy

                n_data += 1

        choices = np.random.choice(['1|3', '1|2', '1', '2', '3'], size=len(raw_data), replace=True)

        density_file, scale_data = [], []

        for mol_id, scale in tqdm(zip(raw_data.keys(), choices), total=len(choices)):
            if scale == '1|3':
                scale_data.append(1/3)
            elif scale == '1|2':
                scale_data.append(0.5)
            else:
                scale_data.append(float(scale))

            density_file.append(['dsgdb9nsd_{}_1.pth'.format(mol_id), 'dsgdb9nsd_{}_{}.pth'.format(mol_id, scale)])

        scale_data = torch.tensor(scale_data, dtype=torch.float)
        return raw_data, density_file, scale_data

class ContrastiveDataModule(LightningDataModule):
    def __init__(
            self,
            dataset_dir,
            subset=None,
            train_ratio=0.8,
            seed=42,
            batch_size=32,
            shuffle=True,
            num_workers=0,
            pin_memory=True,
            drop_last=False,
            *args,
            **kwargs
    ):
        super().__init__(*args, **kwargs)

        self.dataset_dir = dataset_dir
        self.train_ratio = train_ratio
        self.seed = seed
        self.batch_size = batch_size
        self.shuffle = shuffle
        self.num_workers = num_workers
        self.pin_memory = pin_memory
        self.drop_last = drop_last
        self.subset = subset
        self.dataset = DensityContrastiveDataset(dataset_dir, seed, subset=subset)
        self._num_samples = int(self.train_ratio * len(self.dataset))

        n_data = len(self.dataset)
        train_split = int(n_data * train_ratio)
        dataset_train, dataset_val = random_split(
            self.dataset,
            [train_split, len(self.dataset) - train_split],
            generator=torch.Generator().manual_seed(seed)
        )

        self._train_dataloader = DataLoader(
            dataset_train,
            batch_size=batch_size,
            shuffle=shuffle,
            num_workers=num_workers,
            drop_last=drop_last,
            pin_memory=pin_memory,
            generator=torch.Generator().manual_seed(seed)
        )

        self._val_dataloader = DataLoader(
            dataset_val,
            batch_size=batch_size,
            shuffle=False,
            num_workers=num_workers,
            drop_last=drop_last,
            pin_memory=pin_memory
        )

    @property
    def num_samples(self):
        return self._num_samples

    def train_dataloader(self):
        return self._train_dataloader

    def val_dataloader(self):
        return self._val_dataloader

class DensityDataset(Dataset):

    def __init__(self, dataset_dir, seed=42, subset=None, scale=None):
        self.dataset_dir = dataset_dir
        self.seed = seed
        self.scale = scale
        self.subset = subset

        self.mol_files, self.scale_data, self.target = self.__loadfile()

        torch.manual_seed(seed)
        indices = torch.randperm(self.target.shape[0])

        self.mol_files = [self.mol_files[index] for index in indices]
        self.target = self.target[indices]
        self.scale_data = self.scale_data[indices]

        if scale is None:
            self.scale_indices = torch.stack(
                [(self.scale_data == s).nonzero().flatten() for s in [1 / 3, 1 / 2, 1, 2, 3]])

    def __len__(self):
        return len(self.target)

    def __getitem__(self, index):
        mol_file = self.mol_files[index]
        density_data = torch.load(os.path.join(self.dataset_dir, '{}.pth'.format(mol_file)))
        return density_data, self.target[index]

    def __loadfile(self):
        mol_energy_file = os.path.join(self.dataset_dir, 'mol_energy_nwchem.csv')
        assert os.path.exists(mol_energy_file), f'mol_energy file does not exist!'

        n_data = 0
        mol_scales, energies, scale_data = [], [], []
        with open(mol_energy_file, 'r') as f:
            reader = csv.reader(f)
            for mol_scale, energy in reader:
                if self.subset is not None and n_data >= self.subset:
                    break
                scale = mol_scale.split('_')[-1]

                if self.scale is not None:
                    if scale != self.scale:
                        continue

                if scale == '1|3':
                    scale_data.append(1 / 3)
                elif scale == '1|2':
                    scale_data.append(0.5)
                else:
                    scale_data.append(float(scale))

                mol_scales.append(mol_scale)
                energies.append(float(energy))

                n_data += 1

        energies = torch.tensor(energies, dtype=torch.float).unsqueeze(-1)
        scale_data = torch.tensor(scale_data, dtype=torch.float)
        return mol_scales, scale_data, energies

class MainDataModule(LightningDataModule):
    def __init__(
            self,
            dataset_dir,
            subset=None,
            scale='1',
            train_ratio=0.8,
            test_ratio=0.1,
            seed=42,
            batch_size=32,
            num_workers=0,
            pin_memory=True,
            drop_last=False,
            *args,
            **kwargs
    ):
        super().__init__(*args, **kwargs)

        self.dataset_dir = dataset_dir
        self.train_ratio = train_ratio
        self.test_ratio = test_ratio
        self.seed = seed
        self.batch_size = batch_size
        self.num_workers = num_workers
        self.pin_memory = pin_memory
        self.drop_last = drop_last
        self.subset = subset
        self.scale = scale
        self.dataset = DensityDataset(dataset_dir, seed, subset=subset, scale=scale)

        n_data = len(self.dataset)
        train_split = int(n_data * (1 - test_ratio) * train_ratio)
        test_split = int(n_data * test_ratio)
        dataset_train, dataset_val, dataset_test = random_split(
            self.dataset,
            [train_split, len(self.dataset) - train_split - test_split, test_split],
            generator=torch.Generator().manual_seed(seed)
        )

        test_scale_indices = [[i for i in dataset_test.indices if i in scale_index] for scale_index in
                              self.dataset.scale_indices]
        self._num_samples = len(dataset_train)

        train_kwargs = {
            'batch_size': batch_size,
            'shuffle': True,
            'num_workers': num_workers,
            'drop_last': drop_last,
            'pin_memory': pin_memory,
            'generator': torch.Generator().manual_seed(seed)
        }

        val_kwargs = {
            'batch_size': batch_size,
            'shuffle': False,
            'num_workers': num_workers,
            'drop_last': drop_last,
            'pin_memory': pin_memory,
        }

        self._train_dataloader = DataLoader(dataset_train, **train_kwargs)
        self._val_dataloader = DataLoader(dataset_val, **val_kwargs)

        dataset_tests = [Subset(self.dataset, test_scale_index) for test_scale_index in test_scale_indices]
        self._test_dataloaders = [DataLoader(d, **val_kwargs) for d in dataset_tests]

    @property
    def num_samples(self):
        return self._num_samples

    def train_dataloader(self):
        return self._train_dataloader

    def val_dataloader(self):
        return self._val_dataloader

    def test_dataloader(self):
        return self._test_dataloaders

class MainScale1DataModule(LightningDataModule):
    def __init__(
            self,
            dataset_dir,
            subset=None,
            train_ratio=0.8,
            seed=42,
            batch_size=32,
            num_workers=0,
            pin_memory=True,
            drop_last=False,
            *args,
            **kwargs
    ):
        super().__init__(*args, **kwargs)

        self.dataset_dir = dataset_dir
        self.train_ratio = train_ratio
        self.seed = seed
        self.batch_size = batch_size
        self.num_workers = num_workers
        self.pin_memory = pin_memory
        self.drop_last = drop_last
        self.subset = subset
        self.dataset = DensityDataset(dataset_dir, seed, subset=subset, scale='1')

        n_data = len(self.dataset)
        train_split = int(n_data * train_ratio)
        dataset_train, dataset_val = random_split(
            self.dataset,
            [train_split, len(self.dataset) - train_split],
            generator=torch.Generator().manual_seed(seed)
        )

        self._num_samples = len(dataset_train)

        train_kwargs = {
            'batch_size': batch_size,
            'shuffle': True,
            'num_workers': num_workers,
            'drop_last': drop_last,
            'pin_memory': pin_memory,
            'generator': torch.Generator().manual_seed(seed)
        }

        val_kwargs = {
            'batch_size': batch_size,
            'shuffle': False,
            'num_workers': num_workers,
            'drop_last': drop_last,
            'pin_memory': pin_memory,
        }

        self._train_dataloader = DataLoader(dataset_train, **train_kwargs)
        self._val_dataloader = DataLoader(dataset_val, **val_kwargs)

    @property
    def num_samples(self):
        return self._num_samples

    def train_dataloader(self):
        return self._train_dataloader

    def val_dataloader(self):
        return self._val_dataloader


from itertools import combinations
def isPrime(a):
    for i in range(2, a):
        if a % i == 0:
            return False
    return True

def solution(nums):
    data = list(combinations(nums, 3))
    count = 0
    for x in data:
        if isPrime(sum(x)) == True:
            count += 1
    return count