from rest_framework import permissions, views, response, status

from . import serializers

class UserView(views.APIView):
    """ User info view """

    serializer_class = serializers.UserSerializer

    def get(self, request):
        serializer = self.serializer_class(request.user)
        return response.Response(serializer.data, status=status.HTTP_200_OK)

class CreateTokenView(views.APIView):
    """ View for create jwt access and refresh token """

    permission_classes = [permissions.AllowAny]

    @staticmethod
    def post(request):
        serializer = serializers.TokenCreateSerializer(
            data=request.data, context={'request': request}
        )
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data
        result = {
            'access': validated_data['access'],
            'refresh': validated_data['refresh']
        }
        return response.Response(result, status=status.HTTP_202_ACCEPTED)

class TokenRefreshView(views.APIView):
    """ View for refresh jwt access token """

    permission_classes = [permissions.AllowAny]

    @staticmethod
    def post(request):
        serializer = serializers.TokenRefreshSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return response.Response(serializer.validated_data, status=status.HTTP_200_OK)


import os
import re
import nltk
import string as st
import pandas as pd
import src.config as config
from nltk.corpus import stopwords
from nltk import PorterStemmer, WordNetLemmatizer

# nltk.download('stopwords')
# nltk.download('wordnet')

stop_words = stopwords.words('english')

path_csv = config.BASE_DIR + "/data/masters_portal.csv"
data_frame = pd.read_csv(path_csv)

def remove_punctuations(text):
    """
        Remove all punctuations from the text
    """
    if not isinstance(text, str):
        return text

    return "".join([ch for ch in text if ch not in st.punctuation])

def tokenize(text):
    """ Convert text to lower case tokens. Here, split() is applied on white-spaces. But, it could be applied
        on special characters, tabs or any other string based on which text is to be separated into tokens.
    """
    if not isinstance(text, str):
        return text

    return "".join([x.lower() for x in text])

def remove_small_words(text):
    """
        Remove tokens of length less than 3
    """
    if not isinstance(text, str):
        return text

    return "".join([x for x in text if len(x) > 3])

def remove_stopwords(text):
    """ Remove stopwords. Here, NLTK corpus list is used for a match. However,
        a customized user-defined list could be created and used to limit the matches in input text.
    """
    if not isinstance(text, str):
        return text

    return " ".join([word for word in text.split(' ') if word not in stop_words])

# Apply stemming to convert tokens to their root form. This is a rule-based process of word form conversion
# where word-suffixes are truncated irrespective of whether the root word is an actual word in the language dictionary.
# Note that this step is optional and depends on problem type.
def stemming(text):
    """
        Apply stemming to get root words
    """
    if not isinstance(text, str):
        return text

    ps = PorterStemmer()
    return "".join([ps.stem(word) for word in text])

# Lemmatization converts word to its dictionary base form. This process takes language grammar and vocabulary
# into consideration while conversion. Hence, it is different from Stemming in that it does not merely truncate the suffixes
# to get the root word.
def lemmatize(text):
    """
        Apply lemmatization on tokens
    """
    if not isinstance(text, str):
        return text

    word_net = WordNetLemmatizer()
    return "".join([word_net.lemmatize(word) for word in text])

def preprocess_pipeline(
        df,
        tokenize_flag=True,
        remove_punctuations_flag=True,
        remove_stop_words_flag=True,
        remove_small_words_flag=False,
        lemmatize_flag=True,
        stemmer_flag=True
):
    """
    input text
        ↳ [tokenize]
            ↳ [remove punctuations]
                ↳ [remove stop words]
                    ↳ [remove small words]
                        ↳ [lemmatize]
                            ↳ [stemmer]
                                ↳ output text
    """
    if tokenize_flag:
        df = df.apply(lambda x: tokenize(x))

    if remove_punctuations_flag:
        df = df.apply(lambda x: remove_punctuations(x))

    if remove_stop_words_flag:
        df = df.apply(lambda x: remove_stopwords(x))

    if remove_small_words_flag:
        df = df.apply(lambda x: remove_small_words(x))

    if lemmatize_flag:
        df = df.apply(lambda x: lemmatize(x))

    if stemmer_flag:
        df = df.apply(lambda x: stemming(x))

    return df

def pre_process(df: pd.DataFrame, persist: bool = False) -> pd.DataFrame:
    path = config.BASE_DIR + "/data/processed_portals.csv"
    if os.path.exists(path):
        df = pd.read_csv(path)

        return df

    df['extracted_keywords'] = ""

    for i in range(len(df)):
        processed_row = preprocess_pipeline(df=df.iloc[i])
        processed_row = processed_row.astype(str)
        df.at[i, 'extracted_keywords'] = ", ".join(processed_row.values)

    if persist:
        df.to_csv(path)

    return df


import utils
utils.backup(__file__)

import pylab as pl
import numpy.testing as tests
import itertools

class OptimalChannels(object):
    def __init__(self,p_uA_given_A=0.8,p_uA_given_B=0.1,
                 p_uB_given_A=None,p_uB_given_B=None,N_u=10):
        self.p_uA_given_A = p_uA_given_A
        self.p_uA_given_B = p_uA_given_B
        if p_uB_given_A is None:
            self.p_uB_given_A = p_uA_given_B
        else:
            self.p_uB_given_A = p_uB_given_A
        if p_uB_given_B is None:
            self.p_uB_given_B = p_uA_given_A
        else:
            self.p_uB_given_B = p_uB_given_B
        self.N_u = N_u

    def _p_A_given_N_A(self,N_A,p_A,N_B=None):
        if N_B == None:
            N_B = self.N_u-N_A

        p_B = 1-p_A
        p_N_A_given_A = (self.p_uA_given_A**(N_A)
                            * (1-self.p_uA_given_A)**(self.N_u-N_A)
                            * self.p_uB_given_A**(N_B)
                            * (1-self.p_uB_given_A)**(self.N_u-N_B))
        p_N_A_given_B = (self.p_uA_given_B**(N_A)
                            * (1-self.p_uA_given_B)**(self.N_u-N_A)
                            * self.p_uB_given_B**(N_B)
                            * (1-self.p_uB_given_B)**(self.N_u-N_B))
        return p_N_A_given_A*p_A / ((p_N_A_given_A*p_A)
                                     +(p_N_A_given_B*p_B))

    # Speeds shit up by 100x
    def _sample_posteriors_noloop(self,true_N_A,p_A,N_samples):
        true_N_B = self.N_u-true_N_A
        N_values = pl.shape(true_N_A)[0]
        posteriors = pl.zeros((N_samples,N_values))
        for (i,(t_N_A,t_N_B)) in enumerate(zip(true_N_A,true_N_B)):
            A_probs = pl.ones((N_samples,self.N_u))
            A_probs[:,:t_N_A] *= self.p_uA_given_A
            A_probs[:,t_N_A:] *= self.p_uA_given_B
            B_probs = pl.ones((N_samples,self.N_u))
            B_probs[:,:t_N_A] *= self.p_uB_given_A
            B_probs[:,t_N_A:] *= self.p_uB_given_B

            N_A = pl.sum(A_probs>pl.rand(N_samples,self.N_u),1)
            N_B = pl.sum(B_probs>pl.rand(N_samples,self.N_u),1)

            posteriors[:,i] = self._p_A_given_N_A(N_A,p_A,N_B)
        return pl.mean(posteriors,0)

    def _sample_posteriors(self,true_N_A,p_A,N_samples):
        true_N_B = self.N_u-true_N_A
        N_values = pl.shape(true_N_A)[0]
        posteriors = pl.zeros((N_samples,N_values))
        for i in range(N_samples):
            for (j,(t_N_A,t_N_B)) in enumerate(zip(true_N_A,true_N_B)):
                A_given_A = pl.ones(t_N_A)*self.p_uA_given_A
                A_given_B = pl.ones(t_N_B)*self.p_uA_given_B
                A_probs = pl.hstack((A_given_A,A_given_B))
                B_given_A = pl.ones(t_N_A)*self.p_uB_given_A
                B_given_B = pl.ones(t_N_B)*self.p_uB_given_B
                B_probs = pl.hstack((B_given_A,B_given_B))

                N_A = pl.sum(A_probs>pl.rand(self.N_u))
                N_B = pl.sum(B_probs>pl.rand(self.N_u))

                posteriors[i,j] = self._p_A_given_N_A(N_A,N_B)

        return pl.mean(posteriors,0)

    def _exact_posteriors(self,true_N_A,p_A):
        from scipy.stats import binom
        true_N_B = self.N_u-true_N_A
        N_values = pl.shape(true_N_A)
        posteriors = pl.zeros((N_values))
        # Get all ints that sum to a fixed value (e.g. 1: [(1,0),(0,1)])
        combinations = {}
        N_u = int(self.N_u)
        for i in range(N_u+1):
            for j in range(N_u+1):
                try:
                    combinations[i+j] += [(i,j)]
                except KeyError:
                    combinations[i+j] = [(i,j)]
        # For each ambiguity, calculate the probabilities of active
        # channels
        for (i,(t_N_A,t_N_B)) in enumerate(zip(true_N_A,true_N_B)):
            # First calculate the probabilities of drawing specific
            # N_As from both binomials that contribute to N_A
            N_us = pl.arange(0,N_u+1)
            p_N_us_A1 = binom.pmf(N_us,t_N_A,self.p_uA_given_A)
            p_N_us_A2 = binom.pmf(N_us,t_N_B,self.p_uA_given_B)
            p_N_us_B1 = binom.pmf(N_us,t_N_A,self.p_uB_given_A)
            p_N_us_B2 = binom.pmf(N_us,t_N_B,self.p_uB_given_B)
            ps_N_A = pl.zeros(N_u+1)
            ps_N_B = pl.zeros(N_u+1)
            # Then combine the binomials with the combinations
            for j in range(N_u+1):
                for (k,l) in combinations[j]:
                    ps_N_A[j] += p_N_us_A1[k]*p_N_us_A2[l]
                    ps_N_B[j] += p_N_us_B1[k]*p_N_us_B2[l]
            # Some safety checks that probabilities actually sum to 1
            tests.assert_almost_equal(sum(ps_N_A),1.)
            tests.assert_almost_equal(sum(ps_N_B),1.)
            # Combine probabilities to get posterior
            for (N_A,N_B) in itertools.product(range(N_u+1),
                                               range(N_u+1)):
                p_A_given_N_AB = self._p_A_given_N_A(N_A,p_A,N_B)
                posteriors[i] += p_A_given_N_AB*ps_N_A[N_A]*ps_N_B[N_B]
        return posteriors

    def optimal_inference(self,p_A=0.3,N_As=None,N_samples=1000):
        if N_As is None:
            N_As = pl.linspace(0,self.N_u,self.N_u+1)
        #posteriors = self._sample_posteriors_noloop(N_As,p_A,N_samples)
        posteriors = self._exact_posteriors(N_As,p_A)
        return posteriors

    def plot(self,p_A=0.3,N_As=None):
        if N_As is None:
            pl.linspace(0,self.N_u,self.N_u+1)
        posteriors = sample_posteriors_noloop(N_As,p_A,1000)
        pl.figure()
        x = N_As/N_u
        pl.plot(x,pl.mean(posteriors,0),label='A')
        pl.plot(x,1-pl.mean(posteriors,0),label='B')
        #plot([0,1],[1-p_A,1-p_A],'k--',label='1 - p(A) and p(A)')
        #plot([0,1],[p_A,p_A],'k--')
        pl.plot([0.5,0.5],[0,1],'k-.',label='full ambiguity')
        pl.legend(loc='best')
        pl.title('p(on|correct)=%.1f   p(on|incorrect)=%.1f'
                 %(self.p_uA_given_A,self.p_uA_given_B))
        pl.xlabel('Fraction of A in ambiguous stimulus')
        pl.ylabel('Posterior probability')


"""
module to control a Fluke 8845A digital multimeter
uses serialdevice, which uses pySerial
The meter should be in its native (i.e. 8845A) mode and set for 'computer' mode, not 'terminal' mode

"""
__author__ = 'jpindar@jpindar.com'

import sys
import time
from typing import List, Dict, Optional, Any, Union
import serialdevice
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

class Multimeter(serialdevice.SerialDevice):
    """
    controls a Fluke 8845A digital multimeter
    """
    default_address = 5  # address to use if optional parameter is not supplied by caller
    default_baud = 19200
    display_name = 'Fluke Multimeter'  # name to use in GUI messages etc.
    ID_QUERY = "*IDN?\n"
    GOOD_ID_RESPONSE = "FLUKE,8845A,9344019,09/29/06-16:59"  # when in 8845A mode
    GOOD_ID_RESPONSE_45 = "FLUKE, 45, 9344019, 2.0 D2.0"  # when in Fluke 45 emulation mode
    ERROR_CODE_OK = "+0,\"No error\""
    read_delay = 10
    read_mode = 1
    timeout = 5.0

    def __init__(self, address: int = default_address, baud=default_baud):
        super().__init__()
        self.port_num = address
        self.baud_rate = baud
        self.open_port()

    def reset(self):
        self.write("*rst\n")

    def clear_errors(self):
        self.write("*cls\n")

    def get_errors(self):
        self.write("SYST:ERR?\n")
        response = self.read()
        return response

    def remote(self):
        self.write("SYST:REM\n")

    def query(self, cmd: str) -> Optional[str]:
        """ TODO: implement the Abort, Retry, Ignore system that I used in the complete software

        """
        response = None
        finished = False
        while not finished:
            try:
                logger.info("sending <" + cmd + ">")
                self.write(cmd)
                time.sleep(self.read_delay)
                response = self.read()
                finished = True
                # return response
            # except (serialdevice.SerialException, serialdevice.SerialTimeoutException) as e:
            except (serialdevice.serial.SerialException, serialdevice.serial.SerialTimeoutException) as e:
                logger.warning("SerialDevice.query\r\n")
                logger.warning(e.__class__)
                logger.warning(e.__doc__)
                logger.warning(e.args[0])   # or  just args
                # raise e
                return ""
            except Exception as e:
                logger.warning("SerialDevice.query\r\n")
                logger.warning(e.__class__)
                logger.warning(e.__doc__)
                raise e
        return response

    def get_ID(self):
        logger.info(str("sending query: " + self.ID_QUERY))
        try:
            s = self.query(self.ID_QUERY)
        except (serialdevice.serial.SerialException, serialdevice.serial.SerialTimeoutException) as e:
            logger.warning("SerialDevice.query\r\n")
            logger.warning(e.__class__)
            logger.warning(e.__doc__)
            logger.warning(e.args[0])   # or  just args
            # raise e
            return None
        except Exception as e:
            logger.warning(e.__class__)
            logger.warning(e.__doc__)
            logger.warning(e.__cause__)
            raise e
        logger.info(str("response is " + s))
        return s

    def get_DCVolts(self, scale=None):
        """
         note that this returns 9.9E37 when the device's display reads "overload"
        """
        s = None
        cmd = "MEAS:VOLT:DC? "
        if scale is not None:
            cmd += str(scale)
        cmd += "\n"
        logger.info(str("sending query: " + cmd))
        try:
            s = self.query(cmd)
        except (serialdevice.serial.SerialException, serialdevice.serial.SerialTimeoutException) as e:
            logger.warning("SerialDevice.query\r\n")
            logger.warning(e.__class__)
            logger.warning(e.__doc__)
            logger.warning(e.args[0])   # or  just args
            # raise e
            return None
        except Exception as e:
            logger.warning(e.__class__)
            logger.warning(e.__doc__)
            logger.warning(e.__cause__)
            raise e
        logger.info(str("response is " + s))
        try:
            f = float(s)
        except ValueError:
            logger.warning("The response could not be converted to a float")
            return None
        return f

def main():
    """
    basic usage example
    There are checks here that are for testing and aren't needed in production code

    The exception handling here is not good, but would be done differently
    depending on the context anyway
    """
    log_filename = "multimeter.log"
    logging.basicConfig(filename=log_filename, filemode='a', format='%(levelname)-8s:%(asctime)s %(name)s: %(message)s')
    logger.setLevel(logging.INFO)
    logger.info("multimeter demo")
    print("Connecting to Fluke DMM")
    try:
        dmm = Multimeter()
        dmm.open_port()
    except Exception as e:
        print("There was a problem finding the instrument")
        sys.exit()
    else:
        print("Found the instrument at " + str(dmm.port_name))
    if dmm.port is None:
        sys.exit()

    assert isinstance(dmm.port, serialdevice.serial.Serial)
    if dmm.is_open():
        print('serial port is open')
    msg = Multimeter.ID_QUERY
    dmm.write(msg)
    s = dmm.read()
    print("The device returned ID string:  " + s)
    if s == Multimeter.GOOD_ID_RESPONSE:
        print("OK")
    elif s == Multimeter.GOOD_ID_RESPONSE_45:
        print("The meter is in the wrong mode (Fluke 45 emulation).")
        sys.exit()
    else:
        print("ERROR")
        sys.exit()

    s = dmm.get_errors()
    if s != Multimeter.ERROR_CODE_OK:
        print(s)
    dmm.clear_errors()

    dmm.remote()
    print("measuring DC voltage...")
    try:
        v = dmm.get_DCVolts()
    except Exception as e:
        print("get_DCVolts() had a problem: ", e.__class__)
        raise e
    else:
        assert isinstance(v, float)
        print(f"{v} volts dc")

    dmm.close_port()

if __name__ == '__main__':
    main()


import streamlit as st
from send_email import send_email
st.header("Contact me")

with st.form(key="email_forms"):
    user_email=st.text_input("Your email adress")
    message=st.text_area("Your message")
    message=message+"\n" +user_email
    button=st.form_submit_button()
    print(button)

    if button:
        send_email(message)


import sys
from collections import deque

INF = int(1e9)
input = sys.stdin.readline

dy = [-1, 0, 1, 1, 1, 0, -1, -1]
dx = [1, 1, 1, 0, -1, -1, -1, 0]

def bfs():
    queue = deque()
    queue.append((start, 0))
    visit[start[0]][start[1]] = 0

    while queue:
        nowpos, nowcost = queue.popleft()
        y, x = nowpos
        if y == end[0] and x == end[1]:
            return visit[y][x]
        for i in range(8):
            ny = y + dy[i]
            nx = x + dx[i]

            if 0 <= ny < h and 0 <= nx < w and maps[ny][nx] != -1:
                if i in (0, 1, 2):
                    nextcost = nowcost
                else:
                    nextcost = nowcost + 1

                if visit[ny][nx] > nextcost:
                    visit[ny][nx] = nextcost
                    if nextcost == nowcost:
                        queue.appendleft(((ny, nx), nextcost))
                    else:
                        queue.append(((ny, nx), nextcost))
    return -1
h, w = map(int, input().split())
maps = [[0] * w for _ in range(h)]
start = (0, 0)
end = (0, 0)

for i in range(h):
    row = input().strip()
    for j in range(w):
        if row[j] == '#':
            maps[i][j] = -1
        elif row[j] == '*':
            end = (i, j)
        elif row[j] == 'K':
            start = (i, j)

visit = [[INF] * w for _ in range(h)]

print(bfs())


import os
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from bs4 import BeautifulSoup
from FileUtil import FileUtil

class BrowserUtil:
    def __init__(self):
        self.chromedriver_path = os.environ['CHROMEDRIVER_PATH']

        if "RUNNING_IN_CONTAINER" in os.environ:
            self.headless_browser = True
        else:
            self.headless_browser = False

    # write the html to file for easier work later
    @staticmethod
    def write_page_html(page_html, html_file_name):
        f = open(html_file_name, 'w')
        f.write(page_html)
        f.close()

        print(html_file_name)

        if bool(os.getenv('SAVE_HTML', True)):
            FileUtil().upload_to_bucket(html_file_name, html_file_name,
                                        os.getenv('HTML_BUCKET', 'pfr-html-files'))

            os.remove(html_file_name)

    @staticmethod
    def use_cached_file(html_file_name):
        return bool(os.getenv('USE_CACHED_FILES', False)) and FileUtil().check_file_exists(html_file_name,
                                                                                os.getenv('HTML_BUCKET',
                                                                                'pfr-html-files'))

    def parse_html(self, url, html_file_name):
        if self.use_cached_file(html_file_name):
            print('Using cached file: ' + html_file_name)
            FileUtil().download_file(html_file_name, html_file_name, os.getenv('HTML_BUCKET', 'pfr-html-files'))

            soup = BeautifulSoup(open(html_file_name), "html.parser")
            os.remove(html_file_name)

            return soup

        try:
            browser_util = BrowserUtil()
            browser = browser_util.get_browser()
            browser.set_page_load_timeout(45)

            browser.get(url)
            inner_html = browser.execute_script("return document.body.innerHTML")
            browser.close()

        except TimeoutException:
            browser_util = BrowserUtil()
            browser = browser_util.get_browser()
            browser.set_page_load_timeout(45)

            browser.get(url)
            inner_html = browser.execute_script("return document.body.innerHTML")
            browser.close()

        self.write_page_html(inner_html, html_file_name)

        # Parse the page with BeautifulSoup
        return BeautifulSoup(inner_html, 'html.parser')

    def get_browser(self):
        # get headless chrome driver and get inner html
        if self.headless_browser:
            options = webdriver.ChromeOptions()
            options.add_argument('--headless')
            options.add_argument('--no-sandbox')
            options.add_argument('--disable-dev-shm-usage')
        else:
            options = Options()
            options.headless = True

        return webdriver.Chrome(options=options, executable_path=self.chromedriver_path)


from datetime import timedelta
from django.utils import timezone
from logging import raiseExceptions
from xml.dom import ValidationErr
from django.forms import ValidationError
from django.db import models

def relay_check(stroke):
    # 'front crawl', 'butterfly', 'breast', 'back', or 'freestyle
    stroke_list = ['front crawl', 'butterfly', 'breast', 'back', 'freestyle']
    if stroke not in stroke_list:
        raise ValidationError(f"{stroke} is not a valid stroke")

def distance_check(distance):
    limit = 50
    if distance <= limit:
        raise ValidationError(
            "Ensure this value is greater than or equal to 50.")

def date_check(date):
    if date > timezone.now():
        raise ValidationError("Can't set record in the future.")

def record_broken_date_check(record_broken_date):
    if record_broken_date < timezone.now():
        raise ValidationError("Can't break record before record was set.")
    return record_broken_date

class SwimRecord(models.Model):

    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    team_name = models.CharField(max_length=255)
    relay = models.BooleanField(default=False)
    stroke = models.CharField(max_length=255, validators=[relay_check])
    distance = models.IntegerField(validators=[distance_check])
    record_date = models.DateTimeField(validators=[date_check])
    record_broken_date = models.DateTimeField(
        validators=[record_broken_date_check])


import os
import sys
import shutil
import tarfile
import pathlib
import re
import tempfile

# Path where our script is stored
self_path = os.path.dirname(os.path.abspath(__file__))

# Path where we should write extracted files to
out_dir = tempfile.mkdtemp()

# Log to the console in pretty colors
def log(text):
    print(f'\033[94m{text}\033[0m')

# Display usage to the user
def usage():
    log('s: select regex pattern')
    log('n: narrow search further')
    log('x: extract selected items')
    log('r: erase output directory')
    log('q: quit')
    log('h: show basic usage')

def len_of_tar_map(tar_map):
    return sum([len(tar_map[x]) for x in tar_map.keys()])

# Class for handling user selections
class Selector:
    # Map of tar file paths to tar file content paths
    # i.e.: { '/path/to/test.tar': ['1.txt', '2.txt', etc.] }
    selected_tar_map = {}

    def __init__(self):
        self.initial_tar_map = enumerate_tars()

    # Process user-input from the command line
    def ui(self):
        # Request input
        raw_cmd = input('rr> ')
        raw_cmd_split = raw_cmd.split()

        # Handle edge case where no input
        if len(raw_cmd_split) == 0:
            return

        # Split into command and arguments
        cmd = raw_cmd_split[0]
        args = ' '.join(raw_cmd_split[1:])

        # Select recursively via regex pattern
        if cmd == 's':
            self.selected_tar_map = search(self.initial_tar_map, args)
            cnt = len_of_tar_map(self.selected_tar_map)
            log(f'found: {cnt}')
        elif cmd == 'n':
            self.selected_tar_map = search(self.selected_tar_map, args)
            cnt = len_of_tar_map(self.selected_tar_map)
            log(f'found: {cnt}')
        # List selected items
        elif cmd == 'l':
            for tar_path in self.selected_tar_map.keys():
                log(f'tar: {tar_path}')
                for tar_name in self.selected_tar_map[tar_path]:
                    log(f'\t| file: {tar_name}')
        # Extracts selected items
        elif cmd == 'x':
            # Create output directory if nonexistent
            if not os.path.exists(out_dir):
                os.mkdir(out_dir)
            # For each file, extract it
            for tar_path in self.selected_tar_map.keys():
                tar_file = tarfile.open(tar_path)
                for tar_name in self.selected_tar_map[tar_path]:
                    log(f'x [{out_dir}]: {tar_name}')
                    tar_file.extract(tar_name, out_dir)
                tar_file.close()
        # Erase output directory
        elif cmd == 'r':
            shutil.rmtree(out_dir, ignore_errors=True)
            log('erased output directory')
        # Show usage
        elif cmd == 'h':
            usage()
        # Quit
        elif cmd == 'q':
            sys.exit()
        # Not sure
        else:
            log(f'unknown action: {cmd}')

# Return a map of all tarballs to their contents
def enumerate_tars():
    tar_map = {}
    for root, _, files in os.walk(self_path):
        for filename in files:
            tar_path = os.path.join(root, filename)
            suffixes = pathlib.Path(filename).suffixes
            # Only find files with a tar suffix
            if '.tar' in suffixes:
                log(f'enumerating {tar_path}')
                try:
                    tar = tarfile.open(tar_path)
                    tar_map[tar_path] = tar.getnames()
                    tar.close()
                except tarfile.ReadError:
                    log(f'failed to open {tar_path}, skipping')
    return tar_map

# Narrow an existing search for a regex
def search(tar_map, pattern):
    new_tar_map = {}
    for tar_path in tar_map.keys():
        tar_members = []
        for tar_name in tar_map[tar_path]:
            if re.match(pattern, tar_name) is not None:
                tar_members.append(tar_name)
        new_tar_map[tar_path] = tar_members
    return new_tar_map

# Main selector loop
def main():
    selector = Selector()
    while True:
        selector.ui()

if __name__ == '__main__':
    main()


# coding: utf-8

"""
    Investoreight Core API

    Investoreight API Documentation:  https://api.investoreight.com/api-docs/index.html  # noqa: E501

    OpenAPI spec version: 1.0.1
    Contact: info@investoreight.com
    Generated by: https://github.com/swagger-api/swagger-codegen.git
"""

import pprint
import re  # noqa: F401

import six

class StockPopularityDto(object):
    """NOTE: This class is auto generated by the swagger code generator program.

    Do not edit the class manually.
    """
    """
    Attributes:
      swagger_types (dict): The key is attribute name
                            and the value is attribute type.
      attribute_map (dict): The key is attribute name
                            and the value is json key in definition.
    """
    swagger_types = {
        'ticker': 'str',
        'score': 'float',
        'rank': 'int'
    }

    attribute_map = {
        'ticker': 'Ticker',
        'score': 'Score',
        'rank': 'Rank'
    }

    def __init__(self, ticker=None, score=None, rank=None):  # noqa: E501
        """StockPopularityDto - a model defined in Swagger"""  # noqa: E501
        self._ticker = None
        self._score = None
        self._rank = None
        self.discriminator = None
        if ticker is not None:
            self.ticker = ticker
        if score is not None:
            self.score = score
        if rank is not None:
            self.rank = rank

    @property
    def ticker(self):
        """Gets the ticker of this StockPopularityDto.  # noqa: E501

        :return: The ticker of this StockPopularityDto.  # noqa: E501
        :rtype: str
        """
        return self._ticker

    @ticker.setter
    def ticker(self, ticker):
        """Sets the ticker of this StockPopularityDto.

        :param ticker: The ticker of this StockPopularityDto.  # noqa: E501
        :type: str
        """

        self._ticker = ticker

    @property
    def score(self):
        """Gets the score of this StockPopularityDto.  # noqa: E501

        :return: The score of this StockPopularityDto.  # noqa: E501
        :rtype: float
        """
        return self._score

    @score.setter
    def score(self, score):
        """Sets the score of this StockPopularityDto.

        :param score: The score of this StockPopularityDto.  # noqa: E501
        :type: float
        """

        self._score = score

    @property
    def rank(self):
        """Gets the rank of this StockPopularityDto.  # noqa: E501

        :return: The rank of this StockPopularityDto.  # noqa: E501
        :rtype: int
        """
        return self._rank

    @rank.setter
    def rank(self, rank):
        """Sets the rank of this StockPopularityDto.

        :param rank: The rank of this StockPopularityDto.  # noqa: E501
        :type: int
        """

        self._rank = rank

    def to_dict(self):
        """Returns the model properties as a dict"""
        result = {}

        for attr, _ in six.iteritems(self.swagger_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):
                result[attr] = dict(map(
                    lambda item: (item[0], item[1].to_dict())
                    if hasattr(item[1], "to_dict") else item,
                    value.items()
                ))
            else:
                result[attr] = value
        if issubclass(StockPopularityDto, dict):
            for key, value in self.items():
                result[key] = value

        return result

    def to_str(self):
        """Returns the string representation of the model"""
        return pprint.pformat(self.to_dict())

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

    def __eq__(self, other):
        """Returns true if both objects are equal"""
        if not isinstance(other, StockPopularityDto):
            return False

        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        """Returns true if both objects are not equal"""
        return not self == other


import time

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()

driver.get("https://rahulshettyacademy.com/angularpractice/")

driver.maximize_window()

driver.find_element(By.ID,'inlineRadio1').click()

time.sleep(5)

buttons = driver.find_elements(By.NAME,'inlineRadioOptions')

# length of radio buttons
print(len(buttons))

# click on 2st radio button
buttons[1].click()

time.sleep(5)

driver.quit()


from multiprocessing import (Process, Lock, Value,
                             current_process)
from time import sleep
from random import randint

def stackingRobot(delay, lock, stack):
    for i in range(randint(15, 25)):
        with lock:
            sleep(delay)
            stack.value += 1
            print(stack.value, current_process().pid)

def orderRobot(lock, stack):
    while True:
        sleep(0.1)
        with lock:
            if stack.value > 11:
                stack.value -= 7
                print("ORDER ORDER ORDER")

if __name__ == "__main__":
    stack = Value("i", 0)
    lock = Lock()
    robot1 = Process(target=stackingRobot,
                     args=(0.2, lock, stack))
    robot2 = Process(target=stackingRobot,
                     args=(0.3, lock, stack))

    robot3 = Process(target=orderRobot, args=(lock, stack), daemon=True)

    robot1.start()
    robot2.start()
    robot3.start()

    robot1.join()
    robot2.join()

    print("end:", stack.value)


# coding=utf-8

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

class MailManager:
    def __init__(self):
        self.mail_info = {}

    def send_mail(self, mail_from, mail_to_list, subject, content):
        # 格式应该是这样的 Nickname<username@host.address>，中间无空格
        txt = MIMEText(content, 'html')  # 若content中有html编码
        # txt = MIMEText(content) # 若content中没有html编码
        txt.set_charset('utf-8')
        msg = MIMEMultipart()
        msg['Subject'] = subject
        # 格式为： Nickname<username@host.address>，中间无空格
        msg['From'] = 'Ever' + '<' + mail_from + '>'
        msg['To'] = ';'.join(mail_to_list)
        msg.attach(txt)
        try:
            s = smtplib.SMTP()
            s.connect(self.mail_info['host'])
            s.login(self.mail_info['sender'], self.mail_info['sender_password'])
            s.sendmail(self.mail_info['sender_address'], mail_to_list, msg.as_string())
            s.close()
        except:
            return False
        return True


import os
import pandas as pd
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/export/<N>', methods=['GET'])
def export_data(N):
    SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
    url_file = os.path.join(SITE_ROOT,'resources', "egrid2016_data.xlsx" )
    url ="https://www.epa.gov/sites/production/files/2018-02/egrid2016_data.xlsx"
    df_gen16 = pd.read_excel(url_file,sheet_name='GEN16',header=1, usecols='B,C,L')

    #return  df_gen16.to_html();

    return df_gen16.nlargest(N,'GENNTAN',keep='all').to_json(orient="records")

@app.route('/export/state/<filter>', methods=['GET'])
def dataByState(filter):
    SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
    url_file = os.path.join(SITE_ROOT, 'resources', "egrid2016_data.xlsx")
    url ="https://www.epa.gov/sites/production/files/2018-02/egrid2016_data.xlsx"
    columnas = 'A,B' + ',' + filter
    df_st16 = pd.read_excel(url_file,sheet_name='ST16',header=1,usecols=columnas)
    return df_st16.to_json(orient="records")

if __name__ == '__main__':
    app.run(debug=True)


"""Tests mounting from paths other than the cwd"""
import os
import shutil

import pytest

import lug
BASE_TEST_IMAGE = "alpine:3.16.2"

@lug.run(image=BASE_TEST_IMAGE, mount="./temp_test_lug_mount")
def function_with_different_mount(number):
    command = "echo hello world > /lug/output.txt"
    os.system(command)
    return number

@pytest.mark.unit_io
def test_mount_subfolder():
    number = 4
    temp_dir = "./temp_test_lug_mount"
    os.makedirs(temp_dir, exist_ok=True)
    return_value = function_with_different_mount(number)
    assert return_value == number
    with open(f"{temp_dir}/output.txt", "r") as outfile:
        assert outfile.read() == "hello world\n"  # echo piping ends with a newline
    shutil.rmtree(temp_dir)


from threading import Lock
import sublime
import sublime_plugin
# import subprocess
import os
from . import bibmanager
from . import bibphantoms
from . import texcitephantoms as texphantoms
from .utils import process_open

def plugin_loaded():
    try:
        os.makedirs(os.path.join(sublime.cache_path(), "citetex"))
    except OSError:
        pass

    HoverCite.bibman.set_cache_path(os.path.join(sublime.cache_path(), "citetex"))
    print("CiteTex loaded")

class HoverCite(sublime_plugin.ViewEventListener):
    # these cannot be per-instance since focusing on file load would not work.
    bibman = bibmanager.BibManager()
    bibphan = bibphantoms.BibPhantomManager()
    tex_phan = texphantoms.TexPhantomManager()
    current_properties = dict()
    current_path = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.errors = dict()

        self._user_settings = None
        self._default_settings = None
        HoverCite.bibman.set_style("alpha")

    def use_settings(self):
        self._user_settings = sublime.load_settings("CiteTeX-user.sublime-settings")
        self._default_settings = sublime.load_settings("CiteTeX-default.sublime-settings")

    def on_hover(self, point, hover_zone):
        scopes = self.view.scope_name(point).split()
        if 'text.tex.latex' in scopes and 'constant.other.citation.latex' in scopes:
            # print(self.view.substr(self.view.expand_by_class(point, sublime.CLASS_WORD_END | sublime.CLASS_WORD_START, '\{\},')))
            cite_key = self.view.substr(self.view.expand_by_class(point, sublime.CLASS_WORD_END | sublime.CLASS_WORD_START, '\{\},'))
            image_path, HoverCite.current_properties = HoverCite.bibman.serve_entry(cite_key, render=self._user_settings.get("show_rendered_citation", self._default_settings.get("show_rendered_citation")))
            info_content = ""
            if "ref" in HoverCite.current_properties:
                info_content += "
[{}]".format(HoverCite.current_properties["ref"])
                if not image_path:
                    info_content += """ - {} """.format(HoverCite.current_properties['author'])

                info_content += """
\n"""

            if image_path:
                info_content += """

\n""".format(image_path)
            else:
                info_content += """
 {}, {}
\n""".format(HoverCite.current_properties['title'], HoverCite.current_properties['year'])
                info_content += """
 {} - {}
\n""".format(HoverCite.current_properties.get('publisher',""), HoverCite.current_properties.get('address', ''))

            info_content += """BibTeX entry"""

            if "url" in HoverCite.current_properties:
                info_content += """ | View URL"""

            if "doi" in HoverCite.current_properties:
                info_content += """ | View DOI"""

            if "resource" in HoverCite.current_properties:
                info_content += """ | View Resource ({})""".format(HoverCite.current_properties['resource_type'])

            self.view.show_popup(info_content, sublime.HIDE_ON_MOUSE_MOVE_AWAY, point, max_width=800, on_navigate=self.handle_popup)

    def handle_popup(self, command):
        self.use_settings()
        webbrowser_cmd = self._user_settings.get("webbrowser", self._default_settings.get("webbrowser"))
        if command == "gotobib":
            bib_view = self.view.window().open_file(HoverCite.current_properties["bibfile"])
            marker = next(filter(lambda x: x[1] == HoverCite.current_properties['key'], bib_view.symbols()), None)[0]
            bib_view.show_at_center(marker)

        elif command == "viewurl":
            url = HoverCite.current_properties["url"]
            process_open("{} {}".format(webbrowser_cmd, url))

        elif command == "viewdoi":
            url = "http://dx.doi.org/" + HoverCite.current_properties["doi"]
            process_open("{} {}".format(webbrowser_cmd, url))

        elif command == "viewres":
            settings_property = "open_resource_{}".format(HoverCite.current_properties['resource_type'].lower())
            resource_dir = self.view.window().project_data() or {}
            if "settings" in resource_dir and "resource_root" in resource_dir['settings']:
                filepath = os.path.join(resource_dir['settings']['resource_root'], HoverCite.current_properties['resource'])
                open_command = [self._user_settings.get(settings_property, self._default_settings.get(settings_property)), filepath]
                print(open_command)
                process_open(open_command)
                # if sublime.platform() == 'windows':
                #     subprocess.Popen(open_command, shell=True)
                # else:
                #     subprocess.Popen(" ".join(open_command), shell=True)

    def on_activated_async(self):
        file_name = self.view.file_name()
        path_of_file = os.path.dirname(file_name) if file_name else None
        scope = self.view.scope_name(0)
        if self.view.file_name() is not None and ("text.tex.latex" in scope.split() or self.view.file_name().endswith('.bib')):
            self.use_settings() # since we use settings, reload them
            if path_of_file != HoverCite.current_path: # if working directory has changed, reload bibfile and error phantoms
                self.view.window().status_message("Load references...")
                HoverCite.current_path = path_of_file
                # adjust rendering style to project
                project_data = self.view.window().project_data()
                project_settings = project_data['settings'] if(project_data is not None and "settings" in project_data) else {}

                if "bibstyle" in project_settings:
                    HoverCite.bibman.set_style(project_settings['bibstyle'])

                self.errors = HoverCite.bibman.refresh_all_entries(self.view.window().project_data(), self.view.file_name())

                if self.view.file_name().endswith('.bib'):
                    if self._user_settings.get("bib_errors", self._default_settings.get("bib_errors")):
                        HoverCite.bibphan.update_phantoms(self.view, self.errors[self.view.file_name()], self.view.symbols())
                    else:
                        HoverCite.bibphan.clear_phantoms()
                self.view.window().status_message("Cite references loaded.")

            if self.view.file_name().endswith('.tex'): # always refresh title phantoms in tex files
                self.refresh_tex_phantoms()

    def on_post_save_async(self):
        self.use_settings()
        if self.view.file_name().endswith('.bib'):
            if self._user_settings.get("bib_errors", self._default_settings.get("bib_errors")):
                self.errors = HoverCite.bibman.refresh_all_entries(self.view.window().project_data(), self.view.file_name())
                HoverCite.bibphan.update_phantoms(self.view, self.errors[self.view.file_name()], self.view.symbols())

        elif self.view.file_name().endswith('.tex'):
            if self._user_settings.get("tex_phantoms", self._default_settings.get("tex_phantoms")):
                self.refresh_tex_phantoms()

    def refresh_tex_phantoms(self):
        if self._user_settings.get("tex_phantoms", self._default_settings.get("tex_phantoms")):
            HoverCite.tex_phan.update_phantoms(self.view, HoverCite.bibman.bib_entries)
        else:
            HoverCite.tex_phan.clear_phantoms()

    def on_post_text_command(self, command_name, args):
        # used if settings change
        if command_name == "citetex_toggle_tex_phantoms":
            self.use_settings()
            self.refresh_tex_phantoms()


from multiprocessing import Process, Pipe
from multiprocessing.connection import Connection
import yaml

from output_string_template.template import StringTemplate
from settings.settings import Settings
from BondOrderProcessing.bond_order_processing\
    import calculations_for_atoms_lists, input_data
from BondOrderProcessing.bond_order_processing.calculations\
    import PairOfAtoms
from BondOrderProcessing.bond_order_processing.input_data\
    import CoordinatesOfAtoms, MayerBondOrders

from data_process_utils import check_atoms_symbols_in_loaded_data,\
    remove_wrong_atoms

class AppBackEnd:
    """App to run by cli interface.
    """
    settings_file_path: str
    input_file_path: str
    output_file_path: str
    _output_string: str
    _settings: Settings

    def __init__(self, progress_bar: bool, settings=None):
        self._settings = settings
        self.progress_bar = progress_bar
        self._output_string = None

    def load_data(self, input_file_path: str,
                  input_data_cpmd: input_data.InputDataFromCPMD
                  = input_data.InputDataFromCPMD()) -> None:

        input_data_cpmd.load_input_data(input_file_path,
                                        input_data.LoadedData.UnitCell,
                                        input_data.LoadedData.MayerBondOrders,
                                        input_data.LoadedData.CoordinatesOfAtoms)

        unit_cell = input_data_cpmd.return_data(input_data.LoadedData.UnitCell)

        self.mayer_bond_orders = input_data_cpmd.return_data(
            input_data.LoadedData.MayerBondOrders)

        self.coordinates_of_atoms = input_data_cpmd.return_data(
            input_data.LoadedData.CoordinatesOfAtoms)

        self.coordinates_of_atoms.convert_stored_coordinates_to_angstroms()
        unit_cell.convert_cell_data_to_angstroms()
        self.coordinates_of_atoms.add_unit_cell(unit_cell)

    def load_settings(self, settings_file_path: str) -> None:
        with open(settings_file_path, 'r') as file:
            yaml_data = file.read()

        data = yaml.safe_load(yaml_data)
        self._settings = Settings(data)

    @property
    def settings(self):
        return self._settings

    @ settings.setter
    def settings(self, settings: Settings):
        self._settings = settings

    def calculate_only_histograms(self, queue: Connection) -> None:

        if queue is not None:
            queue.put((True, 0, (None, None)))

        wrong_atoms_names = check_atoms_symbols_in_loaded_data(self.mayer_bond_orders,
                                                               self._settings.pairs_atoms_list)

        pairs_atoms_list = remove_wrong_atoms(wrong_atoms_names,
                                              self._settings.pairs_atoms_list)

        # Calculate and generate output data:
        output_string = StringTemplate.get_report_header()
        output_string += StringTemplate.get_wrong_atoms_list(
            wrong_atoms_names)

        try:
            self.settings.histogram['calc'] = True
            output_string = StringTemplate.get_histogram_header()
            output = calculations_for_atoms_lists\
                .HistogramsFromPairOfAtoms.calculate(pairs_atoms_list,
                                                     self.mayer_bond_orders,
                                                     self.settings.histogram['nr_bars'])

            output_string += output.to_string()
            list_of_mbos: list[tuple[str, list]] = []
            for pair in pairs_atoms_list:
                mbos = self.mayer_bond_orders\
                    .get_mayer_bond_orders_list_between_two_atoms(
                        pair.atom_1, pair.atom_2)
                if pair.MBO_max == 'INF':
                    mbos = [mbo for mbo in mbos if mbo > pair.MBO_min]
                else:
                    mbos = [mbo for mbo in mbos if mbo > pair.MBO_min
                            and mbo < pair.MBO_max]
                list_of_mbos.append((pair.id, mbos))
        except:
            output_string = ""
            list_of_mbos = 'error'

        if queue is not None:
            queue.put((True, 100, (list_of_mbos,
                                   output_string)))
        queue.close()

    def perform_calculations(self, queue: Connection | None = None)\
            -> None:
        """Perform calculations."""
        # pipeline = [done=True | False, step_done = True | False]
        if self.progress_bar and (queue is None):
            raise Exception("To progress bar pipeline is required")
        elif queue is not None:
            queue.put((False, 0, None))

        wrong_atoms_names = check_atoms_symbols_in_loaded_data(self.mayer_bond_orders,
                                                               self._settings.pairs_atoms_list)

        pairs_atoms_list = remove_wrong_atoms(wrong_atoms_names,
                                              self._settings.pairs_atoms_list)

        # Calculate and generate output data:
        output_string = StringTemplate.get_report_header()
        output_string += StringTemplate.get_wrong_atoms_list(
            wrong_atoms_names)

        conn_prent_1, conn_child_1 = Pipe()
        conn_prent_2, conn_child_2 = Pipe()

        if queue is not None:
            queue.put((False, 1, None))

        p_1 = Process(target=self._thread_1, args=(
            conn_child_1, self._settings, pairs_atoms_list,
            self.mayer_bond_orders, queue))

        p_2 = Process(target=self._thread_2, args=(
            conn_child_2, self._settings, pairs_atoms_list,
            self.mayer_bond_orders, self.coordinates_of_atoms, queue))

        p_1.start()
        p_2.start()
        output_string += conn_prent_1.recv()
        output_string += conn_prent_2.recv()
        p_1.join()
        p_2.join()

        self._output_string = output_string
        if queue is not None:
            queue.put((True, 100, output_string))
            queue.close()

    @ staticmethod
    def _thread_1(conn: Connection, settings: Settings,
                  pairs_atoms_list: list[PairOfAtoms],
                  mayer_bond_orders: MayerBondOrders,
                  queue: Connection | None) -> None:
        if settings.histogram['calc'] is True:
            output_string = StringTemplate.get_histogram_header()
            output_string += calculations_for_atoms_lists\
                .HistogramsFromPairOfAtoms.calculate(pairs_atoms_list,
                                                     mayer_bond_orders,
                                                     settings.histogram['nr_bars'])\
                .to_string()
        else:
            output_string = ""
        if queue is not None:
            queue.put((False, 11, None))

        if settings.calculations['q_i']['calc'] is True:
            output_string += StringTemplate.get_qi_units_header()
            pairs_atoms_list_tem = [item for item in pairs_atoms_list
                                    if item.id == settings.calculations['q_i']['bond_id']]

            output_string += calculations_for_atoms_lists\
                .QiUnitsFromPairOfAtoms.calculate(pairs_atoms_list_tem,
                                                  mayer_bond_orders)\
                . calculate_statistics().to_string()

        if queue is not None:
            queue.put((False, 12, None))

        if settings.calculations['connections'] is True:
            output_string += StringTemplate.get_connections_header()
            output_string += calculations_for_atoms_lists\
                .ConnectionsFromPairOfAtoms.calculate(pairs_atoms_list,
                                                      mayer_bond_orders)\
                .to_string()

        if queue is not None:
            queue.put((False, 13, None))
        conn.send(output_string)
        conn.close()

    @ staticmethod
    def _thread_2(conn: Connection, settings: Settings,
                  pairs_atoms_list: list[PairOfAtoms],
                  mayer_bond_orders: MayerBondOrders,
                  coordinates_of_atoms: CoordinatesOfAtoms,
                  queue: Connection | None) -> None:

        if settings.calculations['bond_length'] is True:
            output_string = StringTemplate.get_bond_length()
            output_string += calculations_for_atoms_lists\
                .BondLengthFromPairOfAtoms.calculate(pairs_atoms_list,
                                                     mayer_bond_orders,
                                                     coordinates_of_atoms)\
                .to_string()
        else:
            output_string = ""

        if queue is not None:
            queue.put((False, 21, None))

        if settings.calculations['cn'] is True:
            output_string += StringTemplate.get_coordination_numbers_header()
            output_string += calculations_for_atoms_lists\
                .CoordinationNumbersFromPairOfAtoms.calculate(pairs_atoms_list,
                                                              mayer_bond_orders)\
                .calculate_statistics().to_string()

        if queue is not None:
            queue.put((False, 22, None))

        if settings.calculations['covalence'] is True:
            output_string += StringTemplate.get_covalence_header()
            output_string += calculations_for_atoms_lists\
                .CovalenceFromPairOfAtoms.calculate(pairs_atoms_list,
                                                    mayer_bond_orders)\
                .to_string()

        if queue is not None:
            queue.put((False, 23, None))
        conn.send(output_string)
        conn.close()

    def get_output_string(self) -> str:
        if self._output_string is not None:
            return self._output_string
        else:
            raise Exception('No preformed calculations!!!!')

    def save_output(self, output_file_path: str):
        """Save output data to file."""
        if self._output_string is not None:
            with open(output_file_path, 'w', encoding="utf-8") as file:
                file.write(self._output_string)
        else:
            raise Exception('No preformed calculations!!!!')

    def save_settings(self, path: str):
        """Save settings object to yaml file."""
        if self._settings is None:
            raise Exception("No settings")
        else:
            self._settings.save_data(path)


from setuptools import setup, find_packages

with open('requirements.txt') as f:
    REQUIREMENTS = f.read().strip().split('\n')

with open('VERSION') as f:
    VERSION = f.read().strip()

with open('README.rst') as f:
    README = f.read()

setup(
    name='rootwater',
    version=VERSION,
    license='MIT',
    description='Python package to estimate root water uptake (RWU) from rhizosphere soil moisture dynamics and some tools to handle sap flow measurements',
    long_description=README,
    url="https://github.com/cojacoo/rootwater",
    author='Conrad Jackisch',
    author_email='conrad.jackisch@tbt.tu-freiberg.de',
    install_requires=REQUIREMENTS,
    test_require=['nose'],
    test_suite='nose.collector',
    packages=find_packages(),
    include_package_data=True,
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    zip_safe=False
)


import Chemins
from Utils import UTILS_Adaptations
from Utils.UTILS_Traduction import _
import wx
import GestionDB
from Utils import UTILS_Interface
from Ctrl.CTRL_ObjectListView import FastObjectListView, ColumnDefn, Filter, CTRL_Outils
from Utils import UTILS_Utilisateurs

class Track(object):
    def __init__(self, donnees):
        self.IDcontact = donnees[0]
        self.nom = donnees[1]
        self.prenom = donnees[2]
        self.rue_resid = donnees[3]
        self.cp_resid = donnees[4]
        self.ville_resid = donnees[5]
        self.tel_domicile = donnees[6]
        self.tel_mobile = donnees[7]
        self.mail = donnees[8]

class ListView(FastObjectListView):
    def __init__(self, *args, **kwds):
        # Récupération des paramètres perso
        self.mode_coche = kwds.pop("mode_coche", False)
        self.selectionID = None
        self.selectionTrack = None
        self.criteres = ""
        self.itemSelected = False
        self.popupIndex = -1
        self.listeFiltres = []
        # Initialisation du listCtrl
        self.nom_fichier_liste = __file__
        FastObjectListView.__init__(self, *args, **kwds)
        # Binds perso
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
        self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)

    def OnItemActivated(self,event):
        self.Modifier(None)

    def OnCheck(self, track=None):
        try :
            self.GetParent().OnCheck(track)
        except :
            pass

    def SetIDcoches(self, listeID=[]):
        for track in self.donnees :
            if track.IDcontact in listeID:
                self.Check(track)
                self.RefreshObject(track)

    def InitModel(self):
        self.donnees = self.GetTracks()

    def GetTracks(self):
        """ Récupération des données """
        listeID = None
        db = GestionDB.DB()
        req = """SELECT IDcontact, nom, prenom, rue_resid, cp_resid, ville_resid, tel_domicile, tel_mobile, mail
        FROM contacts ORDER BY nom; """
        db.ExecuterReq(req)
        listeDonnees = db.ResultatReq()
        db.Close()

        listeListeView = []
        for item in listeDonnees :
            valide = True
            if listeID != None :
                if item[0] not in listeID :
                    valide = False
            if valide == True :
                track = Track(item)
                listeListeView.append(track)
                if self.selectionID == item[0] :
                    self.selectionTrack = track
        return listeListeView

    def InitObjectListView(self):
        # Couleur en alternance des lignes
        self.oddRowsBackColor = UTILS_Interface.GetValeur("couleur_tres_claire", wx.Colour(240, 251, 237))
        self.evenRowsBackColor = wx.Colour(255, 255, 255)
        self.useExpansionColumn = True

        liste_Colonnes = [
            ColumnDefn(_(u"ID"), "left", 0, "IDcontact", typeDonnee="entier"),
            ColumnDefn(_(u"Nom"), 'left', 160, "nom", typeDonnee="texte"),
            ColumnDefn(_(u"Prénom"), 'left', 160, "prenom", typeDonnee="texte"),
            ColumnDefn(_(u"Rue"), "left", 140, "rue_resid", typeDonnee="texte"),
            ColumnDefn(_(u"C.P."), "left", 45, "cp_resid", typeDonnee="texte"),
            ColumnDefn(_(u"Ville"), "left", 110, "ville_resid", typeDonnee="texte"),
            ColumnDefn(_(u"Fixe"), "left", 100, "tel_domicile", typeDonnee="texte"),
            ColumnDefn(_(u"Portable"), "left", 100, "tel_mobile", typeDonnee="texte"),
            ColumnDefn(_(u"Email"), "left", 200, "mail", typeDonnee="texte"),
            ]

        self.SetColumns(liste_Colonnes)
        if self.mode_coche:
            self.CreateCheckStateColumn(0)
        self.SetEmptyListMsg(_(u"Aucun contact"))
        self.SetEmptyListMsgFont(wx.FFont(11, wx.DEFAULT, False, "Tekton"))
        self.SetSortColumn(self.columns[1])
        self.SetObjects(self.donnees)

    def MAJ(self, ID=None):
        if ID != None :
            self.selectionID = ID
            self.selectionTrack = None
        else:
            self.selectionID = None
            self.selectionTrack = None
        self.InitModel()
        self.InitObjectListView()
        # Sélection d'un item
        if self.selectionTrack != None :
            self.SelectObject(self.selectionTrack, deselectOthers=True, ensureVisible=True)
        self.selectionID = None
        self.selectionTrack = None

    def Selection(self):
        return self.GetSelectedObjects()

    def OnContextMenu(self, event):
        """Ouverture du menu contextuel """
        if len(self.Selection()) == 0:
            noSelection = True
        else:
            noSelection = False
            ID = self.Selection()[0].IDcontact

        # Création du menu contextuel
        menuPop = UTILS_Adaptations.Menu()

        # Item Modifier
        item = wx.MenuItem(menuPop, 10, _(u"Ajouter"))
        bmp = wx.Bitmap(Chemins.GetStaticPath("Images/16x16/Ajouter.png"), wx.BITMAP_TYPE_PNG)
        item.SetBitmap(bmp)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.Ajouter, id=10)

        menuPop.AppendSeparator()

        # Item Ajouter
        item = wx.MenuItem(menuPop, 20, _(u"Modifier"))
        bmp = wx.Bitmap(Chemins.GetStaticPath("Images/16x16/Modifier.png"), wx.BITMAP_TYPE_PNG)
        item.SetBitmap(bmp)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.Modifier, id=20)
        if noSelection == True : item.Enable(False)

        # Item Supprimer
        item = wx.MenuItem(menuPop, 30, _(u"Supprimer"))
        bmp = wx.Bitmap(Chemins.GetStaticPath("Images/16x16/Supprimer.png"), wx.BITMAP_TYPE_PNG)
        item.SetBitmap(bmp)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.Supprimer, id=30)
        if noSelection == True : item.Enable(False)

        menuPop.AppendSeparator()

        # Item Apercu avant impression
        item = wx.MenuItem(menuPop, 40, _(u"Aperçu avant impression"))
        bmp = wx.Bitmap(Chemins.GetStaticPath("Images/16x16/Apercu.png"), wx.BITMAP_TYPE_PNG)
        item.SetBitmap(bmp)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.Apercu, id=40)

        # Item Imprimer
        item = wx.MenuItem(menuPop, 50, _(u"Imprimer"))
        bmp = wx.Bitmap(Chemins.GetStaticPath("Images/16x16/Imprimante.png"), wx.BITMAP_TYPE_PNG)
        item.SetBitmap(bmp)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.Imprimer, id=50)

        self.PopupMenu(menuPop)
        menuPop.Destroy()

    def Apercu(self, event):
        from Utils import UTILS_Printer
        prt = UTILS_Printer.ObjectListViewPrinter(self, titre=_(u"Liste des contacts"), format="A", orientation=wx.PORTRAIT)
        prt.Preview()

    def Imprimer(self, event):
        from Utils import UTILS_Printer
        prt = UTILS_Printer.ObjectListViewPrinter(self, titre=_(u"Liste des contacts"), format="A", orientation=wx.PORTRAIT)
        prt.Print()

    def Ajouter(self, event):
        if UTILS_Utilisateurs.VerificationDroitsUtilisateurActuel("parametrage_contacts", "creer") == False : return
        from Dlg import DLG_Saisie_contact
        dlg = DLG_Saisie_contact.Dialog(self)
        if dlg.ShowModal() == wx.ID_OK:
            IDcontact = dlg.GetIDcontact()
            self.MAJ(IDcontact)
        dlg.Destroy()

    def Modifier(self, event):
        if UTILS_Utilisateurs.VerificationDroitsUtilisateurActuel("parametrage_contacts", "modifier") == False : return
        if len(self.Selection()) == 0 :
            dlg = wx.MessageDialog(self, _(u"Vous n'avez sélectionné aucun contact à modifier dans la liste"), _(u"Erreur de saisie"), wx.OK | wx.ICON_EXCLAMATION)
            dlg.ShowModal()
            dlg.Destroy()
            return
        IDcontact = self.Selection()[0].IDcontact
        from Dlg import DLG_Saisie_contact
        dlg = DLG_Saisie_contact.Dialog(self, IDcontact=IDcontact)
        if dlg.ShowModal() == wx.ID_OK:
            self.MAJ(IDcontact)
        dlg.Destroy()

    def Supprimer(self, event):
        if UTILS_Utilisateurs.VerificationDroitsUtilisateurActuel("parametrage_contacts", "supprimer") == False : return
        if len(self.Selection()) == 0 :
            dlg = wx.MessageDialog(self, _(u"Vous n'avez sélectionné aucun contact dans la liste"), _(u"Erreur de saisie"), wx.OK | wx.ICON_EXCLAMATION)
            dlg.ShowModal()
            dlg.Destroy()
            return
        IDcontact = self.Selection()[0].IDcontact

        # Confirmation de suppression
        dlg = wx.MessageDialog(self, _(u"Souhaitez-vous vraiment supprimer ce contact ?"), _(u"Suppression"), wx.YES_NO|wx.NO_DEFAULT|wx.CANCEL|wx.ICON_INFORMATION)
        if dlg.ShowModal() == wx.ID_YES :
            DB = GestionDB.DB()
            DB.ReqDEL("contacts", "IDcontact", IDcontact)
            DB.Close()
            self.MAJ()
        dlg.Destroy()

# -------------------------------------------------------------------------------------------------------------------------------------------

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
        panel = wx.Panel(self, -1, name="test1")
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(panel, 1, wx.ALL|wx.EXPAND)
        self.SetSizer(sizer_1)
        self.myOlv = ListView(panel, id=-1, name="OL_test", style=wx.LC_REPORT|wx.SUNKEN_BORDER|wx.LC_SINGLE_SEL|wx.LC_HRULES|wx.LC_VRULES)
        self.myOlv.MAJ()
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        sizer_2.Add(self.myOlv, 1, wx.ALL|wx.EXPAND, 4)
        panel.SetSizer(sizer_2)
        self.Layout()

if __name__ == '__main__':
    app = wx.App(0)
    #wx.InitAllImageHandlers()
    frame_1 = MyFrame(None, -1, "OL TEST")
    app.SetTopWindow(frame_1)
    frame_1.Show()
    app.MainLoop()

{'UTILS_Printer': 'Utils.UTILS_Printer', 'DLG_Saisie_contact': 'Dlg.DLG_Saisie_contact'}


import pandas as pd
from sklearn import preprocessing

from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn import metrics
import numpy as np

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras import regularizers

def print_result(model, x_val, y_val):
    y_pred_val = model.predict(x_val)
    print('---Test---')
    print('MAE: ', metrics.mean_absolute_error(y_val, y_pred_val))
    print('MSE: ', metrics.mean_squared_error(y_val, y_pred_val))
    print('R2: ', metrics.r2_score(y_val, y_pred_val))

df = pd.read_csv('./data/selected/data_selected_2.csv', index_col=0)

dataset = df.loc[:, df.columns != 'price']

X = dataset.loc[:, dataset.columns != 'city'].values

Y = df.loc[:, df.columns == 'price'].values

min_max_scaler = preprocessing.MinMaxScaler()
X_scale = min_max_scaler.fit_transform(X)

X_train, X_val_and_test, Y_train, Y_val_and_test = train_test_split(X_scale, Y, test_size=0.3)
X_val, X_test, Y_val, Y_test = train_test_split(X_val_and_test, Y_val_and_test, test_size=0.5)

model = Sequential([
    Dense(100, activation='relu', input_shape=(159,)),
    Dense(100, activation='relu'),
    Dense(50, activation='relu'),
    Dense(10, activation='relu'),
    Dense(1, activation='linear'),
])

model.compile(optimizer='adam',
              loss='mean_squared_error',
              metrics=['accuracy'])

hist = model.fit(X_train, Y_train,
          batch_size=100, epochs=400,
          validation_data=(X_val, Y_val))

print(model.summary())

plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Val'], loc='upper right')
plt.show()

print_result(model, X_train, Y_train)
print_result(model, X_val, Y_val)


import datetime
import json
import logging
import os
import uuid
from dataclasses import asdict, dataclass, field
from multiprocessing import Pool, cpu_count
from pathlib import Path
from typing import List, Optional, Union

import cv2
import exifread
import numpy as np
import pandas as pd
from dacite import from_dict
from PIL import Image as PILImage
from tqdm import tqdm

SCHEMA_VERSION = "1.0"

log = logging.getLogger(__name__)

@dataclass
class BoxCoordinates:
    top_left: np.ndarray
    top_right: np.ndarray
    bottom_left: np.ndarray
    bottom_right: np.ndarray
    is_scaleable: bool = field(init=True, default=True)

    def __bool__(self):
        # The bool function is to check if the coordinates are populated or not
        return all(
            [
                len(coord) == 2
                for coord in [
                    self.top_left,
                    self.top_right,
                    self.bottom_left,
                    self.bottom_right,
                ]
            ]
        )

    @property
    def config(self):
        if isinstance(self.top_left, np.ndarray):
            _top_left = self.top_left.tolist()
            _top_right = self.top_right.tolist()
            _bottom_left = self.bottom_left.tolist()
            _bottom_right = self.bottom_right.tolist()
        else:
            _top_left = self.top_left
            _top_right = self.top_right
            _bottom_left = self.bottom_left
            _bottom_right = self.bottom_right

        _config = {
            "top_left": _top_left,
            "top_right": _top_right,
            "bottom_left": _bottom_left,
            "bottom_right": _bottom_right,
        }

        return _config

    def set_scale(self, new_scale: np.ndarray):
        if not self.is_scaleable:
            raise ValueError("is_scalable set to False, coordinates cannot be scaled.")
        self.scale = new_scale

        self.top_left = self.top_left * self.scale
        self.top_right = self.top_right * self.scale
        self.bottom_left = self.bottom_left * self.scale
        self.bottom_right = self.bottom_right * self.scale

    def copy(self):
        return self.__class__(
            top_left=self.top_left.copy(),
            top_right=self.top_right.copy(),
            bottom_left=self.bottom_left.copy(),
            bottom_right=self.bottom_right.copy(),
            is_scaleable=self.is_scaleable,
        )

    def __getitem__(self, key):
        if not hasattr(self, key):
            raise AttributeError(f"{self.__class__.__name__} has not attribute {key}")

        return getattr(self, key)

def init_empty():
    empty_array = np.array([])
    # Initialize with an empty array
    return BoxCoordinates(empty_array, empty_array, empty_array, empty_array)

@dataclass
class BBox:
    bbox_id: str
    image_id: str
    cls: str
    instance_rgb_id: List[int] = field(default=None)
    local_coordinates: BoxCoordinates = field(init=True, default_factory=init_empty)
    global_coordinates: BoxCoordinates = field(init=True, default_factory=init_empty)
    is_normalized: bool = field(init=True, default=True)
    local_centroid: np.ndarray = field(init=False, default_factory=lambda: np.array([]))
    global_centroid: np.ndarray = field(
        init=False, default_factory=lambda: np.array([])
    )
    is_primary: bool = field(init=False, default=False)
    norm_local_coordinates: BoxCoordinates = field(
        init=False, default_factory=init_empty
    )

    @property
    def local_area(self):
        if self.local_coordinates:
            local_area = self.get_area(self.local_coordinates)
        else:
            raise AttributeError(
                "Local coordinates have to be defined for local area to be calculated."
            )
        return local_area

    @property
    def norm_local_area(self):
        if self.norm_local_coordinates:
            norm_local_area = self.get_area(self.norm_local_coordinates)
        else:
            raise AttributeError(
                "Normalized local coordinates have to be defined for local area to be calculated."
            )
        return norm_local_area

    @property
    def global_area(self):
        if self.global_coordinates:
            global_area = self.get_area(self.global_coordinates)
        else:
            raise AttributeError(
                "Global coordinates have to be defined for the global area to be calculated."
            )
        return global_area

    @property
    def config(self):
        _config = {
            "bbox_id": self.bbox_id,
            "image_id": self.image_id,
            "local_centroid": list(
                self.norm_local_centroid
            ),  # Always use normalized coordinates
            "local_coordinates": self.norm_local_coordinates.config,  # Always use normalized coordinates
            "global_centroid": list(self.global_centroid),
            "global_coordinates": self.global_coordinates.config,
            "is_primary": self.is_primary,
            "cls": self.cls,
            "instance_rgb_id": self.instance_rgb_id,
            "overlapping_bbox_ids": [box.bbox_id for box in self._overlapping_bboxes],
            "num_overlapping_bboxes": len(self._overlapping_bboxes),
        }
        return _config

    def __post_init__(self):
        if self.local_coordinates:
            self.set_local_centroid()
            if self.is_normalized:
                self.norm_local_coordinates = self.local_coordinates.copy()
                self.norm_local_coordinates.is_scaleable = False
                self.set_norm_local_centroid()

        if self.global_coordinates:
            self.set_global_centroid()

        # A list of all overlapping bounding boxes
        self._overlapping_bboxes: List[BBox] = []

    def add_box(self, box):
        """Adds a box as an overlapping box

        Args:
            box (BBox): BBox to add as an overlapping box
        """
        self._overlapping_bboxes.append(box)

    def get_centroid(self, coords: BoxCoordinates) -> np.ndarray:
        """Get the centroid of the bounding box based on the coordinates passed

        Args:
            coords (BoxCoordinates): Bounding box coordinates

        Returns:
            np.ndarray: Centroid
        """
        centroid_x = (coords.bottom_right[0] + coords.bottom_left[0]) / 2.0
        centroid_y = (coords.bottom_left[1] + coords.top_left[1]) / 2.0
        centroid = np.array([centroid_x, centroid_y])

        return centroid

    def get_area(self, coordinates: BoxCoordinates) -> float:
        height = coordinates.bottom_left[1] - coordinates.top_left[1]
        width = coordinates.bottom_right[0] - coordinates.bottom_left[0]
        return float(height * width)

    def set_local_centroid(self):
        self.local_centroid = self.get_centroid(self.local_coordinates)

    def set_norm_local_centroid(self):
        self.norm_local_centroid = self.get_centroid(self.norm_local_coordinates)

    def set_global_centroid(self):
        self.global_centroid = self.get_centroid(self.global_coordinates)

    def set_local_scale(self, new_scale):
        self.local_coordinates.set_scale(new_scale)
        self.set_local_centroid()

    def update_global_coordinates(self, global_coordinates: BoxCoordinates):
        """Update the global coordinates of the bounding box

        Args:
            global_coordinates (BoxCoordinates): Global bounding box coordinates
        """
        assert not self.global_coordinates

        self.global_coordinates = global_coordinates
        self.global_centroid = self.get_centroid(self.global_coordinates)

    def bb_iou(self, comparison_box, type="global"):
        """Function to calculate the IoU of this bounding box
           with another bbox 'comparison_box'.

        Args:
            comparison_box (BBox): Another bounding box
            type (str, optional): IoU in global or local coordinates. Defaults to "global".

        Returns:
            float: IoU of the two boxes
        """
        if type == "global":
            _boxA = self.global_coordinates
            _boxB = comparison_box.global_coordinates
        elif type == "local":
            _boxA = self.local_coordinates
            _boxB = comparison_box.local_coordinates
        else:
            raise ValueError(f"Type {type} not supported.")

        boxA = [
            _boxA.top_left[0],
            -_boxA.top_left[1],
            _boxA.bottom_right[0],
            -_boxA.bottom_right[1],
        ]
        boxB = [
            _boxB.top_left[0],
            -_boxB.top_left[1],
            _boxB.bottom_right[0],
            -_boxB.bottom_right[1],
        ]

        # determine the (x, y)-coordinates of the intersection rectangle
        xA = max(boxA[0], boxB[0])
        yA = max(boxA[1], boxB[1])
        xB = min(boxA[2], boxB[2])
        yB = min(boxA[3], boxB[3])

        # compute the area of intersection rectanglee
        interArea = abs(max((xB - xA, 0)) * max((yB - yA), 0))
        if interArea == 0:
            return 0
        # compute the area of both the prediction and ground-truth
        # rectangles
        boxAArea = abs((boxA[2] - boxA[0]) * (boxA[3] - boxA[1]))
        boxBArea = abs((boxB[2] - boxB[0]) * (boxB[3] - boxB[1]))

        # compute the intersection over union by taking the intersection
        # area and dividing it by the sum of prediction + ground-truth
        # areas - the interesection area
        iou = interArea / float(boxAArea + boxBArea - interArea)

        # return the intersection over union value
        return iou

# Batch Metadata ---------------------------------------------------------------------------

@dataclass
class BatchMetadata:
    """Batch metadata class for yaml loader"""

    blob_home: str
    data_root: str
    batch_id: str
    upload_datetime: str
    image_list: List
    schema_version: str = SCHEMA_VERSION

# Image dataclasses ----------------------------------------------------------

@dataclass
class ImageMetadata:
    ImageWidth: int
    ImageLength: int
    BitsPerSample: int
    Compression: int
    PhotometricInterpretation: int
    Make: str
    Model: str
    Orientation: int
    SamplesPerPixel: int
    XResolution: str
    YResolution: str
    PlanarConfiguration: int
    ResolutionUnit: int
    Software: str
    DateTime: str
    Rating: int
    ExifOffset: int
    ExposureTime: str
    FNumber: str
    ExposureProgram: int
    ISOSpeedRatings: int
    RecommendedExposureIndex: int
    ExifVersion: list
    DateTimeOriginal: str
    DateTimeDigitized: str
    BrightnessValue: str
    ExposureBiasValue: str
    MaxApertureValue: str
    MeteringMode: int
    LightSource: int
    Flash: int
    FocalLength: str
    FileSource: int
    SceneType: int
    CustomRendered: int
    ExposureMode: int
    WhiteBalance: int
    DigitalZoomRatio: str
    FocalLengthIn35mmFilm: int
    SceneCaptureType: int
    Contrast: int
    Saturation: int
    Sharpness: int
    LensSpecification: list
    LensModel: str
    BodySerialNumber: Optional[str] = None
    MakerNote: Optional[str] = None
    ImageDescription: Optional[str] = None
    UserComment: Optional[str] = None
    ApplicationNotes: Optional[str] = None
    Tag: Optional[int] = None
    SubIFDs: Optional[int] = None

@dataclass
class CameraInfo:
    """ """

    camera_location: np.ndarray
    pixel_width: float
    pixel_height: float
    yaw: float
    pitch: float
    roll: float
    focal_length: float
    fov: BoxCoordinates.config = None

@dataclass
class Box:
    bbox_id: str
    image_id: str
    local_centroid: list
    local_coordinates: BoxCoordinates
    global_centroid: list
    global_coordinates: BoxCoordinates
    cls: str
    is_primary: bool
    instance_rgb_id: List[int] = field(default=None)
    overlapping_bbox_ids: List[BBox] = field(init=False, default_factory=lambda: [])

    def assign_species(self, species):
        self.cls = species

@dataclass
class BBoxFOV:
    top_left: list
    top_right: list
    bottom_left: list
    bottom_right: list

@dataclass
class BBoxMetadata:
    data_root: str
    batch_id: str
    image_path: str
    image_id: str
    width: int
    height: int
    camera_info: CameraInfo
    exif_meta: ImageMetadata
    bboxes: List[Box]

@dataclass
class Image:
    """Parent class for RemapImage and ImageData."""

    blob_home: str
    data_root: str
    batch_id: str
    image_path: str
    image_id: str

    def __post_init__(self):
        image_array = self.array
        self.width = image_array.shape[1]
        self.height = image_array.shape[0]

    @property
    def array(self):
        # Read the image from the file and return the numpy array
        img_path = Path(self.rel_path, self.image_id + ".jpg")
        img_array = cv2.imread(str(img_path))
        img_array = np.ascontiguousarray(cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB))
        return img_array

    @property
    def config(self):
        _config = {
            "blob_home": self.blob_home,
            "data_root": self.data_root,
            "batch_id": self.batch_id,
            "image_id": self.image_id,
            "image_path": self.image_path,
            "width": self.width,
            "height": self.height,
            "exif_meta": asdict(self.exif_meta),
            "camera_info": asdict(self.camera_info),
            "bboxes": [box.config for box in self.bboxes],
            "schema_version": self.schema_version,
        }

        return _config

    def save_config(self, save_path):
        try:
            save_file = os.path.join(save_path, f"{self.image_id}.json")
            with open(save_file, "w") as f:
                json.dump(self.config, f, indent=4, default=str)
        except Exception as e:
            raise e
        return True

@dataclass
class RemapImage(Image):
    """For remapping labels (remap_labels)"""

    rel_path: str
    bboxes: List[BBox]
    camera_info: CameraInfo
    fullres_path: str
    width: int = field(init=False, default=-1)
    height: int = field(init=False, default=-1)
    exif_meta: Optional[ImageMetadata] = field(init=False, default=None)
    fullres_height: Optional[int] = field(init=False, default=-1)
    fullres_width: Optional[int] = field(init=False, default=-1)
    schema_version: str = SCHEMA_VERSION

    def __post_init__(self):
        self.height, self.width = self.array.shape[:2]
        self.set_fullres_dims(self.width, self.height)
        self.exif_meta = self.get_exif()

    def set_fullres_dims(self, fullres_width, fullres_height):
        self.fullres_width = fullres_width
        self.fullres_height = fullres_height

    def get_exif(self):
        """Creates a dataclass by reading exif metadata, creating a dictionary, and creating dataclass form that dictionary"""
        # Open image file for reading (must be in binary mode)
        f = open(self.image_path, "rb")
        # Return Exif tags
        tags = exifread.process_file(f, details=False)
        f.close()
        meta = {}
        for x, y in tags.items():
            newval = (
                y.values[0]
                if type(y.values) == list and len(y.values) == 1
                else y.values
            )
            if type(newval) == exifread.utils.Ratio:
                newval = str(newval)
            meta[x.rsplit(" ")[1]] = newval
        imgmeta = ImageMetadata(**meta)
        return imgmeta

    @property
    def config(self):
        _config = super(RemapImage, self).config
        _config["fullres_width"] = self.fullres_width
        _config["fullres_height"] = self.fullres_height
        _config["rel_path"] = self.rel_path

        return _config

@dataclass
class ImageData(Image):
    """Dataclass for segmentation data generation"""

    rel_path: str
    width: int
    height: int
    exif_meta: ImageMetadata
    cutout_ids: List[str] = None
    camera_info: CameraInfo = None
    bboxes: List[Box] = None
    fullres_height: int = -1
    fullres_width: int = -1
    schema_version: str = "1.0"

    def __post_init__(self):
        # Overload the post init of the super class
        # which reads the array for the width and height.
        # The width and height will be available in the metadata
        pass

    @property
    def config(self):
        _config = {
            "blob_home": self.blob_home,
            "data_root": self.data_root,
            "batch_id": self.batch_id,
            "image_id": self.image_id,
            "image_path": self.image_path,
            "width": self.width,
            "height": self.height,
            "exif_meta": asdict(self.exif_meta),
            "camera_info": asdict(self.camera_info),
            "cutout_ids": self.cutout_ids,
            "bboxes": [asdict(x) for x in self.bboxes],
            "fullres_height": self.fullres_height,
            "fullres_width": self.fullres_width,
            "rel_path": self.rel_path,
            "schema_version": self.schema_version,
        }

        return _config

    def save_config(self, save_path):
        try:
            save_image_path = Path(save_path, self.image_id + ".json")
            with open(save_image_path, "w") as f:
                json.dump(self.config, f, indent=4, default=str)
        except Exception as e:
            raise e
        return True

    def save_binary_mask(self, save_path, binary_mask):
        fname = f"{self.image_id}.png"
        mask_path = Path(
            self.blob_home,
            self.data_root,
            self.batch_id,
            "meta_masks",
            "binary_masks",
            fname,
        )
        cv2.imwrite(str(mask_path), binary_mask.astype(np.uint8))
        return True

    def save_semantic_mask(self, save_path, semantic_mask):
        pil_mask = PILImage.fromarray(semantic_mask[..., 1])
        fname = f"{self.image_id}.png"
        # mask_path = Path(self.blob_home, self.data_root, self.batch_id,
        #  "meta_masks", "semantic_masks", fname)
        mask_path = Path(save_path, fname)
        # cv2.imwrite(str(mask_path), semantic_mask.astype(np.uint8))
        pil_mask = pil_mask.save(mask_path)
        return True

    def save_instance_mask(self, save_path, instance_mask):
        fname = f"{self.image_id}.png"
        mask_path = Path(save_path, fname)
        pil_mask = PILImage.fromarray(instance_mask)
        pil_mask = pil_mask.save(mask_path)
        # cv2.imwrite(str(mask_path), instance_mask.astype(np.uint8))
        return True

# Cutouts -------------------------------------------------------------------------------------

@dataclass
class CutoutProps:
    """Region properties for cutouts
    "area",  # float Area of the region i.e. number of pixels of the region scaled by pixel-area.
    "area_bbox",  # float Area of the bounding box i.e. number of pixels of bounding box scaled by pixel-area.
    "area_convex",  # float Are of the convex hull image, which is the smallest convex polygon that encloses the region.
    "axis_major_length",  # float The length of the major axis of the ellipse that has the same normalized second central moments as the region.
    "axis_minor_length",  # float The length of the minor axis of the ellipse that has the same normalized second central moments as the region.
    "centroid",  # array Centroid coordinate list [row, col].
    "eccentricity",  # float Eccentricity of the ellipse that has the same second-moments as the region. The eccentricity is the ratio of the focal distance (distance between focal points) over the major axis length. The value is in the interval [0, 1). When it is 0, the ellipse becomes a circle.
    "extent",  # float Ratio of pixels in the region to pixels in the total bounding box. Computed as area / (rows * cols)
    "solidity",  # float Ratio of pixels in the region to pixels of the convex hull image.
    "perimeter",  # float Perimeter of object which approximates the contour as a line
    "blur_effect", float, Compute a metric that indicates the strength of blur in an image (0 for no blur, 1 for maximal blur)
    "num_components", int number of connected mask components
    "color_distribution", dict (hex number, rgb, and occurnce) of top 12 most common colors. Excludes zero (black)
    "descriptive stats", dict, calculates descriptives stats of individual channels while excluding 0 (black)
    """

    area: Union[float, list]
    area_bbox: Union[float, list]
    area_convex: Union[float, list]
    axis_major_length: Union[float, list]
    axis_minor_length: Union[float, list]
    centroid0: Union[float, list]
    centroid1: Union[float, list]
    eccentricity: Union[float, list]
    extent: float
    solidity: Union[float, list]
    perimeter: Union[float, list]
    is_green: bool
    green_sum: int
    exg_sum: float
    blur_effect: float
    num_components: int
    color_distribution: dict
    cropout_descriptive_stats: dict
    cutout_descriptive_stats: dict

# Cutout -------------------------------------------------------------------------------------
# Cutout -------------------------------------------------------------------------------------
@dataclass
class Cutout:
    """Per cutout. Goes to PlantCutouts"""

    data_root: str
    season: str
    batch_id: str
    image_id: str

    bbox: list
    datetime: datetime.datetime  # Datetime of original image creation
    cutout_props: CutoutProps
    hwc: list
    cutout_id: str
    cutout_num: int = None
    cutout_path: str = None
    exif_meta: dict = None
    camera_info: dict = None
    cls: str = None
    is_primary: bool = False
    extends_border: bool = False
    # schema_version: str = SCHEMA_VERSION
    synth: bool = False

    def __post_init__(self):
        self.cutout_num = int(self.cutout_id.split("_")[-1])
        if not self.synth:
            self.cutout_path = str(Path(self.batch_id, self.cutout_id + ".png"))

    @property
    def array(self):
        # Read the image from the file and return the numpy array
        cut_array = cv2.imread(self.cutout_path)
        cut_array = np.ascontiguousarray(cv2.cvtColor(cut_array, cv2.COLOR_BGR2RGB))
        return cut_array

    @property
    def config(self):
        _config = {
            "data_root": self.data_root,
            "season": self.season,
            "datetime": self.datetime,
            "batch_id": self.batch_id,
            "image_id": self.image_id,
            "cutout_id": self.cutout_id,
            "cutout_num": self.cutout_num,
            "hwc": self.hwc,
            "is_primary": self.is_primary,
            "extends_border": self.extends_border,
            "cutout_path": self.cutout_path,
            "exif_meta": asdict(self.exif_meta),
            "camera_info": asdict(self.camera_info),
            "bbox": self.bbox,
            "cls": self.cls,
            "cutout_props": self.cutout_props,
        }

        return _config

    def save_config(self, save_dir):
        try:
            save_cutout_path = Path(save_dir, self.batch_id, self.cutout_id + ".json")
            with open(save_cutout_path, "w") as f:
                json.dump(self.config, f, indent=4, default=str)
        except Exception as e:
            raise e
        return True

    def save_cutout(self, save_dir, cutout_array):
        fname = f"{self.image_id}_{self.cutout_num}.png"
        cutout_path = Path(save_dir, self.batch_id, fname)
        cv2.imwrite(str(cutout_path), cv2.cvtColor(cutout_array, cv2.COLOR_RGB2BGRA))
        return True

    def save_cropout(self, save_dir, img_array):
        fname = f"{self.image_id}_{self.cutout_num}.jpg"
        cutout_path = Path(save_dir, self.batch_id, fname)
        cv2.imwrite(str(cutout_path), cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR))
        return True

    def save_cutout_mask(self, save_dir, mask):
        fname = f"{self.image_id}_{self.cutout_num}_mask.png"
        mask_path = Path(save_dir, self.batch_id, fname)
        cv2.imwrite(str(mask_path), mask)
        return True

CUTOUT_PROPS = [
    "area",  # float Area of the region i.e. number of pixels of the region scaled by pixel-area.
    "area_bbox",  # float Area of the bounding box i.e. number of pixels of bounding box scaled by pixel-area.
    "area_convex",  # float Are of the convex hull image, which is the smallest convex polygon that encloses the region.
    "axis_major_length",  # float The length of the major axis of the ellipse that has the same normalized second central moments as the region.
    "axis_minor_length",  # float The length of the minor axis of the ellipse that has the same normalized second central moments as the region.
    "centroid",  # array Centroid coordinate tuple (row, col).
    "eccentricity",  # float Eccentricity of the ellipse that has the same second-moments as the region. The eccentricity is the ratio of the focal distance (distance between focal points) over the major axis length. The value is in the interval [0, 1). When it is 0, the ellipse becomes a circle.
    "extent",  # float Ratio of pixels in the region to pixels in the total bounding box. Computed as area / (rows * cols)
    "solidity",  # float Ratio of pixels in the region to pixels of the convex hull image.
    # "label",  # int The label in the labeled input image.
    "perimeter",  # float Perimeter of object which approximates the contour as a line through the centers of border pixels using a 4-connectivity.
    # "intensity_max",  # Float Value with the greatest intensity in the region.
    # "intensity_mean",  # flaot Value with the mean intensity in the region.
    # "intensity_min",  # float Value with the least intensity in the region.
    # "feret_diameter_maxfloat",  # flaot Maximum Feret’s diameter computed as the longest distance between points around a region’s convex hull contour as determined by find_contours
    # "equivalent_diameter_area",  # float The diameter of a circle with the same area as the region.
]