from django.urls import path
from .views import (
    IndexView,
    CategoryDetailView,
    ProductDetailView,
    empty_cart,
    SearchView,
    about_us,
    contacts,
    video,
)

urlpatterns = [
    path('', IndexView.as_view(), name='home'),
    path('category/<str:slug>', CategoryDetailView.as_view(), name='category'),
    path('product/<str:slug>', ProductDetailView.as_view(), name='product_detail'),
    path('empty_cart/', empty_cart, name='empty_cart'),
    path('about_us/', about_us, name='about_us'),
    path('contacts/', contacts, name='contacts'),
    path('video/', video, name='video'),
    path('search/', SearchView.as_view(), name='search'),
]


from fastapi import FastAPI, Request, UploadFile, File
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from starlette.datastructures import Address
import requests
import json

# from tensorflow.keras.models import load_model

import numpy as np

from matplotlib import pyplot as plt

from PIL import Image
import io

# np.random.seed(1000)
# gan = load_model('models/result_gan_nontrainable.h5')
# generator = load_model('models/generator.h5')
latent_dims = 100
number_of_photos = 25

cats_vs_dogs_URL = 'https://kovalevskiy-cats-vs-dogs.herokuapp.com/'
ganime_URL = 'https://kovalevskiy-ganime.herokuapp.com/'

# cats_dogs_classifier = load_model('models/cats_vs_dogs.h5')

app = FastAPI(docs_url=None, redoc_url=None)

app.mount("/static", StaticFiles(directory="static"), name="static")

templates = Jinja2Templates(directory='templates')

def plot_images(images):
    figure = plt.figure(figsize=(5, 5))
    print(images.shape)
    for i in range(number_of_photos):
        plt.subplot(5, 5, i + 1)
        plt.imshow((images[i] + 1) / 2)
        plt.axis('off')
        plt.tight_layout()
    plt.savefig('static/generated.png')

def prediction_result(prediction):
    return f'Cat probability: {round(prediction[0] * 100, 4)}%, \
         Dog probability: {round(prediction[1] * 100, 4)}%'

@app.get('/', response_class=HTMLResponse)
def index(request: Request):
    # print(request)
    return templates.TemplateResponse('index.html', {'request': request})

@app.get('/cats_vs_dogs', response_class=HTMLResponse)
def cats_vs_dogs(request: Request):
    return templates.TemplateResponse(
        'cats_vs_dogs.html', {
            'request': request})

@app.post('/cats_vs_dogs', response_class=HTMLResponse)
async def cats_vs_dogs(request: Request, file_input: UploadFile = File(...)):
    file_format = file_input.content_type
    if 'image' in file_format:
        file_data = await file_input.read()
        im = Image.open(io.BytesIO(file_data))
        im_arr = np.asarray(im)
        filename = 'image.jpg'
        im.save(f'static/{filename}')
        im.close()
        prediction = requests.post(cats_vs_dogs_URL, json=json.dumps(im_arr.tolist())).json()['prediction']
        prediction = prediction_result(prediction)
        image = True
    else:
        prediction = 'Select an image file!'
        image = False
    return templates.TemplateResponse(
        'cats_vs_dogs.html', {
            'request': request, 'prediction': prediction, 'image': image})

@app.get('/ganime', response_class=HTMLResponse)
def ganime(request: Request):
    return templates.TemplateResponse('ganime.html', {'request': request})

@app.post('/ganime', response_class=HTMLResponse)
def ganime(request: Request):
    # prediction = gan.layers[0](np.random.normal(
    #     0, 1, (number_of_photos, latent_dims)))

    # prediction = generator.predict(normal(0, 1, (number_of_photos, latent_dims)))

    images = []
    for i in range(number_of_photos):
        image = requests.get(ganime_URL).json()['Image'][0]
        images.append(image)
    images = np.array(images)
    plot_images(images)
    return templates.TemplateResponse(
        'ganime.html', {'request': request, 'img': True})


import streamlit as st

# Fonction pour calculer la variable nb_watts
def calculate_nb_watts(montant_encours, montant_nouveau, seuil_1, seuil_2, Prix_tranche_1, Prix_tranche_2, Prix_tranche_3):
    #seuil_1 = 14466
    #seuil_2 = 28436
    redevance = 429
    TVA =0.18
    Taxe_Com = 0.025
    #Prix_tranche_1 = 91.17
    #Prix_tranche_2 = 136.49
    #Prix_tranche_3 = 149.06
    cumul = montant_nouveau+montant_encours

    if cumul < seuil_1:
        montant_tranche_1 = montant_nouveau
        montant_tranche_2 = 0
        montant_tranche_3 = 0
    elif cumul<= seuil_2 and cumul> seuil_1 and montant_encours <= seuil_1:
        montant_tranche_1 = seuil_1 - montant_encours
        montant_tranche_2 = cumul - seuil_1
        montant_tranche_3 = 0
    elif cumul<= seuil_2 and cumul> seuil_1 and montant_encours > seuil_1:
        montant_tranche_1 = 0
        montant_tranche_2 = montant_nouveau
        montant_tranche_3 = 0
    elif cumul > seuil_2 and montant_encours<=seuil_1:
        montant_tranche_1 = seuil_1 - montant_encours
        montant_tranche_2 = seuil_2 - seuil_1
        montant_tranche_3 = cumul - seuil_2
    elif cumul > seuil_2 and montant_encours>seuil_1 and montant_encours<= seuil_2:
        montant_tranche_1 = 0
        montant_tranche_2 = seuil_2 - montant_encours
        montant_tranche_3 = cumul - seuil_2
    elif cumul > seuil_2 and montant_encours>seuil_2:
        montant_tranche_1 = 0
        montant_tranche_2 = 0
        montant_tranche_3 = montant_nouveau
    else:
        montant_tranche_1 = 0

    print(montant_tranche_1)
    print(montant_tranche_2)
    print(montant_tranche_3)

    if montant_encours==0:
        montant_tranche_1_HT = montant_tranche_1-redevance-(montant_tranche_1*Taxe_Com)
    else :
        montant_tranche_1_HT = montant_tranche_1-montant_tranche_1*Taxe_Com

    montant_tranche_2_HT = montant_tranche_2-montant_tranche_2*Taxe_Com
    #montant_tranche_3_HT = montant_tranche_3-montant_tranche_3*Taxe_Com-montant_tranche_3*TVA
    montant_tranche_3_HT = (montant_tranche_3-montant_tranche_3*Taxe_Com)/1.18

    conso_tranche_1 = round(montant_tranche_1_HT/Prix_tranche_1,2)
    conso_tranche_2 = round(montant_tranche_2_HT/Prix_tranche_2,2)
    conso_tranche_3 = round(montant_tranche_3_HT/Prix_tranche_3,2)
    nb_watts = conso_tranche_1 + conso_tranche_2 + conso_tranche_3
    return nb_watts, conso_tranche_1, conso_tranche_2, conso_tranche_3

# Interface utilisateur avec Streamlit
st.title("Simulateur Calcul Woyofal")
st.write("Veuillez choisir le type de foyer :")
foyer_type = st.selectbox("Sélectionnez le type de foyer :", ["DPP (Domestique Petite Puissance)", "DMP (Domestique Moyenne Puissance)"])

if foyer_type == "DPP (Domestique Petite Puissance)":
    seuil_1 = 14466
    seuil_2 = 28436
    Prix_tranche_1 = 91.17
    Prix_tranche_2 = 136.49
    Prix_tranche_3 = 149.06

elif foyer_type == "DMP (Domestique Moyenne Puissance)":
    seuil_1 = 7127
    seuil_2 = 43909
    Prix_tranche_1 = 111.23
    Prix_tranche_2 = 143.54
    Prix_tranche_3 = 150.23

st.write("Veuillez préciser les renseignements ci-dessous :")
#montant_encours = st.number_input("Quel est le montant total que vous avez déjà rechargé au cours de ce mois ?", value=0, step=1)
#montant_encours = st.slider("Quel est le montant total que vous avez déjà rechargé au cours de ce mois ?", min_value=0, max_value=100000, step=500, value=0)
montant_encours = st.number_input("Quel est le montant total que vous avez déjà rechargé au cours de ce mois ?",value=0)

#montant_nouveau = st.number_input("Combien souhaitez-vous recharger ?", value=0, step=1)
montant_nouveau = st.number_input("Combien souhaitez-vous recharger ?", value=1000)

if st.button("Calculer"):
    nb_watts, conso_tranche_1, conso_tranche_2, conso_tranche_3=calculate_nb_watts(montant_encours,montant_nouveau, seuil_1, seuil_2, Prix_tranche_1, Prix_tranche_2, Prix_tranche_3)
    st.write("Vous avez droit à :", nb_watts)
    st.write("Détails")
    st.write("Puissance Tranche 1 :", conso_tranche_1)
    st.write("Puissance Tranche 2 :", conso_tranche_2)
    st.write("Puissance Tranche 3 :", conso_tranche_3)

st.write('')
st.write('Application Créée par Omar SECK - www.linkedin.com/in/omar-seck-73a96663', text_size='small')

#st.write('Application Créée par Omar SECK - www.linkedin.com/in/omar-seck-73a96663')


"""Parse date notations."""

from calendar import IllegalMonthError
from datetime import date

import regex
from dateutil import parser
from dateutil.relativedelta import relativedelta
from traiter.old.vocabulary import LOWEST, Vocabulary

from digi_leap.parsers.base import Base
from digi_leap.pylib import patterns
from digi_leap.pylib.trait import Trait

VOCAB = Vocabulary(patterns.VOCAB)

YEAR_LEN = 2

def convert(token):
    """Normalize a parsed date."""
    trait = Trait(start=token.start, end=token.end)

    value = regex.sub(
        r'[^a-z\d]+', '-', token.group['value'], flags=regex.I | regex.X)

    if len(value) < 4:
        return None

    try:
        trait.value = parser.parse(value).date()
    except (parser.ParserError, IllegalMonthError):
        return None

    if trait.value > date.today():
        trait.value -= relativedelta(years=100)
        trait.century_adjust = True

    trait.value = trait.value.isoformat()[:10]
    return trait

def short_date_name(token):
    """Normalize a month name & year notation."""
    if token.group.get('month') and len(token.group['digits']) < YEAR_LEN:
        return None

    trait = convert(token)
    if trait:
        trait.value = str(trait.value[:-2]) + '??'
    return trait

# Until dateutils IllegalMonthError is fixes do this
def short_date_digits(token):
    """Normalize a month year as all digits notation."""
    digits = token.group['digits']

    has_month = any(x for x in digits if 0 < int(x) <= 12)
    has_year = any(x for x in digits if len(x) >= YEAR_LEN)
    if not (has_month and has_year):
        return None

    trait = convert(token)
    if trait:
        trait.value = str(trait.value[:-2]) + '??'
    return trait

LABEL_DATE = Base(
    name=__name__.split('.')[-1],
    rules=[
        VOCAB['eol'],
        VOCAB['uuid'],  # Get rid of these before they're a problem

        VOCAB.term('label', ' date '.split()),

        VOCAB.part(
            'digits', r'(?<! \d ) ( [12]\d{3} | \d{1,2} ) (?! \d )'),

        VOCAB.part('sep', r' [/_-]+ ', capture=False),

        VOCAB.part('noise', r""" \w+ """, priority=LOWEST, capture=False),

        VOCAB.producer(convert, """
            label? (?P<value> digits sep? month_name sep? digits ) """),

        VOCAB.producer(convert, """
            label? (?P<value> month_name sep? digits sep? digits ) """),

        VOCAB.producer(convert, """
            label? (?P<value> digits sep digits sep digits ) """),

        VOCAB.producer(short_date_digits, f"""
            label? (?P<value> digits sep digits ) """),

        VOCAB.producer(short_date_name, f"""
            label? (?P<value> month_name sep? digits ) """),

        VOCAB.producer(short_date_name, f"""
            label? (?P<value> digits sep? month_name ) """),
    ])


import string
from nltk.sentiment.vader import SentimentIntensityAnalyzer
from nltk.stem import PorterStemmer
from tweet import Tweet

STOPWORDS = ['', "i", "me", "my", "myself", "we", "our", "ours", "ourselves", "you",
             "your", "yours", "yourself", "yourselves", "he", "him", "his", "himself",
             "she", "her", "hers", "herself", "it", "its", "itself", "they", "them", "their",
             "theirs", "themselves", "what", "which", "who", "whom", "this", "that", "these",
             "those", "am", "is", "are", "was", "were", "be", "been", "being", "have", "has",
             "had", "having", "do", "does", "did", "doing", "a", "an", "the", "and", "but", "if",
             "or", "because", "as", "until", "while", "of", "at", "by", "for", "with", "about",
             "against", "between", "into", "through", "during", "before", "after", "above",
             "below", "to", "from", "up", "down", "in", "out", "on", "off", "over", "under",
             "again", "further", "then", "once", "here", "there", "when", "where", "why", "how",
             "all", "any", "both", "each", "few", "more", "most", "other", "some", "such",
             "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "s",
             "t", "can", "will", "just", "don", "should", "now"]

MAP_TOPICS = {'conspiracy': {'hoax', 'lie', 'conspiraci', 'trump', 'fake', 'chip'},
              'masks': {'antimask', 'mrna', 'n95', 'kn94', 'facemask', 'mask', 'cloth'},
              'quarantine': {'quarantin', 'lockdown', 'shutdown'},
              'vaccine': {'antivax', 'mrna', 'vaccin', 'needl', 'vax', 'booster', 'unvax'},
              'covid': {'covid-19', 'covid19', 'covid', 'viru', 'pandem', 'epidem'}}
TOPIC_KEYWORDS = {'hoax', 'lie', 'conspiraci', 'trump', 'fake', 'chip', 'antimask',
                  'mrna', 'n95', 'kn94', 'facemask', 'mask', 'cloth', 'quarantin',
                  'lockdown', 'shutdown', 'antivax', 'mrna', 'vaccin', 'needl',
                  'vax', 'booster', 'covid-19', 'covid19', 'covid', 'viru', 'pandem',
                  'epidem'}

def analyze_tweets(tweets: list[Tweet]) -> list[tuple[Tweet, dict[str, float], set[str]]]:
    """ Given a list of tweets, return a list containing tuples with the tweets and their
    sentiment scores and category.

    >>> import datetime
    >>> example = [Tweet(1, datetime.datetime(2021, 12, 1), 'vaccines are good')]
    >>> example_analysis = analyze_tweets(example)
    >>> example_analysis[0][0].content
    'vaccines are good'
    >>> example_analysis[0][1]
    {'neg': 0.0, 'neu': 0.408, 'pos': 0.592, 'compound': 0.4404}
    >>> example_analysis[0][2]
    {'vaccine'}
    """

    sentiment_score = analyze_sentiment(tweets)
    clean = clean_input(tweets)
    stems = split_into_stems(clean)

    # Analyze each sentence
    analyzed_tweets_so_far = []
    for i in range(len(tweets)):
        categories = calc_sentence_topic(stems[i])
        analyzed_tweets_so_far.append((tweets[i], sentiment_score[i], categories))

    return analyzed_tweets_so_far

def analyze_sentiment(msgs: list[Tweet]) -> list[dict[str, float]]:
    """ Return the emotional score in order of the tweets given using the VADER lexicon.
    The return type is a list of dictionaries with compound, neutral, positive and negative
    emotional scores which can be accessed using 'compound', 'neu', 'pos' or 'neg'

    Preconditions:
        - all(len(msg.content) > 0 for msg in msgs)
        - len(msgs) > 0

    >>> import datetime
    >>> example_pos_tweet = Tweet(1, datetime.datetime(2021, 12, 1), 'vaccines are good')
    >>> analysis = analyze_sentiment([example_pos_tweet])
    >>> analysis[0]['compound']
    0.4404

    >>> example_neg_tweet = Tweet(1, datetime.datetime(2021, 12, 1), 'i hate vaccines')
    >>> analysis = analyze_sentiment([example_neg_tweet])
    >>> analysis[0]['compound']
    -0.5719

    """

    analyzer = SentimentIntensityAnalyzer()
    scores_so_far = []
    for msg in msgs:
        vs = analyzer.polarity_scores(msg.content)
        scores_so_far.append(vs)

    return scores_so_far

def calc_word_emotions(scores: list[dict[str, float]], roots: list[list[str]]) -> dict[str, float]:
    """ Return each word with its average emotional score,
    given a list of a list of stems and a list of sentiment dictionaries.

    >>> import datetime
    >>> list_of_tweets = [Tweet(1, datetime.datetime(2021, 12, 1), 'vaccines are good')]
    >>> scores = analyze_sentiment(list_of_tweets)
    >>> clean = clean_input(list_of_tweets)
    >>> roots = split_into_stems(clean)
    >>> emotions = calc_word_emotions(scores, roots)
    >>> emotions['vaccin']
    0.4404
    """

    dict_so_far = {}
    count_so_far = calc_word_count(roots)

    for i in range(len(scores)):
        # Get the emotional score of a sentence and add it to the sum
        # for every root found in the sentence
        stems = roots[i]
        score = scores[i]

        for stem in stems:
            sum_topic_words(stem, dict_so_far, score)

    for root in dict_so_far:
        emotional_score = dict_so_far[root] / count_so_far[root]
        dict_so_far[root] = round(emotional_score, 5)

    return dict_so_far

def sum_topic_words(stem: str, sum_dict: dict[str, float], score: dict[str, float]) -> None:
    """ Checks if a word is a topic keyword. If so, add the sum of its emotional score
    to the sum dictionary.

    >>> stem = 'vaccin'
    >>> sum_dict = {}
    >>> score = {'compound': -1.0}
    >>> sum_topic_words(stem, sum_dict, score)
    >>> sum_dict
    {'vaccin': -1.0}
    """
    if stem in TOPIC_KEYWORDS:
        if stem not in sum_dict:
            sum_dict[stem] = 0
        sum_dict[stem] = sum_dict[stem] + score['compound']

def calc_word_count(stems_list: list[list[str]]) -> dict[str, int]:
    """ For a given list of list of stems, return a dictionary with each stem as a key
    and how many times a stem was counted as the value.

    >>> example_list = [['test', 'one', 'two', 'three'], ['three', 'test']]
    >>> count = calc_word_count(example_list)
    >>> count == {'test': 2, 'one': 1, 'two': 1, 'three': 2}
    True
    """

    count_so_far = {}

    for stems in stems_list:
        for stem in stems:
            if stem not in count_so_far:
                count_so_far[stem] = 0
            count_so_far[stem] = count_so_far[stem] + 1

    return count_so_far

def calc_sentence_topic(sentence: list[str]) -> set[str]:
    """Checks which category a sentence belongs to
    If word is in keyword then check which topic it's in and return the topic as str
    Else return _

    Preconditions:
        - all(len(msg) > 0 for msg in sentence)
        - len(sentence) > 0

    >>> example_sentence = ['vaccines are a hoax']
    >>> example_stems = split_into_stems(example_sentence)
    >>> calc_sentence_topic(example_stems[0]) == {'conspiracy', 'vaccine'}
    True
    """

    category_so_far = set()
    for word in sentence:
        category = calc_word_topic(word)
        if category != '_':
            category_so_far.add(category)

    return category_so_far

def calc_word_topic(word: str) -> str:
    """Checks which category a word belongs to
    If word is in keyword then check which topic it's in and return the topic as str
    Else return _

    Preconditions:
        - len(word) > 0

    >>> calc_word_topic('vaccin')
    'vaccine'
    >>> calc_word_topic('test')
    '_'
    """

    if word in TOPIC_KEYWORDS:
        for tpc in MAP_TOPICS:
            if word in MAP_TOPICS[tpc]:  # this actually won't work unless you convert word to stem
                return tpc

    return '_'

def split_into_stems(msgs: list[str]) -> list[list[str]]:
    """ Takes a list of cleaned input and splits it into word stems.
    Also removes all stopwords, which are words typically ignored in linguistic analysis.

    Preconditions:
        - all(len(msg) > 0 for msg in msgs)
        - len(msgs) > 0

    >>> example_list = ['vaccines are good']
    >>> split_into_stems(example_list)
    [['vaccin', 'good']]
    """
    porter = PorterStemmer()

    stem_lists_so_far = []
    for msg in msgs:
        word_list = msg.split(' ')
        topic_list = [porter.stem(word) for word in word_list if word not in STOPWORDS]
        stem_lists_so_far.append(topic_list)

    return stem_lists_so_far

def clean_input(msgs: list[Tweet]) -> list[str]:
    """ Takes a list of strings and returns a new list of strings that converts all tweet contents
    into lowercase and also removes all punctuation. Removes hyperlinks and new lines.

    Preconditions:
        - all(len(msg.content) > 0 for msg in msgs)
        - len(msgs) > 0

    >>> import datetime
    >>> example_tweet = Tweet(1, datetime.datetime(2021, 12, 1), 'Vaccines, are good!??')
    >>> clean_input([example_tweet])
    ['vaccines are good']
    """

    output = []
    for msg in msgs:

        # Set to lowercase and remove all new lines for better processing.
        lower_msg = msg.content.lower()
        lower_msg = lower_msg.replace('\n', ' ')
        clean_msg = ''
        for word in lower_msg.split(' '):
            clean_word = ''

            # Ignore all word snippets that are hyperlinks or include the @ character
            if '@' not in word and 'http' not in word:
                # Only add characters that are not punctuation
                clean_word = ''.join(char for char in word if char not in string.punctuation)
                clean_word = clean_word + ' '

            clean_msg = clean_msg + clean_word

        output.append(clean_msg.strip())

    return output

if __name__ == '__main__':
    import python_ta
    import python_ta.contracts

    python_ta.contracts.DEBUG_CONTRACTS = False
    python_ta.contracts.check_all_contracts()

    # When you are ready to check your work with python_ta, uncomment the following lines.
    # (Delete the "#" and space before each line.)
    # IMPORTANT: keep this code indented inside the "if __name__ == '__main__'" block
    python_ta.check_all(config={
        'extra-imports': ['string', 'nltk.sentiment.vader', 'nltk.stem', 'tweet'],
        'allowed-io': [],  # the names (strs) of functions that call print/open/input
        'max-line-length': 100,
        'disable': ['R1705', 'C0200']
    })


from DomainMaster import DomainMaster
from googlefinance import getQuotes
import json

### References_
### https://pypi.python.org/pypi/googlefinance

class StockExchange_Domain(DomainMaster):

    def __init__(self, domainDIR, d, PN):
        DomainMaster.__init__(self, domainDIR)
        self.set_pronoun_and_domain(PN, d)

        ## Retrieve stock data of given symbol.
        self.refresh_data()

##### ##### ##### ##### #####
##### ##### ##### ##### ##### STOCK VALUE RETRIEVAL
##### ##### ##### ##### #####

    ### Updates most recently available data.
    def refresh_data(self):
        self.raw = json.dumps(getQuotes(self.get_pronoun()), indent=2)
        self.data = json.loads(self.raw)

        ## Update value dictionary.
        self.set_value_dict(self.domain)

    ### Retrieves specified stock data given a type term.
    def get_data_value(self, term):
        ## Check against available terms.
        if term == "value":
            val = self.data[0]['LastTradePrice']
        else:
            return ("")

        return (val)


#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Date: 2022/3/18 20:03
Desc: 谷歌指数, 必须使用代理, 获得的数据是小时频率的, 所以获取时间周期太长会很慢
"""
from akshare.index.request import TrendReq
import pandas as pd

def google_index(
    symbol: str = "python",
    start_date: str = "20191201",
    end_date: str = "20191204",
):
    """
    谷歌指数
    :param symbol: 关键词
    :type symbol: str
    :param start_date: 开始时间
    :type start_date: str
    :param end_date: 结束时间
    :type end_date: str
    :return: 谷歌指数
    :rtype: pandas
    """

    start_date = "-".join([start_date[:4], start_date[4:6], start_date[6:]])
    end_date = "-".join([end_date[:4], end_date[4:6], end_date[6:]])
    pytrends = TrendReq(hl="en-US", tz=360)
    kw_list = [symbol]
    pytrends.build_payload(
        kw_list, cat=0, timeframe=start_date + " " + end_date, geo="", gprop=""
    )
    search_df = pytrends.interest_over_time()
    search_se = search_df[symbol]
    search_df = pd.DataFrame(search_se)
    search_df.reset_index(inplace=True)
    if "T" not in start_date:
        search_df['date'] = pd.to_datetime(search_df['date']).dt.date
    return search_df

if __name__ == "__main__":
    google_index_df = google_index(symbol="bitcoin", start_date="20000101", end_date="20220303")
    print(google_index_df)

    google_index_df = google_index(symbol="python", start_date='20040101', end_date='20191201')
    print(google_index_df)

    google_index_df = df = google_index(symbol="AI", start_date="20191210T10", end_date="20191210T23")
    print(google_index_df)


import pytest
from django.core.exceptions import ValidationError

pytestmark = pytest.mark.django_db

class TestCategoryModel:
    def test_str_method(self, category_factory):
        post = category_factory(name='test_category')
        assert post.__str__() == 'test_category Category'

class TestProductModel:
    def test_str_method(self, brand_factory):
        post = brand_factory(name='test_brand')
        assert post.__str__() == 'test_brand Brand'

class TestBrandModel:
    def test_str_method(self, product_factory):
        post = product_factory()
        assert post.__str__() == 'test_product Product'

class TestProductLineModel:
    def test_str_method(self, product_factory, product_line_factory):
        post = product_line_factory(sku='sku_1', product=product_factory(name='for_pl_prod'))
        assert post.__str__() == 'for_pl_prod Product Line sku_1'

    def test_clean(self, product_line_factory, product_factory):
        obj = product_factory()
        product_line_factory(product=obj)
        with pytest.raises(ValidationError):
            product_line_factory(order=1, product=obj).clean()
        with pytest.raises(ValidationError):
            product_line_factory(order=0).clean()


import sys
from glob import glob
from os.path import abspath, split

import pep8

testdir, fname = split(abspath(__file__))
config_file = testdir + "/data/pep8_config.txt"
excludes = ["sunposition.py"]

def test_pep8_conformance():
    """Test that we conform to PEP8."""

    config_file = "data/pep8_config.txt"
    files = []
    for fi in glob("../*.py"):
        files.append(fi)

    # After acceptance, this will be uncommented
    pep8style = pep8.StyleGuide(config_file=config_file, quiet=False)
    result = pep8style.check_files(files)
    if result.total_errors != 0:
        print("Found PEP8 conformance error.")
        print("Please fix your style with autopep8.")
    assert result.total_errors == 0


import numpy as np
import matplotlib.pyplot as plt
from sfx_utils.misc.radial import radial_profile, q2pix, pix2q

class AgBehenate:

    def __init__(self):
        self.q0 = 0.1076 # |q| of first peak in Angstrom
        self.delta_qs = np.arange(0.015,0.05,0.00005) # q-spacings to scan over, better in log space?

    def ideal_rings(self, qPeaks):
        """
        Scan over and score various inter-peak distances in q-space, based
        on the equidistance of peaks in q-space for silver behenate powder.

        Parameters
        ----------
        qPeaks : numpy.ndarray, 1d
            positions of powder peaks in q-space (inverse Angstrom)

        Returns
        -------
        rings : numpy.ndarray, 1d
            predicted positions of peaks based on the best fit q-spacing
        scores : numpy.ndarray, 1d
            residual associated with each q-spacing, ordered as self.delta_qs
        """
        order_max=13
        qround = np.round(qPeaks,5)
        scores = []
        for dq in self.delta_qs:
            order  = qround/dq
            remain = np.minimum(qround%dq, np.abs(dq-(qround%dq)))
            score  = np.mean(remain[np.where(order<order_max)])/dq # %mod helps prevent half periods from scoring well
            scores.append(score)
        deltaq_current = self.delta_qs[np.argmin(scores)] #
        rings = np.arange(deltaq_current, deltaq_current*(order_max+1), deltaq_current)
        return rings, np.array(scores)

    def detector_distance(self, est_q0, wavelength, pixel_size):
        """
        Estimate the sample-detector distance, based on the fact that the
        first diffraction ring for silver behenate is at 0.1076/Angstrom.

        Parameters
        ----------
        est_q0 : float
            predicted q-position of first ring based on the best fit q-spacing in Angstrom
        wavelength : float
            beam wavelength in Angstrom
        pixel_size : float
            detector pixel size in mm
        """
        distance = est_q0*pixel_size/np.tan(2.*np.arcsin(wavelength*(self.q0/(2*np.pi))/2))
        print("Detector distance inferred from powder rings: %s mm" % (np.round(distance,2)))
        return distance

    def opt_distance(self, powder, est_distance, pixel_size, wavelength, center=None, plot=False):
        """
        Optimize the sample-detector distance based on the powder image.

        Parameters
        ----------
        powder : numpy.ndarray, 2d
            powder diffraction image, in shape of assembled detector
        est_distance : float
            estimated sample-detector distance in mm
        pixel_size : float
            detector pixel size in mm
        wavelength : float
            beam wavelength in Angstrom
        center : tuple
            detector center (xc,yc) in pixels
        plot : bool
            if True, plot the results

        Returns
        -------
        opt_distance : float
            optimized sample-detector distance in mm
        """
        from scipy.signal import find_peaks

        if center is None:
            center = (int(powder.shape[1]/2), int(powder.shape[0]/2))

        # determine peaks in radial intensity profile and associated positions in q
        iprofile = radial_profile(powder, center=center)
        peaks_observed, properties = find_peaks(iprofile, prominence=1, distance=10)
        qprofile = pix2q(np.arange(iprofile.shape[0]), wavelength, est_distance, pixel_size)

        # optimize the detector distance based on inter-peak distances
        rings, scores = self.ideal_rings(qprofile[peaks_observed])
        peaks_predicted = q2pix(rings, wavelength, est_distance, pixel_size)
        opt_distance = self.detector_distance(peaks_predicted[0], wavelength, pixel_size)

        if plot:
            self.visualize_results(powder, center=center,
                                   peaks_predicted=peaks_predicted, peaks_observed=peaks_observed,
                                   scores=scores, Dq=self.delta_qs,
                                   radialprofile=iprofile, qprofile=qprofile)

        return opt_distance

    def visualize_results(self, image, mask=None, vmax=50,
                          center=None, peaks_predicted=None, peaks_observed=None,
                          scores=None, Dq=None,
                          radialprofile=None, qprofile=None):
        """
        Visualize fit, plotting (1) the residuals for the delta q scan, (2) the
        observed and predicted peaks in the radial intensity profile, and (3) the
        powder image.
        """
        fig = plt.figure(figsize=(8,12),dpi=120)
        nrow,ncol=4,3
        irow,icol=0,0

        # plotting residual for each inter-peak spacing
        if scores is not None:
            ax1 = plt.subplot2grid((nrow, ncol), (irow, icol))
            plt.title('Score')
            ax1.plot(Dq,scores, '--', markersize=2, linewidth=1, color='black')
            ax1.plot(Dq[np.argmin(scores)], np.min(scores), 'o', color='black')
            ax1.text(Dq[np.argmin(scores)], 0.5*np.min(scores), r'inter-ring spacing = %s $\AA^{-1}$' % (str(np.round(Dq[np.argmin(scores)],4))))
            ax1.set_ylim(0)
            ax1.set_xlabel(r'$\Delta q$ ($\AA^{-1}$)')
            icol+=1

        # plotting radial profiles with peaks
        if radialprofile is not None:
            ax2 = plt.subplot2grid((nrow, ncol), (irow, icol), colspan=ncol-icol)
            plt.title('Radially averaged intensity')
            ax2.plot(qprofile, radialprofile, lw=0.5, color='black')
            ax2.plot(qprofile[peaks_observed],radialprofile[peaks_observed], 'o', color='red', markersize=2, label='peaks detected')
            ax2.plot(qprofile[np.floor(peaks_predicted[peaks_predicted<radialprofile.shape[0]]).astype(int)],
                     radialprofile[np.floor(peaks_predicted[peaks_predicted<radialprofile.shape[0]]).astype(int)],
                     'o', color='black', markersize=2, label='peaks predicted')
            ax2.set_xlabel(r'q ($\AA^{-1}$)')
            ax2.legend()
            irow+=1

        # plotting powder
        ax3 = plt.subplot2grid((nrow, ncol), (irow, 0), rowspan=nrow-irow, colspan=ncol)
        if mask is not None:
            image *= mask
        ax3.imshow(image,interpolation='none',vmin=0,vmax=vmax)
        ax3.set_title('Average Silver Behenate')
        if center is not None:
            ax3.plot(center[0],center[1],'ro')
            if peaks_predicted is not None:
                for peak in peaks_predicted:
                    circle = plt.Circle((center[0],center[1]), peak, fill=False, color='black', linestyle=':')
                    ax3.add_artist(circle)
            if peaks_observed is not None:
                for peak in peaks_observed:
                    circle = plt.Circle((center[0],center[1]), peak, fill=False, color='red', linestyle=':')


from numpy import NaN
from pandas._libs.missing import NA
from helpers import *
from sklearn.preprocessing import StandardScaler
import pandas as pd
from joblib import load

# traits: Clothes, Ears, Hat, Fur, Mouth, Eyes

def get_good_listings(trait, ape_data):
    """
    gets good listings, depending on their trait using the ape data
    """
    # print(ape_data)
    for ape_with_trait in ape_data.get("all_sales")[trait].unique():

        # find the cheepest listing for that trait
        cheapest_listing = (
            ape_data.get("recent_listings")[
                ape_data.get("recent_listings")[trait] == ape_with_trait
            ]
            .sort_values(by="listing_price", ascending=True)
            .reset_index()
        )

        # search through listings starting with the lowest
        for index, row in cheapest_listing.iterrows():
            listing = cheapest_listing.iloc[[index]]
            # need exception for transfer event
            if is_still_listed(listing, ape_data):

                # calcualte the rarity of trair as %
                rarity_perc = (
                    ape_data.get("all_apes")[
                        ape_data.get("all_apes")[trait] == ape_with_trait
                    ].shape[0]
                    / 10000
                ) * 100

                # mean sale of that trait
                mean = ape_data.get("all_sales")[
                    ape_data.get("all_sales")[trait] == ape_with_trait
                ].sale_price.mean()

                predict_price(listing)

                # create a object with trait, rarity and diff from avg
                df_trait = {
                    trait: ape_with_trait,
                    "mean": mean,
                    "diff": mean - ape_data.get("total_mean"),
                    "rarity": rarity_perc,
                    "ape_id": listing.ape_id.item(),
                    "listing_price": listing.listing_price.item(),
                }

                # print(df_trait)
                ape_data["good_listings"][trait] = ape_data["good_listings"][
                    trait
                ].append(df_trait, ignore_index=True)
                # print(ape_data["good_listings"][trait])

                break

def predict_price(listing):
    # get the prediction for ape price
    listing["trait_n"] = listing[["Clothes", "Ears", "Hat", "Fur", "Mouth", "Eyes"]].count(axis=1)

    num_cols = ["trait_n"]

    df_t = pd.read_csv("./csvs/apes_model_dummies.csv")

    listing_ape = listing.merge(df_t, on="ape_id", how="left")

    scaler = StandardScaler()

    # guessing this needs to be run on the whole dataset
    scaler.fit(df2[num_cols])

    df2[num_cols] = scaler.transform(df2[num_cols])

    rf = load("./models/randomforest.joblib")

    listing_ape = listing.merge(df_t, on="ape_id", how="left")

    df2_pred = rf.predict(
        df2[df_t.columns]
    )  # only input columns in training set for predictions
    df2["pred_price_diff"] = df2_pred
    # get good listings for trait
    # print(ape_data["good_listings"][trait].head())

# check to make sure ape listing is not canc or sold
def is_still_listed(ape, ape_data):
    ape_id = ape.ape_id.item()

    ape_canc = ape_data.get("recent_canc")[ape_data.get("recent_canc").ape_id == ape_id]
    ape_sale = ape_data.get("all_sales")[ape_data.get("all_sales")["ape_id"] == ape_id]

    if ape_canc.empty:
        has_canc = False
    else:
        has_canc = ape_canc.canc_event_time.item() > ape.listing_event_time.item()

    if ape_sale.empty:
        has_sold = False
    else:
        has_sold = ape_sale.sale_time.item() > ape.listing_event_time.item()

    if has_canc or has_sold:
        return False
    else:
        return True

    # TODO add logic if exchanged to another wallet

def split_apes_by_num_traits():
    """
    Function splits ape data into a dict with 6 dataframes each with number of traits
    Used once, data will never change
    """
    # ape_data["all_apes"] <-- this is what we could use?
    all_apes = pd.read_csv(".\\csvs\\all_the_apes.csv")
    print(all_apes.head())
    apes_by_num_traits = {}
    for i in range(1, 7):
        apes_by_num_traits[f"{i}T"] = pd.DataFrame()

    for index, row in all_apes.iterrows():
        no_traits = 0
        for i in range(2, 8):
            if type(row[i]) == str:
                no_traits += 1
        apes_by_num_traits[f"{no_traits}T"] = apes_by_num_traits[
            f"{no_traits}T"
        ].append(row)

    for key, value in apes_by_num_traits.items():
        value.drop("Unnamed: 0", axis=1, inplace=True)
        value.to_csv(f".\csvs\\apes_by_trait_count\\{key}_apes.csv", index=False)

    return apes_by_num_traits

def get_rank_for_number_of_traits():
    """
    This function ranks each ape for the number of traits they have vs the rariry of their traits
    returns an object containing:
    ape_id

    """
    all_apes = pd.read_csv(".\\csvs\\all_the_apes.csv")
    apes_by_num_traits = {}
    for i in range(1, 7):
        apes_by_num_traits[f"{i}T"] = pd.read_csv(
            f".\\csvs\\apes_by_trait_count\\{i}T_apes.csv"
        )

    for df in apes_by_num_traits.values():
        col_names = df.columns
        df["Sum Rarity"] = NaN
        df["Mean Rarity"] = NaN
        # loop all of the rows in the rarity
        # print(df.head())
        for index, row in df.iterrows():
            # loop trait values
            ape_total_rarity = 0
            no_traits = 0
            for i in range(3, 9):
                # check for each of the values
                if type(row[i]) == str:
                    trait = col_names[i]
                    rarity_for_ape = (
                        all_apes[all_apes[trait] == row[i]].shape[0] / 10000
                    ) * 100
                    ape_total_rarity += rarity_for_ape
                    no_traits += 1
                    # print(rarity_for_ape)
            # val_to_add = pd.Series(ape_total_rarity)
            df.at[index, "Sum Rarity"] = ape_total_rarity
            df.at[index, "Mean Rarity"] = ape_total_rarity / no_traits

        df = df.sort_values("Mean Rarity")
        df.head()

    for i in range(1, 7):
        apes_by_num_traits[f"{i}T"].to_csv(
            f".\\csvs\\apes_by_trait_count\\{i}T_rar_apes.csv", index=False
        )

        # where trait is the trait we are looking for

    # so we wanna get the specific trait from the ape

    # then get the value counts of ALL apes

    # then extract the value count for that trait

# split_apes_by_num_traits()
get_rank_for_number_of_traits()


# -*- coding: utf-8 -*-
"""
This is a simple telegram bot to echo the text input of users
Reference: Gareth Dwyer
@author: Riche
"""

import json
import requests
import time
import urllib

TOKEN = "<your-bot-token>"
URL = "https://api.telegram.org/bot{}/".format(TOKEN)

def get_url(url):
    response = requests.get(url)
    content = response.content.decode("utf8")
    return content

def get_json_from_url(url):
    content = get_url(url)
    js = json.loads(content)
    return js

def get_updates(offset=None):
    # timeout is long polling technique so that telegram keeps connection until there are updates
    url = URL + "getUpdates?timeout=100"
    # offset tells the API that we dont want prev msgs
    if offset:
        url += "&offset={}".format(offset)
    js = get_json_from_url(url)
    return js

# Function that calculates the highest ID of all the updates we receive from get_updates
def get_last_update_id(updates):
    update_ids = []
    for update in updates["result"]:
        update_ids.append(int(update["update_id"]))
    return max(update_ids)

# Function to send an echo reply for each message we receive
def echo_all(updates):
    for update in updates["result"]:
        try:
            text = update["message"]["text"]
            chat = update["message"]["chat"]["id"]
            send_message(text, chat)
        except Exception as e:
            print(e)

def get_last_chat_id_and_text(updates):
    num_updates = len(updates["result"])
    last_update = num_updates - 1
    text = updates["result"][last_update]["message"]["text"]
    chat_id = updates["result"][last_update]["message"]["chat"]["id"]
    return (text, chat_id)

def send_message(text, chat_id):
    # urllib helps to handle special characters
    text = urllib.parse.quote_plus(text)
    url = URL + "sendMessage?text={}&chat_id={}".format(text, chat_id)
    get_url(url)

def main():
    last_update_id = None
    while True:
        updates = get_updates(last_update_id)
        if len(updates["result"]) > 0:
            last_update_id = get_last_update_id(updates) + 1
            echo_all(updates)
        time.sleep(0.5)

# Code will execute every 0.5s
if __name__ == '__main__':
    main()


# comando para converter arquivos UI criados pelo UI Designer para um arquivo .py:
# pyside2-uic arquivo.ui -o ui_arquivo.py (ui_arquivo.py - esse ui é somente uma referência
# de nome pra indicar a origem  do arquivo, dizendo que esse arquivo .py foi convertido de um arquivo .ui)

from PySide2.QtCore import QCoreApplication
from PySide2.QtGui import QIcon, QIntValidator
from PySide2.QtCore import QThread
from PySide2.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu, QAction, QDialog, QFileDialog, QMessageBox
from ui_janela_principal import Ui_MainWindow
from ui_janela_log import Ui_janela_log
from datetime import datetime
import socket, time, pygame, subprocess, sys, os, tempfile, atexit
from version_uptate import verifica_atualizacao
import rede

# Pega o diretório atual da execução do arquivo atual e joga o path dele na variável diretorio_atual
diretorio_atual = os.path.dirname(os.path.abspath(__file__))

# Cria um arquivo na inicialização do programa, para que, se tentar abrir novamente outra instância da mesma aplicação
# a pessoa não consiga. No fim do programa, quando o programa é fechado, o arquivo é excluido
lock_file = diretorio_atual + "/my_app.lock"

# Verificando a existência do arquivo acima
try:
    lock_fd = os.open(lock_file, os.O_CREAT | os.O_EXCL | os.O_RDWR)
# Tratando o erro e dando exit do sistema se o arquivo existir
except Exception as e:
    print(f"Erro ao abrir o arquivo de bloqueio: {str(e)}")
    sys.exit(1)

# Função chamada pelo método atexit.register, e é executado na saída do programa, quando é fechado. Ele exclui o arquivo
# criado acima (my_app.lock).
def encerrar_programa():
    os.close(lock_fd)
    os.unlink(lock_file)

# O método abaixo teve que ser importado, e permite que no fim do programa seja executado o que estiver entre parenteses
atexit.register(encerrar_programa)

# Variáveis que rodam a função obter_endereco_ip_por_nome_interface do arquivo rede.py, que retorna dois valores, o
# primeiro é o endereço de ip da interface ativa e o segundo é o nome da interface ativa, armazenando em variáveis.
endereco_interface_ativa, nome_interface_ativa = rede.obter_endereco_ip_por_nome_interface()
print(endereco_interface_ativa, nome_interface_ativa)

# ____________________________________ Sessão da classe da  threads ____________________________________
# Classe que trabalha com Thread, que é a execução de funções sem travar o programa. Se quiser que uma função seja
# executada sem travar o sistema, deve-se criar uma isntância dessa classe para rodar a função.
class NossaThread(QThread):
    def __init__(self):
        QThread.__init__(self)
        self.func = None

    def __del__(self):
        self.wait()

    def set_function(self, func, param=None, param2=None):
        self.func = func
        self.param = param
        self.param2 = param2

    def run(self):
        if self.param == None:
            # print("Sem parâmetro!")
            if self.func:
                self.func()
        elif self.param != None and self.param2 == None:
            if self.func:
                # print("Com parâmetro!")
                self.func(self.param)
        else:
            if self.param != None and self.param2 != None:
                self.func(self.param, self.param2)

# ____________________________________ Sessão da janela principal ____________________________________
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.setWindowTitle("Cadê minha internet?")
        self.setWindowIcon(QIcon(diretorio_atual + "\images\icon.png"))
        self.setFixedSize(782, 637)
        self.busca_informacoes_rede()
        self.definindo_sons()
        self.chamada_da_NossaThread()
        self.chamada_da_NossaThread3()

        # Criação da lista para o log
        self.lista_log_conexao = []
        self.lista_log_ping = []

        self.ln_quantidade_ping.setValidator(QIntValidator())
        self.ln_quantidade_ping.textChanged.connect(self.teste_valor_quantidade_ping)

        # Botão pingar
        self.btn_pingar.clicked.connect(self.test_site_ip_valido_ping)

        # Botão abrir pagina de logs
        self.btn_abrir_janela_logs_conexao.clicked.connect(self.mostrar_janela_log_conexao)
        self.btn_abrir_janela_logs_ping.clicked.connect(self.mostrar_janela_log_ping)

        # Botão DNS do google
        self.btn_dns_google.clicked.connect(self.alterar_dns_google)
        self.btn_dns_auto.clicked.connect(self.alterar_dns_dhcp)

        # ____________________________________ Sessão do tray do windows ____________________________________
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon(diretorio_atual + "\images\icon.png"))
        self.tray_icon.setToolTip("Cadê minha internet?")

        self.tray_menu = QMenu()
        self.show_action = QAction("Abrir", self)
        self.quit_action = QAction("Sair", self)

        self.show_action.triggered.connect(self.show_or_restore_window)
        self.quit_action.triggered.connect(self.quit_application)

        self.tray_menu.addAction(self.show_action)
        self.tray_menu.addAction(self.quit_action)

        self.tray_icon.setContextMenu(self.tray_menu)
        self.tray_icon.activated.connect(self.tray_activated)
        self.tray_icon.show()

    def show_or_restore_window(self):
        if self.isMinimized() or not self.isVisible():
            self.showNormal()
            self.activateWindow()
        else:
            self.hide()

    def quit_application(self):
        QApplication.quit()

    def closeEvent(self, event):
        event.ignore()
        self.hide()
        self.tray_icon.show()

    def tray_activated(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            self.show_or_restore_window()

    def show_window(self):
        self.show()
    # Aqui termina a série de comandos do Tray

    # ____________________________________ Sessão de threads ____________________________________
    # Função para a chamada do bloco que iniciará uma instância da classe NossaThread, responsável por executar uma
    # função em looping e ainda sim mostrar a tela incial do programa (mainWindow)
    def chamada_da_NossaThread(self):
        ##########################################################
        self.bloco = NossaThread()                               #
        self.bloco.set_function(self.check_internet_connection)  #
        self.bloco.start()                                       #
        ##########################################################

    # Função para a chamada do bloco que iniciará uma instância da classe NossaThread, responsável por executar uma
    # função que capta o ping e o exibe na label)
    def chamada_da_NossaThread2(self):
        #############################################################################################
        self.bloco2 = NossaThread()                                                                 #
        self.bloco2.set_function(self.test_verbose_ping, self.hostname, self.quantidade_de_ping)    #
        self.bloco2.start()                                                                         #
        #############################################################################################

    # Função para a chamada do bloco que iniciará uma instância da classe NossaThread, responsável por executar uma
    # função que capta o ping pela primeira vez ao iniciar o programa
    def chamada_da_NossaThread3(self):
        #############################################################################################
        self.hostname = "www.google.com.br"                                                         #
        self.quantidade_de_ping = 4                                                                 #
        self.bloco3 = NossaThread()                                                                 #
        self.bloco3.set_function(self.test_verbose_ping, self.hostname, self.quantidade_de_ping)    #
        self.bloco3.start()                                                                         #
        #############################################################################################

    # ____________________________________ Sessão de funções principais ____________________________________
    # Função que coleta informações sobre a rede do usuário e mostra na mainWindow
    def busca_informacoes_rede(self):
        # Executa o comando ipconfig /all no Windows

        result = subprocess.run(["ipconfig", "/all"], capture_output=True, text=True)

        # Se não obter erro no retorno, cria a lista output_lines, onde cada posição vai receber uma linha do retorno
        if result.returncode == 0:
            output_lines = result.stdout.split('\n')

            # Cria as variáves para capturar as linhas referentes às posições dos DNS no retorno que está na lista
            linha_dns1 = output_lines[25]
            linha_dns2 = output_lines[26]

            # Cria mais duas listas, e separa a linha tudo que vier depois dos ":" fica na posição 1 da nova lista.
            parts1 = linha_dns1.split(':', 1)
            parts2 = linha_dns2.split(':', 1)

            # Verifica se o tamanho da primeira lista (parts1) tem duas posições
            if len(parts1) >= 2:
                dns1 = parts1[1].strip()
                dns2 = parts2[0].strip()

                if dns1 == "8.8.8.8" and dns2 == "8.8.4.4":
                    self.btn_dns_google.setDisabled(True)

            # Seta os labels da mainWindows com as informações referentes ao DNS1 e DNS2, respectivamente
            html_text1 = f"
{dns1}
"
            self.lb_dns1_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))

            html_text1 = f"
{dns2}
"
            self.lb_dns2_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))

        # Executa o comando ipconfig no Windows
        result = subprocess.run(["ipconfig"], capture_output=True, text=True)

        # Verifica o resultado
        if result.returncode == 0:
            output_lines = result.stdout.split('\n')

            # Verifica se a lista tem pelo menos 11 linhas
            if len(output_lines) >= 11:
                linha_ipv6 = output_lines[7]
                linha_ip = output_lines[8]
                linha_mascara = output_lines[9]
                linha_gateway = output_lines[10]

                # Divide apenas no primeiro ":"
                parts = linha_ipv6.split(':', 1)
                parts2 = linha_ip.split(':', 1)
                parts3 = linha_mascara.split(':', 1)
                parts4 = linha_gateway.split(':', 1)

                # Verifica se a linha foi dividida em pelo menos 2 partes
                if len(parts) >= 2:
                    # Remove espaços extras e seta a variável ipv6 com a segunda posição da lista parts
                    ipv6 = parts[1].strip()
                else:
                    ipv6 = ""

                if len(parts2) >= 2:
                    ip = parts2[1].strip()
                else:
                    ip = ""

                if len(parts3) >= 2:
                    mascara = parts3[1].strip()
                else:
                    mascara = ""

                if len(parts4) >= 2:
                    gateway = parts4[1].strip()
                else:
                    gateway = ""

                # Seta os labels da mainWindows com os respectivos valores de ipv6, ip, mascara e gateway
                html_text1 = f"
{ipv6}
"
                self.lb_ipv6_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))

                html_text = f"
{ip}
"
                self.lb_ip_resultado.setText(QCoreApplication.translate("MainWindow", html_text, None))

                html_text = f"
{mascara}
"
                self.lb_mascara_resultado.setText(QCoreApplication.translate("MainWindow", html_text, None))

                html_text = f"
{gateway}
"
                self.lb_gateway_resultado.setText(QCoreApplication.translate("MainWindow", html_text, None))

            return dns1, dns2

    # Função que checa se a internet está funcionando em looping e em cado de alguma mudança, ele avisa por áudio.
    def check_internet_connection(self):
        previous_status = None
        num_failures = 0

        while True:
            try:
                socket.create_connection(("www.google.com", 80))
                current_status = "Online"
            except OSError:
                current_status = "Offline"

            if previous_status is None:
                if current_status == "Online":
                    self.altera_img_internet_ok()
                    # print("Status da Internet:", current_status)
                else:
                    self.altera_img_internet_nao_ok()
                    # print("Status da Internet:", current_status)
            elif previous_status != current_status:
                if current_status == "Online":
                    self.altera_img_internet_voltou()
                    # print("A Internet voltou!")
                else:
                    if num_failures < 1:
                        num_failures += 1
                        self.altera_img_internet_caiu()
                        # print("A Internet caiu.")
                    elif num_failures >= 1:
                        self.altera_img_internet_caiu_dinovo()
                        # print("A Internet caiu OUTRA VEZ.")

            previous_status = current_status
            time.sleep(5)

    # Função que pega o site e a quantidade de pings passada atraves da função da thread executada, e gera os
    # resultados dos pings
    def test_verbose_ping(self, hostname, quantidade_de_ping):
        self.bloquear_btnPing_lnPing()

        temp_file = tempfile.NamedTemporaryFile(delete=False)

        try:
            subprocess.run(["ping", "-n", str(quantidade_de_ping), hostname], stdout=temp_file)
        except Exception as e:
            print(f"Failed to perform verbose ping: {e}")
        finally:
            temp_file.close()

        with open(temp_file.name, 'r') as file:
            captured_output = file.read()

        os.unlink(temp_file.name)

        # Convertendo bytes para str usando decode()
        captured_output_str = str(captured_output)

        # Separar as linhas relevantes
        lines = captured_output_str.splitlines()

        # Filtrar as linhas que contêm "Resposta"
        response_lines = [line for line in lines if "Resposta" in line]

        # Juntar as linhas filtradas novamente em uma string
        formatted_output = '\n'.join(response_lines)
        self.adiciona_ao_log_ping(formatted_output, str(self.ln_entrada_ping.text()))
        self.lbl_log_ping.setText(formatted_output)

        # Roda a função de liberar as linhas após a execução da função de ping
        self.liberar_lnPing()

        # Limpa o campo ln_quantidade_ping após a execução da função de ping
        self.ln_quantidade_ping.setText("")

        # Após qualquer inserção de texto ou números nos campos abaixo, irá chamar a função para testar se pode ou não
        # liberar o botão de pingar. Só irá liberar se os dois campos estiverem preenchidos.
        self.ln_entrada_ping.textChanged.connect(self.bloquear_botao_ping)
        self.ln_quantidade_ping.textChanged.connect(self.bloquear_botao_ping)

        # Retorno da funçao
        return formatted_output

    # Seta os sons para cada tipo de aviso do programa
    def definindo_sons(self):
        # inicia a entrada de sons no programa
        pygame.mixer.init()
        self.sons = pygame.mixer.Channel(7)
        self.sons.set_volume(2.0)

        self.som_internet_ok = pygame.mixer.Sound(diretorio_atual + "\sounds\internet_ok_1.mp3")
        self.som_internet_sem_internet = pygame.mixer.Sound(diretorio_atual + "\sounds\sem_internet_1.mp3")
        self.som_internet_caiu = pygame.mixer.Sound(diretorio_atual + "\sounds\caiu_internet_1.mp3")
        self.som_internet_voltou = pygame.mixer.Sound(diretorio_atual + "\sounds\internet_voltou.mp3")
        self.som_internet_caiu_dinovo = pygame.mixer.Sound(diretorio_atual + "\sounds\sem_internet_dinovo_1.mp3")

    # Testa se a quantidade inserida no campo ln_quantidade_ping é entre 1 e 19. Se não for, ele automáticamente
    # preenche com um dos valores, ou o mais alto ou o mais baixo
    def teste_valor_quantidade_ping(self, text):
        valor_minimo = 1
        valor_maximo = 19

        try:
            valor = int(text)
            if valor < valor_minimo:
                self.ln_quantidade_ping.setText(str(valor_minimo))
            elif valor > valor_maximo:
                self.ln_quantidade_ping.setText(str(valor_maximo))
        except ValueError:
            pass

    # Testa se a o site digitano no campo do ping é válido
    def test_site_ip_valido_ping(self):
        self.hostname = self.ln_entrada_ping.text()  #
        self.quantidade_de_ping = self.ln_quantidade_ping.text()

        try:
            # Verifica se é um IP válido
            socket.inet_aton(self.hostname)

            # Realize o teste de ping aqui
            self.chamada_da_NossaThread2()
        except socket.error:
            # Se não for um IP válido, tente resolver como um nome de domínio
            try:
                socket.gethostbyname(self.hostname)

                # Realize o teste de ping aqui
                self.chamada_da_NossaThread2()
            except socket.gaierror:
                # Se não for um nome de domínio válido, mostre uma mensagem de erro
                self.lbl_log_ping.setText("Domínio ou IP inválido!")

    # Função que chama a janela de log acionada pelo botão logs (btn_abrir_janela_logs)
    def mostrar_janela_log_conexao(self):
        self.log_windows = LogWindow(self.lista_log_conexao)
        self.historico_log = "\n".join(self.lista_log_conexao)
        self.log_windows.ptx_log.setPlainText(self.historico_log)
        self.log_windows.exec_()

    def mostrar_janela_log_ping(self):
        self.log_windows = LogWindow(self.lista_log_ping)
        self.historico_log = "\n".join(self.lista_log_ping)
        self.log_windows.ptx_log.setPlainText(self.historico_log)
        self.log_windows.exec_()

    # Função chamada pelo botão de alterar dns para o google, na qual chama a função change_dns_to_google do arquivo
    # rede.py passando o argumento do endereço de IP da rede principal
    def alterar_dns_google(self):
        status_troca_dns = rede.change_dns_to_google(endereco_interface_ativa)
        self.busca_informacoes_rede()
        if status_troca_dns == "ok":
            self.lbl_log_rede.setText(
                f"DNS da interface com IP {endereco_interface_ativa} foi atualizado \npara DNS1: 8.8.8.8  &  "
                f" DNS2: 8.8.4.4")
        else:
            self.lbl_log_rede.setText(
                f"DNS da interface com IP {endereco_interface_ativa} não teve êxito \nna alteração")

    # Função chamada pelo botão de alterar dns para o google, na qual chama a função change_dns_to_dhcp do arquivo
    # rede.py passando o argumento do endereço de IP da rede principal
    def alterar_dns_dhcp(self):
        self.btn_dns_google.setEnabled(True)
        status_troca_dns = rede.change_dns_to_dhcp(endereco_interface_ativa)
        self.busca_informacoes_rede()
        if status_troca_dns == "ok":
            self.lbl_log_rede.setText(
                f"DNS da interface com IP {endereco_interface_ativa} foi atualizado \npara automático")
        else:
            self.lbl_log_rede.setText(
                f"DNS da interface com IP {endereco_interface_ativa} não teve êxito \nna alteração")

    # Adiciona o evento ao log (Está sendo chamado cada vez que a internet cai, volta, etc, na função de trocar imagem
    # e executar os sons.
    def adiciona_ao_log_conexao(self, evento):
        log = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
        self.lista_log_conexao.append(f"{log}: {evento}")

    def adiciona_ao_log_ping(self, evento, site_pingado):
        log = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
        if site_pingado == "":
            site = "www.google.com.br - Padrão primeiro ping"
        else:
            site = str(site_pingado)
        self.lista_log_ping.append(f"{log} ({site}): \n{evento}")

    # ____________________________________ Sessão de funções de alteração da img da internet ________________
    # Função chamada pela função check_internet_connection em caso da internet estiver ok!
    def altera_img_internet_ok(self):
        self.show_window() # Exibe a janela do programa se ele estiver no tray
        self.lb_img_status_conexao.setPixmap(diretorio_atual + '\images\sinal_verde_grande.png')
        html_text1 = (f"
Funcionando
")
        self.lb_status_da_conexao_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))
        self.sons.play(self.som_internet_ok, loops=0)
        self.adiciona_ao_log_conexao("Internet funcionando na primeira execução do programa")

    # Função chamada pela função check_internet_connection em caso da internet não estiver ok!
    def altera_img_internet_nao_ok(self):
        self.show_window() # Exibe a janela do programa se ele estiver no tray
        self.lb_img_status_conexao.setPixmap(diretorio_atual + '\images\sinal_vermelho_grande.png')
        html_text1 = (f"
Sem "
                      f"Internet
")
        self.lb_status_da_conexao_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))
        self.sons.play(self.som_internet_sem_internet, loops=0)
        self.adiciona_ao_log_conexao("Sem Internet na primeira execução do programa")

    # Função chamada pela função check_internet_connection em caso da internet cair!
    def altera_img_internet_caiu(self):
        self.show_window() # Exibe a janela do programa se ele estiver no tray
        self.lb_img_status_conexao.setPixmap(diretorio_atual + '\images\sinal_vermelho_grande.png')
        html_text1 = (f"
A "
                      f"Internet caiu!
")
        self.lb_status_da_conexao_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))
        self.sons.play(self.som_internet_caiu, loops=0)
        self.adiciona_ao_log_conexao("Internet caiu")

    # Função chamada pela função check_internet_connection em caso da internet voltar!
    def altera_img_internet_voltou(self):
        self.show_window() # Exibe a janela do programa se ele estiver no tray
        self.lb_img_status_conexao.setPixmap(diretorio_atual + '\images\sinal_verde_grande.png')
        html_text1 = (f"
A "
                      f"Internet voltou!
")
        self.lb_status_da_conexao_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))
        self.sons.play(self.som_internet_voltou, loops=0)
        self.adiciona_ao_log_conexao("Internet voltou")

    # Função chamada pela função check_internet_connection em caso da internet cair a partir da segunda vez!
    def altera_img_internet_caiu_dinovo(self):
        self.show_window() # Exibe a janela do programa se ele estiver no tray
        self.lb_img_status_conexao.setPixmap(diretorio_atual + '\images\sinal_vermelho_grande.png')
        html_text1 = (f"
A "
                      f"Internet caiu dinovo!
")
        self.lb_status_da_conexao_resultado.setText(QCoreApplication.translate("MainWindow", html_text1, None))
        self.sons.play(self.som_internet_caiu_dinovo, loops=0)
        self.adiciona_ao_log_conexao("Internet caiu dinovo")

    # ____________________________________ Sessão de bloqueio de botões e linhas  _____________________________
    # Função para bloquear as entradas de linha e botão referenets ao ping. Essa função também é responsável por criar
    # e mensagem de processando ping na lb de exibição de ping
    def bloquear_btnPing_lnPing(self):
        texto_processando_ping = "Processando ping para " + self.hostname
        self.lbl_log_ping.setText(texto_processando_ping)
        self.btn_pingar.setDisabled(True)
        self.ln_entrada_ping.setDisabled(True)
        self.ln_quantidade_ping.setDisabled(True)

    # Função para liberar as entradas de linha referentes ao ping (site e quantidade de pings)
    def liberar_lnPing(self):
        self.ln_entrada_ping.setEnabled(True)
        self.ln_quantidade_ping.setEnabled(True)

    # Função que libera somente o botão de pingar
    def bloquear_botao_ping(self):
        habilitado = bool(self.ln_entrada_ping.text() and self.ln_quantidade_ping.text())
        self.btn_pingar.setEnabled(habilitado)

# Classe da janela de log
class LogWindow(QDialog, Ui_janela_log):
    def __init__(self, lista_log):
        super(LogWindow, self).__init__()
        self.setupUi(self)
        self.lista_log = lista_log

        self.setWindowTitle("Cadê minha internet? - Logs")
        self.setWindowIcon(QIcon(diretorio_atual + "\images\icon.png"))
        self.setFixedSize(535, 505)

        self.btn_exportar_log.clicked.connect(self.exportar_log)

    # Função que gera o txt do log.
    def exportar_log(self):
        self.historico_log = "\n".join(self.lista_log)

        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        file_name, _ = QFileDialog.getSaveFileName(self, "Exportar Log", "",
                                                   "Arquivos de Texto (*.txt);;Todos os Arquivos (*)",
                                                   options=options)

        if file_name:
            try:
                file_name += ".txt"
                with open(file_name, 'w') as file:
                    file.write(self.historico_log)
                self.mostrar_mensagem("Log exportado com sucesso para:\n" + file_name)
            except Exception as e:
                self.mostrar_mensagem("Erro ao exportar o log:\n" + str(e))

    # Função para mostrar uma mensagem para o usuário na tela
    def mostrar_mensagem(self, mensagem):
        msg_box = QMessageBox()
        msg_box.setIcon(QMessageBox.Information)
        msg_box.setText(mensagem)
        msg_box.setWindowTitle("Mensagem")
        msg_box.exec_()

# Precisa de apenas uma instância por aplicação
app = QApplication(sys.argv)
# Chama a função de verificar atualização do progrma
verifica_atualizacao(app)
# Criação do qtwidget que será a janela
window = MainWindow()
# Começa o evento loop
window.show()

app.exec_()


import time
import math
#import audioio
import json
from secrets import secrets
import board
from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
from digitalio import DigitalInOut, Direction, Pull
import analogio
import displayio
#import logging
#import adafruit_logging as logging

class logging:
    """A place holder for the missing adafruit_logging class"""
    name="";

    ERROR=0;
    WARN=1;
    DEBUG=2;
    INFO=3;
    level=INFO;

    def getLogger(name):
        ret=logging();
        ret.name=name;
        return ret;

    def setLevel(self, lvl):
        self.level=lvl;

    def info(self, *args):
        if(self.INFO <= self.level):
            print(args);

    def debug(self, *args):
        if(self.DEBUG <= self.level):
            print(args);

    def warn(self, *args):
        if(self.WARN <= self.level):
            print(args);

    def error(self, *args):
        if(self.ERROR <= self.level):
            print(args);

# Set up where we'll be fetching data from
DATA_SOURCE = 'http://api.openweathermap.org/data/2.5/weather?id='+secrets['city_id']
DATA_SOURCE += '&appid='+secrets['openweather_token']
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
DATA_LOCATION = []

####################
# setup hardware

pyportal = PyPortal(url=DATA_SOURCE,
                    json_path=DATA_LOCATION,
                    status_neopixel=board.NEOPIXEL)

light = analogio.AnalogIn(board.LIGHT)

snooze_button = DigitalInOut(board.D3)
snooze_button.direction = Direction.INPUT
snooze_button.pull = Pull.UP

####################
# variables

# alarm support

alarm_background = 'red_alert.bmp'
alarm_file = 'alarm.wav'
alarm_enabled = True
alarm_armed = True
alarm_interval = 12.0
alarm_hour = 7
alarm_minute = 45
snooze_time = None
snooze_interval = 600.0

# mugsy support
mugsy_background = 'mugsy_background.bmp'

# weather support

icon_file = None
icon_sprite = None
celcius = secrets['celcius']

# display/data refresh timers

refresh_time = None
update_time = None
weather_refresh = None

# The most recently fetched time
current_time = None

# track whether we're in low light mode

low_light = False

####################
# Load the fonts

time_font = bitmap_font.load_font('/fonts/Anton-Regular-104.bdf')
time_font.load_glyphs(b'0123456789:') # pre-load glyphs for fast printing

alarm_font = bitmap_font.load_font('/fonts/Helvetica-Bold-36.bdf')
alarm_font.load_glyphs(b'0123456789:')

temperature_font = bitmap_font.load_font('/fonts/Arial-16.bdf')
temperature_font.load_glyphs(b'0123456789CF')

####################
# Set up logging

logger = logging.getLogger('alarm_clock')
logger.setLevel(logging.ERROR)            # change as desired

####################
# Functions

def create_text_areas(configs):
    """Given a list of area specifications, create and return test areas."""
    text_areas = []
    for cfg in configs:
        textarea = Label(cfg['font'], text=' '*cfg['size'])
        textarea.x = cfg['x']
        textarea.y = cfg['y']
        textarea.color = cfg['color']
        text_areas.append(textarea)
    return text_areas

def clear_splash():
    for _ in range(len(pyportal.splash) - 1):
        pyportal.splash.pop()

def touch_in_button(t, b):
    in_horizontal = b['left'] <= t[0] <= b['right']
    in_vertical = b['top'] <= t[1] <= b['bottom']
    return in_horizontal and in_vertical

def button_meridian(b):
    meridian = b['right'] - b['left'];
    meridian=meridian/2;
    meridian=meridian+b['left'];
    return meridian;

def button_equator(b):
    equator = b['bottom'] - b['top'];
    equator=equator/2;
    equator=equator+b['top'];
    return equator;

touched = False

####################
# states

class State(object):
    """State abstract base class"""

    def __init__(self):
        pass

    @property
    def name(self):
        """Return the name of teh state"""
        return ''

    def tick(self, now):
        """Handle a tick: one pass through the main loop"""
        pass

    #pylint:disable=unused-argument
    def touch(self, t, touched):
        """Handle a touch event.
        :param (x, y, z) - t: the touch location/strength"""
        return bool(t)

    def enter(self):
        """Just after the state is entered."""
        pass

    def exit(self):
        """Just before the state exits."""
        clear_splash()

class Time_State(State):
    """This state manages the primary time display screen/mode"""

    def __init__(self):
        super().__init__()
        self.background_day = 'main_background_day.bmp'
        self.background_night = 'main_background_night.bmp'
        self.refresh_time = None
        self.update_time = None
        self.weather_refresh = None
        text_area_configs = [dict(x=88, y=170, size=5, color=0xFFFFFF, font=time_font),
                             dict(x=210, y=50, size=5, color=0xFF0000, font=alarm_font),
                             dict(x=88, y=90, size=6, color=0xFFFFFF, font=temperature_font)]
        self.text_areas = create_text_areas(text_area_configs)
        self.weather_icon = displayio.Group()
        self.weather_icon.x = 88
        self.weather_icon.y = 20
        self.icon_file = None

        self.snooze_icon = displayio.Group()
        self.snooze_icon.x = 260
        self.snooze_icon.y = 70
        self.snooze_file = None

        # each button has it's edges as well as the state to transition to when touched
        self.buttons = [dict(left=0, top=50, right=80, bottom=120, next_state='settings'),
                        dict(left=0, top=155, right=80, bottom=220, next_state='mugsy')]

    @property
    def name(self):
        return 'time'

    def adjust_backlight_based_on_light(self, force=False):
        """Check light level. Adjust the backlight and background image if it's dark."""
        global low_light
        if light.value <= 1000 and (force or not low_light):
            pyportal.set_backlight(0.01)
            pyportal.set_background(self.background_night)
            low_light = True
        elif force or (light.value >= 2000 and low_light):
            pyportal.set_backlight(1.00)
            pyportal.set_background(self.background_day)
            low_light = False

    def tick(self, now):
        #print("time.tick");
        global alarm_armed, snooze_time, update_time, current_time

        # is the snooze button pushed? Cancel the snooze if so.
        if not snooze_button.value:
            if snooze_time:
                self.snooze_icon.pop()
            snooze_time = None
            alarm_armed = False

        # is snooze active and the snooze time has passed? Transition to alram is so.
        if snooze_time and ((now - snooze_time) >= snooze_interval):
            change_to_state('alarm')
            return

        # check light level and adjust background & backlight
        #self.adjust_backlight_based_on_light()

        # only query the online time once per hour (and on first run)
        if (not self.refresh_time) or ((now - self.refresh_time) > 3600):
            logger.debug('Fetching time')
            try:
                pyportal.get_local_time(location=secrets['timezone'])
                self.refresh_time = now
            except RuntimeError as e:
                self.refresh_time = now - 3000   # delay 10 minutes before retrying
                logger.error('Some error occured, retrying! - %s', str(e))

        # only query the weather every 10 minutes (and on first run)
        if (not self.weather_refresh) or (now - self.weather_refresh) > 600:
            logger.debug('Fetching weather')
            try:
                value = pyportal.fetch()
                weather = json.loads(value)

                # set the icon/background
                weather_icon_name = weather['weather'][0]['icon']
                try:
                    self.weather_icon.pop()
                except IndexError:
                    pass
                filename = "/icons/"+weather_icon_name+".bmp"
                if filename:
                    if self.icon_file:
                        self.icon_file.close()
                    self.icon_file = open(filename, "rb")
                    icon = displayio.OnDiskBitmap(self.icon_file)
                    try:
                        icon_sprite = displayio.TileGrid(icon,
                                                         pixel_shader=displayio.ColorConverter(),
                                                         x=0, y=0)
                    except TypeError:
                        icon_sprite = displayio.TileGrid(icon,
                                                         pixel_shader=displayio.ColorConverter(),
                                                         position=(0, 0))

                    self.weather_icon.append(icon_sprite)

                temperature = weather['main']['temp'] - 273.15 # its...in kelvin
                if celcius:
                    temperature_text = '%3d C' % round(temperature)
                else:
                    temperature_text = '%3d F' % round(((temperature * 9 / 5) + 32))
                self.text_areas[2].text = temperature_text
                self.weather_refresh = now
                board.DISPLAY.refresh_soon()
                board.DISPLAY.wait_for_frame()

            except RuntimeError as e:
                self.weather_refresh = now - 540   # delay a minute before retrying
                logger.error("Some error occured, retrying! - %s", str(e))

        if (not update_time) or ((now - update_time) > 30):
            # Update the time
            update_time = now
            #time_string=datetime.strftime("%I:%M%p", time.localtime());
            suffix="a";
            current_time = time.localtime();
            hr=current_time.tm_hour;
            mn=current_time.tm_min;
            if(hr>12):
                hr=hr-12;
                suffix="p";

            #time_string = '%02d:%02d' % (current_time.tm_hour,current_time.tm_min)
            time_string = '%02d:%02d' % (hr,mn);
            self.text_areas[0].text = time_string
            board.DISPLAY.refresh_soon()
            board.DISPLAY.wait_for_frame()

            # Check if alarm should sound
        if current_time is not None and not snooze_time:
            minutes_now = current_time.tm_hour * 60 + current_time.tm_min
            minutes_alarm = alarm_hour * 60 + alarm_minute
            #print("now: ", minutes_now);
            #print("alarm: ", minutes_alarm);
            if minutes_now == minutes_alarm:
                #print("alarm should fire if armed");
                if alarm_armed:
                    #print("alarm firing");
                    change_to_state('alarm')
            else:
                alarm_armed = alarm_enabled

    def touch(self, t, touched):
        #print("time.touch(): ", t, touched);
        if t and not touched:             # only process the initial touch
            for button_index in range(len(self.buttons)):
                b = self.buttons[button_index]
                if touch_in_button(t, b):
                    change_to_state(b['next_state'])
                    break
        return bool(t)

    def enter(self):
        print("time.enter()");
        self.adjust_backlight_based_on_light(force=True)
        for ta in self.text_areas:
            pyportal.splash.append(ta)
        pyportal.splash.append(self.weather_icon)
        if snooze_time:
            if self.snooze_file:
                self.snooze_file.close()
            self.snooze_file = open('/icons/zzz.bmp', "rb")
            icon = displayio.OnDiskBitmap(self.snooze_file)
            try:
                icon_sprite = displayio.TileGrid(icon,
                                                 pixel_shader=displayio.ColorConverter(),
                                                 x=0, y=0)
            except TypeError:
                icon_sprite = displayio.TileGrid(icon,
                                                 pixel_shader=displayio.ColorConverter(),
                                                 position=(0, 0))
            self.snooze_icon.append(icon_sprite)
            pyportal.splash.append(self.snooze_icon)
        if alarm_enabled:
            self.text_areas[1].text = '%2d:%02d' % (alarm_hour, alarm_minute)
        else:
            self.text_areas[1].text = '     '
        board.DISPLAY.refresh_soon()
        board.DISPLAY.wait_for_frame()

    def exit(self):
        super().exit()
        for _ in range(len(self.snooze_icon)):
            self.snooze_icon.pop()

class Mugsy_State(Time_State):
    """This state tells Mugsey 'Make me a coffee' """

    def __init__(self):
        super().__init__()

    @property
    def name(self):
        return 'mugsy'

    def tick(self, now):
        # Once the job is done, go back to the main screen
        print("mugsy.tick: change state to time");
        change_to_state('time')

    def enter(self):
        global low_light
        print("mugsy.enter: set backlight and draw mugsy background");
        low_light = False
        pyportal.set_backlight(1.00)
        pyportal.set_background(mugsy_background)
        board.DISPLAY.refresh_soon()
        board.DISPLAY.wait_for_frame()

class Alarm_State(State):
    """This state shows/sounds the alarm.
    Touching anywhere on the screen cancells the alarm.
    Pressing the snooze button turns of the alarm, starting it again in 10 minutes."""

    def __init__(self):
        super().__init__()
        self.sound_alarm_time = None
        #self.audio = audioio.AudioOut(board.SPEAKER)
        #self.sine_wave_sample = audioio.RawSample(sine_wave)

    @property
    def name(self):
        return 'alarm'

    def tick(self, now):
        global snooze_time
        #print("alarm.tick()");
        # is the snooze button pushed
        if not snooze_button.value:
            snooze_time = now
            change_to_state('time')
            return
        #return;
        # is it time to sound the alarm?
        #if self.sound_alarm_time and (now - self.sound_alarm_time) > alarm_interval:
        if True:
            #print("alarm: playing audio");
            self.sound_alarm_time = now

            #self.audio.play(sine_wave_sample, loop=True);
            if(not pyportal.audio.playing):
                logger.info("alarm: playing audio");
                pyportal.play_file(alarm_file, False)

    def touch(self, t, touched):
        global snooze_time
        #print("alarm.touch()", t, touched);
        if t and not touched:
            #self.audio.stop();
            snooze_time = None
            change_to_state('time')
        return bool(t)

    def enter(self):
        global low_light
        #print("entered alarm state");
        self.sound_alarm_time = time.monotonic() - alarm_interval
        pyportal.set_backlight(1.00)
        pyportal.set_background(alarm_background)
        low_light = False
        board.DISPLAY.refresh_soon()
        board.DISPLAY.wait_for_frame()
        #print("alarm.enter() end of method");

    def exit(self):
        global alarm_armed, pyportal
        super().exit()
        pyportal.audio.stop()
        alarm_armed = bool(snooze_time)

class Setting_State(State):
    """This state lets the user enable/disable the alarm and set its time.
    Swiping up/down adjusts the hours & miniutes separately."""

    def __init__(self):
        super().__init__()
        self.previous_touch = None
        self.background = 'settings_background.bmp'
        text_area_configs = [dict(x=88, y=120, size=5, color=0xFFFFFF, font=time_font)]

        self.text_areas = create_text_areas(text_area_configs)
        self.buttons = [dict(left=0, top=30, right=80, bottom=93),    # on
                        dict(left=0, top=98, right=80, bottom=152),   # return
                        dict(left=0, top=155, right=80, bottom=220),  # off
                        dict(left=100, top=0, right=200, bottom = 240), # hours
                        dict(left=220, top=0, right=320, bottom = 240)]   # minutes

    @property
    def name(self):
        return 'settings'

    def touch(self, t, touched):
        global alarm_hour, alarm_minute, alarm_enabled, alarm_armed
        if t:
            if touch_in_button(t, self.buttons[0]):   # on
                logger.debug('ON touched')
                alarm_enabled = True
                alarm_armed=True
                self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute)
            elif touch_in_button(t, self.buttons[1]):   # return
                logger.debug('RETURN touched')
                change_to_state('time')
            elif touch_in_button(t, self.buttons[2]): # off
                logger.debug('OFF touched')
                alarm_enabled = False
                self.text_areas[0].text = '     '
            elif alarm_enabled:
                if not self.previous_touch:
                    self.previous_touch = t
                else:
                    if touch_in_button(t, self.buttons[3]):   # HOURS
                        logger.debug('HOURS touched')
                        if t[1] < 120: # top half
                            alarm_hour = (alarm_hour + 1) % 24;
                        else:
                            alarm_hour = (alarm_hour - 1) % 24;

                        logger.debug("Alarm hour now: %d", alarm_hour);
                        self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute)
                    elif touch_in_button(t, self.buttons[4]): # MINUTES

                        logger.debug('MINUTES touched')
                        increment=0;

                        if t[1] < button_equator(self.buttons[4]):   # top half
                            increment=1;
                        else:
                            increment=-1;

                        if t[0] < button_meridian(self.buttons[4]): # left half
                            # toggle 10's digit
                            increment=increment*10;

                        alarm_minute = (alarm_minute + increment) % 60;

                        logger.debug('Alarm minute now: %d', alarm_minute)
                        self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute)

                    self.previous_touch = t
            board.DISPLAY.refresh_soon()
            board.DISPLAY.wait_for_frame()
        else:
            self.previous_touch = None
        return bool(t)

    def enter(self):
        global snooze_time
        snooze_time = None

        pyportal.set_background(self.background)
        for ta in self.text_areas:
            pyportal.splash.append(ta)
        if alarm_enabled:
            self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute) # set time textarea
        else:
            self.text_areas[0].text = '     '

    def exit(self):
        super().exit();
        logger.debug("armed: ", alarm_armed);

####################
# State management

states = {'time': Time_State(),
          'mugsy': Mugsy_State(),
          'alarm': Alarm_State(),
          'settings': Setting_State()}

current_state = None

def change_to_state(state_name):
    global current_state
    if current_state:
        logger.debug('Exiting %s', current_state.name)
        current_state.exit()
    current_state = states[state_name]
    logger.debug('Entering %s', current_state.name)
    current_state.enter()

class SmartTouch:
    """A smart object to collect touch point information"""
    # consider: track maxZ observed and set minZ to 25% of maxZ

    minZ=15000; # default minimum touch pressure
    proximity=20; # default minimum proximity in pixels (square; used for both x and y)

    consecutiveSamps=4; # default number of consecutive valid samples for a touch point to be captured
    currentSamples=[0,0,0,0,0,0,0,0]; # dynamic list size? oh python you confound me. using static for now
    currentIndex=0;

    dbgLvl=0;

    def __init__(self):
        self.zeroize();

    def debug(self, *args):
        self.debugLvl(2, args);

    def debugLvl(self, lvl, *args):
        if(lvl <= self.dbgLvl):
            print(args);

    def zeroize(self):
        self.debug("      * zeroizing...");
        zeroSample=(0,0,0);
        i=0;
        while(i<self.consecutiveSamps):
            self.currentSamples[i]=zeroSample;
            i=i+1;

        self.currentIndex=0;

    def push(self, tp):
        if(tp):
            if(self.currentIndex>=self.consecutiveSamps):
                return True; # exit early if we've already collected enough data

            # valid tp tuple; test minZ
            if(tp[2]>=self.minZ):
                self.debugLvl(1, "RX TP: ", tp);
                #print("TP satisfies minZ");

                if(self.test(tp)):
                    self.debugLvl(1, "   > TP is valid (within proximity)");
                    # if this touchpoint is WITHIN PROXIMITY LIMITS, extend the data set
                    self.currentSamples[self.currentIndex]=tp;
                    self.currentIndex=self.currentIndex+1;
                else:
                    self.debugLvl(0, "   < TP is invalid (outside proximity)", tp);
                    # if this touchpoint is OUTSIDE PROXIMITY LIMITS, zeroize and wait for the next touch
                    self.zeroize();
            else:
                # doesn't satisfy minZ, so IGNORE IT (don't zeroize!)
                self.debugLvl(1, "RX'd TP below minZ: ", tp);

            #self.debugLvl(1, "   currentIndex: ", self.currentIndex);
            #self.debugLvl(1, "   currentSamples:\r\n", self.currentSamples);
            return(self.currentIndex >= self.consecutiveSamps);
        else:
            return False; # return value indicates when we are done

    def test(self, tp):
        # test against minZ in the push() function so we can differentiate between
        # fails due to minZ and fails due to x/y proximity
        #if(tp[2] < self.minZ):
        #    return False;
        #else:
        #    print("TP satisfies minZ");

        if(self.currentIndex==0):
            self.debug("   TP is first sample; always passes test");
            return True; # always accept the first sample
        else:
            self.debug("   testing against average x/y locale");

        pprox=self.proximity;
        nprox=(-1)*self.proximity;

        avgSamp=self.getAvgPosition();
        self.debug("        avgSamp x,y: ", avgSamp[0], avgSamp[1]);
        xDif=avgSamp[0]-tp[0];
        yDif=avgSamp[1]-tp[1];
        self.debug("        xDif, yDif: ", xDif, yDif);

        if(xDif > pprox):
            return False;
        if(xDif < nprox):
            return False;
        if(yDif > pprox):
            return False;
        if(yDif < nprox):
            return False;

        # we've filtered for minZ, x-proximity, and y-proximity
        # so if we reach this line the touchpoint is VALID and the test PASSES
        return True;

    def getAvgPosition(self):
        avgX=0;
        avgY=0;
        i=0;
        while(i<self.currentIndex):
            tmpSamp=self.currentSamples[i];
            avgX=avgX+tmpSamp[0];
            avgY=avgY+tmpSamp[1];
            i=i+1;

        avgX=avgX/(self.currentIndex);
        avgY=avgY/(self.currentIndex);

        #avgX=math.floor(avgX);
        #avgY=math.floor(avgY);

        return (avgX, avgY, self.minZ); # return a touchpoint tuple with > minZ pressure value

    def consume(self):
        ret = self.getAvgPosition();
        self.zeroize();
        self.debugLvl(1, "avgSamp: ", ret);
        return ret;

####################
# And... go

clear_splash()
change_to_state("time")

#pyportal.touchscreen.samples=8;

smarttouch = SmartTouch();
validTouchPoint=False;

#change_to_state("alarm");
tpValid=False;

while True:
    try:
        tp=pyportal.touchscreen.touch_point;
        tpValid=True;
    except:
        logger.error("touchscreen input failure");
        tp=None;
        tpValid=False;

    if(tpValid):
        if(not tp):
            touched=False;

        if(smarttouch.push(tp)):
            # we have captured a smart point
            stp=smarttouch.consume();
            #print("consuming the average touchpoint");
            touched = current_state.touch(stp, touched);
            #smarttouch=SmartTouch(); # create a new smarttouch container
    else:
        time.sleep(1);

    current_state.tick(time.monotonic());

print("System exit!");


# -*- coding: utf-8 -*-
"""
Created on Sat Nov 27 11:56:13 2021

@author: Checkout
"""

import requests
import json
import pandas as pd
if __name__=="__main__":
    url = 'http://localhost:5000/testApi'
    r = requests.post(url,json={"side": 8,
                                "bomb_count": 6,
                                "start": [2,2],
                                "goal":[7,7]})
    print(r.content)
    data = json.loads(r.text)
    pd.DataFrame.from_dict(eval(data))