# Based on Bojan's -> https://www.kaggle.com/tunguz/more-effective-ridge-lgbm-script-lb-44944
#
import gc
import time
import numpy as np
import pandas as pd

from joblib import Parallel, delayed

from scipy.sparse import csr_matrix, hstack

from sklearn.linear_model import Ridge
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.preprocessing import LabelBinarizer, MinMaxScaler, Normalizer
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import SGDRegressor
from textblob import TextBlob
import lightgbm as lgb
import os, psutil
from multiprocessing import Pool
from sklearn.metrics.pairwise import cosine_similarity, pairwise_distances
from scipy.spatial.distance import pdist, squareform
from collections import Counter
import re
import lzma
import Levenshtein
from numba import jit

NUM_BRANDS = 4000
NUM_CATEGORIES = 1000
NAME_MIN_DF = 10
MAX_FEATURES_ITEM_DESCRIPTION = 2 ** 14
NUM_PARTITIONS = 12 #number of partitions to split dataframe
NUM_CORES = 8 #number of cores on your machine

###################################################################################
import random, copy, struct
from hashlib import sha1

# The size of a hash value in number of bytes
hashvalue_byte_size = len(bytes(np.int64(42).data))

# http://en.wikipedia.org/wiki/Mersenne_prime
_mersenne_prime = (1 << 61) - 1
_max_hash = (1 << 32) - 1
_hash_range = (1 << 32)

class MinHash(object):
    '''MinHash is a probabilistic data structure for computing
    `Jaccard similarity`_ between sets.

    Args:
        num_perm (int, optional): Number of random permutation functions.
            It will be ignored if `hashvalues` is not None.
        seed (int, optional): The random seed controls the set of random
            permutation functions generated for this MinHash.
        hashobj (optional): The hash function used by this MinHash.
            It must implements
            the `digest()` method similar to hashlib_ hash functions, such
            as `hashlib.sha1`.
        hashvalues (`numpy.array` or `list`, optional): The hash values is
            the internal state of the MinHash. It can be specified for faster
            initialization using the existing state from another MinHash.
        permutations (optional): The permutation function parameters. This argument
            can be specified for faster initialization using the existing
            state from another MinHash.

    Note:
        To save memory usage, consider using :class:`datasketch.LeanMinHash`.

    Note:
        Since version 1.1.1, MinHash will only support serialization using
        `pickle`_. ``serialize`` and ``deserialize`` methods are removed,
        and are supported in :class:`datasketch.LeanMinHash` instead.
        MinHash serialized before version 1.1.1 cannot be deserialized properly
        in newer versions (`need to migrate? <https://github.com/ekzhu/datasketch/issues/18>`_).
    Note:
        Since version 1.1.3, MinHash uses Numpy's random number generator
        instead of Python's built-in random package. This change makes the
        hash values consistent across different Python versions.
        The side-effect is that now MinHash created before version 1.1.3 won't
        work (i.e., ``jaccard``, ``merge`` and ``union``)
        with those created after.
    .. _`Jaccard similarity`: https://en.wikipedia.org/wiki/Jaccard_index
    .. _hashlib: https://docs.python.org/3.5/library/hashlib.html
    .. _`pickle`: https://docs.python.org/3/library/pickle.html
    '''

    def __init__(self, num_perm=128, seed=1, hashobj=sha1,
            hashvalues=None, permutations=None):
        if hashvalues is not None:
            num_perm = len(hashvalues)
        if num_perm > _hash_range:
            # Because 1) we don't want the size to be too large, and
            # 2) we are using 4 bytes to store the size value
            raise ValueError("Cannot have more than %d number of\
                    permutation functions" % _hash_range)
        self.seed = seed
        self.hashobj = hashobj
        # Initialize hash values
        if hashvalues is not None:
            self.hashvalues = self._parse_hashvalues(hashvalues)
        else:
            self.hashvalues = self._init_hashvalues(num_perm)
        # Initalize permutation function parameters
        if permutations is not None:
            self.permutations = permutations
        else:
            generator = np.random.RandomState(self.seed)
            # Create parameters for a random bijective permutation function
            # that maps a 32-bit hash value to another 32-bit hash value.
            # http://en.wikipedia.org/wiki/Universal_hashing
            self.permutations = np.array([(generator.randint(1, _mersenne_prime, dtype=np.uint64),
                                           generator.randint(0, _mersenne_prime, dtype=np.uint64))
                                          for _ in range(num_perm)], dtype=np.uint64).T
        if len(self) != len(self.permutations[0]):
            raise ValueError("Numbers of hash values and permutations mismatch")

    def _init_hashvalues(self, num_perm):
        return np.ones(num_perm, dtype=np.uint64)*_max_hash

    def _parse_hashvalues(self, hashvalues):
        return np.array(hashvalues, dtype=np.uint64)
    @jit
    def update(self, b):
        '''Update this MinHash with a new value.

        Args:
            b (bytes): The value of type `bytes`.

        Example:
            To update with a new string value:

            .. code-block:: python
                minhash.update("new value".encode('utf-8'))
        '''
        hv = struct.unpack('<I', self.hashobj(b).digest()[:4])[0]
        a, b = self.permutations
        phv = np.bitwise_and((a * hv + b) % _mersenne_prime, np.uint64(_max_hash))
        self.hashvalues = np.minimum(phv, self.hashvalues)
    @jit
    def jaccard(self, other):
        '''Estimate the `Jaccard similarity`_ (resemblance) between the sets
        represented by this MinHash and the other.

        Args:
            other (datasketch.MinHash): The other MinHash.

        Returns:
            float: The Jaccard similarity, which is between 0.0 and 1.0.
        '''
        if other.seed != self.seed:
            raise ValueError("Cannot compute Jaccard given MinHash with\
                    different seeds")
        if len(self) != len(other):
            raise ValueError("Cannot compute Jaccard given MinHash with\
                    different numbers of permutation functions")
        return np.float(np.count_nonzero(self.hashvalues==other.hashvalues)) /\
                np.float(len(self))
    @jit
    def count(self):
        '''Estimate the cardinality count based on the technique described in
        `this paper <http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=365694>`_.

        Returns:
            int: The estimated cardinality of the set represented by this MinHash.
        '''
        k = len(self)
        return np.float(k) / np.sum(self.hashvalues / np.float(_max_hash)) - 1.0
    @jit
    def merge(self, other):
        '''Merge the other MinHash with this one, making this one the union
        of both.

        Args:
            other (datasketch.MinHash): The other MinHash.
        '''
        if other.seed != self.seed:
            raise ValueError("Cannot merge MinHash with\
                    different seeds")
        if len(self) != len(other):
            raise ValueError("Cannot merge MinHash with\
                    different numbers of permutation functions")
        self.hashvalues = np.minimum(other.hashvalues, self.hashvalues)
    @jit
    def digest(self):
        '''Export the hash values, which is the internal state of the
        MinHash.

        Returns:
            numpy.array: The hash values which is a Numpy array.
        '''
        return copy.copy(self.hashvalues)
    @jit
    def is_empty(self):
        '''
        Returns:
            bool: If the current MinHash is empty - at the state of just
                initialized.
        '''
        if np.any(self.hashvalues != _max_hash):
            return False
        return True
    @jit
    def clear(self):
        '''
        Clear the current state of the MinHash.
        All hash values are reset.
        '''
        self.hashvalues = self._init_hashvalues(len(self))

    @jit
    def copy(self):
        '''
        Returns:
            datasketch.MinHash: A copy of this MinHash by exporting its
                state.
        '''
        return MinHash(seed=self.seed, hashvalues=self.digest(),
                permutations=self.permutations)

    def __len__(self):
        '''
        Returns:
            int: The number of hash values.
        '''
        return len(self.hashvalues)

    def __eq__(self, other):
        '''
        Returns:
            bool: If their seeds and hash values are both equal then two
                are equivalent.
        '''
        return self.seed == other.seed and \
                np.array_equal(self.hashvalues, other.hashvalues)

    @classmethod
    @jit
    def union(cls, *mhs):
        '''Create a MinHash which is the union of the MinHash objects passed as arguments.
        Args:
            *mhs: The MinHash objects to be united. The argument list length is variable,
                but must be at least 2.

        Returns:
            datasketch.MinHash: A new union MinHash.
        '''
        if len(mhs) < 2:
            raise ValueError("Cannot union less than 2 MinHash")
        num_perm = len(mhs[0])
        seed = mhs[0].seed
        if any((seed != m.seed or num_perm != len(m)) for m in mhs):
            raise ValueError("The unioning MinHash must have the\
                    same seed and number of permutation functions")
        hashvalues = np.minimum.reduce([m.hashvalues for m in mhs])
        permutations = mhs[0].permutations
        return cls(num_perm=num_perm, seed=seed, hashvalues=hashvalues,
                permutations=permutations)
###################################################################################

def rmsle(y, y0):
    assert len(y) == len(y0)
    return np.sqrt(np.mean(np.power(np.log1p(y)-np.log1p(y0), 2)))

def split_cat(text):
    try: return text.split("/")
    except: return ("No Label", "No Label", "No Label")

def handle_missing_inplace(dataset):
    dataset['category_name'].fillna(value='missing', inplace=True)
    dataset['brand_name'].fillna(value='missing', inplace=True)
    dataset['item_description'].fillna(value='missing', inplace=True)

def cutting(dataset):
    pop_brand = dataset['brand_name'].value_counts().loc[lambda x: x.index != 'missing'].index[:NUM_BRANDS]
    dataset.loc[~dataset['brand_name'].isin(pop_brand), 'brand_name'] = 'missing'
    pop_category = dataset['category_name'].value_counts().loc[lambda x: x.index != 'missing'].index[:NUM_BRANDS]
    dataset.loc[~dataset['category_name'].isin(pop_category), 'category_name'] = 'missing'

def to_categorical(dataset):
    dataset['category_name'] = dataset['category_name'].astype('category')
    dataset['brand_name'] = dataset['brand_name'].astype('category')
    dataset['item_condition_id'] = dataset['item_condition_id'].astype('category')

def print_memory_usage():
    print('cpu: {}'.format(psutil.cpu_percent()))
    print('consuming {:.2f}GB RAM'.format(
    	   psutil.Process(os.getpid()).memory_info().rss / 1073741824),
    	  flush=True)

def _sigmoid(score):
    p = 1. / (1. + np.exp(-score))
    return p

def _logit(p):
    return np.log(p/(1.-p))

def _softmax(score):
    score = np.asarray(score, dtype=float)
    score = np.exp(score - np.max(score))
    score /= np.sum(score, axis=1)[:,np.newaxis]
    return score

def _cast_proba_predict(proba):
    N = proba.shape[1]
    w = np.arange(1,N+1)
    pred = proba * w[np.newaxis,:]
    pred = np.sum(pred, axis=1)
    return pred

def _one_hot_label(label, n_classes):
    num = label.shape[0]
    tmp = np.zeros((num, n_classes), dtype=int)
    tmp[np.arange(num),label.astype(int)] = 1
    return tmp

def _majority_voting(x, weight=None):
    ## apply weight
    if weight is not None:
    	assert len(weight) == len(x)
    	x = np.repeat(x, weight)
    c = Counter(x)
    value, count = c.most_common()[0]
    return value

def _voter(x, weight=None):
    idx = np.isfinite(x)
    if sum(idx) == 0:
    	value = config.MISSING_VALUE_NUMERIC
    else:
    	if weight is not None:
    		value = _majority_voting(x[idx], weight[idx])
    	else:
    		value = _majority_voting(x[idx])
    return value

def _array_majority_voting(X, weight=None):
    y = np.apply_along_axis(_voter, axis=1, arr=X, weight=weight)
    return y

def _mean(x):
    idx = np.isfinite(x)
    if sum(idx) == 0:
    	value = float(config.MISSING_VALUE_NUMERIC) # cast it to float to accommodate the np.mean
    else:
    	value = np.mean(x[idx]) # this is float!
    return value

def _array_mean(X):
    y = np.apply_along_axis(_mean, axis=1, arr=X)
    return y

def _corr(x, y_train):
    if _dim(x) == 1:
    	corr = pearsonr(x.flatten(), y_train)[0]
    	if str(corr) == "nan":
    		corr = 0.
    else:
    	corr = 1.
    return corr

def _dim(x):
    d = 1 if len(x.shape) == 1 else x.shape[1]
    return d

@jit
def _entropy(proba):
    entropy = -np.sum(proba*np.log(proba))
    return entropy

@jit
def _try_divide(x, y, val=0.0):
    """try to divide two numbers"""
    if y != 0.0:
    	val = float(x) / y
    return val

@jit
def _jaccard_coef(A, B):
    if not isinstance(A, set):
        A = set(A)
    if not isinstance(B, set):
        B = set(B)
    return _try_divide(float(len(A.intersection(B))), len(A.union(B)))

@jit
def _dice_dist(A, B):
    if not isinstance(A, set):
        A = set(A)
    if not isinstance(B, set):
        B = set(B)
    return _try_divide(2.*float(len(A.intersection(B))), (len(A) + len(B)))

@jit
def entropy(obs, token_pattern=' '):
    obs_tokens = obs.split(token_pattern)
    counter = Counter(obs_tokens)
    count = np.asarray(list(counter.values()))
    proba = count/np.sum(count)
    # del obs_tokens
    return _entropy(proba)

def digit_count(obs):
    return len(re.findall(r"\d", obs))

def digit_ratio(obs, token_pattern = ' '):
    obs_tokens = obs.split(token_pattern)
    return _try_divide(len(re.findall(r"\d", obs)), len(obs_tokens))

def emoji_count(obs):
    return len(re.findall(r'[^\w\s,]', obs))

def emoji_ratio(obs, token_pattern = ' '):
    obs_tokens = obs.split(token_pattern)
    return _try_divide(len(re.findall(r'[^\w\s,]', obs)), len(obs_tokens))

@jit
def _unigrams(words):
    """
    	Input: a list of words, e.g., ["I", "am", "Denny"]
    	Output: a list of unigram
    """
    assert type(words) == list
    return words

@jit
def _bigrams(words, join_string, skip=0):
    """
       Input: a list of words, e.g., ["I", "am", "Denny"]
       Output: a list of bigram, e.g., ["I_am", "am_Denny"]
       I use _ as join_string for this example.
    """
    assert type(words) == list
    L = len(words)
    if L > 1:
    	lst = []
    	for i in range(L-1):
    		for k in range(1,skip+2):
    			if i+k < L:
    				lst.append( join_string.join([words[i], words[i+k]]) )
    else:
    	# set it as unigram
    	lst = _unigrams(words)
    return lst

def _trigrams(words, join_string, skip=0):
    """
       Input: a list of words, e.g., ["I", "am", "Denny"]
       Output: a list of trigram, e.g., ["I_am_Denny"]
       I use _ as join_string for this example.
    """
    assert type(words) == list
    L = len(words)
    if L > 2:
    	lst = []
    	for i in range(L-2):
    		for k1 in range(1,skip+2):
    			for k2 in range(1,skip+2):
    				if i+k1 < L and i+k1+k2 < L:
    					lst.append( join_string.join([words[i], words[i+k1], words[i+k1+k2]]) )
    else:
    	# set it as bigram
    	lst = _bigrams(words, join_string, skip)
    return lst

def UniqueCount_Ngram(obs, count, token_pattern=' '):
    obs_tokens = obs.lower().split(token_pattern)
    obs_ngrams = _ngrams(obs_tokens, count)
    l = len(set(obs_ngrams))
    del obs_tokens
    del obs_ngrams
    return l

def UniqueRatio_Ngram(obs, count, token_pattern=' '):
    obs_tokens = obs.lower().split(token_pattern)
    obs_ngrams = _ngrams(obs_tokens, count)
    r = _try_divide(len(set(obs_ngrams)), len(obs_ngrams))
    del obs_tokens
    del obs_ngrams
    return r

def _ngrams(words, ngram, join_string=" "):
    """wrapper for ngram"""
    if ngram == 1:
    	return _unigrams(words)
    elif ngram == 2:
    	return _bigrams(words, join_string)
    elif ngram == 3:
    	return _trigrams(words, join_string)
    elif ngram == 4:
    	return _fourgrams(words, join_string)
    elif ngram == 12:
    	unigram = _unigrams(words)
    	bigram = [x for x in _bigrams(words, join_string) if len(x.split(join_string)) == 2]
    	return unigram + bigram
    elif ngram == 123:
    	unigram = _unigrams(words)
    	bigram = [x for x in _bigrams(words, join_string) if len(x.split(join_string)) == 2]
    	trigram = [x for x in _trigrams(words, join_string) if len(x.split(join_string)) == 3]
    	return unigram + bigram + trigram

def parallelize_dataframe(df, func):
    df_split = np.array_split(df, NUM_PARTITIONS)
    pool = Pool(NUM_CORES)
    df = pd.concat(pool.map(func, df_split))
    pool.close()
    pool.join()
    return df

def get_sentiment_score(df):
    df['sentiment_score'] = df['item_description'].map(lambda x: TextBlob(x).sentiment.polarity)
    return df

# def main():
start_time = time.time()

train = pd.read_table('../input/train.tsv', engine='c')
test = pd.read_table('../input/test.tsv', engine='c')
print('[{}] Finished to load data'.format(time.time() - start_time))
print('Train shape: ', train.shape)
print('Test shape: ', test.shape)

nrow_test = test.shape[0]

test_id = test['test_id'].values
submission: pd.DataFrame = test[['test_id']]

if nrow_test < 700000:
    test = pd.concat([test,test,test,test,test])
    print('Test shape ', test.shape)

nrow_train = train.shape[0]
y = np.log1p(train["price"])
del train['price']
merge: pd.DataFrame = pd.concat([train, test])

train_cols = set(train.columns)
del train
del test
gc.collect()

handle_missing_inplace(merge)
print('[{}] Handle missing completed.'.format(time.time() - start_time))

def get_doclen_name(df):
    df['name_doclen'] = df['name'].map(lambda x: len(str(x).lower().split(' ')))
    return df

def get_doclen_itemdesc(df):
    df['item_description_doclen'] = df['item_description'].map(lambda x: len(str(x).lower().split(' ')))
    return df

def get_doclen_brand_name(df):
    df['brand_name_doclen'] = df['brand_name'].map(lambda x: len(str(x).lower().split(' ')))
    return df

def get_entropy_name(df):
    df['name_entropy'] = df['name'].map(lambda x: entropy(str(x).lower(), ' '))
    return df

def get_entropy_itemdesc(df):
    df['item_description_entropy'] = \
    	df['item_description'].map(lambda x: entropy(str(x).lower(), ' '))
    return df

def get_entropy_brand_name(df):
    df['brand_name_entropy'] = \
    	df['brand_name'].map(lambda x: entropy(str(x).lower(), ' '))
    return df

def get_digit_count_name(df):
    df['name_dc'] = df['name'].map(lambda x: digit_count(str(x).lower()))
    return df

def get_digit_count_itemdesc(df):
    df['item_description_dc'] = \
    	df['item_description'].map(lambda x: digit_count(str(x).lower()))
    return df

def get_digit_count_brand_name(df):
    df['brand_name_dc'] = \
    	df['brand_name'].map(lambda x: digit_count(str(x).lower()))
    return df

def get_digit_ratio_name(df):
    df['name_dr'] = df['name'].map(lambda x: digit_ratio(str(x).lower()))
    return df

def get_digit_ratio_itemdesc(df):
    df['item_description_dr'] = \
    	df['item_description'].map(lambda x: digit_ratio(str(x).lower()))
    return df

def get_digit_ratio_brand_name(df):
    df['brand_name_dr'] = \
    	df['brand_name'].map(lambda x: digit_ratio(str(x).lower()))
    return df

def get_emoji_count_name(df):
    df['name_ec'] = df['name'].map(lambda x: emoji_count(str(x).lower()))
    return df

def get_emoji_count_itemdesc(df):
    df['item_description_ec'] = \
    	df['item_description'].map(lambda x: emoji_count(str(x).lower()))
    return df

def get_emoji_count_brand_name(df):
    df['brand_name_ec'] = \
    	df['brand_name'].map(lambda x: emoji_count(str(x).lower()))
    return df

def get_emoji_ratio_name(df):
    df['name_er'] = df['name'].map(lambda x: emoji_ratio(str(x).lower()))
    return df

def get_emoji_ratio_itemdesc(df):
    df['item_description_er'] = \
    	df['item_description'].map(lambda x: emoji_ratio(str(x).lower()))
    return df

def get_emoji_ratio_brand_name(df):
    df['brand_name_er'] = \
    	df['brand_name'].map(lambda x: emoji_ratio(str(x).lower()))
    return df

cols1 = set(merge.columns)
cols = []
obs_fields = ['name', 'brand_name', 'item_description']
merge = parallelize_dataframe(merge, get_doclen_name)
merge = parallelize_dataframe(merge, get_doclen_itemdesc)
merge = parallelize_dataframe(merge, get_doclen_brand_name)

merge = parallelize_dataframe(merge, get_entropy_name)
merge = parallelize_dataframe(merge, get_entropy_itemdesc)
merge = parallelize_dataframe(merge, get_entropy_brand_name)

merge = parallelize_dataframe(merge, get_digit_count_name)
merge = parallelize_dataframe(merge, get_digit_count_itemdesc)
merge = parallelize_dataframe(merge, get_digit_count_brand_name)

merge = parallelize_dataframe(merge, get_digit_ratio_name)
merge = parallelize_dataframe(merge, get_digit_ratio_itemdesc)
merge = parallelize_dataframe(merge, get_digit_ratio_brand_name)

# merge = parallelize_dataframe(merge, get_emoji_count_name)
# merge = parallelize_dataframe(merge, get_emoji_count_itemdesc)
# merge = parallelize_dataframe(merge, get_emoji_count_brand_name)

# merge = parallelize_dataframe(merge, get_emoji_ratio_name)
# merge = parallelize_dataframe(merge, get_emoji_ratio_itemdesc)
# merge = parallelize_dataframe(merge, get_emoji_ratio_brand_name)

print('[{}] Finished basic creation for name, bn, item_desc'.format(time.time() - start_time))

for f in obs_fields:
    counter = Counter(merge[f].values)
    merge[f+'_docfreq'] = merge[f].map(lambda x: counter[x])

    cols.append(f+'_doclen')
    cols.append(f+'_docfreq')
    cols.append(f+'_docEntropy')
    cols.append(f+'_digitCount')
    cols.append(f+'_digitRatio')
    # cols.append(f+'_emojiCount')
    # cols.append(f+'_emojiRatio')

f = 'category_name'
def get_category_name_doclen(df):
    df[f+'_doclen'] = df[f].map(lambda x: len(str(x).lower().split('/')))
    return df

merge = parallelize_dataframe(merge, get_category_name_doclen)

counter = Counter(merge[f].values)
merge[f+'_docfreq'] = merge[f].map(lambda x: counter[x])

token_pattern = '/'

def get_category_name_entropy(df):
	df[f+'_docEntropy'] = df[f].map(lambda x: entropy(str(x).lower(),token_pattern))
	return df
merge = parallelize_dataframe(merge, get_category_name_entropy)

def get_category_name_dc(df):
	df[f+'_dc'] = df[f].map(lambda x: digit_count(str(x).lower()))
	return df
merge = parallelize_dataframe(merge, get_category_name_dc)

def get_category_name_dr(df):
	df[f+'_dr'] = df[f].map(lambda x: digit_ratio(str(x).lower(), token_pattern))
	return df
merge = parallelize_dataframe(merge, get_category_name_dr)

def get_category_name_ec(df):
	df[f+'_emojiCount'] = df[f].map(lambda x: emoji_count(str(x).lower()))
	return df
# merge = parallelize_dataframe(merge, get_category_name_ec)

def get_category_name_er(df):
	df[f+'_emojiRatio'] = df[f].map(lambda x: emoji_ratio(str(x).lower()))
	return df
# merge = parallelize_dataframe(merge, get_category_name_er)

cols.append(f+'_doclen')
cols.append(f+'_docfreq')
cols.append(f+'_docEntropy')
cols.append(f+'_digitCount')
cols.append(f+'_digitRatio')
# cols.append(f+'_emojiCount')
# cols.append(f+'_emojiRatio')

print('[{}] Finished basic creation for category_name'.format(time.time() - start_time))

obs_fields = ["name", "item_description"]

# def get_onegram_uc_name(df):
# 	df['name_1_uc'] = df['name'].map(lambda x: UniqueCount_Ngram(str(x), 1))
# 	return df
# merge = parallelize_dataframe(merge, get_onegram_uc_name)

# def get_onegram_uc_item_desc(df):
# 	df['item_desc_1_uc'] = \
# 		df['item_description'].map(lambda x: UniqueCount_Ngram(str(x), 1))
# 	return df
# merge = parallelize_dataframe(merge, get_onegram_uc_item_desc)

# def get_onegram_ur_name(df):
# 	df['name_1_ur'] = df['name'].map(lambda x: UniqueRatio_Ngram(str(x), 1))
# 	return df
# merge = parallelize_dataframe(merge, get_onegram_ur_name)

# def get_onegram_ur_item_desc(df):
# 	df['item_desc_1_ur'] = \
# 		df['item_description'].map(lambda x: UniqueRatio_Ngram(str(x), 1))
# 	return df
# merge = parallelize_dataframe(merge, get_onegram_ur_item_desc)

def get_bigram_uc_name(df):
	df['name_2_uc'] = df['name'].map(lambda x: UniqueCount_Ngram(str(x), 2))
	return df
merge = parallelize_dataframe(merge, get_bigram_uc_name)

def get_bigram_uc_item_desc(df):
	df['item_desc_2_uc'] = \
		df['item_description'].map(lambda x: UniqueCount_Ngram(str(x), 2))
	return df
merge = parallelize_dataframe(merge, get_bigram_uc_item_desc)

def get_bigram_ur_name(df):
	df['name_2_ur'] = df['name'].map(lambda x: UniqueRatio_Ngram(str(x), 2))
	return df
merge = parallelize_dataframe(merge, get_bigram_ur_name)

def get_bigram_ur_item_desc(df):
	df['item_desc_2_ur'] = \
		df['item_description'].map(lambda x: UniqueRatio_Ngram(str(x), 2))
	return df
merge = parallelize_dataframe(merge, get_bigram_ur_item_desc)

# def get_trigram_uc_name(df):
# 	df['name_3_uc'] = df['name'].map(lambda x: UniqueCount_Ngram(str(x), 3))
# 	return df
# merge = parallelize_dataframe(merge, get_trigram_uc_name)

# def get_trigram_uc_item_desc(df):
# 	df['item_desc_3_uc'] = \
# 		df['item_description'].map(lambda x: UniqueCount_Ngram(str(x), 3))
# 	return df
# merge = parallelize_dataframe(merge, get_trigram_uc_item_desc)

# def get_trigram_ur_name(df):
# 	df['name_3_ur'] = df['name'].map(lambda x: UniqueRatio_Ngram(str(x), 3))
# 	return df
# merge = parallelize_dataframe(merge, get_trigram_ur_name)

# def get_trigram_ur_item_desc(df):
# 	df['item_desc_3_ur'] = \
# 		df['item_description'].map(lambda x: UniqueRatio_Ngram(str(x), 3))
# 	return df
# merge = parallelize_dataframe(merge, get_trigram_ur_item_desc)

# print('[{}] Finished ngram count for name, item_desc'.format(time.time() - start_time))

# ngrams = [1,2,3]
# token_pattern =' '
# for f in obs_fields:
# 	for n in ngrams:
# 		cols.append(f+'_{}_uc'.format(n))
# 		cols.append(f+'_{}_ur'.format(n))

# f = 'category_name'
# merge[f+'_{}_uc'.format(n)] = merge[f].map(lambda x: UniqueCount_Ngram(str(x), n, '/'))
# merge[f+'_{}_ur'.format(n)] = merge[f].map(lambda x: UniqueRatio_Ngram(str(x), n, '/'))
# cols.append(f+'_{}_uc'.format(n))
# cols.append(f+'_{}_ur'.format(n))

# remove constatnt cols
merge =  merge.loc[:, (merge != merge.iloc[0]).any()]
print(len(cols))
del cols
cols = list(set(merge.columns) - cols1)
print(len(cols))

X_b = merge[cols]

print('[{}] Finished X_basic1'.format(time.time() - start_time))

scaler = MinMaxScaler()
X_b = scaler.fit_transform(X_b)
X_basic = csr_matrix(X_b)
print('basic: ', X_basic.data.nbytes)
print('[{}] Finished X_basic2'.format(time.time() - start_time))
del X_b
for c in cols:
    merge = merge.drop(c, axis=1)
print_memory_usage()

# jaccard and dice
merge['n_id'] = merge['name'].astype('str') + '___' + merge['item_description'].astype('str')

from sklearn.metrics.pairwise import pairwise_distances

def jaccard_skl(text):
    obs, target = text.split('___')
    obs_tokens = obs.split(' ')
    target_tokens = target.split(' ')
    j1 = 1 - pairwise_distances(obs_tokens, target_tokens, metric = "hamming")
    del obs, target, target_tokens, obs_tokens
    return j1

@jit
def jaccard_1(text):
    obs, target = text.split('___')
    obs_tokens = obs.split(' ')
    target_tokens = target.split(' ')
    obs_ngrams = _ngrams(obs_tokens, 1)
    target_ngrams = _ngrams(target_tokens, 1)
    j1 = _jaccard_coef(obs_ngrams, target_ngrams)
    # del obs, target, target_tokens, obs_tokens, obs_ngrams, target_ngrams
    return j1

def jaccard_2(text):
    obs, target = text.split('___')
    obs_tokens = obs.split(' ')
    target_tokens = target.split(' ')
    obs_ngrams = _ngrams(obs_tokens, 2)
    target_ngrams = _ngrams(target_tokens, 2)
    j2 = _jaccard_coef(obs_ngrams, target_ngrams)
    del obs, target, target_tokens, obs_tokens, obs_ngrams, target_ngrams
    return j2

def jaccard_3(text):
    obs, target = text.split('___')
    obs_tokens = obs.split(' ')
    target_tokens = target.split(' ')
    obs_ngrams = _ngrams(obs_tokens, 3)
    target_ngrams = _ngrams(target_tokens, 3)
    j3 = _jaccard_coef(obs_ngrams, target_ngrams)
    del obs, target, target_tokens, obs_tokens, obs_ngrams, target_ngrams
    return j3

@jit
def jaccard_minhash(text):
    obs, target = text.split('___')
    obs_tokens = obs.split(' ')
    target_tokens = target.split(' ')
    m1, m2 = MinHash(), MinHash()
    for d in obs_tokens:
        m1.update(d.encode('utf8'))
    for d in target_tokens:
        m2.update(d.encode('utf8'))
    j = m1.jaccard(m2)
    return j

# def get_j2(df):
    # merge['j2'] = merge['n_id'].map(lambda x: jaccard_2(x))
    # return df
# merge = parallelize_dataframe(merge, get_j2)

def get_j1(df):
    merge['j1'] = merge['n_id'].map(lambda x: jaccard_1(x))
    return df
merge = parallelize_dataframe(merge, get_j1)

X_j = merge[['j1']]
del merge['n_id']
np.min(X_j)
np.max(X_j)
print('[{}] Finished X_j'.format(time.time() - start_time))
print_memory_usage()

abbr = {}
abbr['BNWT'] = ['bnwt', 'brand new with tags']
abbr['NWT'] = ['nwt', 'new with tags']
abbr['BNWOT'] = ['bnwot', 'brand new with out tags', 'brand new without tags']
abbr['NWOT'] = ['nwot', 'new with out tags', 'new without tags']
abbr['BNIP'] = ['bnip', 'brand new in packet', 'brand new in packet']
abbr['NIP'] = ['nip', 'new in packet', 'new in packet']
abbr['BNIB'] = ['bnib', 'brand new in box']
abbr['NIB'] = ['nib', 'new in box']
abbr['MIB'] = ['mib', 'mint in box']
abbr['MWOB'] = ['mwob', 'mint with out box', 'mint without box']
abbr['MIP'] = ['mip', 'mint in packet']
abbr['MWOP'] = ['mwop', 'mint with out packet', 'mint without packet']

merge['tag'] = merge['item_description'].map(lambda a: 'BNWT' if any(x in a.lower() for x in abbr['BNWT'])
										   else 'NWT' if any(x in a.lower() for x in abbr['NWT'])
										   else 'BNWOT' if any(x in a.lower() for x in abbr['BNWOT'])
										   else 'NWOT' if any(x in a.lower() for x in abbr['NWOT'])
										   else 'BNIP' if any(x in a.lower() for x in abbr['BNIP'])
										   else 'NIP' if any(x in a.lower() for x in abbr['NIP'])
										   else 'BNIB' if any(x in a.lower() for x in abbr['BNIB'])
										   else 'NIB' if any(x in a.lower() for x in abbr['NIB'])
										   else 'MIB' if any(x in a.lower() for x in abbr['MIB'])
										   else 'MWOB' if any(x in a.lower() for x in abbr['MWOB'])
										   else 'MIP' if any(x in a.lower() for x in abbr['MIP'])
										   else 'MWOP' if any(x in a.lower() for x in abbr['MWOP'])
										   else 'None')
print('[{}] Finished tag'.format(time.time() - start_time))
del abbr
print_memory_usage()

merge['bci'] = merge['brand_name'].astype('str') + ' ' + merge['category_name'].astype('str') + ' ' + \
			merge['item_condition_id'].astype('str')

merge['bc'] = merge['brand_name'].astype('str') + ' ' + merge['category_name'].astype('str')

merge['bcis'] = merge['brand_name'].astype('str') + ' ' \
				+ merge['category_name'].astype('str') + ' ' + \
				merge['item_condition_id'].astype('str') + ' ' + \
				merge['shipping'].astype('str')

merge['bcs'] = merge['brand_name'].astype('str') + ' ' + \
				merge['category_name'].astype('str') + ' ' + \
				merge['shipping'].astype('str')

# merge['bi'] = merge['brand_name'].astype('str') + '_' +   merge['item_condition_id'].astype('str')

# merge['ci'] = merge['category_name'].astype('str') + '_' + merge['item_condition_id'].astype('str')

print('[{}] Finished creating bci bc bi ci bcs bcis'.format(time.time() - start_time))
print_memory_usage()

# merge.drop(['bci', 'bc'], axis=1, inplace=True)

# merge = parallelize_dataframe(merge, get_sentiment_score)
# merge['sentiment_score'] = merge['item_description'].map(lambda x: TextBlob(x).sentiment.polarity)

# print('[{}] Finished sentiment score'.format(time.time() - start_time))
# a = merge['sentiment_score'].values
# print(np.min(a))
# print(np.max(a))

# print_memory_usage()
# merge['sentiment'] = merge['sentiment_score'].map(lambda x: 'VPos' if x > 0.5
													# else 'Pos' if (x <= 0.5) and (x > 0)
													# else 'Neu' if  x == 0
													# else 'Neg' if (x < 0) and (x >= -0.5)
													# else 'VNeg')

# print('[{}] Finished sentiment'.format(time.time() - start_time))

cutting(merge)
print('[{}] Finished to cut'.format(time.time() - start_time))

to_categorical(merge)
print('[{}] Finished to convert categorical'.format(time.time() - start_time))

# tv = TfidfVectorizer(max_features=2 ** 14,
#                      min_df=NAME_MIN_DF,
# 					 ngram_range=(1, 3),
# 					 stop_words='english')
# X_name1 = tv.fit_transform(merge['name'])
# print('[{}] Finished TFIDF vectorize `name`'.format(time.time() - start_time))
# print(X_name1.shape)
# print(np.min(X_name1))
# print(np.max(X_name1))
# # del merge['item_description']
# print_memory_usage()

cv = CountVectorizer(min_df=NAME_MIN_DF, stop_words='english')
X_name = cv.fit_transform(merge['name'])
norm = Normalizer()
X_name = norm.fit_transform(X_name)
print('[{}] Finished count vectorize `name`'.format(time.time() - start_time))
print(X_name.shape)
print(np.min(X_name))
print(np.max(X_name))
del merge['name']
print_memory_usage()

cv = CountVectorizer()
X_category = cv.fit_transform(merge['category_name'])
norm = Normalizer()
X_category = norm.fit_transform(X_category)
print('[{}] Finished count vectorize `category_name`'.format(time.time() - start_time))
print(X_category.shape)
print(np.min(X_category))
print(np.max(X_category))
del merge['category_name']
gc.collect()
print_memory_usage()

# cv = CountVectorizer()
# X_bci_cv = cv.fit_transform(merge['bci'])
# norm = Normalizer()
# X_bci_cv = norm.fit_transform(X_bci_cv)
# print('[{}] Finished count vectorize `X_bci_cv`'.format(time.time() - start_time))
# print(X_bci_cv.shape)
# print(np.min(X_bci_cv))
# print(np.max(X_bci_cv))
# del merge['bci']
# gc.collect()
# print_memory_usage()

tv = TfidfVectorizer(max_features=MAX_FEATURES_ITEM_DESCRIPTION,
					 ngram_range=(1, 3),
					 stop_words='english')
X_description = tv.fit_transform(merge['item_description'])
print('[{}] Finished TFIDF vectorize `item_description`'.format(time.time() - start_time))
print(X_description.shape)
print(np.min(X_description))
print(np.max(X_description))
del merge['item_description']
print_memory_usage()

# X_cos = cosine_similarity(X_description, dense_output=False)
# X_cos = squareform(pdist(np.asarray(X_description.toarray()), 'cosine'))
# print(X_cos.shape)
# print('[{}] Finished cosine similarity'.format(time.time() - start_time))
# print_memory_usage()

lb = LabelBinarizer(sparse_output=True)
X_brand = lb.fit_transform(merge['brand_name'])
print('[{}] Finished label binarize `brand_name`'.format(time.time() - start_time))
print(X_brand.shape)
del merge['brand_name']
print_memory_usage()

lb = LabelBinarizer(sparse_output=True)
X_bci = lb.fit_transform(merge['bci'])
print('[{}] Finished label binarize `bci`'.format(time.time() - start_time))
print(X_bci.shape)
del merge['bci']
print_memory_usage()

# lb = LabelBinarizer(sparse_output=True)
# X_bc = lb.fit_transform(merge['bc'])
# print('[{}] Finished label binarize `bc`'.format(time.time() - start_time))
# print(X_bc.shape)
# del merge['bc']
# print_memory_usage()

lb = LabelBinarizer(sparse_output=True)
X_bcis = lb.fit_transform(merge['bcis'])
print('[{}] Finished label binarize `bcis`'.format(time.time() - start_time))
print(X_bcis.shape)
del merge['bcis']
gc.collect()
print_memory_usage()

lb = LabelBinarizer(sparse_output=True)
X_bcs = lb.fit_transform(merge['bcs'])
print('[{}] Finished label binarize `bcs`'.format(time.time() - start_time))
print(X_bcs.shape)
del merge['bcs']
gc.collect()
print_memory_usage()

X_dummies = csr_matrix(pd.get_dummies(merge[['item_condition_id', 'shipping',
											'tag']], sparse=True).values)
print('[{}] Finished to get dummies on `item_condition_id` and `shipping`'.format(time.time() - start_time))
print(X_dummies.shape)
print_memory_usage()

del merge
gc.collect()
print_memory_usage()

print('basic: ', X_basic.data.nbytes)
print('bcis: ', X_bcis.data.nbytes)
print('bci: ', X_bci.data.nbytes)
print('dummies: ', X_dummies.data.nbytes)
print('description: ', X_description.data.nbytes)
print('brand: ', X_brand.data.nbytes)
print('category: ', X_category.data.nbytes)
print('name: ', X_name.data.nbytes)
print('name1: ', X_name1.data.nbytes)

sparse_merge = hstack((X_j, X_basic, X_bci, X_bcis, X_dummies, X_description, X_brand, X_category, X_name)).tocsr()
print('[{}] Finished to create sparse merge'.format(time.time() - start_time))

del X_j, X_basic, X_bcis, X_bci, X_bcs, X_dummies, X_description, X_brand, X_category, X_name, X_name1
gc.collect()
print_memory_usage()

X = sparse_merge[:nrow_train]
X_test = sparse_merge[nrow_train:]

print(X.shape)
print_memory_usage()

del sparse_merge
gc.collect()
print_memory_usage()

np.random.seed(0)
train_X, valid_X, train_y, valid_y = train_test_split(X, y, test_size = 0.01, random_state = 0)
print_memory_usage()
# d_train = lgb.Dataset(X, label=y, max_bin=8192)
d_train = lgb.Dataset(train_X, label=train_y, max_bin=8192)
d_valid = lgb.Dataset(valid_X, label=valid_y, max_bin=8192)
watchlist = [d_train, d_valid]
print_memory_usage()

params = {
	'learning_rate': 0.75,
	'application': 'regression',
	'max_depth': 3,
	'num_leaves': 100,
	'verbosity': -1,
	'metric': 'RMSE',
	'num_threads': 4
}

model = lgb.train(params, train_set=d_train, valid_sets=watchlist,
					num_boost_round=5000,early_stopping_rounds=100,verbose_eval=500)
print('[{}] Finished to train lgbm'.format(time.time() - start_time))
preds = model.predict(X_test)
print('[{}] Finished to train predict lgbm'.format(time.time() - start_time))
del model, d_train, d_valid
print_memory_usage()

# submission=pd.DataFrame()
# submission['test_id'] = test_id
# submission['price'] = np.expm1(preds)
# submission.to_csv("submission_lgbm_nlp2.csv", index=False)
preds *= 0.6
# print('[{}] Finished submission lgbm'.format(time.time() - start_time))
if nrow_test < 700000:
	preds = preds[:nrow_test]

model = Ridge(solver="saga", fit_intercept=True, random_state=205)
model.fit(X, y)
print('[{}] Finished to train ridge'.format(time.time() - start_time))
preds1 = model.predict(X=X_test)
print('[{}] Finished to predict ridge'.format(time.time() - start_time))
# submission['price'] = np.expm1(preds1)
# submission.to_csv("submission_ridge_nlp2.csv", index=False)
print_memory_usage()
if nrow_test < 700000:
	preds1 = preds1[:nrow_test]

preds += 0.4*preds1
submission['price'] = np.expm1(preds)
submission.to_csv("submission_lgbm_ridge_nlp2.csv", index=False)
# if __name__ == '__main__':
# main()


myList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def ist_gerade(zahl):
    if zahl % 2 == 0:
        return True
    else:
        return False

def print_elements(myList):
    for element in myList:
        print(element)

if __name__ == '__main__':
    for zahl in myList:
        if ist_gerade(zahl):
            print(zahl)

    # counter = 0
    # while counter < len(myList):
    #     if ist_gerade(myList[counter]):
    #         print(myList[counter])
    #         counter += 1
    #     else:
    #         counter += 1


# -*- coding: utf-8 -*-
'''
  This module clean element tags.

  Data

    keys (dict): keys changes.
    values (dict): values changes.
'''

keys = {'endereço':'addr:street','Futsal':'sport'}
values = {'Cond.':'Condomínio '
          ,'Av.':'Avenida '
          ,'AV.':'Avenida '
          ,'Ed.':'Edifício '
          ,'ED.':'Edifício '
          ,'A.E.':'Área Especial '
          ,'Bl.':'Bloco '
          ,'BL.':'Bloco '
          ,'Qd.':'Quadra '
          ,'Q. ':'Quadra '
          ,'Conj.':'Conjunto '
          ,'Cj.':'Conjunto '
          ,'Ch.':'Chácara '
          ,'Lt.':'Lote '
         }

def fix_tag(attr):
  ''' Fix element tags according to the keys and values dict.

  Args:
    attr (dict): tag element attributes.

  Returns:
    dict: tag element attributes fixed.
  '''
  for k in keys:
    if k == attr['k']:
      attr['k'] = keys[k]

  for v in values:
    if v in attr['v']:
      attr['v'] = attr['v'].replace(v, values[v]).replace('  ',' ')

    if attr['k'].count(':') > 1:
      comma = attr['k'].find(':')+1
      attr['k'] = attr['k'][:comma] + attr['k'][comma:].replace(':','_')

  return attr

if __name__ == '__main__':
  import xml.etree.ElementTree as ET
  for _,element in ET.iterparse('area.osm'):
    if element.tag == 'tag':
      tag2 = element.attrib.copy()
      fix_tag(element.attrib)
      if tag2 != element.attrib:
        print(element.attrib)

  print('Done!!!')


import boto3

def get_secret(region_name='us-east-2', secret_name='haslo', endpoint_url=None):

    # Create a Secrets Manager client
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name,
        endpoint_url=endpoint_url
    )

    get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )

    # Your code goes here.
    return get_secret_value_response['SecretString']


#!/usr/bin/python
# -*- coding: utf-8 -*-
"""Module for implementation of the SpikeCount metric."""
import numpy as np
from protobrain import neuron
from protobrain.metrics import metric

class SpikeCount(metric.Metric):
    """How many times each neuron spiked during the experiment."""

    def __init__(self):
        """Initialize the metric."""
        super().__init__('spike_count')

    def reset(self):
        """Reset all accumulators."""
        self._accumulator = None

    def next(self, neurons):
        """Record the next state for the metric computation.

        Args:
            neurons: Brain state to record
        """
        self._accumulator = self._accumulate_partials(self._accumulator, neurons)

    def _accumulate_partials(self, accumulator, neurons):
        if not isinstance(neurons, neuron.LayeredNeurons):
            return np.array(
                neurons.output.values +
                (accumulator if accumulator is not None else 0))

        if not accumulator:
            accumulator = [None] * len(neurons.layers)

        for i, sublayers in enumerate(neurons.layers):
            accumulator[i] = self._accumulate_partials(
                accumulator[i], sublayers)

        return accumulator

    def _count(self, accumulator):
        if isinstance(accumulator, list):
            return [self._count(subacc) for subacc in accumulator]

        non_zero_indices = list(zip(*np.where(accumulator > 0)))
        counts = {
            0: accumulator.size - len(non_zero_indices),
        }
        for indices in non_zero_indices:
            value = accumulator[indices]
            counts[value] = counts.get(value, 0) + 1

        return counts

    def _aggregate(self, counts):
        if not counts:
            return {}

        if isinstance(counts, dict):
            return dict(counts)

        aggregated_counts = {}
        for subs in counts:
            aggregated_subs = self._aggregate(subs)
            for value, sub_count in aggregated_subs.items():
                aggregated_counts[value] = aggregated_counts.get(value, 0) + sub_count

        return aggregated_counts

    def compute(self):
        """Compute the metric based on the saved state."""
        if self._accumulator is None:
            raise RuntimeError('No iterations - cannot compute metric')
        counts = self._count(self._accumulator)
        return metric.MetricResults(
            self.name,
            global_result=self._aggregate(counts),
            per_layer_result=counts,
        )


#!/usr/bin/python3
# -*- coding: utf-8 -*-

import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django
django.setup()
#import sys

# Django-Datenbank importieren
import db.models as db

# Grafik Toolkit imporotieren
import tkinter as tk
import tkinter.ttk as ttk

# Default-Werte importieren
import Default_Werte as default

# Damit das tut... ohne gehen die Special-Gui-Vars nicht
root = tk.Tk()

# Konvertiert von doulbe/float zu String mit Dezimalkomma
def dez_str(input):
    return str(input).replace('.', ',')

# Konvertiert von String mit Dezimalkomma auf Gleitkommazahl
def str_dez(input):
    return float(input.replace(',','.'))

class KA_Datensatz():
    def __init__(self, id = 1):

        # Alle benötigten Variablen bauen
        self.statuszeile = tk.StringVar()
        self.statuszeile.set("Laden..")

        self.id = 1
        # Datensatz
        self.ds = db.Klaeranlage.objects.filter(id = self.id).select_related('ort', 'probe_fluessig', 'probe_schlamm_asche',
            'probenahmestelle', 'verfahren_ablauf', 'verfahren_asche', 'verfahren_faulschlamm',
            'verfahren_schlammwasser', 'zeitspanne')[0]

        self.name = tk.StringVar()
        self.name.set("Default")
        self.ort = tk.StringVar()
        self.ort.set("Default-Wort")

        self.abwasserabgabe_p = tk.DoubleVar()
        self.abwasserabgabe_p.set(default.ABWASSERABGABE_P)
        self.abwasserabgabe_n = tk.DoubleVar()
        self.abwasserabgabe_n.set(default.ABWASSERABGABE_N)
        self.kosten_schlammentsorgung = tk.DoubleVar()
        self.kosten_schlammentsorgung = tk.DoubleVar(default.KOSTEN_SCHLAMMENTSORGUNG)

        # Dictionary für alle Probenahmestellen bauen pro Wert
        self.pns = {}

        # Alle flüssigen Probenahmestellen erstellen und auf Null stellen
        for alle in [1, 2, 3, 4, 5, 6]:
            self.pns[alle] = {}
            self.pns[alle]["p_ges"] = tk.DoubleVar()
            self.pns[alle]["p_ges"].set(0)
            self.pns[alle]["p_po4"] = tk.DoubleVar()
            self.pns[alle]["p_po4"].set(0)
            self.pns[alle]["durchfluss"] = tk.DoubleVar()
            self.pns[alle]["durchfluss"].set(0)

        # Alle nicht-flüssigen Probenahmestellen erstellen und auf Null stellen
        for alle in [7,8]:
            self.pns[alle] = {}
            self.pns[alle]["menge"] = tk.DoubleVar()
            self.pns[alle]["menge"].set(0)
            self.pns[alle]["p_ges_massengehalt"] = tk.DoubleVar()
            self.pns[alle]["p_ges_massengehalt"].set(0)

        # Variablen für die Verfahren bauen erst die die bei allen gleich sind
        self.verf_ablauf = {}
        self.verf_schlammwasser = {}
        self.verf_faulschlamm = {}
        self.verf_asche = {}
        for x in ["p_prozent_entnahme", "investkosten", "betriebskosten_pro_p", "verkaufserloes_pro_p", "zeitspanne_abschreibung"]:
            self.verf_ablauf[x] = tk.DoubleVar()
            self.verf_ablauf[x].set(0)
            self.verf_schlammwasser[x] = tk.DoubleVar()
            self.verf_schlammwasser[x].set(0)
            self.verf_faulschlamm[x] = tk.DoubleVar()
            self.verf_faulschlamm[x].set(0)
            self.verf_asche[x] = tk.DoubleVar()
            self.verf_asche[x].set(0)

        # Zusätzliche Variablen die nicht in jedem Verfahren vorkommen
        self.verf_schlammwasser["n_nh4_vorher"] = tk.DoubleVar()
        self.verf_schlammwasser["n_nh4_vorher"].set(0)
        self.verf_schlammwasser["n_nh4_prozent_entnahme"] = tk.DoubleVar()
        self.verf_schlammwasser["n_nh4_prozent_entnahme"].set(0)
        self.verf_schlammwasser["kosten_schlammentsorgung"] = tk.DoubleVar()
        self.verf_schlammwasser["kosten_schlammentsorgung"].set(0)

        self.verf_faulschlamm["kosten_schlammverbrennung"] = tk.DoubleVar()
        self.verf_faulschlamm["kosten_schlammverbrennung"].set(0)
        self.verf_faulschlamm["n_nh4_vorher"] = tk.DoubleVar()
        self.verf_faulschlamm["n_nh4_vorher"].set(0)
        self.verf_faulschlamm["n_nh4_prozent_entnahme"] = tk.DoubleVar()
        self.verf_faulschlamm["n_nh4_prozent_entnahme"].set(0)

        self.verf_asche["kosten_schlammverbrennung"] = tk.DoubleVar()
        self.verf_asche["kosten_schlammverbrennung"].set(0)

        self.statuszeile.set("Initialisieren fertig")
        print(self.statuszeile.get())

        # Daten_berechnen

    # Fkt lädt einen bestimmten Datensatz
    def lade_datensatz(self, ka_id = 1):
        pass
        # Am Ende Statuszeile auf "Datensatz Nr. bla geladen"
        # Testen ob es den Datensatz überhaupt gibt:
        try:
            self.ds = db.Klaeranlage.objects.get(id = ka_id).select_related('ort', 'probe_fluessig', 'probe_schlamm_asche',
                'probenahmestelle', 'verfahren_ablauf', 'verfahren_asche', 'verfahren_faulschlamm',
                'verfahren_schlammwasser', 'zeitspanne')
            self.id = ka_id
            # Erst den KA Datensatz auslesen
            if not self.lade_klaeranlage():
                return False
            # Dann die Probenahmestellen Datensätze auslesen - alle
            if not self.lade_alle_proben():
                return False
            # Dann die Verfahren Datensätze auslesen
            if not self.lade_alle_verfahren():
                return False

            self.statuszeile.set("Datensatz %s geladen" % str(self.ds.id))
            return True
        except:
            self.statuszeile.set("Den angeforderten Datensatz gibt es nicht oder irgend etwas ist schief gelaufen")
            return False

    # Fkt lädt letzten aktiven Datensatz, das Flag "zuletzt_aktiv" wird dafür ausgelesen
    def lade_letzten_aktiven_datensatz(self):
        try:
            # Alle Datensätze mit dem aktive-Flag heraus holen
            aktive_ds = db.Klaeranlage.objects.filter(zuletzt_aktiv = True)
            # Falls es keinen Datensatz mit dem zuletzt_aktiv Flag gibt, den aktuellen als solchen markieren
            if aktive_ds.count() == 0:
                self.ds.zuletzt_aktiv = True
                self.ds.save()
                return self.lade_datensatz(ka_id = self.ds.id)
            # Falls es mehrere Datensätze mit dem "zuletzt_aktiv" Flag gibt den ersten nehmen und die anderen zurück setzen
            if aktive_ds.count() != 1:
                aktive_ds.update(zuletzt_aktiv = False)
                aktive_ds[0].zuletzt_aktiv = True
                aktive_ds.save()
                # Datensatz ID an die laden-Fkt übergeben
                return self.lade_datensatz(ka_id = aktive_ds[0].id)
        except:
            return False

    # Funktion speichert den aktiven Datensatz als neuen Datensatz oder überschreibt einen alten je nach dem Flag "ueberschreiben"
    # id_ueberschreiben wird ignoriert wenn ueberschreiben = False
    def speicher_datensatz(self, ueberschreiben = False):
        # Genannten Datensatz ueberschreiben
        if ueberschreiben:
            try:
                # Checken ob die ID überhaupt existiert
                self.ds = db.Klaeranlage.objects.get(id = self.id).select_related('ort', 'probe_fluessig',
                    'probe_schlamm_asche', 'probenahmestelle', 'verfahren_ablauf', 'verfahren_asche',
                    'verfahren_faulschlamm', 'verfahren_schlammwasser', 'zeitspanne')
            except:
                status_string = "Datensatz mit der id: " + str(self.id) + " existiert nicht, überschreiben nicht möglich"
                self.statuszeile.set(status_string)
                return False

        # Neuen Datensatz bauen und in self.ds laden
        else:
            # Funktion erstellt kompletten neuen Datensatz und gibt die ID zurück
            self.__new__()
            # Den neu erstellten Datensatz in der DB mit self.ds verknüpfen
            try:
                self.ds = db.Klaeranlage.objects.get(id = self.id).select_related('ort', 'probe_fluessig',
                    'probe_schlamm_asche', 'probenahmestelle', 'verfahren_ablauf', 'verfahren_asche',
                    'verfahren_faulschlamm', 'verfahren_schlammwasser', 'zeitspanne')
            except:
                return False

        # Alles speichern
        try:
            self.speicher_alle_verfahren()
            self.speicher_alle_probenahmestellen()
            self.speicher_klaeranlage()
            self.statuszeile.set("Datensatz #%s geschrieben" % self.id)
            return True
        except:
            return False

    # Funktion erstellt einen neuen leeren Datensatz und gibt die ID des Kläranlagen-Objekts zurück
    def __new__(self):
        try:
            # Ort Objekt neu erstellen und übergeben
            mein_ort = db.Ort.objects.create(ort = "Default Ort")
            mein_ort.save()
            # Kläranlage Objekt erstellen und mit Ort verknüpfen
            meine_ka = db.Klaeranlage.objects.create(ort = mein_ort)
            meine_ka.save()
            # ID aus dem Kläranlagenobjekt holen und merken
            meine_id = meine_ka.id
            # Verfahren Ablauf erstellen und verknüpfen
            mein_verf_abl = db.verfahren_ablauf.objects.create(klaeranlage = meine_ka, ansatzpunkt_id = 3)
            mein_verf_abl.save()
            # Verfahren Schlwammwasser erstellen und verknüpfen
            mein_verf_schlw = db.verfahren_schlammwasser.objects.create(klaeranlage = meine_ka, ansatzpunkt_id = 2)
            mein_verf_schlw.save()
            # Verfahren Faulschlamm erstellen und verknüpfen
            mein_verf_faulschl = db.verfahren_faulschlamm.objects.create(klaeranlage = meine_ka, ansatzpunkt_id = 6)
            mein_verf_faulschl.save()
            # Verfahren Asche erstellen und verknüpfen
            mein_verf_asche = db.verfahren_asche.objects.create(klaeranlage = meine_ka, ansatzpunkt_id = 8)
            mein_verf_asche.save()
            # Probenahmestellen flüssig erstellen und verknüpfen
            for i in [1, 2, 3, 4, 5, 6]:
                meine_probenahmestelle = db.probe_fluessig.objects.create(klaeranlage = meine_ka, probe_probenahmestelle_id = i)
                meine_probenahmestelle.save()
            # Probenahmestellen Schlamm / Asche erstellen und verknüpfen
            for i in [7, 8]:
                meine_probenahmestelle = db.probe_schlamm_asche.objects.create(klaeranlage = meine_ka, probe_probenahmestelle = i)
                meine_probenahmestelle.save()
            self.id = meine_id
            return meine_id
        except:
            return False

    # Einmal alles berechnen neu Triggern
    def alles_berechnen(self):
        # Tu Dinge
        pass

    # Funktion schreibt Kläranlagen Datensatz in die DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_klaeranlage(self):
        try:
            self.ds.abwasserabgabe_n = self.abwasserabgabe_n.get()
            self.ds.abwasserabgabe_p = self.abwasserabgabe_p.get()
            self.ds.kosten_schlammentsorgung = self.kosten_schlammentsorgung.get()
            # Ort erstellen oder aus der DB holen
            mein_ort = db.Ort.objects.get_or_create(ort = self.org.get())[0]
            mein_ort.save()
            self.ds.ort = mein_ort
            # Kläranlagen Datensatz speichern
            self.ds.save()
            return True
        except:
            return False

    # Fkt schreibt Probenahmestelle in DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_probenahmestelle(self, pns = 0):
        if pns <= 0:
            return False
        elif pns <= 6:
            try:
                # Passende Probenahmestelle zur KA und zur Stelle aus der DB holen oder erstellen
                meine_probenahmestelle = db.probe_fluessig.objects.get_or_create(klaeranlage = self.ds, probe_probenahmestelle_id = pns)[0]
                meine_probenahmestelle.durchfluss = self.pns[pns]["durchfluss"].get()
                meine_probenahmestelle.p_ges = self.pns[pns]["p_po4"].get()
                meine_probenahmestelle.p_po4 = self.pns[pns]["p_ges"].get()
                meine_probenahmestelle.save()
                return True
            except:
                return False
        elif pns <= 8:
            try:
                # Passende Probenahmestelle zur KA und zur Stelle aus der DB holen oder erstellen
                meine_probenahmestelle = db.probe_schlamm_asche.objects.get_or_create(klaeranlage = self.ds, probe_probenahmestelle_id = pns)[0]
                meine_probenahmestelle.menge = self.pns[pns]["menge"].get()
                meine_probenahmestelle.p_ges_massenanteil = self.pns[pns]["p_ges_massenanteil"].get()
                meine_probenahmestelle.save()
                return True
            except:
                return False
        else:
            return False

    # Fkt schreibt alle Probenahmestellen in DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_alle_probenahmestellen(self):
        for i in [1, 2, 3, 4, 5, 6, 7, 8]:
            if not self.speicher_probenahmestelle(i):
                return False
        return True

    # Fkt schreibt Verfahren Ablauf Daten in die DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_verfahren_ablauf(self):
        try:
            # Passendes Verfahren aus der Datenbank holen
            mein_verf = self.ds.verfahren_ablauf.objects.get_or_create(klaeranlage = self.ds)[0]
            mein_verf.p_prozent_entnahme = self.verf_ablauf["p_prozent_entnahme"].get()
            mein_verf.investkosten = self.verf_ablauf["investkosten"].get()
            mein_verf.betriebskosten_pro_p = self.verf_ablauf["betriebskosten_pro_p"].get()
            mein_verf.verkaufserloes_pro_p = self.verf_ablauf["verkaufserloes_pro_p"].get()
            mein_verf.zeitspanne_abschreibung = self.verf_ablauf["zeitspanne_abschreibung"].get()
            mein_verf.save()
            return True
        except:
            return False

    # Fkt schreibt Verfahren Schlammwasser Daten in die DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_verfahren_schlammwasser(self):
        try:
            # Passendes Verfahren aus der Datenbank holen
            mein_verf = self.ds.verfahren_schlammwasser.objects.get_or_create(klaeranlage = self.ds)[0]
            mein_verf.p_prozent_entnahme = self.verf_schlammwasser["p_prozent_entnahme"].get()
            mein_verf.investkosten = self.verf_schlammwasser["investkosten"].get()
            mein_verf.betriebskosten_pro_p = self.verf_schlammwasser["betriebskosten_pro_p"].get()
            mein_verf.verkaufserloes_pro_p = self.verf_schlammwasser["verkaufserloes_pro_p"].get()
            mein_verf.zeitspanne_abschreibung = self.verf_schlammwasser["zeitspanne_abschreibung"].get()
            mein_verf.n_nh4_vorher = self.verf_schlammwasser["n_nh4_vorher"].get()
            mein_verf.n_nh4_prozent_entnahme = self.verf_schlammwasser["n_nh4_prozent_entnahme"].get()
            mein_verf.save()
            return True
        except:
            return False

    # Fkt schreibt Verfahren Faulschlamm Daten in die DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_verfahren_faulschlamm(self):
        try:
            # Passendes Verfahren aus der Datenbank holen
            mein_verf = self.ds.verfahren_faulschlamm.objects.get_or_create(klaeranlage = self.ds)[0]
            mein_verf.p_prozent_entnahme = self.verf_faulschlamm["p_prozent_entnahme"].get()
            mein_verf.investkosten = self.verf_faulschlamm["investkosten"].get()
            mein_verf.betriebskosten_pro_p = self.verf_faulschlamm["betriebskosten_pro_p"].get()
            mein_verf.verkaufserloes_pro_p = self.verf_faulschlamm["verkaufserloes_pro_p"].get()
            mein_verf.zeitspanne_abschreibung = self.verf_faulschlamm["zeitspanne_abschreibung"].get()
            mein_verf.kosten_schlammentsorgung = self.verf_faulschlamm["kosten_schlammentsorgung"].get()
            mein_verf.n_nh4_vorher = self.verf_faulschlamm["n_nh4_vorher"].get()
            mein_verf.n_nh4_prozent_entnahme = self.verf_faulschlamm["n_nh4_prozent_entnahme"].get()
            mein_verf.save()
            return True
        except:
            return False

    # Fkt schreibt Verfahren Asche Daten in die DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_verfahren_asche(self):
        try:
            # Passendes Verfahren aus der Datenbank holen
            mein_verf = self.ds.verfahren_asche.objects.get_or_create(klaeranlage = self.ds)[0]
            mein_verf.p_prozent_entnahme = self.verf_asche["p_prozent_entnahme"].get()
            mein_verf.investkosten = self.verf_asche["investkosten"].get()
            mein_verf.betriebskosten_pro_p = self.verf_asche["betriebskosten_pro_p"].get()
            mein_verf.verkaufserloes_pro_p = self.verf_asche["verkaufserloes_pro_p"].get()
            mein_verf.zeitspanne_abschreibung = self.verf_asche["zeitspanne_abschreibung"].get()
            mein_verf.kosten_schlammverbrennung = self.verf_asche["kosten_schlammverbrennung"].get()
            mein_verf.save()
            return True
        except:
            return False

    # Fkt schreibt alle Verfahren in die DB
    # self.ds muss vorher schon auf den richtigen DS gesetzt sein!
    def speicher_alle_verfahren(self):
        if not self.speicher_verfahren_ablauf():
            return False
        if not self.speicher_verfahren_schlammwasser():
            return False
        if not self.speicher_verfahren_faulschlamm():
            return False
        if not self.speicher_verfahren_asche():
            return False
        return True

    # Fkt lädt Kläranlagen-Tabelle aus DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_klaeranlage(self):
        try:
            self.abwasserabgabe_n.set(self.ds.abwasserabgabe_n)
            self.abwasserabgabe_p.set(self.ds.abwasserabgabe_p)
            self.kosten_schlammentsorgung.set(self.ds.kosten_schlammentsorgung)
            self.ort.set(self.ds.ort.ort)
            return True
        except:
            return False

    # Fkt lädt Probe zu Probenahmestelle 1-6, aber nur eine!
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_probe_fluessig(self, stelle = 0):
        if stelle <= 0:
            return False
        # Nur wenn die Probenahme stelle 1-6 ist etwas tun
        elif stelle <= 6:
            try:
                # Passende Probe aus der Datenbank holen
                meine_probe = self.ds.probe_fluessig_set.filter(probe_probenahmestelle_id = stelle)[0]
                self.pns[stelle]["p_ges"].set(meine_probe.p_ges)
                self.pns[stelle]["p_po4"].set(meine_probe.p_po4)
                self.pns[stelle]["durchfluss"].set(meine_probe.durchfluss)
                return True
            except:
                return False
        else:
            return False

    # Fkt lädt Probe zu Probenahmestelle 7-8, aber nur eine!
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_probe_schlamm_asche(self, stelle = 0):
        if stelle <= 6:
            return False
        elif stelle <= 8:
            try:
                # Passende Probe aus der Datenbank holen
                meine_probe = self.ds.probe_schlamm_asche_set.filter(probe_probenahmestelle_id = stelle)[0]
                self.pns[stelle]["menge"].set(meine_probe.menge)
                self.pns[stelle]["p_ges_massengehalt"].set(meine_probe.p_ges_massengehalt)
                return True
            except:
                return False
        else:
            return False

    # Fkt lädt Probe zu einzelner Probenahmestelle aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_probe(self, stelle = 0):
        # Falls nichts angegeben
        if stelle <= 0:
            return False
        # Falls flüssige Probe
        elif stelle <= 6:
            return self.lade_probe_fluessig(stelle = stelle)
        # Falls nicht flüssige Probe
        elif stelle <= 8:
            return self.lade_probe_schlamm_asche(stelle = stelle)
        else:
            return False

    # Fkt lädt alle Proben an allen Probenahmestellen aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_alle_proben(self):
        for x in [1, 2, 3, 4, 5, 6, 7, 8]:
            rueck = self.lade_probe(stelle = x)
            if not rueck:
                return False
        return True

    # Fkt lädt Verfahren Ablauf Daten aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_verfahren_ablauf(self):
        # Passendes Verfahren aus der Datenbank holen
        mein_verf = self.ds.verfahren_ablauf_set.all()[0]
        self.verf_ablauf["p_prozent_entnahme"].set(mein_verf.p_prozent_entnahme)
        self.verf_ablauf["investkosten"].set(mein_verf.investkosten)
        self.verf_ablauf["betriebskosten_pro_p"].set(mein_verf.betriebskosten_pro_p)
        self.verf_ablauf["verkaufserloes_pro_p"].set(mein_verf.verkaufserloes_pro_p)
        self.verf_ablauf["zeitspanne_abschreibung"].set(mein_verf.zeitspanne_abschreibung)

    # Fkt lädt Verfahren Schlammwasser Daten aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_verfahren_schlammwasser(self):
        # Passendes Verfahren aus der Datenbank holen
        mein_verf = self.ds.verfahren_schlammwasser_set.all()[0]
        self.verf_schlammwasser["p_prozent_entnahme"].set(mein_verf.p_prozent_entnahme)
        self.verf_schlammwasser["investkosten"].set(mein_verf.investkosten)
        self.verf_schlammwasser["betriebskosten_pro_p"].set(mein_verf.betriebskosten_pro_p)
        self.verf_schlammwasser["verkaufserloes_pro_p"].set(mein_verf.verkaufserloes_pro_p)
        self.verf_schlammwasser["zeitspanne_abschreibung"].set(mein_verf.zeitspanne_abschreibung)
        self.verf_schlammwasser["n_nh4_vorher"].set(mein_verf.n_nh4_vorher)
        self.verf_schlammwasser["n_nh4_prozent_entnahme"].set(mein_verf.n_nh4_prozent_entnahme)

    # Fkt lädt Verfahren Faulschlamm Daten aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_verfahren_faulschlamm(self):
        # Passendes Verfahren aus der Datenbank holen
        mein_verf = self.ds.verfahren_faulschlamm_set.all()[0]
        self.verf_faulschlamm["p_prozent_entnahme"].set(mein_verf.p_prozent_entnahme)
        self.verf_faulschlamm["investkosten"].set(mein_verf.investkosten)
        self.verf_faulschlamm["betriebskosten_pro_p"].set(mein_verf.betriebskosten_pro_p)
        self.verf_faulschlamm["verkaufserloes_pro_p"].set(mein_verf.verkaufserloes_pro_p)
        self.verf_faulschlamm["zeitspanne_abschreibung"].set(mein_verf.zeitspanne_abschreibung)
        self.verf_faulschlamm["kosten_schlammentsorgung"].set(mein_verf.kosten_schlammentsorgung)
        self.verf_faulschlamm["n_nh4_vorher"].set(mein_verf.n_nh4_vorher)
        self.verf_faulschlamm["n_nh4_prozent_entnahme"].set(mein_verf.n_nh4_prozent_entnahme)

    # Fkt lädt Verfahren Asche Verfahren aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_verfahren_asche(self):
        # Passendes Verfahren aus der Datenbank holen
        mein_verf = self.ds.verfahren_asche_set.all()[0]
        self.verf_asche["p_prozent_entnahme"].set(mein_verf.p_prozent_entnahme)
        self.verf_asche["investkosten"].set(mein_verf.investkosten)
        self.verf_asche["betriebskosten_pro_p"].set(mein_verf.betriebskosten_pro_p)
        self.verf_asche["verkaufserloes_pro_p"].set(mein_verf.verkaufserloes_pro_p)
        self.verf_asche["zeitspanne_abschreibung"].set(mein_verf.zeitspanne_abschreibung)
        self.verf_asche["kosten_schlammverbrennung"].set(mein_verf.kosten_schlammverbrennung)

    # Fkt lädt alle Verfahren aus der DB
    # in self.ds muss schon der richtige Datensatz geladen sein
    def lade_alle_verfahren(self):
        try:
            # Lade Verfahrensdaten für das Verfahren Ablauf
            if not self.lade_verfahren_ablauf():
                return False
            # Lade Verfahrensdaten für das Verfahren Schlammwasser
            if not self.lade_verfahren_schlammwasser():
                return False
            # Lade Verfahrensdaten für das Verfahren Faulschlamm
            if not self.lade_verfahren_faulschlamm():
                return False
            # Lade Verfahrensdaten für das Verfahren Asche
            if not self.lade_verfahren_asche():
                return False
            return True
        except:
            return False

    # Funktion setzt aktuellen Datensatz auf den zuletzt aktiven und alle anderen zurück
    def setze_aktiv(self):
        # Alle KA Datensätze heraus holen und auf nicht-aktiv setzen und speichern
        ka = db.Klaeranlage.objects.all()
        ka.update(zuletzt_aktiv = False)
        ka.save()
        # Aktuellen Datensatz heraus holen und auf aktiv setzen und speichern
        diese_ka = ka.filter(id = self.id)
        diese_ka.zuletzt_aktiv = True
        diese_ka.save()

    # CSV Zeile aus einem einzelnen KA-Datensatz bauen
    def csv_zeile_aus_ds(self, klaeranlage):
        rueckgabe_string = str(klaeranlage.id) +";" +\
            klaeranlage.ort.ort + ";" +\
            dez_str(klaeranlage.abwasserabgabe_p) + ";" +\
            dez_str(klaeranlage.abwasserabgabe_n) + ";" +\
            dez_str(klaeranlage.kosten_schlammentsorgung) + ";"
        # Alle Probenahmestellen durch iterieren
        for i in  [1, 2, 3, 4, 5, 6]:
            rueckgabe_string += str_dez(klaeranlage.probe_fluessig_set.filter(probe_probenahmestelle_id = i)[0].durchfluss) +\
                ";" + str_dez(klaeranlage.probe_fluessig_set.filter(probe_probenahmestelle_id = i)[0].p_ges) + ";" +\
                str_dez(klaeranlage.probe_fluessig_set.filter(probe_probenahmestelle_id = i)[0].po4) + ";"
        for i in [7, 8]:
            rueckgabe_string += str_dez(klaeranlage.probe_schlamm_asche_set.filter(probe_probenahmestelle_id = i)[0].menge) +\
                ";" + str_dez(klaeranlage.probe_schlamm_asche_set.filter(probe_probenahmestelle_id = i)[0].p_ges) + ";"

        # Verfahren Ablauf in CSV-String konvertieren
        rueckgabe_string += str_dez(klaeranlage.verfahren_ablauf_set.all()[0].p_prozent_entnahme) + ";" +\
            str_dez(klaeranlage.verfahren_ablauf_set.all()[0].investkosten) + ";" +\
            str_dez(klaeranlage.verfahren_ablauf_set.all()[0].betriebskosten_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_ablauf_set.all()[0].verkaufserloes_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_ablauf_set.all()[0].zeitspanne_abschreibung) + ";"

        # Verfahren Schlammwasser in CSV-String konvertieren
        rueckgabe_string += str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].p_prozent_entnahme) + ";" +\
            str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].investkosten) + ";" +\
            str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].betriebskosten_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].verkaufserloes_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].zeitspanne_abschreibung) + ";" +\
            str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].n_nh4_vorher) + ";" +\
            str_dez(klaeranlage.verfahren_schlammwasser_set.all()[0].n_nh4_prozent_entnahme) + ";"

        #Verfahren Faulschlamm in CSV-String konvertieren
        rueckgabe_string += str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].p_prozent_entnahme) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].investkosten) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].betriebskosten_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].verkaufserloes_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].zeitspanne_abschreibung) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].kosten_schlammentsorgung) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].n_nh4_vorher) + ";" +\
            str_dez(klaeranlage.verfahren_faulschlamm_set.all()[0].n_nh4_prozent_entnahme) + ";"

        #Verfahren Asche in CSV-String konvertieren
        rueckgabe_string += str_dez(klaeranlage.verfahren_asche_set.all()[0].p_prozent_entnahme) + ";" +\
            str_dez(klaeranlage.verfahren_asche_set.all()[0].investkosten) + ";" +\
            str_dez(klaeranlage.verfahren_asche_set.all()[0].betriebskosten_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_asche_set.all()[0].verkaufserloes_pro_p) + ";" +\
            str_dez(klaeranlage.verfahren_asche_set.all()[0].zeitspanne_abschreibung) + ";" +\
            str_dez(klaeranlage.verfahren_asche_set.all()[0].kosten_schlammverbrennung) + ";"

        return rueckgabe_string

    # Aus allen verfügbaren KA-Datensätzen in der DB einen CSV-Block erstellen
    def csv_inhalt_aus_allen_ds(self):
        # alle KA ausser der ersten aus der DB holen
        alle_ka = db.Klaeranlage.objects.all().select_related('ort', 'probe_fluessig', 'probe_schlamm_asche',
            'probenahmestelle', 'verfahren_ablauf', 'verfahren_asche', 'verfahren_faulschlamm',
            'verfahren_schlammwasser', 'zeitspanne')#.exclude(id = 1)

        csv_string = []
        # Aus jeder Kläranlage eine CSV-Zeile erstellen und aneinander hängen
        for jede_ka in alle_ka:
            csv_string.append(self.csv_zeile_aus_ds((jede_ka)))
        return csv_string

    # CSV-File schreiben mit Spaltenüberschriften und kompletter DB als Inhalt
    def schreibe_csv_datei(self, dateiname):
        titelzeile = "ID;Name der Kläranlage;Ort;Abwasserabgabe Phosphor [€/kg];Abwasserabgabe N [€/kg];" +\
            "Kosten Schlammentsorgung [€/t];PNS 1 Durchfluss [m³/a];PNS 1 P_ges [kg/a];PNS 1 P_PO4 [kg/a];" +\
            "PNS 2 Durchfluss [m³/a];PNS 2 P_ges [kg/a];PNS 2 P_PO4 [kg/a];PNS 3 Durchfluss [m³/a];PNS 3 P_ges [kg/a];"+\
            "PNS 3 P_PO4 [kg/a];PNS 4 Durchfluss [m³/a];PNS 4 P_ges [kg/a];PNS 4 P_PO4 [kg/a];PNS 5 Durchfluss [m³/a];"+\
            "PNS 5 P_ges [kg/a];PNS 5 P_PO4 [kg/a];PNS 6 Durchfluss [m³/a];PNS 6 P_ges [kg/a];PNS 6 P_PO4 [kg/a];"+\
            "PNS 7 Menge [t/a];PNS 7 P_ges Prozent Massengehalt [%];PNS 8 Menge [t/a];"+\
            "PNS 8 P_ges Prozent Massengehalt[%];Verfahren KA Ablauf Prozent P-Entnahme [%];"+\
            "Verfahren KA Ablauf Investkosten [€];Verfahren KA Ablauf Betriebskosten pro Jahr pro kg P[€/a/kg P];"+\
            "Verfahren KA Ablauf Verkaufserlös [€/kg P];Verfahren KA Ablauf Abschreibungszeitspanne [a];"+\
            "Verfahren Schlammwasser Prozent P-Entnahme [%];Verfahren Schlammwasser Investkosten [€];"+\
            "Verfahren Schlammwasser Betriebskosten pro Jahr pro kg P[€/a/kg P];"+\
            "Verfahren Schlammwasser Verkaufserlös [€/kg P];Verfahren Schlammwasser Abschreibungszeitspanne [a];"+\
            "Verfahren Schlammwasser N_NH4 Vorher [kg/a];Verfahren Schlammwasser Prozent N_NH4 Entnahme [%];"+\
            "Verfahren Faulschlamm Prozent P-Entnahme [%];Verfahren Faulschlamm Investkosten [€];"+\
            "Verfahren Faulschlamm Betriebskosten pro Jahr pro kg P[€/a/kg P];"+\
            "Verfahren Faulschlamm Verkaufserlös [€/kg P];Verfahren Faulschlamm Abschreibungszeitspanne [a];"+\
            "Verfahren Faulschlamm Kosten Schlammentsorgung [€/t];Verfahren Faulschlamm N_NH4 Vorher [kg/a];"+\
            "Verfahren Faulschlamm Prozent N_NH4 Entnahme [%];Verfahren Asche Prozent P-Entnahme [%];"+\
            "Verfahren Asche Investkosten [€];Verfahren Asche Betriebskosten pro Jahr pro kg P[€/a/kg P];"+\
            "Verfahren Asche Verkaufserlös [€/kg P];Verfahren Asche Abschreibungszeitspanne [a];"+\
            "Verfahren Asche Kosten Schlammverbrennung [€/t]\r\n"
        # Aufhören wenn es die Datei schon gibt oder GUI-Abfrage ob überschrieben werden soll
        # Falls es das File schon gibt aufhören
        if os.path.isfile(dateiname):
            self.statuszeile.set("Datei "+dateiname+" existiert schon, wird nicht überschrieben")
            return False
        # Falls es das File noch nicht gibt weiter machen

        # CSV-Inhalt aus allen Datensätzen holen
        inhalt_csv = self.csv_inhalt_aus_allen_ds()

        # Komplette Datenbank ausser Default-Datensatz in ein CSV mit Titelzeile schreiben
        offene_datei = open(dateiname, "rw")
        offene_datei.writelines(titelzeile)
        offene_datei.writelines(inhalt_csv)
        offene_datei.close()

    # Aus einer CSV-Datei Datensätze lesen
    def lese_csv_datei(self, dateiname):
        # Checken ob es das File überhaupt gibt -> sonst Fehler in Statuszeile und
        # Erst checken ob das File dem gewünschten Format entspricht!
        # Wenn nicht wenn möglich sagen in welcher Zeile ein Fehler ist

        # Tu Dinge
        pass


# 1105. Filling Bookcase Shelves
class Solution:
    def minHeightShelves(self, books: List[List[int]], shelf_width: int) -> int:
        '''
        Let f[i] be the min total height to arrange books[:i], aka, the first i-1 books.

        State transitions:
        f[0] = 0
        f[i] = min(f[i], f[j] + max(heights[j:i]) for all j such that sum(width[j:i][0]) <= shelf_width)
        '''
        n = len(books)
        f = [math.inf] * (n + 1)
        f[0] = 0
        for i in range(1, n + 1):
            height = totalWidth = 0
            for j in range(i-1, -1, -1):
                if totalWidth + books[j][0] > shelf_width:
                    break
                totalWidth += books[j][0]
                height = max(height, books[j][1])
                f[i] = min(f[i], f[j] + height)
        return f[-1]

medium/1105. Filling Bookcase Shelves.py


import doctest

# All functions in this file were implemented by me as well as the type alias DateInfo

# represents information for a date (year number, month number, day number)
# assumes year number, month number, and day number represent a valid date in the calendar year
DateInfo = tuple[int, int, int]
YEAR_INDEX = 2
MONTH_INDEX = 1
DAY_INDEX = 0

# represents information for a Netflix show (show type, show title, list of show director names, list of show actor names, date show was added)
NetflixShowInfo = tuple[str, str, list[str], list[str], DateInfo]
TITLE_INDEX = 1
ACTOR_LIST = 3
DATE_ADDED = 4

def multiply_by(list1: list[float], list2: list[int]) -> None:
    '''
    given a list of elements that can be multiplied by an integer and a second list of non-negative integers, the function will upadte the first list so that every element in the first list is multiplied by value at the corresponding position in the second list.
    >>> multiply_by([1, 2, 3], [2, 4, 0])
    >>> multiply_by([1, 2, 3], [2, 4])
    >>> multiply_by([1, 2, 3], [2, 4, 0, 2])
    >>> multiply_by([5.5, -4, 0], [23, 3])
    >>> multiply_by([5.5, -4, 0], [23, 3, 1, 0])
    >>> multiply_by([1.2, 0.5], [13, 2, 3, 0])
    '''
    len_list1 = len(list1)
    len_list2 = len(list2)

    if len_list1 > 0 and len_list2 > 0:
        if len_list2 <= len_list1:
            for index in range(len_list2):
                list1[index] *= list2[index]
        elif len_list2 > len_list1:
            for index in range(len_list1):
                list1[index] *= list2[index]

def create_date(date: str) -> DateInfo:
    '''
    given a string that represents a valid date in the calendar year and returns a date as a tuple (as specified in DateInfo).

    Precondition: date must be in the format 'day-month-year' where day is a 2 digit integer representing a valid day in the month, month is the first 3 letters of a valid month, and its first letter is uppercase, and year is a 2 digit integer representing a year in the 2000's
    >>> create_date('10-Jan-18')
    (2018, 1, 10)
    >>> create_date('22-Feb-00')
    (2000, 2, 22)
    >>> create_date('23-Mar-10')
    (2010, 3, 23)
    >>> create_date('01-Apr-01')
    (2001, 4, 1)
    >>> create_date('02-May-02')
    (2002, 5, 2)
    >>> create_date('30-Jun-05')
    (2005, 6, 30)
    >>> create_date('21-Jul-19')
    (2019, 7, 21)
    >>> create_date('05-Aug-18')
    (2018, 8, 5)
    >>> create_date('14-Sep-20')
    (2020, 9, 14)
    >>> create_date('31-Oct-21')
    (2021, 10, 31)
    >>> create_date('22-Nov-10')
    (2010, 11, 22)
    >>> create_date('20-Dec-09')
    (2009, 12, 20)
    '''
    final_tuple = []
    date_list = date.split('-')
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    index = 0

    final_tuple.append(int(str(20) + str(date_list[YEAR_INDEX])))

    while not months[index] == date_list[MONTH_INDEX]:
        index += 1

    final_tuple.append(index+1)

    if str(date_list[DAY_INDEX])[0] == 0:
        final_tuple.append(int(str(date_list[DAY_INDEX])[1]))
    else:
        final_tuple.append(int(date_list[DAY_INDEX]))

    return tuple(final_tuple)

def create_show(show_type: 'str', title: 'str', director_names: 'str', actor_names: 'str', date_added_Netflix: 'str') -> NetflixShowInfo:
    '''
    given show type, title, directors, actors, and date added to Netflix, the function returns a Netflix show tuple

    Precondition: names of directors and actors are separated by ':' and the date added to Netflix is given in the format 'day-month-year'. Where day is a 2 digit integer representing a valid day in the calendar month, month is the first 3 letters of a valid month, where the first letter is uppercase, and year is a 2 digit integer representing a year in the 2000's. date_added_Netflix is not an empty string.
    >>> create_show('Movie', 'The Invention of Lying', 'Ricky Gervais:Matthew Robinson', 'Ricky Gervais:Jennifer Garner:Jonah Hill:Rob Lowe:Tina Fey', '02-Jan-18')
    ('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2))
    >>> create_show('TV Show', 'The Mind Explained', '', 'Emma Stone', '12-Sep-09')
    ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2009, 9, 12))
    >>> create_show('Movie', 'The Bad Kids', 'Keith Fulton:Louis Pepe', '', '01-Apr-17')
    ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1))
    '''
    final_tuple = []
    directors_list = director_names.split(':')
    actors_list = actor_names.split(':')
    len_directors = len(director_names)
    len_actors = len(actor_names)

    final_tuple.append(show_type)
    final_tuple.append(title)

    if not len_directors == 0:
        final_tuple.append(directors_list)
    else:
        final_tuple.append([])

    if not len_actors == 0:
        final_tuple.append(actors_list)
    else:
        final_tuple.append([])

    date = create_date(date_added_Netflix)
    final_tuple.append(date)

    return tuple(final_tuple)

def get_titles(show_list: list[NetflixShowInfo]) -> list[str]:
    '''
    given a list of NetflixShowInfo tuples, the function returns a list of strings containing the titles of each of the Netflix shows in the order that they appear in the given list.
    >>> get_titles([('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2009, 9, 12))])
    ['The Invention of Lying', 'The Mind Explained']
    >>> get_titles([(), ('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2))])
    ['The Invention of Lying']
    >>> get_titles([('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2009, 9, 12)), ()])
    ['The Mind Explained']
    '''
    final_list = []
    len_show_list = len(show_list)
    index = 0

    while index < len_show_list:
        show_tuple = show_list[index]
        if len(show_tuple) > 0:
            final_list.append(show_tuple[TITLE_INDEX])
        index += 1

    return final_list

def is_actor_in_show(show_tuple: NetflixShowInfo, actor: str) -> bool:
    '''
    given a tuple containing information on a Netflix show and a string representing an actor, the function returns True if the given actor is an actor in the given Netflix show. False otherwise.

    Precondition: actor is not an empty string
    >>> is_actor_in_show(('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2)), 'Rob Lowe')
    True
    >>> is_actor_in_show(('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2)), 'MAtthew Robinson')
    False
    >>> is_actor_in_show(('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2009, 9, 12)), 'emma Stone')
    True
    >>> is_actor_in_show(('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2009, 9, 12)), 'Rob Lowe')
    False
    >>> is_actor_in_show(('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2)), 'emma stone')
    False
    >>> is_actor_in_show(('Movie', 'The Invention of Lying', ['Ricky Gervais', 'Matthew Robinson'], ['Ricky Gervais', 'Jennifer Garner', 'Jonah Hill', 'Rob Lowe', 'Tina Fey'], (2018, 1, 2)), 'roB lowE')
    True
    >>> is_actor_in_show(('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), 'emma Stone')
    False
    '''
    actors = show_tuple[ACTOR_LIST]
    len_actors = len(actors)
    index = 0

    while index < len_actors and not actors[index].lower() == actor.lower():
        index += 1

    if len_actors == 0:
        return False
    elif index == len_actors:
        return actors[index-1].lower() == actor.lower()
    else:
        return True

def count_shows_before_date(show_list: list[NetflixShowInfo], date: DateInfo) -> int:
    '''
    given a list of tuples containing NetflixShowInfo and a date in the format DateInfo, the function will return a count of the number of Neflix show tuples for which the date they were added is BEFORE the given date in the calendar.
    >>> count_shows_before_date([('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))], (2018, 12, 12))
    2
    >>> count_shows_before_date([('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))], (2020, 1, 3))
    4
    >>> count_shows_before_date([('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))], (2015, 10, 31))
    0
    '''
    count = 0
    len_show_list = len(show_list)

    for index in range(len_show_list):
        show_tuple = show_list[index]
        show_date = show_tuple[DATE_ADDED]
        if show_date < date:
            count += 1

    return count

def get_shows_with_actor(show_list: list[NetflixShowInfo], actor: 'str') -> list[NetflixShowInfo]:
    '''
    given a list of tuples that contain NetflixShowInfo and a string representing an actor, the function returns a list of only the Netflix show tuples that the given actor has acted in.
    >>> get_shows_with_actor([('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))], 'Emma Stone')
    [('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))]

    >>> get_shows_with_actor([('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))], 'emmA sTone')
    [('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))]

    >>> get_shows_with_actor([('Movie', 'Superbad', ['Greg Mottola'], ['Jonah Hill', 'Michael Cera', 'Christopher Mintz-Plasse', 'Bill Hader', 'Seth Rogen', 'Martha MacIsaac', 'Emma Stone', 'Aviva Baumann', 'Joe Lo Truglio', 'Kevin Corrigan'], (2019, 9, 1)), ('Movie', 'The Bad Kids', ['Keith Fulton', 'Louis Pepe'], [], (2017, 4, 1)), ('TV Show', 'Maniac', [], ['Emma Stone', 'Jonah Hill', 'Justin Theroux', 'Sally Field', 'Gabriel Byrne', 'Sonoya Mizuno', 'Julia Garner', 'Billy Magnussen', 'Jemima Kirke'], (2018, 9, 21)), ('TV Show', 'The Mind Explained', [], ['Emma Stone'], (2019, 9, 12))], 'Ricky Gervais')
    []
    '''
    final_list = []
    len_show_list = len(show_list)

    for index in range(len_show_list):
        show_tuple = show_list[index]
        if is_actor_in_show(show_tuple, actor) == True:
            final_list.append(show_tuple)

    return final_list


#
# @lc app=leetcode id=1180 lang=python3
#
# [1180] Count Substrings with Only One Distinct Letter
#

# @lc code=start
class Solution:
    def countLetters(self, S: str) -> int:
        lo, hi = 0, 0
        cnt = 0
        while hi <= len(S):
            if hi == len(S) or S[hi] != S[lo]:
                n = hi - lo
                cnt += (n + 1) * n // 2
                lo = hi
            hi += 1

        return cnt

Solution().countLetters("aaaba")
# @lc code=end


"""
Creates a class which defines a lattice for calculation
"""

import numpy as np

class Lattice():

    def __init__(self, length, p_max, p_newspacing, init_params, params):

        self.length = length
        self.p_max  = p_max
        self.deltap = p_max / length

        self.alphas = init_params[0]
        self.Qs     = init_params[1]
        self.f0     = init_params[2]

        self.Nc = params[0]
        self.Nf = params[1]
        self.CF = (self.Nc**2 - 1) / (2*self.Nc)

        self.lattice  = self.construct(p_newspacing)
        self.lattice_ = self.construct2()

        # Gluon distribution functions
        self.function_ = np.array([self.f0/self.alphas]*len(self.lattice_)) * \
                            np.heaviside(self.Qs - self.lattice_, 0.5)
        self.function  = np.array([self.f0/self.alphas]*(len(self.lattice_)-1)) * \
                            np.heaviside(self.Qs - self.lattice, 0.5)
        # Quark distribution function
        self.Function_ = np.array([self.f0/self.alphas]*len(self.lattice_)) * \
                            np.heaviside(self.Qs - self.lattice_, 0.5)
        self.Function  = np.array([self.f0/self.alphas]*(len(self.lattice_)-1)) * \
                            np.heaviside(self.Qs - self.lattice, 0.5)
        # Antiquark distribution function
        self.Fbunction_ = np.zeros(len(self.lattice_))
        self.Fbunction  = np.zeros(len(self.lattice))

        return

    def construct(self, p_newspacing):

        """
        Creates the lattice according to the __init__ parameters
        """

        lat1 = np.linspace(p_newspacing, self.Qs, int(self.length*self.Qs/self.p_max))
        lat2 = np.arange(self.Qs, self.p_max, self.deltap)

        if p_newspacing != 0:
            #lat_aux = np.arange(0, p_newspacing, self.deltap*0.2)
            lat_aux = np.logspace(-3.5, np.log10(p_newspacing), 10)
            return np.concatenate((lat_aux[:-1], lat1, lat2[1:]))
        else:
            return lat

    def construct2(self):

        lat_ = (self.lattice[1:] + self.lattice[:-1]) / 2

        return np.concatenate(([0], lat_, [2*lat_[-1] - lat_[-2]]))

    def number(self, i, pf, particle):

        """
        Computes the number of particles in the volume of the phase space which
        momentum is in the interval (p_lattice[i], p_lattice[i+1])
        :param i: int, index of the lattice on which we want to compute the number
        :param pf: function distribution of momentum times momentum
        :param particle: string with the name of particle (gluon or quark)
        :return: float
        """

        if particle == 'gluon':
            return 4 * self.Nc * (self.lattice_[i+1]**2 - self.lattice_[i]**2) * \
                    pf[i] * self.CF / (4 * np.pi**2)
        elif particle == 'quark':
            return 2 * self.Nc * (self.lattice_[i+1]**2 - self.lattice_[i]**2) * \
                    pf[i] * self.Nf / (4 * np.pi**2)
        else:
            print('Particle %s is not included in computation' %particle)

    def energy(self, i, pf, particle):

        """
        Computes the energy density in the volume of the phase space which momentum
        is in the interval (p_lattice[i], p_lattice[i+1])
        :param i: int, index of the lattice on which we want to compute the number
        :param pf: function distribution of momentum times momentum
        :param particle: string with the name of particle (gluon or quark)
        :return: float
        """

        if particle == 'gluon':
            return 4 * self.Nc * (self.lattice_[i+1]**3 - self.lattice_[i]**3) * \
                    pf[i] * self.CF / (6 * np.pi**2)
        elif particle == 'quark':
            return 2 * self.Nc * (self.lattice_[i+1]**3 - self.lattice_[i]**3) * \
                    pf[i] * self.Nf / (6 * np.pi**2)
        else:
            print('Particle %s is not included in computation' %particle)

    def entropy(self, i, f, particle):

        """
        Computes the entropy in the volume of the phase space which momentum is
        in the interval (p_lattice[i-1], p_lattice[i])
        Notice that in f=0, there is an indetermination in the term f*log(f),
        so we take the limit 0*log(0)=0
        :param i: int, index of the lattice on which we want to compute the number
        :param f: function distribution of momentum
        :param particle: string with the name of particle (gluon or quark)
        :return: float
        """

        if particle == 'gluon':
            if f[i] <= 0:
                return 4 * self.Nc * self.CF * (self.lattice_[i + 1] ** 3 - self.lattice_[i] ** 3) * \
                        (1 + f[i]) * np.log(1 + f[i]) / (6 * np.pi ** 2)
            else:
                return 4 * self.Nc * self.CF * (self.lattice_[i + 1] ** 3 - self.lattice_[i] ** 3) * (
                        (1 + f[i]) * np.log(1 + f[i]) - f[i] * np.log(f[i])) / \
                        (6 * np.pi ** 2)

        if particle == 'quark':
            if f[i] <= 0:
                return -2 * self.Nc * self.Nf * (self.lattice_[i + 1] ** 3 - self.lattice_[i] ** 3) * \
                        (1 - f[i]) * np.log(1 - f[i]) / (6 * np.pi ** 2)
            else:
                return -2 * self.Nc * self.Nf * (self.lattice_[i + 1] ** 3 - self.lattice_[i] ** 3) * (
                        (1 - f[i]) * np.log(1 - f[i]) + f[i] * np.log(f[i])) / \
                        (6 * np.pi ** 2)

        else:
            print('Particle %s is not included in computation' %particle)

    def save_lattice(self):

        """
        Save the lattice in a .txt file
        :return:
        """

        np.savetxt('data/lattice.txt', self.lattice)
        np.savetxt('data/lattice_.txt', self.lattice_)


import tkinter as tk

class TodoListApp:
    def __init__(self):
        self.tasks = []

        # Create a Main Window
        self.root = tk.Tk()  # Tk() creates a new window
        self.root.title("Todo List")

        # Task entry field creation
        self.task_entry = tk.Entry(self.root)
        self.task_entry.pack(side=tk.LEFT, padx = 5)

    # Add button creation
        self.add_button = tk.Button(self.root, text="Add Task", command=self.add_task)
        self.add_button.pack(side = tk.LEFT, padx = 5)

    # Task list creation
        self.task_list = tk.Listbox(self.root)
        self.task_list.pack(side = tk.LEFT, padx = 5)

    # Delete Button Creation
        self.delete_button = tk.Button(self.root, text="Delete Task", command=self.delete_task)
        self.delete_button.pack(side = tk.LEFT, padx = 5)

    # Loop

        self.root.mainloop()

    def add_task(self):
        task = self.task_entry.get()
        if task:
            self.tasks.append(task)
            self.task_list.insert(tk.END, task)
            self.task_entry.delete(0, tk.END)

    def delete_task(self):
        selection = self.task_list.curselection()
        if selection:
            index = selection[0]
            del self.tasks[index]
            self.task_list.delete(index)