#!/usr/bin/env python
import npyscreen, curses
import random, math
import drawille
import worldmap_to_canvas as map

def circle(canvas,x0=0,y0=0,radius=0):
    for g in range(0,360,18):
        t = math.radians(g)
        x = x0 + math.cos(t) * radius
        y = y0 + math.sin(t) * radius
        canvas.set(x,y)

    #npyscreen.disableColor()
class InputBox(npyscreen.BoxTitle):
    # MultiLineEdit now will be surrounded by boxing
    _contained_widget = npyscreen.MultiLineEdit

class SecForm(npyscreen.FormBaseNew):
    def create(self):
        self.add(npyscreen.TitleFixedText, name = "Fixed Text:" , value="This is fixed text")
        self.add(npyscreen.TitleText, name = "Text:", )
        self.add(npyscreen.TitlePassword, name = "Password:")
        self.add(npyscreen.TitleFilename, name = "Filename:")
        self.add(npyscreen.TitleDateCombo, name = "Date:")
        self.add(npyscreen.Checkbox, name = "A Checkbox")
        self.add(npyscreen.TitleSlider, out_of=12, name = "Slider")
        self.ed = self.add(npyscreen.MultiLineEdit,
            value = """try typing here! Mutiline text, press ^R to reformat.\nPress ^X for automatically created list of menus""",
            max_height=5, rely=9)
        self.ed.autowrap = True
        self.add(npyscreen.TitleSelectOne, max_height=4, value = [1,], name="Pick One",
                values = ["Option1","Option2","Option3"], scroll_exit=True, width=30)
        self.add(npyscreen.MultiSelect, max_height=4, value = [1,],
                values = ["Option1","Option2","Option3"], scroll_exit=True, width=20)

        self.add(npyscreen.MiniButton, name = "Button",)

        #gd = F.add(npyscreen.SimpleGrid, relx = 42, rely=15, width=20)
        gd = self.add(npyscreen.GridColTitles, relx = 42, rely=15, width=20, col_titles = ['1','2','3','4'])
        gd.values = []
        for x in range(36):
            row = []
            for y in range(x, x+36):
                row.append(y)
            gd.values.append(row)

        new_handlers={
            curses.KEY_F1 : self.h_change_form
        }
        self.add_handlers(new_handlers)

    def h_change_form(self,_input):
        to=""
        if(_input == curses.KEY_F1):
            to="MAIN"
        elif(_input == curses.KEY_F2):
            to="SECONDARY"
        else:
            to="MAIN"
        self.parentApp.change_form(to)

class MainForm(npyscreen.FormBaseNew):
    def create(self, *args, **keywords):
        super(MainForm, self).create(*args, **keywords)
        self.form_canvas = drawille.Canvas()
        y, x = self.useable_space()
        self.dot_pos=(10,10)

        obj = self.add(npyscreen.BoxTitle, name="",
            values=["WORLD", "ORBIT"],
            max_width=20,
            rely=1
            )

        self.t = self.add(InputBox,
            name="CHART",
            max_width= x-obj.max_width-5,
            max_height = y-3,
            relx = obj.relx + obj.width + 1,
            rely=1
            )
        self.t.editable=False
        self.t.footer="NOW TRACKING: EXAMPLE | Width {} Height {}\tDot X:{} Y:{}".format(
            self.t.width,self.t.height,self.dot_pos[0],self.dot_pos[1])

        new_handlers={
            "^R" : self.h_update,
            curses.KEY_F2 : self.h_change_form
            }
        self.add_handlers(new_handlers)

    def h_change_form(self,_input):
        to=""
        if(_input == curses.KEY_F1):
            to="MAIN"
        elif(_input == curses.KEY_F2):
            to="SECONDARY"
        else:
            to="MAIN"
        self.parentApp.change_form(to)

    def h_update(self,_input):
        self.update()

    def update(self):
        self.t.update(clear=True)
        self.t.value=(self.draw_chart(self.form_canvas,self.t,self.dot_pos))
        self.dot_pos=(self.dot_pos[0]+1,self.dot_pos[1]+1)
        self.t.footer="NOW TRACKING: EXAMPLE | Width {} Height {}\tDot X:{} Y:{}".format(
            self.t.width,self.t.height,self.dot_pos[0],self.dot_pos[1])

    def draw_chart(self,canvas,chart,pos):
        """
        Drawile "PIXEL"
        dots:
        ,___,
        |1 4|
        |2 5|
        |3 6|
        |7 8|
        `````
        """
        canvas.clear()
        map.draw_map(canvas)
        window_max_x,window_max_y = self.useable_space()
        max_x = chart.max_width*2 - window_max_x - 2
        max_y = (chart.max_height - chart.rely)*4

        canvas.set(pos[0],pos[1])

        chart.name="Max Pix X: {} Y: {}".format(max_x,max_y)

        #canvas.set(chart_height*2+chart_height,chart_height*2)

        #Pense no canvas como uma dimensão paralela com coordenadas X,Y
        #O que de fato aparece na tela depende das coordenadas de COmeço e Fim
        #Do frame, i.e. da janelinha que vc está abrindo pra essa dimensão paralela
        circle(canvas,pos[0],pos[1],3)

        return canvas.frame(0,0,max_x,max_y)

class TestApp(npyscreen.NPSAppManaged):
    def while_waiting(self):
        #UPDATE THE CURRENT FORM / FETCH API
        self.w1.update()

    def onStart(self):
        #THIS NEEDS TO BE BEFORE REGISTERING THE FORM
        self.keypress_timeout_default = 10

        self.w1 = MainForm(parentApp=self, name = "MAP")
        self.w2 = SecForm(parentApp=self, name="ORBITAL ASSETS")

        self.registerForm("MAIN",self.w1)
        self.registerForm("SECONDARY",self.w2)

    # def onCleanExit(self):
    #     npyscreen.notify_wait("Goodbye!")

    def change_form(self, name):
        # Switch forms.  NB. Do *not* call the .edit() method directly (which
        # would lead to a memory leak and ultimately a recursion error).
        # Instead, use the method .switchForm to change forms.
        self.switchForm(name)

        # By default the application keeps track of every form visited.
        # There's no harm in this, but we don't need it so:
        self.resetHistory()

if __name__ == "__main__":
    App = TestApp()
    App.run()


import sqlite3
import numpy as np

'''Data Exercise 1, part  B- List Count of patients by gender'''
conn = None
cursor = None

try:
    conn = sqlite3.connect('openmrs.db')
    cursor = conn.cursor()
    cursor.execute('''
        SELECT gender, count(gender)
        FROM patient
        GROUP BY gender
    ''')
    data = (cursor.fetchall())

    for datum in data:
        gender = ''

        if datum[0].strip().lower() == 'm':
            gender = 'Male'
        elif datum[0].strip().lower() == 'f':
            gender = 'Female'
        else:
            gender = 'Unspecified'

        print(f'{gender}: {datum[1]}')

finally:
    cursor.close()
    conn.close()


from rest_framework import permissions

from django.conf import settings
from django.contrib.auth import get_user_model

from .constants import DEFAULT_ROLE_RULE_DENIED_ACCESS_MESSAGE
from .helpers import is_user_permitted

class GroupPermission(permissions.BasePermission):
    """A Permission class, which checks the role authorization of a user."""

    message = 'Only this group is allowed'
    groups_required = None

    def has_permission(self, request, view):
        """Overriden method, which checks role authorization of the user.

        Args:
            request (Request): the current request object
            view (View): the current View object

        Returns:
            (bool): True if the user is granted access.
        """

        # allow all access if the user is a superuser
        is_permitted = False
        is_group_in_tree = False

        if request.user.is_superuser:
            return True

        # prepare the url name
        url_name = request.resolver_match.url_name

        # if the user is anonymous, try to fetch it from query parameters
        user_id = (request.GET.get('rbac_user')
                   if request.user.is_anonymous else None)
        if user_id:
            User = get_user_model()
            user = User.objects.get(id=user_id)
        else:
            user = request.user

        # for each group required, check if the current user is
        # senior / junior or equivalent to this required group within the
        # hierarchy
        for group_required in self.groups_required:
            # is_in_tree means that the user group / role is within the
            # required group / role tree (parent - child or equivalent)
            user_permitted, is_in_tree = is_user_permitted(
                user,
                group_required,
                url_name,
                request.method.lower()
            )
            is_permitted |= user_permitted
            is_group_in_tree |= is_in_tree

        if is_group_in_tree and not is_permitted:
            message = getattr(
                settings,
                'ROLE_RULE_DENIED_ACCESS_MESSAGE',
                DEFAULT_ROLE_RULE_DENIED_ACCESS_MESSAGE
            )
            self.message = message
        return is_permitted

# Mixins
class MultiplePermissionsMixin(object):
    """A mixin class which enables conditional permission checks."""

    def check_permissions(self, request):
        """Run has_permission for each permission class on the view."""
        exception_states = []
        permissions = self.get_permissions()

        all_groups = [group for permission in permissions
                      for group in permission.groups_required]

        for permission in permissions:
            if not permission.has_permission(request, self):
                permission_message_mappings = [
                    {
                        'role': role_name,
                        'message': permission.message
                    } for role_name in permission.groups_required
                ]
                exception_states.extend(permission_message_mappings)

        raise_permissions = len(exception_states) == len(all_groups)

        if raise_permissions:
            self.permission_denied(request, exception_states)


import pickle

import torch
import torch.nn as nn
import torchvision.transforms as T
import os

from PIL import Image

class Flatten(nn.Module):
    def forward(self, input):
        return input.view(input.size(0), -1)

class FruitClassifier(nn.Module):

    def __init__(self, classes):
        super(FruitClassifier, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=5, stride=1),
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(64, 64, kernel_size=7, stride=1),
            nn.ReLU(),
            nn.MaxPool2d(3),
            nn.Conv2d(64, 64, kernel_size=7),
            nn.ReLU(),
            nn.MaxPool2d(5),
            Flatten(),
            nn.Linear(64, 100),
            nn.ReLU(),
            nn.Linear(100, classes)
        )
        with open('id2class.pkl', 'rb') as fd:
            id2class = pickle.load(fd)
        self.id2class = id2class

    def forward(self, x):
        return self.model(x)

    def load_weights(self, path):
        self.model.load_state_dict(torch.load(path))

    def predict(self, x):
        return self.id2class[self.model(x).max(1)[1].cpu().item()]

def load_image(filename, device='cpu'):
    transform = T.Compose([T.ToTensor(),
                           T.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])
    img = Image.open(filename)
    img_t = transform(img)
    return img_t.unsqueeze(0).to(device)

def predictFolder(path):
    predictions = []
    clf = FruitClassifier(5)
    clf.load_weights('fruit_classifier.pth')
    for img_path in os.listdir(path):
        img = load_image(path+"/"+img_path)
        predictions.append(clf.predict(img))
    return predictions


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import utils
import scipy as sp
from scipy import ndimage

def plot_stars(p,x,y,size='large',horizontalalignment='center',**kwargs):
    ''' Plots significance stars '''
    plt.text(x,y,stars(p),size=size,horizontalalignment=horizontalalignment,**kwargs)

def plot_linestar(p,x1,x2,y):
    hlines(y, x1, x2)
    plot_stars(0.5*(x1+x2),y+0.02,stars(p),size='large',horizontalalignment='center')

def _date_labels(dates):
    months = np.array([mdt.month for mdt in dates])
    months_idx = np.append([True] , months[:-1] != months[1:])
    strfs = np.array(['%d', '%b %d'])[months_idx.astype(int)]
    return [dt.strftime(strf) for dt, strf in zip(dates, strfs)]

def plot_filtered_performance_calendar(subj,df,num_days=7, **kwargs):
    '''
    plots a calendar view of the performance for a subject on the past num_days.
    '''
    plot_performance_calendar(subj, utils.filter_normal_trials(utils.filter_recent_days(df, num_days)), **kwargs)

def plot_performance_calendar(subj, data_to_analyze, disp_counts=False, vmins=(0, 0, 0), vmaxs=(None, 1, None)):
    '''
    plots a calendar view of performance for a subject.
    Plots all trials from data_to_analyze so make sure it is filtered.

    Parameters:
    -----------
    subj : str
        the subject
    data_to_analyze : pandas DataFrame
        filtered data to plot. Can be a slice, a copy is made anyways.
    disp_counts : boolean
        display values in grid, removes colorbars, default False
    vmins, vmaxs : iterable of floats, length 3, optional
        Values to anchor the colormaps. If None, they are inferred from the data.
    '''
    data_to_analyze = data_to_analyze.copy()
    data_to_analyze['date'] = data_to_analyze.index.date
    data_to_analyze['hour'] = data_to_analyze.index.hour

    blocked = data_to_analyze.groupby(['date', 'hour'])
    aggregated = pd.DataFrame(blocked.agg({'correct': lambda x: np.mean(x.astype(float)),
                                              'reward': lambda x: np.sum(x.astype(float)),
                                              'type_': lambda x: np.sum((x=='normal').astype(float))}).to_records())

    f, ax = plt.subplots(1,3,sharex=True,sharey=True,figsize=(16.0, 4.0))

    columns = ('type_', 'correct', 'reward')
    titles = (subj+': Trials per hour', 'Accuracy', 'Feeds')
    cmaps = [plt.get_cmap(cmap) for cmap in ('Oranges', 'RdYlBu', 'BuGn')]
    for cmap in cmaps:
        cmap.set_bad(color='Grey')

    pivoted = aggregated.pivot('hour', 'date')

    for i, (column, title, cmap, vmin, vmax) in enumerate(zip(columns, titles, cmaps, vmins, vmaxs)):
        g = sns.heatmap(pivoted[column], annot=disp_counts, ax=ax[i],
                        cmap=cmap, cbar=not disp_counts,
                        vmin=vmin, vmax=vmax)
        g.set_title(title)
    g.set_xticklabels(_date_labels(pivoted.keys().levels[1]));

def plot_filtered_accperstim(title,df,num_days=7, **kwargs):
    '''
    plots accuracy per stim for a subject on the past num_days.
    '''
    plot_accperstim(title, utils.filter_normal_trials(utils.filter_recent_days(df, num_days)), **kwargs)

def plot_accperstim(title, data_to_analyze, stim_ids='stimulus', stims_all=None, label_count_cutoff=50, extract_stim_names=True):
    '''
    percent correct broken out by stimulus and day.

    Parameters:
    -----------
    title : str
        the plot title
    data_to_analyze : pandas DataFrame
        filtered data to plot. Can be a slice, a copy is made anyways.
    stim_ids : str
        label of the column to group-by.
    stims_all : None or list-like
        order of stims. must match values in stim_ids
    label_count_cutoff : int
        max number of stimuli labels. If below this value will sort stim_ids by class.
    extract_stim_names : boolean
        whether to extract stimuli names from full stimuli paths. If true, ignores stim_ids.
    '''
    data_to_analyze = data_to_analyze.copy()
    if extract_stim_names:
        stim_ids = 'stim_name'
        utils.extract_filename(data_to_analyze, target=stim_ids)
    data_to_analyze['date'] = data_to_analyze.index.date

    blocked = data_to_analyze.groupby(['date', stim_ids])
    aggregated = pd.DataFrame(blocked.agg({'correct': lambda x: np.mean(x.astype(float))}).to_records())
    pivoted = aggregated.pivot(stim_ids, 'date', 'correct')
    if stims_all:
        yticklabels = stims_all
    elif len(pivoted)<label_count_cutoff:
        yticklabels = data_to_analyze.groupby(['class_', stim_ids]).index.unique().index.get_level_values(stim_ids).values
    else:
        yticklabels = int(len(pivoted)/label_count_cutoff)
    cmap = sns.diverging_palette(15, 250, as_cmap=True)
    cmap.set_bad(color = 'k', alpha = 0.5)
    plt.figure()
    g = sns.heatmap(pivoted, vmin=0, vmax=1, cmap=cmap,
                    xticklabels=_date_labels(pivoted.keys().values),
                    yticklabels=yticklabels)
    g.set_title(title)

def plot_daily_accuracy(subj, df, x_axis='trial_num', smoothing='gaussian', day_lim=0):
    '''
    plots the accuracy of the subject throughout the day.
    a preset for the more general plot_accuracy_bias

    Parameters:
    -----------
    subj : str
        the subject
    df : pandas DataFrame
        data frame of behavior data
    x_axis : str
        whether to plot 'time' or 'trial_num' along the x axis
    smoothing : str
        whether to smooth using 'exponential', 'rolling' average,
        'gaussian' filter'
    day_lim : None or non-negative int
        max number of days of trials to include. Zero means just today.
    '''
    plot_accuracy_bias(subj, df, x_axis=x_axis, smoothing=smoothing, trial_lim=None, day_lim=day_lim,
                        plt_correct_smoothed=True, plt_correct_shade=True, plt_correct_line=True,
                        plt_L_response_smoothed=False, plt_L_response_shade=False, plt_L_response_line=False,
                        plt_R_response_smoothed=False, plt_R_response_shade=False, plt_R_response_line=False,
                        plt_ci=False, block_size=100)

def plot_ci_accuracy(subj, df, x_axis='time', day_lim=7, trial_lim=None, bias=True):
    '''
    plots the accuracy (and bias) of the subject throughout the day.
    a preset for the more general plot_accuracy_bias

    Parameters:
    -----------
    subj : str
        the subject
    df : pandas DataFrame
        data frame of behavior data
    x_axis : str
        whether to plot 'time' or 'trial_num' along the x axis
    trial_lim : None or int
        max number of most recent trials to include
    day_lim : None or non-negative int
        max number of days of trials to include. Zero means just today.
    bias : boolean
        whether to plot the line for the left bias
    '''
    plot_accuracy_bias(subj, df, x_axis=x_axis, smoothing='rolling', trial_lim=None, day_lim=day_lim,
                        plt_correct_smoothed=True, plt_correct_shade=False, plt_correct_line=False,
                        plt_L_response_smoothed=bias, plt_L_response_shade=False, plt_L_response_line=False,
                        plt_R_response_smoothed=False, plt_R_response_shade=False, plt_R_response_line=False,
                        plt_ci=True, block_size=100)

def plot_accuracy_bias(subj, df, x_axis='time', smoothing='exponential', trial_lim=None, day_lim=7,
                        plt_correct_smoothed=True, plt_correct_shade=True, plt_correct_line=True,
                        plt_L_response_smoothed=False, plt_L_response_shade=False, plt_L_response_line=False,
                        plt_R_response_smoothed=False, plt_R_response_shade=False, plt_R_response_line=False,
                        plt_ci=False, block_size=100):
    '''
    plots the accuracy or bias of the subject.

    Parameters:
    -----------
    subj : str
        the subject
    df : pandas DataFrame
        data frame of behavior data
    x_axis : str
        whether to plot 'time' or 'trial_num' along the x axis
    smoothing : str
        whether to smooth using 'exponential', 'rolling' average,
        'gaussian' filter'
    trial_lim : None or int
        max number of most recent trials to include
    day_lim : None or non-negative int
        max number of days of trials to include. Zero means just today.
    plt_{correct, L_response, R_response}_smoothed : boolean
        whether to plot a smoothed line for the value
    plt_{correct, L_response, R_response}_shade : boolean
        whether to plot a red shaded region filling in the line of actual responses
    plt_{correct, L_response, R_response}_line : boolean
        whether to plot a red line of the actual responses
    '''
    fig = plt.figure(figsize=(16, 2))
    if trial_lim is not None:
        df = df[-trial_lim:]
    if day_lim is not None:
        df = utils.filter_recent_days(df, day_lim)
    df = utils.filter_normal_trials(df)
    if x_axis == 'time':
        x = df.index._mpl_repr()
        use_index=True
    elif x_axis == 'trial_num':
        x = np.arange(len(df))
        use_index=False
    else:
        raise Exception('invalid value for x_axis')

    datas = (df['correct'], df['response']=='L', df['response']=='R')
    plot_smoothed_mask = (plt_correct_smoothed, plt_L_response_smoothed, plt_R_response_smoothed)
    plot_shaded_mask = (plt_correct_shade, plt_L_response_shade, plt_R_response_shade)
    plot_line_mask = (plt_correct_line, plt_L_response_line, plt_R_response_line)

    for data, smoothed, shaded, line in zip(datas, plot_smoothed_mask, plot_shaded_mask, plot_line_mask):
        if shaded:
            plt.fill_between(x, .5, data.values.astype(bool), color = 'r', alpha = .25)
        if line:
            g = data.plot(color='r', marker='o', linewidth = .5, use_index=use_index)
        if smoothed:
            if smoothing == 'exponential':
                data.ewm(halflife=20).mean().plot(use_index=use_index)
            elif smoothing == 'gaussian':
                plt.plot(x, sp.ndimage.filters.gaussian_filter(data.values.astype('float32'), 3, order=0))
            elif smoothing == 'rolling':
                data.rolling(window=block_size, center=True).mean().plot(use_index=use_index)
            else:
                raise Exception('invalid value for smoothing')

    if plt_ci and smoothing == 'rolling':
        ci = utils.binomial_ci(0.5*block_size,block_size)
        plt.axhspan(ci[0],ci[1],color='grey',alpha=0.5)
    plt.axhline(y=.5, c='black', linestyle='dotted')
    plt.title('Today\'s Performance: '+subj)
    plt.xlabel(x_axis)

def plot_trial_feeds(behav_data, num_days=7):
    '''
    plots numer of trials and number of feeds for all birds across time

    Parameters:
    -----------
    behav_data : dict of pandas dataframes
        from loading.load_data_pandas
    num_days : non-negative int
        number of days to include data for
    '''
    colors = sns.hls_palette(len(behav_data))
    fig = plt.figure(figsize=(16.0, 4.0))
    ax1 = fig.gca()
    ax2 = ax1.twinx()

    for (subj, df), color in zip(behav_data.items(), colors):
        data_to_analyze = utils.filter_recent_days(df, num_days).copy()
        if not data_to_analyze.empty:
            data_to_analyze['date'] = data_to_analyze.index.date
            blocked = data_to_analyze.groupby('date')

            days = np.sort(blocked.groups.keys())
            trials_per_day = blocked['response'].count().values
            line = ax1.plot(days, trials_per_day, label=subj + ' trials per day', c=color)
            if len(days) == 1:
                plot(0, trials_per_day[-1], 'o', c=color, ax=ax1)

            aggregated = blocked.agg({'reward': lambda x: np.sum((x==True).astype(float))})
            aggregated['reward'].plot(ax=ax2, label=subj + ' feeds per day', ls='--', c=color)
            if len(days) == 1:
                ax2.plot(0, aggregated['reward'][0], 'o', c=color)

    plt.title('trials and feeds per day')
    for ax, label, loc in zip((ax1, ax2), ('trials per day', 'feeds per day'), ('upper left', 'upper right')):
        ax.set_ylabel(label)
        ax.set_ylim(bottom=0)
        ax.legend(loc=loc)
    ax1.set_xticklabels(_date_labels(days))


from urllib.error import HTTPError
import rich
import json
import requests
import sys, os
import time
from dotenv import load_dotenv
from rich.console import Console
from rich.traceback import install
from rich.markdown import Markdown
import lang_supported
import tts_voice  # file module

load_dotenv()  # loads API TOKEN
install()  # traceback for error prompts
console = Console()  # rich console for styling

# get the .env variables
API_TOKEN = os.getenv('API_TOKEN')

# api calling
def api_call(text_converting, to_language):
    url_detect = "https://google-translate78.p.rapidapi.com/language_detect"
    url_translate = "https://google-translate78.p.rapidapi.com/translate"

    payload_detect = {"text": text_converting}

    headers = {
        "content-type": "application/json",
        "X-RapidAPI-Key": API_TOKEN,
        "X-RapidAPI-Host": "google-translate78.p.rapidapi.com"
    }

    response_detect = requests.request(
        "POST", url_detect, json=payload_detect, headers=headers)
    source_language = response_detect.json()["language_detection"]["language"]

    payload_translate = {
        "text": text_converting,
        "source": source_language,
        "target": to_language
    }

    response_translate = requests.request(
        "POST", url_translate, json=payload_translate, headers=headers)
    return response_translate.json()["translations"]["translation"]

# count letters in a string to check if the language is valid
def count_letters(word):
    return len([x for x in word if x.isalpha()])

# ----------------------------- MARKDOWN -------------------------------- #

MARKDOWN = """
# Translator v1.0

This is a simple translator that uses the Google Translate API to translate text from one language to another.

## Usage
1. Enter the text you want to translate.
2. Enter the language you want to translate to.
3. The translated text will be displayed.
##
"""
md = Markdown(MARKDOWN)
console.print(md)

# ----------------------------- main ----------------------------- #

if __name__ == '__main__':

    choice = console.input(
        '[bold yellow]Would you like to see the languages?[/bold yellow] [bold purple](y/n)\n[/bold purple]')
    if choice == 'y' or choice == 'Y':
        lang_supported.langs()

    # variables input
    text_converting = console.input(
        "\n[bold yellow]Enter the text you want to convert: ")
    to_language = console.input(
        "\n[bold yellow]Enter the language you want to convert to: ")

    # check if the language is valid (language cannot have more than 2 chars)
    while ((count_letters(to_language) > 2) or (to_language.isnumeric())):
        console.print(
            "\n[red blink]Invalid language[/red blink], [cyan]use the format [purple]'en'[/purple] for English[/cyan]")
        to_language = console.input(
            "\n[bold yellow]Enter the language you want to convert to: ")

    # output
    try:
        translated_text = api_call(text_converting, to_language)
    except HTTPError as e:
        console.print(f'[red]The following error occured:[/red] [bold red]{e}')
    except Exception as e:
        console.print(f'[bold red]{e}')
    else:
        console.print(
            f'\n[green]The translated text is: [/green][purple]{translated_text}[/purple]')

    # Hear the translation
    hear_choice = console.input(
        "\n[bold yellow]Do you want to hear the translation?[/bold yellow] [purple](y/n)\n[/purple]")
    if hear_choice == 'y' or hear_choice == 'Y':
        try:
            tts_voice.speak(translated_text, to_language)
        except Exception as e:
            console.print(f'[bold red]{e}')
        else:
            sys.exit()
# ----------------------------------------------------------------- #


try:
    from .common.ElementView import *
    from .models.TextModels import *
except:
    from common.ElementView import *
    from models.TextModels import *

import string
import pygame
import sys
import re

class BaseTextView(ElementView):
    def __init__(self, model):
        ElementView.__init__(self, model)
        self._model.on('changed', self.on_change)

        self.dirty = True

    def on_change(self, even, **kwargs):
        self.make_dirty()   # change emits in this method
        self.dirty = True

    def init_surface(self):
        self._calculate_size()

        font = self._model.font
        text = self._model.text
        color = self._model.color

        scalar = pygame.transform.scale
        rot = pygame.transform.rotate

        self._surface = font.render(text, False, color)
        self._surface = scalar(self._surface, [int(x) for x in (self.width, self.height)])
        self._surface = rot(self._surface, self._rotation)

        self.dirty = False

    def on_render(self, event, **kwargs):
        if self.dirty:
            self.init_surface()

        ElementView.on_render(self, event, **kwargs)

    def _get_font_size(self):
        return self._model.size
    def _set_font_size(self, value):
        self._model.load_font(self._model.resource, value)
    fontsize = property(_get_font_size, _set_font_size)

    def _get_text(self):
        return self._model.text
    def _set_text(self, text):
        self._model.text = text
    text = property(_get_text, _set_text)

    def _get_color(self):
        return self._model.color
    def _set_color(self, value):
        self._model.set_color(value)
    color = property(_get_color, _set_color)

class TextView(BaseTextView):
    def __init__(self, model):
        BaseTextView.__init__(self, model)

    def render(self, surface):
        if self.text == '\n':
            return

        blitter = pygame.Surface.blit
        blitter(surface, self._surface, (self.x, self.y, self.width, self.height))

class ParagraphView(ElementView):
    def __init__(self, model, width=300):
        ElementView.__init__(self, model)

        self._model.on('changed', self.on_model_changed)

        self._offset_x = self._offset_y = 6

        self.last_text = None
        self.model_dirty = True

        self._width = width
        self.ignore_model_size = True

        self.ignore_in_phrases = []

    ignore = '~`!@#$%^&*()_+=-{[}]\|'";:/?.>,<"

    def init_words(self):
        if self._model.text == self.last_text:
            self.handle_resize()
            return

        self.clear_children()

        txt = re.sub('\n', ' \n ', self._model.text).split(' ')
        for word in txt:
            if word == '\n':
                view = TextView(TextModel(self._model.resource, '\n', self._model.size))
                self.add_child(view)
                continue
            elif len(word.strip()) == 0:
                continue

            clean_word = word.translate(string.maketrans('', ''), string.punctuation)
            if len(clean_word) == 0:
                continue

            # strip punctuation before the word
            for character in word:
                if character == clean_word[0]:
                    break
                elif character in ParagraphView.ignore:
                    view = TextView(TextModel(self._model.resource, character, self._model.size))
                    view.justify = 'left'
                    self.add_child(view)

            view = TextView(TextModel(self._model.resource, clean_word, self._model.size))
            self.add_child(view)

            # strip punctuation after word
            for character in word[::-1]:
                if character == clean_word[-1]:
                    break
                elif character in ParagraphView.ignore:
                    view = TextView(TextModel(self._model.resource, character, self._model.size))
                    view.justify = 'right'
                    self.add_child(view)

        self.handle_resize()
        self.last_text = self._model.text

    def handle_resize(self):
        max_height = 0
        x, y = self._offset_x * 2, self._offset_y

        newline = False
        for child in self.children:
            newline = False
            max_height = max(max_height, child.height)

            if child.text == '\n' or x + child.width > self._width - (self._offset_x * 2):
                x = self._offset_x * 2
                y += max(max_height, child.height)
                newline = True

            try:
                justify = child.justify
                if justify == 'left':
                    child.x = x + self._offset_x
                    child.y = y
                elif justify == 'right':
                    child.x = x - self._offset_x
                    child.y = y

                    if newline:
                        child.y -= max(max_height, child_height)
            except:
                child.x = x
                child.y = y

            if newline:
                max_height = 0

            if child.text != '\n':
                x += child.width + self._offset_x
            else:
                x + self._offset_x

        self._height = y + max_height + self._offset_y * 2
        self.emit('changed')

    def on_model_changed(self, event, **kwargs):
        self.model_dirty = True

    def _get_font_size(self):
        return self._model.size
    def _set_font_size(self, value):
        for child in self.children:
            child.fontsize = value
        self._model.load_font(self._model.resource, value)
    fontsize = property(_get_font_size, _set_font_size)

    def pre_render(self, surface):
        if self.model_dirty or self._size_dirty:
            self.init_words()
            self.model_dirty = False

    def views_for_word(self, word):
        return [child for child in self.children if child._model.text == word]

    def views_for_phrase(self, phrase):
        words = phrase.split()
        # TODO

    def _get_text(self):
        return self._model.text
    def _set_text(self, value):
        self._model.text = value
    text = property(_get_text, _set_text)


import os
import pytest
from qubit.parsing import Parser
from qubit.parsing import XYZ

methane = os.path.join(os.path.dirname(__file__), 'test_data/methane.xyz')

@pytest.fixture
def parser():
    return Parser()

def test_default_load(parser):
    with pytest.raises(Exception) as e:
        parser.load()

def test_default_write(parser):
    with pytest.raises(Exception) as e:
        parser.write()

@pytest.fixture
def xyz_parser():
    return XYZ()

def test_XYZ_load(xyz_parser):
    atoms = ['C', 'H', 'H', 'H', 'H']
    xyz = [
        [0.000000, 0.000000, 0.000000],
        [0.000000, 0.000000, 1.089000],
        [1.026719, 0.000000, -0.363000],
        [-0.513360, -0.889165, -0.363000],
        [-0.513360, 0.889165, -0.363000]]

    p_atoms, p_xyz = xyz_parser.load(methane)

    assert (p_atoms, p_xyz) == (atoms, xyz)


from enum import Enum

class BookingClass:
    def __init__(self, bselem):
        self.value = bselem.attrs["value"]
        self.string = bselem.text.replace('\xa0', ' ')

    def print(self):
        print(self.value, self.string)

class BookingDate:
    def __init__(self, data):
        self.value = data["DATE_VALUE"]
        self.string = data["DATE_STRING"].replace('\xa0', ' ')

    def print(self):
        print(self.value, self.string)

class BookingBranch:
    def __init__(self, data):
        self.name = data["BRANCH_NAME"].replace('\xa0', ' ')
        self.expiredate = data["EFFECTIVE_DATE"]
        self.value = data["LOCATION"]

    def print(self):
        print(self.value, self.name, self.expiredate)

class BookingTime:
    def __init__(self, data):
        self.value = data["SEGMENT"]
        self.seatnum = data["ALLSEAT"]
        self.remaining = data["SEATNUM"]
        self.openrate = data["OPEN_RATE"]
        self.starttime = data["INIT_TIME"]
        self.endtime = data["END_TIME"]
        self.stop = True if data["OFFCLASS"] == "1" else False
        self.reserved = True if data["HASCLASS"] == "1" else False

    def print(self):
        print(self.value, self.starttime, "~", self.endtime,
              (str(int(self.seatnum) - int(self.remaining)) + "/" + self.seatnum), ("Close" if self.stop else ""),
              ("Reserved" if self.reserved else ""))

class BookingResponse(Enum):
    OK = "預約成功"
    NOCONTINOUS = "無連續座位"
    FAIL = "預約失敗"
    NOTAVAILABLE = "該時間不可預約"
    TIMEOUT = "Timeout錯誤"

class CheckingResponse(Enum):
    OK = "檢查OK"
    CLASSNOTREACHABLE = "可上課程取得發生錯誤"
    BRANCHNOTREACHABLE = "可上課地點取得發生錯誤"
    DATENOTREACHABLE = "可上課日期取得發生錯誤"
    TIMENOTREACHABLE = "可上課時段取得發生錯誤"

class TokenResponse(Enum):
    OK = "成功獲得Token"
    FAIL = "獲得TOKEN失敗"

class AsciiArt:
    logo = """

////////////////////////////////////////////////////////////////////////////
_/_/_/_/_/  _/    _/  _/_/_/                             author: JasonLiTW
   _/      _/  _/    _/    _/                                      2018.07
  _/      _/_/      _/_/_/
 _/      _/  _/    _/    _/
_/      _/    _/  _/_/_/

      _/_/    _/    _/  _/_/_/_/_/    _/_/
   _/    _/  _/    _/      _/      _/    _/
  _/_/_/_/  _/    _/      _/      _/    _/
 _/    _/  _/    _/      _/      _/    _/
_/    _/    _/_/        _/        _/_/

    _/_/_/      _/_/      _/_/    _/    _/  _/_/_/  _/      _/    _/_/_/
   _/    _/  _/    _/  _/    _/  _/  _/      _/    _/_/    _/  _/
  _/_/_/    _/    _/  _/    _/  _/_/        _/    _/  _/  _/  _/  _/_/
 _/    _/  _/    _/  _/    _/  _/  _/      _/    _/    _/_/  _/    _/
_/_/_/      _/_/      _/_/    _/    _/  _/_/_/  _/      _/    _/_/_/
////////////////////////////////////////////////////////////////////////////

    """
    success = """

////////////////////////////////////////////////////////////////////////////
      _/_/_/  _/    _/    _/_/_/    _/_/_/  _/_/_/_/    _/_/_/    _/_/_/  _/
   _/        _/    _/  _/        _/        _/        _/        _/        _/
    _/_/    _/    _/  _/        _/        _/_/_/      _/_/      _/_/    _/
       _/  _/    _/  _/        _/        _/              _/        _/
_/_/_/      _/_/      _/_/_/    _/_/_/  _/_/_/_/  _/_/_/    _/_/_/    _/
////////////////////////////////////////////////////////////////////////////

    """


# noteParse contains methods that are used in breaking
#  apart evernote .enex files and giving them a parsed
#  and more accessible format

import xml.etree.ElementTree as ET
import note
import book
import os

def parseNote(fileName):
    tree = ET.parse(fileName)
    enExport = tree.getroot()
    noteTree = enExport[0]
    title = noteTree.findall('title')[0].text
    author = noteTree.findall('note-attributes')[0].findall('author')[0].text
    created = noteTree.findall('created')[0].text
    updated = noteTree.findall('updated')[0].text

    contentStr = noteTree.findall('content')[0].text
    contentStr = contentStr.replace("""<?xml version="1.0" encoding="UTF-8" standalone="no"?>""", "")
    contentStr = contentStr.replace("""<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">""","")
    contentTree = ET.fromstring(contentStr)

    resourceListTree = noteTree.findall('resource')

    noteObj = note.Note()
    noteObj.author = author
    noteObj.title = title
    noteObj.created = created
    noteObj.updated = updated
    noteObj.contentTree = contentTree
    noteObj.resources = resourceListTree

    for tagNode in noteTree.findall('tag'):
        noteObj.addTag(tagNode.text)

    return noteObj

def parseNotebook(fileName):
    tree = ET.parse(fileName)
    enExport = tree.getroot()
    noteTree = enExport[0]

    bookObj = book.Book()
    bookObj.title = os.path.splitext(os.path.basename(fileName))[0]

    for noteTree in enExport.findall('note'):
        title = noteTree.findall('title')[0].text
        try:
            author = noteTree.findall('note-attributes')[0].findall('author')[0].text
        except:
            author = "Joey Kilgore"
        created = noteTree.findall('created')[0].text
        updated = noteTree.findall('updated')[0].text

        contentStr = noteTree.findall('content')[0].text
        contentStr = contentStr.replace("""<?xml version="1.0" encoding="UTF-8" standalone="no"?>""", "")
        contentStr = contentStr.replace("""<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">""","")
        contentStr = contentStr.replace(" ", "")
        contentStr = ''.join([i if ord(i) < 128 else '' for i in contentStr]) # remove non ascii
        contentTree = ET.fromstring(contentStr.strip())

        resourceListTree = noteTree.findall('resource')

        noteObj = note.Note()
        noteObj.author = author
        noteObj.title = title
        noteObj.created = created
        noteObj.updated = updated
        noteObj.contentTree = contentTree
        noteObj.resources = resourceListTree

        bookObj.notes.append(noteObj)
        bookObj.author = noteObj.author

    return bookObj

def recursivePrintTree(prefix, node):
    print(prefix + "|-->" + node.tag)
    for child in node:
        recursivePrintTree(prefix+"    ", child)

def printTree(fileName):
    tree = ET.parse(fileName)
    root = tree.getroot()
    print(fileName)
    recursivePrintTree("", root)


#Build as of 6.5.23
import nltk
import openai
import logging
from nltk.sentiment.vader import SentimentIntensityAnalyzer
from typing import Any, Dict, List, Union

class ChatCompletionResponse:
    choices: List[Dict[str, Any]]

    def __init__(self, **kwargs):
        self.choices = kwargs.get('choices', [])

class SentimentAnalysis:
    def __init__(self, openai_api_key: str):
        nltk.download('vader_lexicon')
        self.vader = SentimentIntensityAnalyzer()
        openai.api_key = openai_api_key
        # Set up logging
        logging.basicConfig(filename='sentiment_analysis.log', level=logging.ERROR)

    def analyze_vader(self, text: str) -> float:
        try:
            scores = self.vader.polarity_scores(text)
            return scores['compound']
        except Exception as e:
            logging.error(f"Error analyzing text with Vader: {str(e)}")
            return 0  # Return neutral sentiment score in case of error

    def analyze_openai(self, text: str) -> str:
        try:
            response = openai.ChatCompletion.create(
                model="text-curie-0001",
                messages=[
                    {"role": "system", "content": "This is a sentiment analysis task. Please categorize the following news article's sentiment as 'Good', 'Bad', or 'Unknown'."},
                    {"role": "user", "content": text}
                ]
            )
            if 'choices' in response and len(response.choices) > 0:
                return response.choices[0].message['content']
            else:
                logging.error("OpenAI API returned no choices or the list itself was empty.")
                return 'Unknown'
        except Exception as e:
            logging.error(f"Error analyzing text with OpenAI: {str(e)}")
            return 'Unknown'  # Return 'Unknown' sentiment in case of error


# -*- coding: utf-8 -*-
#Import libraries

import numpy as np
import gc
from sklearn.linear_model import LogisticRegression
from scipy import linalg
import random

## MCMC sampler for the minimum information dependence model with canonical statistics h and canonical parameter theta

def min_info_mcmc(X,h,loop,theta,printop=False):

###############################################################################
  #INPUT
  ## X: initial sample list [[multivariate sample 1],[multivariate sample 2],...]
  ## h   : canonical statistics function (from X (d-dim list) to R^K K-dim array)
  ## loop: number of mcmc steps
  ## theta: canonical parameter (np.array)
  ## printop: print option (if True then print the acceptance rate every 1000 steps)

  #OUTPUT
  ## list of Markov chains (list of Xs)
  ## output[0]  yields the input
  ## output[-1] yields the final output
###############################################################################

  if len(X) ==0:
    print("NULL list")
  else:
    n = len(X)
    d = len(X[0])
    output_list = [X]
    acceptancenum = 0
    for Li in range(loop):
      i  = random.sample(range(d),1)[0]
      s,t = random.sample(range(n),2)[0:2]
      X_iswap_s = output_list[Li][s].copy()
      X_iswap_t = output_list[Li][t].copy()

      X_iswap_s[i] = output_list[Li][t][i]
      X_iswap_t[i] = output_list[Li][s][i]

      h_diff = h(output_list[Li][s])+h(output_list[Li][t])-h(X_iswap_s)-h(X_iswap_t)
      log_rho_tilde  = np.min([np.log(1),-theta.dot(h_diff)])
      u  = np.random.uniform(low=0,high=1,size=1)
      if np.log(u) < log_rho_tilde:
        X_new = output_list[Li].copy()
        X_new[s] = X_iswap_s
        X_new[t] = X_iswap_t
        output_list.append(X_new)
        del X_new

        acceptancenum = acceptancenum + 1
      else:
        output_list.append(output_list[Li])
      del X_iswap_s
      del X_iswap_t
      if (Li % 10000 == 0):
        gc.collect()
      if (Li % 1000 == 0) and (printop == True):
        print("Step/Total : "+str(Li)+"/"+str(loop)+" Acceptance rate : "+str(np.round(acceptancenum/(Li+1),2)))
    return output_list

## Besag pseudo-likelihood estimation

def min_info_Besag(X,h,max_iter,tol,sparseop=False,sparsepen=1.):

###############################################################################
  #INPUT
  ## X        : initial sample list [[multivariate sample 1],[multivariate sample 2],...]
  ## h        : canonical statistics (from X (d-dim list) to R^K)
  ## max_iter : number of iteration
  ## tol      : tolerance value for the optimizer
  ## sparseop : if True, add l1 penalty
  ## sparsepen: penalty strength valid only if sparseop is True.

  #OUTPUT
  ## estimate (K-dim array)
###############################################################################

  if len(X) ==0:
    print("NULL list")
  else:
    n = len(X)
    d = len(X[0])
    Us = []
    Ys = []
    for i in range(d):
          for t in range(n):
            for s in range(t):

              X_iswap_s = X[s][:]
              X_iswap_t = X[t][:]
              X_iswap_s[i] = X[t][i]
              X_iswap_t[i] = X[s][i]
              Us.append(h(X[s])+h(X[t])-h(X_iswap_s)-h(X_iswap_t))
              if (t==(n-1))&(s == (n-2)):
                Ys.append(0)
              else:
                Ys.append(1)
    if sparseop==False:
      model = LogisticRegression(solver='liblinear',random_state=0,C=1e10,fit_intercept=False,tol=tol,max_iter=max_iter).fit(np.array(Us), Ys)
    else:
      model = LogisticRegression(solver='saga',random_state=0,C=sparsepen,penalty="l1",fit_intercept=False,tol=tol,max_iter=max_iter).fit(np.array(Us), Ys)
    return model.coef_[0]

## Bootstrap for Besag pseudo-likelihood estimation

def min_info_Besag_bootstrap(X,h,tol,max_iter,B,sparseop=False,sparsepen=1.):

###############################################################################
  #INPUT
  ## X       : initial sample list [[multivariate sample 1],[multivariate sample 2],...]
  ## h       : canonical statistics (from X (d-dim list) to R^K)
  ## max_iter: size of iteration
  ## tol     : tolerance value for the optimizer
  ## B       : size of bootstrap samples
  ## sparseop : if True, add l1 penalty
  ## sparsepen: penalty strength valid only if sparseop is True.

  #OUTPUT
  ## bootstrap samples of estimates
  ## mean of bootstraped estimates
  ## covariance of bootstraped estimates
###############################################################################

  n = len(X)
  bootstrap_Besag=[]
  for b in range(B):
    boot_sample=[X[i] for i in random.choices(range(n),k=n)]
    bootstrap_Besag.append(min_info_Besag(X=boot_sample,h=h,tol=tol,max_iter=max_iter,sparseop=sparseop,sparsepen=sparsepen))
  boot_mean = sum(bootstrap_Besag)/B
  boot_cov_1   = sum([np.tensordot(bootstrap_Besag[l],bootstrap_Besag[l],axes=0) for l in range(B)])/B
  boot_cov_2   = np.tensordot(boot_mean,boot_mean,axes=0)
  boot_cov     = boot_cov_1-boot_cov_2
  return bootstrap_Besag, boot_mean, boot_cov

## Calculation of score vector & Hessian of Besag pseudo likelihood
## Calculation of asymptotic covariance of Besag pseudo likelihood estimates

def min_info_Besag_score(x1,x2,h,theta):
###############################################################################
  #INPUT
  ## x1      : multivariate sample
  ## x2      : multivariate sample
  ## h       : canonical statistics (from X (d-dim list) to R^K)
  ## theta   : dependence parameter

  #OUTPUT
  ## score of Pseudo likelihood

  d = len(x1)
  K = theta.shape[0]
  score = 0*h(x1)

  for i in range(d):
    x_iswap_1 = x1[:]
    x_iswap_2 = x2[:]
    x_iswap_1[i] = x2[i]
    x_iswap_2[i] = x1[i]

    ui = h(x1)+h(x2)-h(x_iswap_1)-h(x_iswap_2)
    score = score + (-1)*ui/(1+np.exp(theta.dot(ui)))

  return score

def min_info_Besag_Hessian(x1,x2,h,theta):
###############################################################################
  #INPUT
  ## x1      : multivariate sample
  ## x2      : multivariate sample
  ## h       : canonical statistics (from X (d-dim list) to R^K)
  ## theta   : dependence parameter

  #OUTPUT
  ## Hessian of Pseudo likelihood

  d = len(x1)
  K = theta.shape[0]
  H = np.zeros(K*K).reshape((K,K))

  for i in range(d):
    x_iswap_1 = x1[:]
    x_iswap_2 = x2[:]
    x_iswap_1[i] = x2[i]
    x_iswap_2[i] = x1[i]

    ui = h(x1)+h(x2)-h(x_iswap_1)-h(x_iswap_2)
    Mi = np.tensordot(ui,ui,axes=0)
    H = H + Mi/(1+np.exp(theta.dot(ui)))**2

  return H

def min_info_Besag_asymptoticCovariance(X,h,tol,max_iter):

###############################################################################
  #INPUT
  ## X       : initial sample list [[multivariate sample 1],[multivariate sample 2],...]
  ## h       : canonical statistics (from X (d-dim list) to R^K)
  ## max_iter: size of iteration
  ## tol     : tolerance value for the optimizer

  #OUTPUT
  ## Asymptotic Covariance of PLE
###############################################################################

  n = len(X)
  theta_PLE=min_info_Besag(X=X,h=h,tol=tol,max_iter=max_iter,sparseop=False)
  d = theta_PLE.shape[0]
  I = np.zeros(d*d).reshape((d,d))
  J = np.zeros(d*d).reshape((d,d))

  for s in range(n):
    conditional_score = np.zeros(d)
    for t in range(n):
      conditional_score = conditional_score + (1/n)* min_info_Besag_score(x1=X[s],x2=X[t],h=h,theta=theta_PLE)
      if (t<s):
        B=min_info_Besag_Hessian(x1=X[s],x2=X[t],h=h,theta=theta_PLE)
        J=J+B/(n*(n-1)/2)
    I = I + np.tensordot(conditional_score ,conditional_score ,axes=0) / n

  asymptoticCov = (linalg.inv(J) @ I @ linalg.inv(J)) * (4/n)

  return asymptoticCov

## conditional maximum likelihood estimation

def min_info_CLE(X,h,L,burnin,thin,max_L,max_iter,tol,detailop=False):

###############################################################################
  #INPUT
  ## X       : initial sample list [[multivariate sample 1],[multivariate sample 2],...]
  ## h       : canonical statistics (from X (d-dim list) to R^K array)
	## L       : MCMC iteration (length of chain)
	## burnin  : burnin of MCMC
	## thin    : thinning
  ## max_iter: number of iteration
  ## tol     : tolerance value for the optimizer
  ##detailop : if True, print (iteration,L,current estimate, current residual for optimization) at each step

	#OUTPUT
	## list of estimates (by iteration)
	## list of residuals for optimization (by iteration)
	## list of L (by iteration)
	## list of asymptotic covariances (by iteration)
###############################################################################

	if len(X) ==0:
		print("NULL list")
	else:
		n = len(X)
		d = len(X[0])
		init_theta = min_info_Besag(X=X,h=h,tol=10**(-2),max_iter=1000)
		init_res = 10**3
		Li         = 0
		theta_list = [init_theta]
		res_list = [init_res]
		L_list   = [L]
		AsymptoticCov_list = [0]
		current_theta = init_theta #array
		current_res = init_res #real number

		while ((current_res>tol) and (Li<max_iter) and (L<max_L)):
			Li = Li + 1
			current_theta = theta_list[Li-1]
			current_res = res_list[Li-1]
			current_X_MCMC= min_info_mcmc(X=X,h=h,loop=L,theta=current_theta,printop=False)[burnin:L:thin]
			total_MCMC_num=len(current_X_MCMC)
			current_hsum             = sum(list(map(h,X)))
			current_hsum_MCMCsamples = [sum(list(map(h, current_X_MCMC[l]))) for l in range(total_MCMC_num)]
			current_hsum_mean        = sum(current_hsum_MCMCsamples)/total_MCMC_num
			current_hsum_cov_1         = sum([np.tensordot(current_hsum_MCMCsamples[l],current_hsum_MCMCsamples[l],axes=0) for l in range(total_MCMC_num)])/total_MCMC_num
			current_hsum_cov_2         = np.tensordot(current_hsum_mean,current_hsum_mean,axes=0)
			current_hsum_cov           = current_hsum_cov_1-current_hsum_cov_2
			score = current_hsum-current_hsum_mean
			modified_score            = linalg.solve(current_hsum_cov, score)
			theta_list.append(current_theta+modified_score)
			res_list.append(max(abs(score)))
			L_list.append(L)
			AsymptoticCov_list.append( linalg.pinv(current_hsum_cov) )
			##for debug
			if (detailop==True):
				print(Li,L,theta_list[Li],res_list[Li])
			##adaptive scaling of L on the basis of the residual update
			if(current_res>1.1*res_list[Li]):
				L = int(np.floor(1.5*L))
			else:
				L = L
			##restart if estimates are very far from PLE
			if(max(abs(theta_list[Li]) / (1 + abs(theta_list[0]))) > 100):
				theta_list[Li] = theta_list[0]
				print("parameter reseted.")

		theta_list.pop(-1)
		res_list.pop(-1)
		AsymptoticCov_list.pop(-1)
	return theta_list,res_list,L_list,AsymptoticCov_list


import datetime
import logging.config
import argparse

from scrapers import *
from util.latest_round import CALENDAR_DICT
from util.latest_round import find_latest_round
from logging_config import LOGGING_CONFIG

logging.config.dictConfig(LOGGING_CONFIG)
LOGGER = logging.getLogger(__name__)

def main(from_year, to_year, from_round, to_round):
    LOGGER.info("Starting Full DataScrape")
    playerScrape.main()
    injuryScrape.main()
    gameScrape.scrape_games(from_year, to_year)
    fantasyScrape.main(from_year, to_year, from_round, to_round)
    match_statsScrape.main(from_year, to_year, from_round, to_round)
    LOGGER.info("Finished Full DataScrape")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Scrape AFL data")
    parser.add_argument('--from_year', help='scrape data from this year onwards',
                        type=int, choices=range(1990, datetime.datetime.now().year + 1), default=2021)
    parser.add_argument('--to_year', help='scrape data up until this year (inclusive)',
                        type=int, choices=range(1990, datetime.datetime.now().year + 1), default=datetime.datetime.now().year)
    parser.add_argument('--from_round', help='scrape data from this round onwards',
                        type=int, choices=range(1, 30), default=1)
    parser.add_argument('--to_round', help='scrape data up until this round (inclusive)',
                        type=int, choices=range(1, 30), default=30)
    parser.add_argument('-l', '--latest', help='scrape the latest data only', action="store_true")
    args = parser.parse_args()
    if args.latest:
        LOGGER.info("\"Latest\" flag provided: Ignoring any from/to arguments and scraping for the latest rounds")
        args.from_year = datetime.datetime.now().year
        args.to_year = datetime.datetime.now().year
        args.from_round = find_latest_round(CALENDAR_DICT, datetime.datetime.now())
        args.to_round = args.from_round

    main(from_year=args.from_year, to_year=args.to_year, from_round=args.from_round, to_round=args.to_round)


""" timed rotating text file """
# pylint: disable=too-many-instance-attributes,too-many-arguments
import os
import re
import time
from datetime import datetime
from io import TextIOWrapper
from stat import ST_MTIME

class TimedRotatingTextFile(TextIOWrapper):
    """ Rotating the text file at certain timed intervals. """

    def __init__(
            self,
            filename,
            when="d",
            interval=1,
            backup_count=0,
            mode="ab+",
            delay=False,
            utc=False,
            **kwargs,
    ):
        self.filename = filename
        self.when = when.upper()
        self.backup_count = backup_count
        self.mode = mode
        self.delay = delay
        self.utc = utc
        self.file = open(filename, self.mode)

        TextIOWrapper.__init__(self, self.file, **kwargs)

        if self.when in ["D", "MIDNIGHT"]:
            self.interval = 60 * 60 * 24  # One day
            self.suffix = "%Y-%m-%d"
            self.regex_match = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
        elif self.when == "H":
            self.interval = 60 * 60  # One hour
            self.suffix = "%Y-%m-%d_%H"
            self.regex_match = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
        elif self.when == "M":
            self.interval = 60  # one minute
            self.suffix = "%Y-%m-%d_%H-%M"
            self.regex_match = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
        elif self.when == 'S':
            self.interval = 1  # one second
            self.suffix = "%Y-%m-%d_%H-%M-%S"
            self.regex_match = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
        else:
            raise ValueError("Invalid rollover interval specified: %s" % self.when)

        self.interval = self.interval * interval  # multiply by units requested
        self.ext_match = re.compile(self.regex_match, re.ASCII)

        file_name = os.fspath(self.filename)
        # keep the absolute path, otherwise derived classes which use this
        # may come a cropper when the current directory changes
        self.base_file_name = os.path.abspath(file_name)
        if os.path.exists(self.base_file_name):
            modified_time = os.stat(self.base_file_name)[ST_MTIME]
        else:
            modified_time = int(time.time())

        self.rollover_at = self.compute_rollover(modified_time)

    def compute_rollover(self, current_time):
        """ Work out the rollover time based on the specified time. """
        result = current_time + self.interval
        if self.when == "MIDNIGHT":
            rotate_ts = 24 * 60 * 60
            if self.utc:
                struct_time = time.gmtime(current_time)
            else:
                struct_time = time.localtime(current_time)
            current_hour = struct_time[3]
            current_minute = struct_time[4]
            current_second = struct_time[5]

            rotate_time = rotate_ts - ((current_hour * 60 + current_minute) * 60 + current_second)

            if rotate_time < 0:
                rotate_time += 24 * 60 * 60
            result = current_time + rotate_time
        return result

    def should_rollover(self):
        """ Determine if rollover shoud occur. """
        current_time = int(time.time())
        if current_time >= self.rollover_at:
            return 1
        return 0

    def get_files_to_delete(self):
        """ Determine the files to delete when rolling over. """
        dir_name, base_name = os.path.split(self.base_file_name)
        files_name = os.listdir(dir_name)
        result = []
        prefix = base_name + "."
        prefix_len = len(prefix)
        for filename in files_name:
            if filename[:prefix_len] == prefix:
                suffix = filename[prefix_len:]
                if self.ext_match.match(suffix):
                    result.append(os.path.join(dir_name, filename))
        if len(result) < self.backup_count:
            return []
        result.sort()
        return result[: len(result) - self.backup_count]

    def do_rollover(self):
        """do a rollover; in this case, a date/time stamp is appended to the filename
        when the rollover happens.  However, you want the file to be named for the
        start of the interval, not the current time.  If there is a backup count,
        then we have to get a list of matching filenames, sort them and remove
        the one with the oldest suffix."""
        current_time = int((time.time()))
        dst_now = time.localtime(current_time)[-1]
        timed = self.rollover_at - self.interval
        if self.utc:
            time_tuple = time.gmtime(timed)
        else:
            time_tuple = time.localtime(timed)
            dst_then = time_tuple[-1]
            if dst_now != dst_then:
                addend = 3600 if dst_now else -3600
                time_tuple = time.localtime(timed + addend)
        dfn = self.base_file_name + "." + time.strftime(self.suffix, time_tuple)
        if os.path.exists(dfn):
            os.remove(dfn)
        if os.path.exists(self.base_file_name):
            os.rename(self.base_file_name, dfn)
        if self.backup_count > 0:
            for file_path in self.get_files_to_delete():
                os.remove(file_path)
        if not self.delay:
            self.close()
            self.file = open(self.base_file_name, self.mode)
            TextIOWrapper.__init__(self, self.file)
        new_rollover_at = self.compute_rollover(current_time)
        while new_rollover_at <= current_time:
            new_rollover_at = new_rollover_at + self.interval
        if self.when == "MIDNIGHT" and not self.utc:
            dst_at_rollover = time.localtime(new_rollover_at)[-1]
            if dst_now != dst_at_rollover:
                addend = -3600 if not dst_now else 3600
                new_rollover_at += addend
        self.rollover_at = new_rollover_at

    def write(self, line):
        if self.should_rollover():
            self.do_rollover()

        self.file.write(line.encode())
        # super().write(line)

    def close(self):
        self.file.close()
        super().close()

################
# MAIN SECTION #
################
if __name__ == "__main__":
    with TimedRotatingTextFile("/tmp/tmp.log", when="MIDNIGHT", backup_count=5) as filepath:
        filepath.write(f"Report Generated on {datetime.now().strftime('%A %d %B %Y %H:%M:%S')}\n")
        time.sleep(5)
        filepath.write("Another line\n")


'''
This is the main.py module, and is essentially where the "backend"
portion of this project, namely, the server, is created and run.
'''
import sys

from flask import Flask, request, render_template
from flask_graphql import GraphQLView
from flask_cors import CORS
from jinja2 import Template

from pkg.schema import SCHEMA
from pkg.config import ARENA_USERNAME
from pkg.constants import REQUEST_LENGTH, DATA_CHECK
from pkg.html import JINJA_HTML
from pkg.blocks import get_random_blocks
from pkg.db import add_test_data, clear_database
from pkg.mail import create_content, send_email

# starting Flask application, adding graphiql url
APP = Flask('__main__')
APP.add_url_rule(
    '/graphql',
    view_func=GraphQLView.as_view(
        'graphql',
        schema=SCHEMA,
        graphiql=True
    )
)

@APP.before_request
def get_block_data() -> None:
    '''
    description:            this is where block data is downloaded via the
                            are.na API, using functions defined in pkg/

    return:                 None
    '''
    # only get blocks when specific request from UI is called
    if request.method == 'POST' and request.content_length == REQUEST_LENGTH:
        get_random_blocks(3, ARENA_USERNAME)

def get_blocks_for_email() -> None:
    '''
    description:            this is where block data is downloaded via the
                            are.na API, using functions defined in pkg/

    return:                 None
    '''
    get_random_blocks(3, ARENA_USERNAME)

    # execute query to database using GraphQL query
    data = SCHEMA.execute(DATA_CHECK).data['allBlocks']['edges'][1:]
    content = []

    # get data, parse for info on each block
    for block in data:

        block_id = block['node']['blockId']
        block_content = block['node']['blockContent']
        block_channel = block['node']['channelTitle']
        block_add_date = block['node']['blockCreateDate']

        if block['node']['blockType'] == 'Text':
            content.append(create_content(block_id, block_content, 'Text'))
        else:
            content.append(create_content(block_id, block_content, 'Media'))

        content.append(create_content(block_channel, block_add_date, 'Info'))

    # create email content with data
    email_template = Template(JINJA_HTML)
    email_content = email_template.render(
        block1=content[0],
        block1_info=content[1],
        block2=content[2],
        block2_info=content[3],
        block3=content[4],
        block3_info=content[5],
    )

    # send email with final html
    send_email(email_content)

@APP.route('/')
def my_index() -> str:
    '''
    description:            this function renders the index.html file created
                            via React, to the endpoint defined above

    return:                 the rendered index.html file
    '''
    return render_template("index.html")

@APP.teardown_appcontext
def shutdown_session(exception=None) -> None:  # pylint:disable=unused-argument
    '''
    description:            this function shuts the database in
                            DB_SESSION down as part of shutting down the
                            Flask application

    return:                 None
    '''
    clear_database()

if __name__ == '__main__':

    if len(sys.argv) > 1 and sys.argv[1] == '--email':
        # add test data to sqlite3 database
        add_test_data()

        # get data for email
        get_blocks_for_email()

    else:
        # add test data to sqlite3 database
        add_test_data()

        # enable cross origin resource sharing to allow front end access
        CORS(APP, resources={r'/graphql': {'origins': '*'}})

        # run Flask app
        APP.run(host='0.0.0.0', use_reloader=False)
        # APP.run(debug=True, use_reloader=False)


from backend.graph_io import json_to_lkg
import networkx as nx

# Compute pagerank score
def set_pagerank_scores_for_all_cases(j=None):
    if not j:
        j = json_to_lkg("LKG.json")
    print("Imported lkg")
    j = j.query(judges=[], subjects=[], keywords=[], judgements=[], types=['case'], year_range=[])
    print("Filtered lkg")
    pagerank_dict = nx.pagerank(j)
    print("pageranked lkg")
    return(pagerank_dict)


import sys
import time
import numpy as np
import os
from pyspark import SparkContext

if __name__ == "__main__":

    sc = SparkContext(appName="LR")
    D = 10  # Number of dimensions
    iterations = 20
    N = 10
    if len(sys.argv)>1:
        N = int(sys.argv[1])
    if len(sys.argv)>2:
        iterations = int(sys.argv[2])
    if len(sys.argv)>3:
        D = int(sys.argv[3])
    print("N %d D %d iterations %d" %(N,D,iterations))

    points = sc.parallelize(range(1,N)).mapPartitions(lambda r: [np.random.ranf(size=(len(list(r)),D+1))])

    points.cache().first()
    start = time.time()

    w = 2 * np.random.ranf(size=D) - 1
    print("Initial w: " + str(w))

    # Compute logistic regression gradient for a matrix of data points
    def gradient(matrix, w):
        Y = matrix[:, 0]    # point labels (first column of input file)
        X = matrix[:, 1:]   # point coordinates
        # For each point (x, y), compute gradient function, then sum these up
        return ((1.0 / (1.0 + np.exp(-Y * X.dot(w))) - 1.0) * Y * X.T).sum(1)

    def add(x, y):
        x += y
        return x

    for i in range(iterations):
        #print("On iteration %i" % (i + 1))
        w -= points.map(lambda m: gradient(m, w)).reduce(add)

    print("Final w: " + str(w))

    print("lr exec time %f" % (time.time()-start))
    sc.stop()


from flask_babel import Babel
from flask_security import Security
from flask_sqlalchemy import SQLAlchemy

from qualitas.ext.flask_github import GitHub, GitHubV4
from qualitas.ext.flask_zenhub import ZenHub

# Instantiate Extensions
from sqlalchemy import MetaData

babel = Babel()
db = SQLAlchemy(
    metadata=MetaData(naming_convention={
        'pk': 'pk_%(table_name)s',
        'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s',
        'uq': 'uq_%(table_name)s_%(column_0_name)s',
        'ix': 'ix_%(table_name)s_%(column_0_name)s',
        'ck': 'ck_%(table_name)s_%(constraint_name)s',
    }),
)
github = GitHub()
githubv4 = GitHubV4()
security = Security()
zenhub = ZenHub()


from teleportapi import *
import multiprocessing
import os.path
import sys
import json

def moreInfo(loc):
	print("details: {}".format(loc))
	return("More details to come soon for {}".format(loc))

def GUI(GUICmd, seqNum, fullName):
	GUIProc = multiprocessing.Process(target=updateGUI, args=(GUICmd, seqNum, fullName,))
	GUIProc.start()

def updateGUI(GUICmd, seqNum, fullName):
	getImage(fullName)
	GUICom(GUICmd, seqNum)

def GUICom(GUICmd, seqNum):
	# Commands: switchView, help, welcome, goodbye, defaultZoom
	# zoomIn, zoomOut, globetrotters, details, map, about, Option{}
	if(GUICmd == "in"):
		GUICmd = "zoomIn"
	elif(GUICmd == "out"):
		GUICmd = "zoomOut"
	elif(GUICmd == "reset"):
		GUICmd = "defaultZoom"

	print("GUICom: {}. Sequence# {}".format(GUICmd, seqNum))
	# Create JSON file to comm with HTML GUI
	data = {"GUICommand" : GUICmd, "SeqNum" : seqNum}
	with open('/var/www/html/GUICom.json', 'w') as outfile:
		json.dump(data, outfile)

def globeCntrl(command):
	# code to control the rotation and speed of the globe Comm with PIC uC
	print("globeCntrl: {}".format(command))
	with open('/home/pi/globe/Rotation/commands', 'w') as f:
    		f.write(command)


import cgi
import hashlib
import http.server
import hmac
import logging
import json
import os
import threading

class WebhookHandler(http.server.BaseHTTPRequestHandler):
    def send(self, status, message, content=None):
        if content is None:
            content = message
        self.send_response(status, message)
        self.send_header('Content-type', 'text/plain; charset=utf-8')
        self.end_headers()

        self.wfile.write(content.encode('utf-8'))

    def do_POST(self):
        try:
            type = cgi.parse_header(self.headers['Content-Type'])[0]
            if type != 'application/json':
                self.send(415, "Payload must be application/json")
                return

            data = self.rfile.read(int(self.headers['Content-Length']))

            if not self.authenticate(data):
                self.send_header('WWW-Authenticate', 'X-Hub-Signature sha1')
                self.send(401, "Valid signature required")
                return

            event = self.headers['X-Github-Event']
            payload = json.loads(data.decode("utf-8", "strict"))

            self.do_webhook(event, payload)
        except:
            self.send(500, "Internal server error")
            raise

    def do_webhook(self, event, payload):
        if self.server.payload_log:
            self.server.payload_log.write(json.dumps({
                "event": event,
                "payload": payload
            }))

        if event == "ping":
            self.send(200, "pong")
        elif event == "repository":
            if payload["action"] == "deleted":
                self.server.manager.delete_mirror(
                    payload["repository"]["full_name"])
            else:
                # There will be a push event too.
                pass
            self.send(202, "Accepted")
        elif event == "push":
            self.server.manager.mirror_repo(
                payload["repository"]["full_name"])
            self.send(202, "Accepted")
        else:
            self.server.logger.error(
                "501 Event not implemented: {}".format(event))
            self.send(501, "Event not implemented")

    def authenticate(self, data):
        if self.server.secret is None:
            # No authentication configured
            return True

        signature = self.headers['X-Hub-Signature']
        if signature is None:
            return False

        secret_string = self.server.secret.encode("utf-8")
        digest = hmac.new(secret_string, data, hashlib.sha1).hexdigest()
        correct = "sha1={}".format(digest)

        return hmac.compare_digest(correct, signature)

class WebhookServer(http.server.HTTPServer):
    def __init__(self, address, secret, manager, payload_log):
        self.manager = manager
        self.secret = secret
        self.timer = None
        self.periodic_interval = 0
        self.logger = logging.getLogger("WebhookServer")
        self.payload_log = payload_log

        http.server.HTTPServer.__init__(self, address, WebhookHandler)

        self.logger.info("Webhook server listening on {}:{}"
            .format(address[0], address[1]))

    def start_periodic(self, periodic_interval=15*60, update_orgs=[],
            update_older_than=0):
        self.periodic_interval = periodic_interval
        self.update_orgs = update_orgs
        self.update_older_than = update_older_than
        self._schedule_periodic()

        self.logger.info("Configuration: run periodic tasks every {} seconds"
            .format(self.periodic_interval))
        self.logger.info("Configuration: keep orgs {} in sync"
            .format(self.update_orgs))
        self.logger.info("Configuration: update mirrors older than {} seconds"
            .format(self.update_older_than))

    def stop_periodic(self):
        if self.timer:
            self.timer.cancel()
            self.timer = None

    def _schedule_periodic(self):
        if self.periodic_interval > 0:
            self.timer = threading.Timer(self.periodic_interval, self._periodic)
            self.timer.start()

    def _periodic(self):
        for org in self.update_orgs:
            self.logger.info("Periodic: synchronizing '{}'".format(org))
            self.manager.sync_org(org)

        if self.update_older_than:
            self.logger.info("Periodic: updating mirrors older than {} seconds"
                .format(self.update_older_than))
            self.manager.update_old_repos(maximum_age=self.update_older_than)

        self._schedule_periodic()

def serve(manager, secret=None, listen=("127.0.0.1", 8080),
        periodic_interval=15*60, update_orgs=[], update_older_than=0,
        payload_log=None):
    """
    Start an HTTP server to serve webhooks.

    This runs in the foreground.

    manager: a MirrorManager object.
    secret: the shared secret used to authenticate GitHub.
    address: (ip, port) to listen on.
    periodic_interval: how frequently to run periodic tasks.
    update_orgs: organizations to keep in sync.
    update_older_than: update mirrors that haven't been updated in this long.
    payload_log: file to log payloads to.
    """
    server = WebhookServer(listen, secret, manager, payload_log)
    if update_orgs or update_older_than:
        server.start_periodic(
            periodic_interval, update_orgs, update_older_than)
    try:
        server.serve_forever()
    finally:
        server.stop_periodic()


#!/usr/bin/env python3

import sys
import os
import json

json_file = sys.argv[1]
out_file = sys.argv[2]

with open(json_file) as f:
    packages = json.load(f)

dic = {}
for p in packages:
    dic[p['name']] = p

def check(p_name):
    return _check([], p_name)

def _check(visited, p_name):
    if p_name in visited:
        return True
    visited1 = visited + [p_name]
    p = dic[p_name]
    dkeys = p['dependencies_core'].keys()
    for d in dkeys:
        try:
            dp = dic[d]
        except KeyError:
            continue
        if _check(visited1, dp['name']):
            return True
    return False

pkeep = []
for p in packages:
    # if check(p['name']):
    #     print(p['name'])
    if not check(p['name']):
        pkeep.append(p)

with open(out_file, 'w') as f:
    json.dump(pkeep, f, sort_keys=True, indent=2, ensure_ascii=False)


from django.conf.urls import url, include

from . import views

course_patterns = [

    url(r'^list/$',
        views.CourseList.as_view(),
        name='list'),

    url(r'^(?P<pk>[0-9]+)/$',
        views.CourseDetail.as_view(),
        name='detail'),

    url(r'^add/$',
        views.CourseAdd.as_view(),
        name='add'),

    url(r'^edit/(?P<pk>[0-9]+)/$',
        views.CourseEdit.as_view(),
        name='edit'),

]

urlpatterns = [

    url(r'^course/',
        include(course_patterns,
                namespace='course')),
]


import logging
import os

# Create logger
logger = logging.getLogger('FLYTHON LOG')
logger.setLevel(logging.DEBUG)
# handlers instaces
fh = NotImplemented
ch = NotImplemented

def file_logger(level):

    global fh

    # create file handler and set level to debug
    if fh is NotImplemented:
        fh = logging.FileHandler(os.path.dirname(__file__) + '/' + 'flython.log')
        fh.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
        logger.addHandler(fh)
    fh.setLevel(getattr(logging, level.upper()))

def console_logger(level):

    global ch

    # create console handler and set level to warning
    if ch is NotImplemented:
        ch = logging.StreamHandler()
        ch.setFormatter(logging.Formatter('%(name)s: %(message)s'))
        logger.addHandler(ch)
    ch.setLevel(getattr(logging, level.upper()))


import dolfin
from .block_mat import block_mat
from .block_vec import block_vec
from .block_util import wrap_in_list, create_diagonal_matrix
from .helpers import supports_mpi
from .splitting import split_bcs
import itertools
import numpy

class block_bc:
    """
    This class applies Dirichlet BCs to a block matrix.  It is not a block
    operator itself.

    Creating bcs:

    >>> bcs = block_bc([...], symmetric=...)

    Basic inplace usage, with static BCs:

    >>> rhs_bcs = bcs.apply(A)
    >>> rhs_bcs.apply(b)

    with the shortcut

    >>> bcs.apply(A,b)

    If boundary conditions are applied multiple times, it may be useful
    to keep the original assembled mat/vec unchanged (for example, to
    avoid having to re-assemble the RHS if the BC is time dependent but
    the form itself isn't):

    >>> Ainv = conjgrad(bcs(A))
    >>> while True:
    >>>     source.t = t
    >>>     x = Ainv * bcs(b)
    """
    def __init__(self, bcs, symmetric=False, signs=None, subspace_bcs=None):
        # Clean up self, and check arguments
        self.symmetric = symmetric
        bcs = [wrap_in_list(bc, dolfin.DirichletBC) for bc in bcs]
        if subspace_bcs is not None:
            subspace_bcs = split_bcs(subspace_bcs, None)
            combined_bcs = []
            for ss,ns in itertools.zip_longest(subspace_bcs, bcs):
                combined_bcs.append([])
                if ns is not None: combined_bcs[-1] += ns
                if ss is not None: combined_bcs[-1] += ss
            self.bcs = combined_bcs
        else:
            self.bcs = bcs
        self.signs = signs or [1]*len(self.bcs)
        if not all(s in [-1,1] for s in self.signs):
            raise ValueError('signs should be a list of length n containing only 1 or -1')
        supports_mpi(not symmetric, 'Symmetric BCs not supported in parallel')

    @classmethod
    def from_mixed(cls, bcs, *args, **kwargs):
        return cls([], *args, **kwargs, subspace_bcs=bcs)

    def __call__(self, other, A=None):
        if isinstance(other, block_mat):
            self._A = other # opportunistic, expecting a block_vec later
            A = other.copy()
            self.apply(A)
            return A
        elif isinstance(other, block_vec):
            if A is None:
                A = getattr(self, '_A', None) # opportunistic
            if self.symmetric and A is None:
                raise ValueError('A not available. Call with A first, or pass A=A.')
            rhs = self.rhs(A)
            return rhs(other)
        else:
            raise TypeError('BCs can only act on block_mat (A) or block_vec (b)')

    def apply(self, A, b=None):
        """
        Apply BCs to a block_mat LHS, and return an object which modifies the
        corresponding block_vec RHS.  Typical use:

        b_bcs = A_bcs.apply(A); b_bcs.apply(b)
        """
        if not isinstance(A, block_mat):
            raise RuntimeError('A is not a block matrix')

        A_orig = A.copy() if self.symmetric else A
        self._apply(A)

        # Create rhs_bc with a copy of A before any symmetric modifications
        rhs = self.rhs(A_orig)
        if b is not None:
            rhs.apply(b)
        return rhs

    def rhs(self, A):
        return block_rhs_bc(self.bcs, A, symmetric=self.symmetric, signs=self.signs)

    def _apply(self, A):
        if self.symmetric:
            # dummy vec, required by dolfin api -- we don't use this
            # corrections directly since it may be time dependent
            b = A.create_vec(dim=0)
        for i,bcs in enumerate(self.bcs):
            if bcs:
                for j in range(len(A)):
                    if i==j:
                        if numpy.isscalar(A[i,i]):
                            # Convert to a diagonal matrix, so that the individual rows can be modified
                            A[i,i] = create_diagonal_matrix(dolfin.FunctionSpace(bc.function_space()), A[i,i])
                        if self.symmetric:
                            for bc in bcs:
                                bc.zero_columns(A[i,i], b[i], self.signs[i])
                        else:
                            if self.signs[i] != 1:
                                A[i,i] *= 1/self.signs[i]
                            for bc in bcs:
                                bc.apply(A[i,i])
                            if self.signs[i] != 1:
                                A[i,i] *= self.signs[i]
                    else:
                        if numpy.isscalar(A[i,j]):
                            if A[i,j] != 0.0:
                                dolfin.error("can't modify nonzero scalar off-diagonal block (%d,%d)" % (i,j))
                        else:
                            for bc in bcs:
                                bc.zero(A[i,j])
                        if self.symmetric:
                            if numpy.isscalar(A[j,i]):
                                if A[j,i] != 0.0:
                                    dolfin.error("can't modify nonzero scalar off-diagonal block (%d,%d)" % (i,j))
                            else:
                                for bc in bcs:
                                    bc.zero_columns(A[j,i], b[j])

class block_rhs_bc:
    """
    This class applies Dirichlet BCs to a block block vector.  It can be used
    as a block operator; but since it is nonlinear, it can only operate
    directly on a block vector, and not combine with other operators.
    """
    def __init__(self, bcs, A, symmetric, signs):
        if symmetric:
            assert A is not None
        self.bcs = bcs
        self.A = A
        self.symmetric = symmetric
        self.signs = signs

    def apply(self, b):
        """Apply Dirichlet boundary conditions statically.  In the
        inhomogeneous symmetric case b is changed globally; if BCs are mutable
        (time dependent for example), it is then more convenient to use the
        callable form -- rhs_bc(b) -- to preserve the original contents of b
        for repeated application without reassembly.
        """
        if not isinstance(b, block_vec):
            raise TypeError('not a block vector')

        try:
            b.allocate(self.A, dim=0, alternative_templates=[self.bcs])
        except Exception:
            raise ValueError('Failed to allocate block vector, call b.allocate(something) first')

        # Apply lifting to correct for the matrix columns zeroed by symmetricization
        if self.symmetric:
            # First, collect a vector containing all non-zero BCs.
            g = self.A.create_vec(dim=0)
            g.zero()
            for i,bcs in enumerate(self.bcs):
                for bc in bcs:
                    bc.apply(g[i])
            # The non-zeroes of g are now exactly the inhomogeneous BC
            # values. We can thus create the necessary modifications to b by
            # just multiplying with the un-symmetricised original matrix. The
            # bc values are overwritten below, hence only the non-BC rows of A
            # matter.
            b -= self.A * g

        # Apply the actual BC dofs to b. (This must be done after the symmetric
        # correction above, since A*g is not necessarily zero at BC dofs.)
        # If the sign is negative, we negate twice to effectively apply -bc.
        for i,bcs in enumerate(self.bcs):
            if self.signs[i] != 1 and bcs:
                b[i] *= 1/self.signs[i]
            for bc in bcs:
                bc.apply(b[i])
            if self.signs[i] != 1 and bcs:
                b[i] *= self.signs[i]

        return self

    def __call__(self, other):
        if not isinstance(other, block_vec):
            raise TypeError()
        b = other.copy()
        self.apply(b)
        return b


"""
Object Detection
Sources: https://www.pyimagesearch.com/2018/11/12/yolo-object-detection-with-opencv/
yolo model: https://pjreddie.com/darknet/yolo/
tf models: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md

"""

import numpy as np
import tensorflow as tf
import cv2 as cv
import PIL.Image
import PIL.ImageDraw
import time
import ObjectLists

CONFIDENCE_THRESHOLD = 0.5

def faster_rcnn_oi(path):
    tf.reset_default_graph()

    # read the cnn
    with tf.io.gfile.GFile('object detection models/faster_rcnn_oi.pb', 'rb') as f:
        graph_def = tf.compat.v1.GraphDef()
        graph_def.ParseFromString(f.read())

    with tf.Session() as sess1:
        # Restore session
        sess1.graph.as_default()
        tf.import_graph_def(graph_def, name='')

        # read the image, run the model and draw the bounding boxes
        read_image(path)
        run_model(sess1)
        draw_boxes("open images")

    return draw_image

def faster_rcnn_coco(path):
    tf.reset_default_graph()

    # read the cnn
    with tf.io.gfile.GFile('object detection models/faster_rcnn_coco.pb', 'rb') as f:
        graph_def = tf.compat.v1.GraphDef()
        graph_def.ParseFromString(f.read())

    with tf.Session() as sess2:
        # Restore session
        sess2.graph.as_default()
        tf.import_graph_def(graph_def, name='')

        # read the image, run the model and draw the bounding boxes
        read_image(path)
        run_model(sess2)
        draw_boxes("coco")

    return draw_image

def mask_rcnn_coco(path):
    tf.reset_default_graph()

    # read the cnn
    with tf.io.gfile.GFile('object detection models/mask_rcnn_coco.pb', 'rb') as f:
        graph_def = tf.compat.v1.GraphDef()
        graph_def.ParseFromString(f.read())

    with tf.Session() as sess3:
        # Restore session
        sess3.graph.as_default()
        tf.import_graph_def(graph_def, name='')

        # read the image, run the model and draw the bounding boxes
        read_image(path)
        run_model(sess3)
        draw_boxes("coco")

    return draw_image

def ssd_inception_coco(path):
    tf.reset_default_graph()

    # read the cnn
    with tf.io.gfile.GFile('object detection models/ssd_inception_coco.pb', 'rb') as f:
        graph_def = tf.compat.v1.GraphDef()
        graph_def.ParseFromString(f.read())

    with tf.Session() as sess4:
        # Restore session
        sess4.graph.as_default()
        tf.import_graph_def(graph_def, name='')

        # read the image, run the model and draw the bounding boxes
        read_image(path)
        run_model(sess4)
        draw_boxes("coco")

    return draw_image

def ssd_resnet50_coco(path):
    tf.reset_default_graph()

    # read the cnn
    with tf.io.gfile.GFile('object detection models/ssd_resnet50_coco.pb', 'rb') as f:
        graph_def = tf.compat.v1.GraphDef()
        graph_def.ParseFromString(f.read())

    with tf.Session() as sess5:
        # Restore session
        sess5.graph.as_default()
        tf.import_graph_def(graph_def, name='')

        # read the image, run the model and draw the bounding boxes
        read_image(path)
        run_model(sess5)
        draw_boxes("coco")

    return draw_image

# yolo has to be run a bit differently
def yolo_v3_coco(path):
    tf.reset_default_graph()

    # load model
    model_file = "object detection models/yolov3.weights"
    config_file = "object detection models/yolov3.cfg"
    dnn = cv.dnn.readNetFromDarknet(config_file, model_file)

    # read the image, run the model and draw the bounding boxes
    read_image(path)
    run_model_yolo_v3(dnn)
    draw_boxes_yolo_v3("coco")

    return draw_image

def yolo_v3_oi(path):
    tf.reset_default_graph()

    # load model
    model_file = "object detection models/yolov3oi.weights"
    config_file = "object detection models/yolov3oi.cfg"
    dnn = cv.dnn.readNetFromDarknet(config_file, model_file)

    # read the image, run the model and draw the bounding boxes
    read_image(path)
    run_model_yolo_v3(dnn)
    draw_boxes_yolo_v3("open images")

    return draw_image

# helping methods
def read_image(path):
    global image, draw_image, height, width, inp
    image = cv.imread(path)
    draw_image = PIL.Image.fromarray(image)
    (height, width) = image.shape[:2]
    inp = image
    inp = inp[:, :, [2, 1, 0]]  # BGR2RGB

# runs the yolo model
def run_model_yolo_v3(dnn):
    global out, start, end
    layer_names = dnn.getLayerNames()
    layer_names = [layer_names[i[0] - 1] for i in dnn.getUnconnectedOutLayers()]
    # yolo only takes 320x320, 416x416 or 608x608
    blob = cv.dnn.blobFromImage(image, 1 / 255.0, size=(608, 608), swapRB=True, crop=False)
    dnn.setInput(blob)
    start = time.time()
    out = dnn.forward(layer_names)
    end = time.time()

# runs the tensorflow model
def run_model(sess):
    global out, start, end
    start = time.time()
    out = sess.run([sess.graph.get_tensor_by_name('num_detections:0'),
                    sess.graph.get_tensor_by_name('detection_scores:0'),
                    sess.graph.get_tensor_by_name('detection_boxes:0'),
                    sess.graph.get_tensor_by_name('detection_classes:0')],
                   feed_dict={'image_tensor:0': inp.reshape(1, inp.shape[0], inp.shape[1], 3)})
    end = time.time()
    sess.close()

def draw_boxes_yolo_v3(trainingsdataset):
    global number_of_objects_detected, detected_objects
    number_of_objects_detected = 0
    detected_objects = []
    color = "red"

    for o in out:
        for detection in o:

            scores = detection[5:]
            class_id = np.argmax(scores)
            score = scores[class_id]

            if score > CONFIDENCE_THRESHOLD:
                if trainingsdataset == "coco":
                    obj = ObjectLists.get_yolo_v3_coco_object_by_classID(class_id)
                elif trainingsdataset == "open images":
                    obj = ObjectLists.get_oi_object_by_classID(class_id + 1)
                rounded_score_in_percent = 100 * (round(score, 4))
                detected_objects.append("{} {:.2f}%".format(obj, rounded_score_in_percent))
                number_of_objects_detected += 1

                # scale the bounding box coordinates back relative to the
                # size of the image, keeping in mind that YOLO actually
                # returns the center (x, y)-coordinates of the bounding
                # box followed by the boxes' width and height
                box = detection[0:4] * np.array([width, height, width, height])
                (center_x, center_y, w, h) = box.astype("int")
                # use the center (x, y)-coordinates to derive the top and
                # and left corner of the bounding box
                x = int(center_x - (w / 2))
                y = int(center_y - (h / 2))

                left, top, right, bottom = x, y, x + w, y + h
                draw = PIL.ImageDraw.Draw(draw_image)
                draw.rectangle([left, top, right, bottom], outline=color)
                draw.text([left, bottom], obj, fill=color)

# draws the bounding boxes for the tensorflow model
def draw_boxes(trainingsdataset):
    global number_of_objects_detected, detected_objects

    detected_objects = []
    number_of_objects_detected = 0
    number_of_total_detections = int(out[0][0])
    color = "red"

    for i in range(number_of_total_detections):
        score = float(out[1][0][i])
        bbox = [float(v) for v in out[2][0][i]]
        if score > CONFIDENCE_THRESHOLD:

            # append to list of detected objects
            class_id = int(out[3][0][i])
            if trainingsdataset == "coco":
                obj = ObjectLists.get_coco_object_by_classID(class_id)
            elif trainingsdataset == "open images":
                obj = ObjectLists.get_oi_object_by_classID(class_id)
            rounded_score_in_percent = 100 * (round(score, 4))
            detected_objects.append("{} {:.2f}%".format(obj, rounded_score_in_percent))
            number_of_objects_detected += 1

            # draw bounding box and object name
            left = bbox[1] * width
            top = bbox[0] * height
            right = bbox[3] * width
            bottom = bbox[2] * height
            draw = PIL.ImageDraw.Draw(draw_image)
            draw.rectangle([left, top, right, bottom], outline=color)
            draw.text([left, bottom], obj, fill=color)

# get the time it took to detect the objects
def get_detection_time():
    global end, start
    return end - start

# get the number of objects detected
def get_number_of_objects_detected():
    return number_of_objects_detected

# returns a list of all the objects that have been detected
def get_detected_objects():
    return detected_objects


import serial
from pattern.Subject import Subject
from pattern.Observer import Observer
from threading import Thread

class ArduinoSerial(Subject, Observer):

    def __init__(self):
        super().__init__()
        self.stop = False
        try:
            portx = "COM4" #串口名称
            bps = 9600     #波特率
            # 超时设置,None：永远等待操作，0为立即返回请求结果，其他值为等待超时时间(单位为秒）
            timex = None
            self.ser = serial.Serial(portx, bps, timeout=timex) #创建对象同时已经打开串口
            print("Serial:Serial {} opened".format(self.ser.name))

            def readSerial():
                while not self.stop:
                    if self.ser.inWaiting():  # Return the number of bytes in the receive buffer.
                        # Read size bytes from the serial port. If a timeout is set it
                        # may return less characters as requested. With no timeout it
                        # will block until the requested number of bytes is read.
                        data = self.ser.read(self.ser.inWaiting())
                        print("Sensor: <<", data)
                        state = int.from_bytes(data, byteorder='big')
                        if state == 0:  #arduino发送来0，表示传感器检测到磁铁进入
                            print("Serial:Sensor detected Megnet in")
                        elif state == 1: #arduino发送来1，表示传感器检测到磁铁远离
                            print("Serial:Sensor detected Megnet out")

            self.ser_thread = Thread(target=readSerial)  #线程轮训访问串口
            self.ser_thread.start()
        except Exception as e:
            print("Serial:Exception ", e)

    def send(self, msg):
        self.ser.write(msg)
        print("Serial: >> ", msg)

    def onMegnetIn(self):
        for observer in self.observers:
            observer.update("close")

    def onMegnetOut(self):
        pass

    def update(self, state):
        if state == "open":
            self.send(1) #向arduino发送1，使得arduino控制继电器开门
            print("Serial:Lock open")
        elif state == "close":
            self.send(0) #向arduino发送0，arduino控制继电器锁门
            print("Serial:Lock close")

    def close(self):
        self.stop = True
        self.update("close")
        self.ser_thread.join()
        self.ser.close()


import cv2

img = cv2.imread("Computer-Vision-py/DATA/00-puppy.jpg")
cv2.imshow("sU[p?]", img)
cv2.waitKey()

# proper way to display || uses q to close the windows
while (1):
    cv2.imshow("sU[p?]", img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()
# creating shapes on images