# -*- coding: utf-8 -*-
''' Utilities for limit state checking.'''

from __future__ import print_function
from __future__ import division

__author__= "Luis C. Pérez Tato (LCPT), Ana Ortega(AO_O)"
__copyright__= "Copyright 2016,LCPT, AO_O"
__license__= "GPL"
__version__= "3.0"
__email__= "l.pereztato@ciccp.es, ana.Ortega@ciccp.es"

import pickle
import os
import sys
from solution import predefined_solutions
from postprocess.reports import export_internal_forces as eif
from postprocess.reports import export_reactions as er
from misc_utils import log_messages as lmsg
from materials.sections import internal_forces
from collections import defaultdict
import csv
from postprocess import control_vars as cv
import json

defaultSolutionProcedureType=  predefined_solutions.SimpleStaticLinear

class VerifOutVars(object):
    '''Variables that control the output of limit state verifications.

    :ivar setCalc: set of elements to be checked (defaults to 'None' which
           means that all the elements in the file of internal forces
           results are analyzed)
    :ivar appendToResFile:  'Yes','Y','y',.., if results are appended to
           existing file of results (defaults to 'N')
    :ivar listFile: 'Yes','Y','y',.., if latex listing file of results
           is desired to be generated (defaults to 'N')
    :ivar calcMeanCF: 'Yes','Y','y',.., if average capacity factor is
           meant to be calculated (defaults to 'N')
    :ivar controller: object that controls the limit state checking.
    '''
    def __init__(self, setCalc=None, appendToResFile='N', listFile='N', calcMeanCF='N', controller= None):
        ''' Constructor.

        :param setCalc: set of elements to be checked (defaults to 'None' which
               means that all the elements in the file of internal forces
               results are analyzed)
        :param appendToResFile:  'Yes','Y','y',.., if results are appended to
               existing file of results (defaults to 'N')
        :param listFile: 'Yes','Y','y',.., if latex listing file of results
               is desired to be generated (defaults to 'N')
        :param calcMeanCF: 'Yes','Y','y',.., if average capacity factor is
               meant to be calculated (defaults to 'N')
        :param controller: object that controls the limit state checking.
        '''
        self.setCalc= setCalc
        self.appendToResFile= appendToResFile
        self.listFile= listFile
        self.calcMeanCF= calcMeanCF
        self.controller= controller

    def getCalcSetElements(self, preprocessor):
        ''' Return the set of elements to be analyzed.

        :param preprocessor: pre-processor for the XC finite element problem.
        '''
        retval= None
        if(self.setCalc):
            retval= self.setCalc.elements
        else:
            retval= preprocessor.getSets['total'].elements
        return retval

    def runChecking(self, intForcCombFileName, outputDataBaseFileName, sections):
        '''Launch checking.

        :param intForcCombFileName: Name of the file containing the internal
                                    forces on the element sections.
        :param outputDataBaseFileName: Name of the file to write the results on.
        :param sections: names of the sections to write the output for.
        '''
        retval=None
        if(self.setCalc):
            if(len(self.setCalc.elements)>0):
                # Initialize control variables.
                self.controller.initControlVars(self.setCalc)
                # Update efficiency values.
                self.controller.updateEfficiencyForSet(intForcCombFileName,self.setCalc)
                prep= self.setCalc.getPreprocessor
                retval= cv.writeControlVarsFromElements(prep,outputDataBaseFileName,self, sections)
            else:
                className= type(self).__name__
                methodName= sys._getframe(0).f_code.co_name
                lmsg.warning(className+'.'+methodName+"; element set is empty. Nothing done.")
        else:
            className= type(self).__name__
            methodName= sys._getframe(0).f_code.co_name
            lmsg.error(className+'.'+methodName+"; result file hasn't been created, you must specify a valid set of elements.")
        return retval

class LimitStateData(object):
    ''' Data used when checking limit states.

    :ivar label: limit state check label; Something like "Fatigue"
                 or "CrackControl".
    :ivar outputDataBaseFileName: name (whithout extension) of the
                                       file that contains the results to
                                       display.
    :ivar designSituation: design situation; permanent, quasi-permanent,
                           frequent, rare, earthquake.
    :ivar woodArmerAlsoForAxialForces: if true, use Wood-Armer method for
                                       both axial and bending internal
                                       forces otherwise, use it only for
                                       bending moments.
    '''

    envConfig= None

    def __init__(self, limitStateLabel, outputDataBaseFileName, designSituation, woodArmerAlsoForAxialForces= False):
        '''Limit state data constructor.

        :param limitStateLabel: limit state check label; Something like "Fatigue"                               or "CrackControl".
        :param outputDataBaseFileName: name (whithout extension) of the
                                       file that contains the results to
                                       display.
        :param designSituation: design situation; permanent, quasi-permanent,
                                frequent, rare, earthquake.
        :param woodArmerAlsoForAxialForces: if true, use Wood-Armer method for
                                            both axial and bending internal
                                            forces otherwise, use it only for
                                            bending moments.
        '''
        self.label= limitStateLabel
        self.outputDataBaseFileName= outputDataBaseFileName
        self.designSituation= designSituation
        self.woodArmerAlsoForAxialForces= woodArmerAlsoForAxialForces

    def getInternalForcesFileName(self):
        '''Return the name of the file where internal forces are stored.'''
        return self.envConfig.projectDirTree.getInternalForcesResultsPath()+'intForce_'+ self.label +'.json'

    def getReactionsFileName(self):
        '''Return the name of the file where reactions are stored.'''
        return self.envConfig.projectDirTree.getReactionsResultsPath()+'reactions_'+ self.label +'.json'

    def readInternalForces(self, setCalc):
        ''' Read the internal forces for the elements in the set argument.

        :param setCalc: elements to read internal forces for.
        '''
        return readIntForcesFile(self.getInternalForcesFileName(), setCalc)

    # def readReactions(self, nodeSet):
    #     ''' Read the reactions for the nodes in the set argument.

    #     :param nodeSet: nodes to read reactions for.
    #     '''
    #     return readReactionsFile(self.getReactionsFileName(), nodeSet)

    def getInternalForcesDict(self, nmbComb, elems):
        '''Creates a dictionary with the element's internal forces.

         :param nmbComb: combination name.
         :param elems: element set.
        '''
        return eif.getInternalForcesDict(nmbComb= nmbComb, elems= elems,  woodArmerAlsoForAxialForces= self.woodArmerAlsoForAxialForces)

    def getReactionsDict(self, nmbComb, constrainedNodes):
        '''Creates a dictionary with the element's internal forces.

         :param nmbComb: combination name.
         :param constrainedNodes: constrainedNodes.
        '''
        return er.getReactionsDict(nmbComb, constrainedNodes)

    def getDisplacementsFileName(self):
        '''Return the file name to read: combination name, node number and
        displacements (ux,uy,uz,rotX,rotY,rotZ).'''
        return self.envConfig.projectDirTree.getInternalForcesResultsPath()+'displ_'+ self.label +'.csv'

    def getOutputDataBaseFileName(self):
        '''Return the output file name without extension.'''
        return self.envConfig.projectDirTree.getFullVerifPath()+self.outputDataBaseFileName

    def getOutputDataFileName(self):
        '''Return the Python executable file name.'''
        return self.getOutputDataBaseFileName() + '.py'

    @staticmethod
    def loadPickleObject(objName):
        '''Read a Python object from a pickle file.'''
        with open(objName + '.pkl', 'r') as f:
            return pickle.load(f)

    def getCorrespondingLoadCombinations(self, combContainer):
        '''Return the load combinations needed for this limit state.

        :param combContainer: container with the definition of the different
                              combination families (ULS, fatigue, SLS,...)
                              see actions/combinations module.
        '''
        return combContainer.getCorrespondingLoadCombinations(self.designSituation)

    def dumpCombinations(self,combContainer,loadCombinations):
        '''Load into the solver the combinations needed for this limit state.

        :param combContainer: container with the definition of the different
                              combination families (ULS, fatigue, SLS,...)
                              see actions/combinations module.
        :param loadCombinations: load combination handler inside the XC solver.
        '''
        loadCombinations.clear()
        # Get the combinations corresponding to this design situation.
        combinations= self.getCorrespondingLoadCombinations(combContainer)
        if(combinations):
            combinations.dumpCombinations(loadCombinations)
        else:
            className= type(self).__name__
            methodName= sys._getframe(0).f_code.co_name
            lmsg.warning(className+'.'+methodName+"; something went wrong. Couldn\'t dump the load combinations.")
        return loadCombinations

    def getLastCalculationTime(self):
        ''' Return the time of last modification of the internal
           forces file.'''
        fNameIntForc= self.getInternalForcesFileName()
        retval= None
        if(os.path.exists(fNameIntForc)):
            retval= os.path.getmtime(fNameIntForc)
        return retval

    def createOutputFiles(self):
        ''' Create the internal forces and displacement output files.'''
        self.envConfig.projectDirTree.createTree()
        self.fNameIntForc= self.getInternalForcesFileName()
        self.fNameReactions= self.getReactionsFileName()
        self.fNameDispl= self.getDisplacementsFileName()
        os.system("rm -f " + self.fNameIntForc) #Clear obsolete files.
        os.system("rm -f " + self.fNameReactions)
        os.system("rm -f " + self.fNameDispl)
        fDisp= open(self.fNameDispl,"w")
        fDisp.write('Comb., Node, uX, uY, uZ, rotX, rotY , rotZ\n')
        fDisp.close()

    def writeDisplacements(self, combNm, nodSet):
        '''Writes the resuls of displacements in a load combination
           and set of nodes given as parameters

        :param combNM: name of the load combination
        :param nodSet: set of nodes
        '''
        fDisp= open(self.fNameDispl,"a")
        for n in nodSet:
            strDisp= str(n.getDisp).rstrip().replace(' ',', ') #displacement vector [ux,uy,uz,rotx,roty,rotz]
            fDisp.write(combNm+", "+str(n.tag)+", " + strDisp+'\n')
        fDisp.close()

    def writeInternalForces(self, internalForcesDict):
        '''Write the internal forces results.

        :param internalForcesDict: dictionary containing the internal forces.
        '''
        with open(self.fNameIntForc, 'w') as outfile:
            json.dump(internalForcesDict, outfile)
        outfile.close()

    def writeReactions(self, reactionsDict):
        '''Write the reactions.

        :param reactionsDict: dictionary containing the reactions.
        '''
        with open(self.fNameReactions, 'w') as outfile:
            json.dump(reactionsDict, outfile)
        outfile.close()

    def saveAll(self, combContainer, setCalc, solutionProcedureType= defaultSolutionProcedureType, constrainedNodeSet= None, bucklingMembers= None):
        '''Write internal forces, displacements, .., for each combination

        :param combContainer: load combination container.
        :param setCalc: set of entities for which the verification is
                          going to be performed
        :param solutionProcedureType: type of the solution strategy to solve
                                      the finite element problem.
        :param constrainedNodeSet: constrained nodes (defaults to None)
        :param bucklingMembers: list of members whose buckling reduction
                                factors need to be updated after each
                                commit (defaults to None)
        '''
        preprocessor= setCalc.getPreprocessor
        feProblem= preprocessor.getProblem
        solutionProcedure= solutionProcedureType(feProblem)
        preprocessor= feProblem.getPreprocessor
        loadCombinations= preprocessor.getLoadHandler.getLoadCombinations
        #Putting combinations inside XC.
        loadCombinations= self.dumpCombinations(combContainer,loadCombinations)

        self.createOutputFiles()
        internalForcesDict= dict()
        reactionsDict= dict()
        for key in loadCombinations.getKeys():
            comb= loadCombinations[key]
            preprocessor.resetLoadCase()
            preprocessor.getDomain.revertToStart()
            comb.addToDomain() #Combination to analyze.
            #Solution
            result= solutionProcedure.solve()
            if(result!=0):
                className= type(self).__name__
                methodName= sys._getframe(0).f_code.co_name
                lmsg.error(className+'.'+methodName+"; can't solve.")
            if(bucklingMembers): # Update reduction factors for buckling members
                for bm in bucklingMembers:
                    bm.updateReductionFactors()
            #Writing results.
            internalForcesDict.update(self.getInternalForcesDict(comb.getName,setCalc.elements))
            reactionsDict.update(self.getReactionsDict(comb.getName,constrainedNodeSet))
            self.writeDisplacements(comb.getName,setCalc.nodes)
            comb.removeFromDomain() #Remove combination from the model.
        self.writeInternalForces(internalForcesDict)
        self.writeReactions(reactionsDict)
#20181117
    def runChecking(self, outputCfg, sections= ['Sect1', 'Sect2']):
        '''This method reads, for the elements in setCalc,  the internal
        forces previously calculated and saved in the corresponding file.
        Using the 'initControlVars' and 'updateEfficiencyForSet' methods of
        the controller, the appropriate attributes are assigned to the
        elements and the associated limit state verification is run.
        The results are written to a file in order to be displayed or listed.

        :param outputCfg: instance of class 'VerifOutVars' which defines the
               variables that control the output of the checking (set of
               elements to be analyzed, append or not the results to the
               result file [defatults to 'N'], generation or not
               of list file [defatults to 'N', ...)
        :param sections: names of the sections to write the output for.
        '''
        intForcCombFileName= self.getInternalForcesFileName()
        return outputCfg.runChecking(intForcCombFileName, self.getOutputDataBaseFileName(), sections)

#20181117 end

class ULS_LimitStateData(LimitStateData):
    ''' Ultimate limit state data for permanent or transient combinations.'''

    def __init__(self, limitStateLabel, outputDataBaseFileName, designSituation):
        '''Constructor.

        :param limitStateLabel: limit state check label; Something like "Fatigue"
                              or "CrackControl".
        :param outputDataBaseFileName: name (whithout extension) of the
                                       file that contains the results to
                                       display.
        :param designSituation: design situation; permanent, quasi-permanent,
                                frequent, rare, earthquake.
        '''
        super(ULS_LimitStateData,self).__init__(limitStateLabel= limitStateLabel, outputDataBaseFileName= outputDataBaseFileName, designSituation= designSituation)

    def check(self, crossSections, outputCfg= VerifOutVars(), threeDim= True):
        '''Checking of normal stresses in ultimate limit states
        (see self.dumpCombinations).

        :param crossSections: cross sections on each element.
        :param outputCfg: instance of class 'VerifOutVars' which defines the
               variables that control the output of the checking (set of
               elements to be analyzed, append or not the results to a file,
               generation or not of lists, ...)
        :param threeDim: true if it's 3D (Fx,Fy,Fz,Mx,My,Mz)
               false if it's 2D (Fx,Fy,Mz).
        '''
        if(threeDim):
            return crossSections.internalForcesVerification3D(self, "d",outputCfg)
        else:
            return crossSections.internalForcesVerification2D(self, "d",outputCfg)

class NormalStressesRCLimitStateData(ULS_LimitStateData):
    ''' Reinforced concrete normal stresses data for limit state checking.'''
    def __init__(self):
        '''Constructor '''
        super(NormalStressesRCLimitStateData,self).__init__(limitStateLabel= 'ULS_normalStressesResistance', outputDataBaseFileName= 'verifRsl_normStrsULS', designSituation= 'permanent')

class NormalStressesSteelLimitStateData(ULS_LimitStateData):
    ''' Steel normal stresses data for limit state checking.'''
    def __init__(self):
        '''Constructor '''
        super(NormalStressesSteelLimitStateData,self).__init__(limitStateLabel= 'ULS_normalStressesResistance', outputDataBaseFileName= 'verifRsl_normStrsULS', designSituation= 'permanent')

class ShearResistanceRCLimitStateData(ULS_LimitStateData):
    ''' Reinforced concrete shear resistance limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(ShearResistanceRCLimitStateData,self).__init__(limitStateLabel= 'ULS_shearResistance', outputDataBaseFileName= 'verifRsl_shearULS', designSituation= 'permanent')

class ShearResistanceSteelLimitStateData(ULS_LimitStateData):
    ''' Reinforced concrete shear resistance limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(ShearResistanceSteelLimitStateData,self).__init__(limitStateLabel= 'ULS_shearResistance', outputDataBaseFileName= 'verifRsl_shearULS', designSituation= 'permanent')

class SLS_LimitStateData(LimitStateData):
    ''' Serviceability limit state data for frequent load combinations.'''

    def __init__(self, limitStateLabel, outputDataBaseFileName, designSituation):
        '''Constructor.

        :param limitStateLabel: limit state check label; Something like "Fatigue"                               or "CrackControl".
        :param outputDataBaseFileName: name (whithout extension) of the
                                       file that contains the results to
                                       display.
        :param designSituation: design situation; permanent, quasi-permanent,
                                frequent, rare, earthquake.
        '''
        super(SLS_LimitStateData,self).__init__(limitStateLabel= limitStateLabel, outputDataBaseFileName= outputDataBaseFileName, designSituation= designSituation)

    def check(self,reinfConcreteSections,outputCfg= VerifOutVars()):
        '''Checking of crack width under frequent loads in serviceability
           limit states (see self.dumpCombinations).

        :param reinfConcreteSections: Reinforced concrete sections on each
               element.
        :param outputCfg: instance of class VerifOutVars which defines the
               variables that control the output of the checking (set of
               elements to be analyzed, append or not the results to file,
               generation or not of lists, ...)
         '''
        return reinfConcreteSections.internalForcesVerification3D(self, "k",outputCfg)

class RareLoadsCrackControlRCLimitStateData(SLS_LimitStateData):
    ''' Reinforced concrete crack control under rare loads limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(RareLoadsCrackControlRCLimitStateData,self).__init__('SLS_rareLoadsCrackControl','verifRsl_crackingSLS_rare', designSituation= 'rare')

class FreqLoadsCrackControlRCLimitStateData(SLS_LimitStateData):
    ''' Reinforced concrete crack control under frequent loads limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(FreqLoadsCrackControlRCLimitStateData,self).__init__('SLS_frequentLoadsCrackControl','verifRsl_crackingSLS_freq', designSituation= 'frequent')

class QPLoadsCrackControlRCLimitStateData(SLS_LimitStateData):
    ''' Reinforced concrete crack control under quasi-permanent loads limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(QPLoadsCrackControlRCLimitStateData,self).__init__('SLS_quasiPermanentLoadsLoadsCrackControl','verifRsl_crackingSLS_qperm', designSituation= 'quasi-permanent')

class FreqLoadsDisplacementControlLimitStateData(SLS_LimitStateData):
    ''' Displacement control under frequent loads limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(FreqLoadsDisplacementControlLimitStateData,self).__init__('SLS_frequentLoadsDisplacementControl','', designSituation= 'frequent')

    def check(self,reinfConcreteSections):
        '''Checking of displacements under frequent loads in
        serviceability limit states (see self.dumpCombinations).

        :param reinfConcreteSections: Reinforced concrete sections on each element.
        '''
        className= type(self).__name__
        methodName= sys._getframe(0).f_code.co_name
        lmsg.error(className+'.'+methodName+"; not implemented.")

class FatigueResistanceRCLimitStateData(ULS_LimitStateData):
    ''' Reinforced concrete shear resistance limit state data.'''
    def __init__(self):
        '''Limit state data constructor '''
        super(FatigueResistanceRCLimitStateData,self).__init__('ULS_fatigueResistance','verifRsl_fatigueULS', designSituation= 'fatigue')

class VonMisesStressLimitStateData(ULS_LimitStateData):
    ''' Steel Von Mises stress limit state data.'''
    def __init__(self, vonMisesStressId= 'max_von_mises_stress'):
        '''Von Mises stress limit state data constructor.

        :param vonMisesStressId: identifier of the Von Mises stress to read
                                (see NDMaterial and MembranePlateFiberSection).
        '''
        super(VonMisesStressLimitStateData,self).__init__('ULS_VonMisesStressResistance','verifRsl_VonMisesStressULS', designSituation= 'permanent')
        self.vonMisesStressId= vonMisesStressId

    def getInternalForcesDict(self, nmbComb, elems):
        '''Creates a dictionary with the element's internal forces.

        :param nmbComb: combination name.
        :param elems: element set.
        '''
        return eif.getInternalForcesDict(nmbComb,elems, vonMisesStressId= self.vonMisesStressId, woodArmerAlsoForAxialForces= False) # Wood-Armer has no sense here.

    def readInternalForces(self, setCalc):
        ''' Read the internal forces for the elements in the set argument.

        :param setCalc: elements to read internal forces for.
        '''
        return readIntForcesFile(self.getInternalForcesFileName(), setCalc, vonMisesStressId= self.vonMisesStressId)

    def check(self,elementsToCheck,outputCfg= VerifOutVars()):
        '''Checking of fatigue under fatigue combinations loads in
        ultimate limit states (see self.dumpCombinations).

        :param elementsToCheck: elements to check.
        :param outputCfg: instance of class 'VerifOutVars' which defines the
               variables that control the output of the checking (set of
               elements to be analyzed, append or not the results to a file,
               generation or not of lists, ...)
        '''
        return elementsToCheck.internalForcesVerification3D(self, "d",outputCfg)

    def runChecking(self,outputCfg):
        '''This method reads, for the elements in setCalc,  the internal
        forces previously calculated and saved in the corresponding file.
        Using the 'initControlVars' and 'updateEfficiencyForSet' methods of
        the controller, the appropriate attributes are assigned to the
        elements and the associated limit state verification is run.
        The results are written to a file in order to be displayed or listed.

        :param outputCfg: instance of class 'VerifOutVars' which defines the
               variables that control the output of the checking (set of
               elements to be analyzed, append or not the results to the
               result file [defatults to 'N'], generation or not
               of list file [defatults to 'N', ...)
        '''
        outputCfg.controller.vonMisesStressId= self.vonMisesStressId
        retval= super(VonMisesStressLimitStateData,self).runChecking(outputCfg, sections= [''])
        return retval

freqLoadsDisplacementControl= FreqLoadsDisplacementControlLimitStateData()

rareLoadsCrackControl= RareLoadsCrackControlRCLimitStateData()
freqLoadsCrackControl= FreqLoadsCrackControlRCLimitStateData()
quasiPermanentLoadsCrackControl= QPLoadsCrackControlRCLimitStateData()

# Normal stresses.
normalStressesResistance= NormalStressesRCLimitStateData()
steelNormalStressesResistance= NormalStressesSteelLimitStateData()

# Shear strength.
shearResistance= ShearResistanceRCLimitStateData()
steelShearResistance= ShearResistanceSteelLimitStateData()

fatigueResistance= FatigueResistanceRCLimitStateData()
vonMisesStressResistance= VonMisesStressLimitStateData()

def readIntForcesDict(intForcCombFileName,setCalc=None, vonMisesStressId= 'max_von_mises_stress'):
    '''Extracts element and combination identifiers from the internal
    forces JSON file. Return elementTags, idCombs and
    internal-forces values

    :param   intForcCombFileName: name of the file containing the internal
                                  forces obtained for each element for
                                  the combinations analyzed
    :param setCalc: set of elements to be analyzed (defaults to None which
                    means that all the elements in the file of internal forces
                    results are analyzed)
    :param vonMisesStressId: identifier of the Von Mises stress to read
                            (see NDMaterial and MembranePlateFiberSection).
    '''
    elementTags= set()
    idCombs= set()
    with open(intForcCombFileName) as json_file:
        combInternalForcesDict= json.load(json_file)
    json_file.close()

    internalForcesValues= defaultdict(list)

    if(not setCalc):
        for comb in combInternalForcesDict.keys():
            idComb= str(comb)
            idCombs.add(idComb)
            elements= combInternalForcesDict[comb]
            for elemId in elements.keys():
                tagElem= eval(elemId)
                elementData= elements[elemId]
                #elementType= elementData['type']
                internalForces= elementData['internalForces']
                for k in internalForces.keys():
                    idSection= eval(k)
                    elementTags.add(tagElem)
                    crossSectionInternalForces= internal_forces.CrossSectionInternalForces()
                    forces= internalForces[k]
                    crossSectionInternalForces.setFromDict(forces)
                    crossSectionInternalForces.idComb= idComb
                    crossSectionInternalForces.tagElem= tagElem
                    crossSectionInternalForces.idSection= idSection
                    if(vonMisesStressId in forces):
                        crossSectionInternalForces.vonMisesStress= forces[vonMisesStressId]
                    internalForcesValues[tagElem].append(crossSectionInternalForces)
    else:
        setElTags= frozenset(setCalc.getElementTags()) # We construct a frozen set to accelerate searching.
        for idComb in combInternalForcesDict.keys():
            idCombs.add(idComb)
            elements= combInternalForcesDict[idComb]
            for elemId in elements.keys():
                tagElem= eval(elemId)
                if(tagElem in setElTags): # search element tag
                    elementData= elements[elemId]
                    #elementType= elementData['type']
                    internalForces= elementData['internalForces']
                    for k in internalForces.keys():
                        idSection= eval(k)
                        elementTags.add(tagElem)
                        crossSectionInternalForces= internal_forces.CrossSectionInternalForces()
                        forces= internalForces[k]
                        crossSectionInternalForces.setFromDict(forces)
                        crossSectionInternalForces.idComb= idComb
                        crossSectionInternalForces.tagElem= tagElem
                        crossSectionInternalForces.idSection= idSection
                        if(vonMisesStressId in forces):
                            crossSectionInternalForces.vonMisesStress= forces[vonMisesStressId]
                        internalForcesValues[tagElem].append(crossSectionInternalForces)
    return (elementTags,idCombs,internalForcesValues)

def oldReadIntForcesFile(intForcCombFileName,setCalc=None):
    '''Extracts element and combination identifiers from the internal
    forces listing file. Return elementTags, idCombs and
    internal-forces values

    :param   intForcCombFileName: name of the file containing the internal
                                  forces obtained for each element for
                                  the combinations analyzed
    :param setCalc: set of elements to be analyzed (defaults to None which
                    means that all the elements in the file of internal forces
                    results are analyzed)
    '''
    # errMsg= 'oldReadIntForcesFile will be deprecated soon.'
    # lmsg.error(errMsg)
    elementTags= set()
    idCombs= set()
    f= open(intForcCombFileName,"r")
    internalForcesValues= defaultdict(list)
    internalForcesListing= csv.reader(f)
    next(internalForcesListing) # skip first line (head)
    if setCalc is None:
        for lst in internalForcesListing: # lst: list of internal forces for each combination and element
            if(len(lst)>0):
                idComb= lst[0]
                idCombs.add(idComb)
                tagElem= eval(lst[1])
                idSection= eval(lst[2])
                elementTags.add(tagElem)
                crossSectionInternalForces= internal_forces.CrossSectionInternalForces()
                crossSectionInternalForces.setFromCSVString(lst,3)
                crossSectionInternalForces.idComb= idComb
                crossSectionInternalForces.tagElem= tagElem
                crossSectionInternalForces.idSection= idSection
                internalForcesValues[tagElem].append(crossSectionInternalForces)
    else:
        setElTags= frozenset(setCalc.getElementTags()) # We construct a frozen set to accelerate searching.
        for lst in internalForcesListing:
            if(len(lst)>0):
                tagElem= eval(lst[1])
                if tagElem in setElTags: # search element tag
                    idComb= lst[0]
                    idCombs.add(idComb)
                    tagElem= eval(lst[1])
                    idSection= eval(lst[2])
                    elementTags.add(tagElem)
                    crossSectionInternalForces= internal_forces.CrossSectionInternalForces()
                    crossSectionInternalForces.setFromCSVString(lst,3)
                    crossSectionInternalForces.idComb= idComb
                    crossSectionInternalForces.tagElem= tagElem
                    crossSectionInternalForces.idSection= idSection
                    internalForcesValues[tagElem].append(crossSectionInternalForces)
    f.close()
    return (elementTags,idCombs,internalForcesValues)

def readIntForcesFile(intForcCombFileName, setCalc=None, vonMisesStressId= 'max_von_mises_stress'):
    '''Extracts element and combination identifiers from the internal
    forces listing file. Return elementTags, idCombs and
    internal-forces values

    :param intForcCombFileName: name of the file containing the internal
                                forces obtained for each element for
                                the combinations analyzed
    :param setCalc: set of elements to be analyzed (defaults to None which
                    means that all the elements in the file of internal forces
                    results are analyzed)
    :param vonMisesStressId: identifier of the Von Mises stress to read
                            (see NDMaterial and MembranePlateFiberSection).
    '''
    f= open(intForcCombFileName,"r")
    c= f.read(1)
    if(c=='{'):
        retval= readIntForcesDict(intForcCombFileName,setCalc, vonMisesStressId)
    else:
        retval= oldReadIntForcesFile(intForcCombFileName,setCalc)
    f.close()
    return retval

def string_el_max_axial_force(element,section,setName,combName,axialForc):
    retval='preprocessor.getElementHandler.getElement('+str(element)+').setProp("maxAxialForceSect'+str(section)+'",AxialForceControlVars('+'idSection= "' + setName + 'Sects'+str(section)+'"' + ', combName= "' + combName +'", N= ' + str(axialForc) + ')) \n'
    return retval

def calc_max_tension_axial_forces(setCalc,intForcCombFileName,outputFileName):
    '''Calculate maximum tension forces for the elements included in setCalc
    among the load combinations for which internal-force results are stored in
    intForcCombFileName. The maximum tension forces calculated are written to
    outputFileName file.

    :param setCalc: set of elements to be analyzed.
    '''
    f=open(outputFileName,"w")
    etags,combs,intForc=readIntForcesFile(intForcCombFileName,setCalc)
    setName=setCalc.name
    for el in etags:
        #init tension axial forces
        maxNsect1,maxNsect2=(0.0,0.0)
        maxCmbsect1,maxCmbsect2=('','')
        elIntF=intForc[el]
        for ind in range(len(combs)):
            Nsect1=elIntF[2*ind].N
            Nsect2=elIntF[2*ind+1].N
            if Nsect1>maxNsect1:
                maxNsect1=Nsect1
                maxCmbsect1=elIntF[2*ind].idComb
            if Nsect2>maxNsect2:
                maxNsect2=Nsect2
                maxCmbsect2=elIntF[2*ind+1].idComb
        strSect1=string_el_max_axial_force(el,1,setName,maxCmbsect1,maxNsect1)
        strSect2=string_el_max_axial_force(el,2,setName,maxCmbsect2,maxNsect2)
        f.write(strSect1)
        f.write(strSect2)
    f.close()

def calc_max_compression_axial_forces(setCalc,intForcCombFileName,outputFileName):
    '''Calculate maximum compression forces for the elements included in setCalc
    among the load combinations for which internal-force results are stored in
    intForcCombFileName. The maximum tension forces calculated are written to
    outputFileName file.

    :param setCalc: set of elements to be analyzed.
    '''
    f=open(outputFileName,"w")
    etags,combs,intForc=readIntForcesFile(intForcCombFileName,setCalc)
    setName= setCalc.name
    for el in etags:
        #init compression axial forces
        maxNsect1,maxNsect2=(0.0,0.0)
        maxCmbsect1,maxCmbsect2=('','')
        elIntF=intForc[el]
        for ind in range(len(combs)):
            Nsect1=elIntF[2*ind].N
            Nsect2=elIntF[2*ind+1].N
            if Nsect1<maxNsect1:
                maxNsect1=Nsect1
                maxCmbsect1=elIntF[2*ind].idComb
            if Nsect2<maxNsect2:
                maxNsect2=Nsect2
                maxCmbsect2=elIntF[2*ind+1].idComb
        strSect1=string_el_max_axial_force(el,1,setName,maxCmbsect1,maxNsect1)
        strSect2=string_el_max_axial_force(el,2,setName,maxCmbsect2,maxNsect2)
        f.write(strSect1)
        f.write(strSect2)
    f.close()


import httpx
import json
import time
from dynaconf import settings, LazySettings
from prometheus_client import start_http_server
from prometheus_client.core import REGISTRY, GaugeMetricFamily
settings = LazySettings(ENVVAR_PREFIX_FOR_DYNACONF='MONITIS')

class Monitis():
    def __init__(self, apikey, secretkey):
        self.apikey = apikey
        self.secretkey = secretkey

    def token(self):
        params = {
            'action': 'authToken',
            'apikey': self.apikey,
            'secretkey': self.secretkey,
            'output': 'json'
        }
        r = httpx.get('https://api.monitis.com/api', params=params)
        output = json.loads(r.content)
        return output['authToken']

    def tests(self, test_type='https'):
        params = {
            'apikey': self.apikey,
            'output': 'json',
            'version': 3,
            'action': 'tests',
            'validation': 'token',
            'authToken': self.token()
        }
        list_output = httpx.get('https://api.monitis.com/api', params=params)
        output = json.loads(list_output.content)
        return [elem['name'] for elem in output['testList'] if elem['type'] == test_type]

    def last_test_result(self):
        params = {
            'apikey': self.apikey,
            'output': 'json',
            'version': 3,
            'action': 'testsLastValues',
            'validation': 'token',
            'authToken': self.token(),
        }
        last_result_output = httpx.get('https://api.monitis.com/api', params=params)
        output = json.loads(last_result_output.content)
        return output

class MonitisCollector():
    def __init__(self, monitis):
        self.monitis = monitis

    def collect(self):
        result = self.monitis.last_test_result()
        labels = ['name', 'location', 'testType', 'status']
        gauge = GaugeMetricFamily("monitis_https", "HTTP/S Response Time", value=None, labels=labels)
        for location in result:
            for site in location['data']:
                if site['testType'] == 'https':
                    labelvalues = [site['name'], location['name'], 'https', site['status']]
                    gauge.add_metric(labelvalues, site['perf'])
        yield gauge

        labels = ['name', 'location', 'status']
        gauge = GaugeMetricFamily("monitis_certificate", "Certificate Expiry time", value=None, labels=labels)
        for location in result:
            for site in location['data']:
                if site['testType'] == 'certificate':
                    labelvalues = [site['name'], location['name'], site['status']]
                    gauge.add_metric(labelvalues, site['perf'])
        yield gauge
def setup():
    m = Monitis(settings.APIKEY, settings.SECRETKEY)
    REGISTRY.register(MonitisCollector(m))
    start_http_server(8000)

if __name__ == '__main__':
    setup()
    while True:
        time.sleep(1)


import json
from pathlib import Path
import os
import time

class CommonUtils:
    def readfromjson( key,filename):
        try:
            filename = filename + ".json"
            path = Path(__file__).parent.parent
            path =os.path.join(path,"Resources\\JSON")
            os.chdir(path)
            with open(filename,'r+')as f:
                jso = json.load(f)
            return jso[key]
        except FileNotFoundError:
            print(filename + " Not found")
            return None

    def Click(Element):
        Element.click()

    def isDisplayed(Element):
        try:
            if Element != None:
                Flag = Element.is_displayed()
                if Flag == True:
                    return True
                else:
                    return False
            else:
                return False
        except Exception as e:
            print(e.message)

    def wait():
        time.sleep(1)


# Undirected Graph Data Structures
# Vertices are IDs
# Edges are most important, hold weighted connections between IDs
from collections import deque

# Non-directional vertex, essentially just a string ID
class Vertex:
	def __init__(self, name: str):
		# Potential attributes: Rank, IO degree
		self.ID = name
	# Map ID str to object reference
	def __str__(self):
		return self.ID
	def __repr__(self):
		return self.ID

# Weighted edge shared between two vertices
# Not necessarily undirectional (we'll use this in a directed flow network)
# In UndiGraph, start and end distinction is arbitrary
# However, in a DiGraph, start/end can indicate direction of travel
class Edge:
	def __init__(self, start, end, weight=0):
		self.start = start
		self.end = end
		self.W = weight

# Undirected Graph Object using Adjacency Matrix
class UndiGraph:
	def __init__(self, vertex_list, edge_list=None):
		# key:		dict {"ID": Index}, map Vertex IDs to Matrix index
		# size:		number of vertices
		# Matrix:	Adjacency matrix, plot of edge weights
		self.key = {name.ID: num for num, name in enumerate(vertex_list)}
		self.size = len(vertex_list)
		self.Matrix = [[0 for col in range(self.size)]for row in range(self.size)]

		if edge_list:
			self.add_edges(edge_list)

	# Takes an Edge, adds to Matrix
	# Add s->e to Matrix[key[s]][key[e]]
	# Add e->s to Matrix[key[e]][key[s]]
	def add_edge(self, e):
		# s->e
		self.Matrix[self.key[e.start]][self.key[e.end]] = e.W
		# e->s
		self.Matrix[self.key[e.end]][self.key[e.start]] = e.W

	# Takes list of Edges, adds each to Matrix
	def add_edges(self, edges):
		for edge in edges:
			self.add_edge(edge)

	# Check if something is a vertex in self.key
	def is_v(self, s):
		return s in self.key.keys()

	# Takes a number and gives the ID with corresponding index
	def get_ID(self, s):
		for k, v in self.key.items():
			if v == s:
				return k
		print("Could not find index...")

	def show_matrix(self):
		print('\n')

		# Column names
		for t in self.key.keys():
			print(f'\t{t}', end='')
		# Row
		for k, v in self.key.items():
			# Column
			print(f'\n{k}', end='\t')
			for k2, v2 in self.key.items():
				print(self.Matrix[v][v2], end='\t')

		print('\n')

	# Return edge between (start,end) in Matrix
	# Accepts either string of int
	def edge_at(self, s, e):
		# If string IDs, convert and return
		if isinstance(s, str) and isinstance(e, str):
			return self.Matrix[self.key[s]][self.key[e]]
		# Otherwise, if num, simply return
		return self.Matrix[s][e]

	# Breadth-first search from given source (int or str)
	# Depth=0, 1, 2, 3, until no edges left, next vertex
	# Unlike traverse(), we will be checking every cell in every row
	# Completeness counts, we want to explore all vertices and edges, even dups
	def bfs(self, source):
		# If source is str ID, convert to int
		if isinstance(source, str):
			if not self.is_v(source):
			 	print("No corresponding start point...")
			 	return
			root = self.key[source]
		# Otherwise, use int(source) as root
		elif isinstance(source, int):
			root = source

		# Data structures
		# Visited list with len() of matrix, initialize all to False
		visited = [False]*self.size
		# Depth queue
		q = deque()
		q.appendleft(root)
		# Visit source
		visited[root] = True
		# BFS path expansion
		while q:
			current = q.popleft()
			print(f'Current Node: {self.get_ID(current)}')

			# For every non-visited adj vertex to current
			for i in range(self.size):
				if ((self.edge_at(current, i)) and (not visited[i])):
					q.appendleft(i)
					visited[i] = True

	# Perform BFS on all vertices
	def bfs_all(self):
		c = 0
		for i in range(self.size):
			print(f'Search #{i+1}')
			self.bfs(i)

	# Query the Matrix
	# Since edges are two-way, we only need to check ((size^2)/2) cells
	# Also, we do not need to check a vertex against itself (ex.A->A)
	def traverse(self):
		counter = 0
		# Row
		for i in range(self.size):
			# Col
			# Ignore vertices before and including this one
			for j in range(i+1, self.size):
				# Get current cell value (index = [i][j])
				cell = self.edge_at(i, j)
				counter+=1

				# Do something if there's an edge
				if cell:
					print(f'Edge between {self.get_ID(i)} and {self.get_ID(j)}: {cell}')
				else:
					print(f'No edge between {self.get_ID(i)} and {self.get_ID(j)}')

		print(f'Number of cells in Matrix:\t{self.size**2}')
		print(f'Number of cells searched:\t{counter}')


from colorthief import ColorThief
import webcolors

color_thief = ColorThief('a.jpg')
# get the dominant color
dominant_color = color_thief.get_color(quality=1)
# build a color palette
palette = color_thief.get_palette(color_count=6)

print(webcolors.rgb_to_hex(dominant_color))
palette = [webcolors.rgb_to_hex(color) for color in palette]
print(palette)

import math
def distance(color1, color2):
    return math.sqrt(sum([(e1-e2)**2 for e1, e2 in zip(color1, color2)]))

def best_match(sample, colors):
    by_distance = sorted(colors, key=lambda c: distance(c, sample))
    return by_distance[0:5]

colors = [

(0, 0, 0),
(0, 0, 170),
(0, 170, 0),
(0, 170, 170),
(170, 0, 0),
(170, 0, 170),
(255, 170, 0),
(170, 170, 170),
(85, 85, 85),
(85, 85, 255),
(85, 255, 85),
(85, 255, 255),
(255, 85, 85),
(255, 85, 255),
(255, 255, 85),
(255, 255, 255)

]

result = best_match((60,43,44), colors)
palette = [webcolors.rgb_to_hex(color) for color in result]
print(palette)

from flask_sqlalchemy import SQLAlchemy


import data
import T_list_astr

URL = "https://api.nasa.gov/neo/rest/v1/feed"

def run():
    request = data.requests.get(URL, data.params)
    count = request.headers['X-RateLimit-Remaining']
    data.count_req = count
    text = data.json.loads(request.text)
    list_astr = text['near_earth_objects']['2020-08-18']
    l_astr = ""
    for i in range(len(list_astr)):
        l = list_astr[i]
        l_astr+= str(l['name']) + "  "   + str(l['estimated_diameter']['kilometers']['estimated_diameter_max']) + "\n"
    t = T_list_astr.show_astr()
    t.show(l_astr)


import math
import random

import arcade
import numpy as np

VELOCITY = 800
SIZE = 30
ACCELERATION = 500

class SmokeElement(arcade.SpriteSolidColor):
    def __init__(self, x, y, dest_x, dest_y):
        super(SmokeElement, self).__init__(width=SIZE, height=SIZE, color=arcade.color.WHITE)
        self.angle_tan = math.atan2(dest_y - y, dest_x - x)
        self.velocity = np.array([VELOCITY*(random.randint(90, 110)/100) * math.cos(self.angle_tan),
                                  VELOCITY*(random.randint(90, 110)/100) * math.sin(self.angle_tan)],
                                 dtype=float)
        self.center_x = x
        self.center_y = y
        self.position_current = np.array([x, y], dtype=float)
        self.position_old = np.array([x, y], dtype=float)
        self.time = random.randint(2, 4)
        self.acceleration = np.array([ACCELERATION * math.cos(self.angle_tan), ACCELERATION * math.sin(self.angle_tan)],
                                     dtype=float)

    def updateCenter(self):
        self.center_x = self.position_current[0]
        self.center_y = self.position_current[1]

    def updateVelocity(self, dt):
        if self.velocity[0] > 0:
            self.velocity -= self.acceleration * dt
        if self.velocity[0] <= random.randint(0, 600)/600:
            self.velocity[0] = 0
            self.velocity[1] = 0

    def update(self, dt=0):
        self.updateVelocity(dt)
        self.position_current += self.velocity * dt
        self.time -= dt
        self.updateCenter()

        # self.velocity = self.position_current - self.position_old
        # self.position_old = self.position_current
        # self.position_current = self.position_current + self.velocity + self.acceleration * dt * dt
        # self.updateCenter()


import logging
import traceback
import os
import webbrowser
from pathlib import Path
from typing import List, Optional

from PyQt5 import QtCore, QtGui, QtWidgets

from ..settings import Settings
from .dashboard import DashboardWidget, formatTemperature, formatHumidity
from .profiles import ProfilesDialog, readProfiles
from .resources import ResourcesDialog
from .preferences import PreferencesDialog
from .databrowser import DataBrowserWindow
from .loggerwidget import QueuedLoggerWidget
from .alignment import AlignmentDialog
from .sequence import SequenceController
from .recover import RecoverDialog
# from .measurement import CreateMeasurementDialog

__all__ = ["MainWindow"]

APP_TITLE = "SQC"
APP_COPY = "Copyright © 2022-2023 HEPHY"
APP_LICENSE = "This software is licensed under the GNU General Public License v3.0"
APP_DECRIPTION = """Sensor Quality Control (SQC) characterises a sample of
sensors from each batch delivered by the producer and ensures that they
fully satisfy the specifications so they can be used to build modules for
the CMS Tracker."""

logger = logging.getLogger(__name__)

class MainWindow(QtWidgets.QMainWindow):

    startRequested = QtCore.pyqtSignal()
    stopRequested = QtCore.pyqtSignal()

    def __init__(self, context, parent=None):
        super().__init__(parent)

        self.context = context

        self.plugins = []

        # Dock widgets

        self.loggerWidget = QueuedLoggerWidget(self)

        self.loggerDockWidget = QtWidgets.QDockWidget(self)
        self.loggerDockWidget.setObjectName("logger")
        self.loggerDockWidget.setWindowTitle("Logger")
        self.loggerDockWidget.setFloating(False)
        self.loggerDockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
        self.loggerDockWidget.setWidget(self.loggerWidget)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.loggerDockWidget)

        # Actions

        self.newMeasurementAction = QtWidgets.QAction("&New Measurement...")
        self.newMeasurementAction.setToolTip("New Measurement")
        self.newMeasurementAction.setStatusTip("Create a new measurement")
        self.newMeasurementAction.setIcon(QtGui.QIcon("icons:document-new.svg"))
        self.newMeasurementAction.triggered.connect(self.newMeasurement)

        self.quitAction = QtWidgets.QAction("&Quit")
        self.quitAction.setShortcut(QtGui.QKeySequence("Ctrl+Q"))
        self.quitAction.triggered.connect(self.close)

        self.profilesAction = QtWidgets.QAction("Sensor &Profiles...")
        self.profilesAction.setStatusTip("Manage sensor profiles")
        self.profilesAction.triggered.connect(self.showProfiles)

        self.resourcesAction = QtWidgets.QAction("&Resources...")
        self.resourcesAction.triggered.connect(self.showResources)

        self.preferencesAction = QtWidgets.QAction("Pre&ferences...")
        self.preferencesAction.triggered.connect(self.showPreferences)

        self.dataBrowserAction = QtWidgets.QAction("Data &Browser")
        self.dataBrowserAction.setIcon(QtGui.QIcon.fromTheme("icons:view-browser.svg"))
        self.dataBrowserAction.setStatusTip("Browse previous measurement data")
        self.dataBrowserAction.setCheckable(True)
        self.dataBrowserAction.setChecked(False)
        self.dataBrowserAction.triggered.connect(self.setDataBrowserVisible)

        self.loggerAction = self.loggerDockWidget.toggleViewAction()
        self.loggerAction.setIcon(QtGui.QIcon("icons:view-logger.svg"))
        self.loggerAction.setChecked(False)

        self.startAction = QtWidgets.QAction("&Start")
        self.startAction.setStatusTip("Run measurement sequence")
        self.startAction.setIcon(QtGui.QIcon("icons:sequence-start.svg"))
        self.startAction.triggered.connect(self.requestStart)

        self.suspendAction = QtWidgets.QAction("S&uspend")
        self.suspendAction.setStatusTip("Suspend measurement sequence")
        self.suspendAction.setIcon(QtGui.QIcon("icons:sequence-suspend.svg"))
        self.suspendAction.setCheckable(True)
        self.suspendAction.toggled.connect(self.toggleSuspend)

        self.stopAction = QtWidgets.QAction("Sto&p")
        self.stopAction.setStatusTip("Abort measurement sequence")
        self.stopAction.setIcon(QtGui.QIcon("icons:sequence-stop.svg"))
        self.stopAction.triggered.connect(self.requestStop)

        self.alignmentAction = QtWidgets.QAction("&Alignment")
        self.alignmentAction.setIcon(QtGui.QIcon.fromTheme("icons:alignment.svg"))
        self.alignmentAction.setStatusTip("Show sensor alignment dialog")
        self.alignmentAction.triggered.connect(self.showAlignmentDialog)

        self.recoverAction = QtWidgets.QAction("Recover Station")
        self.recoverAction.setIcon(QtGui.QIcon.fromTheme("icons:recover.svg"))
        self.recoverAction.setStatusTip("Safely recover station by ramping down SMUs, discarge and releasing switches")
        self.recoverAction.triggered.connect(self.showRecoverStation)

        self.boxLightAction = QtWidgets.QAction("Box Light")
        self.boxLightAction.setIcon(QtGui.QIcon.fromTheme("icons:light.svg"))
        self.boxLightAction.setStatusTip("Toggle box light")
        self.boxLightAction.setCheckable(True)
        self.boxLightAction.setVisible(False)  # TODO
        self.boxLightAction.toggled.connect(self.toggleBoxLight)

        self.identifyAction = QtWidgets.QAction("Identify Instruments")
        self.identifyAction.setStatusTip("Identify Instruments")
        self.identifyAction.setVisible(False)
        self.identifyAction.triggered.connect(self.showIdentify)

        self.contentsAction = QtWidgets.QAction("&Contents")
        self.contentsAction.setShortcut(QtGui.QKeySequence("F1"))
        self.contentsAction.triggered.connect(self.showContents)

        self.aboutQtAction = QtWidgets.QAction("&About Qt")
        self.aboutQtAction.triggered.connect(self.showAboutQt)

        self.aboutAction = QtWidgets.QAction("&About")
        self.aboutAction.triggered.connect(self.showAbout)

        # Menus

        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.newMeasurementAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.quitAction)

        self.editMenu = self.menuBar().addMenu("&Edit")
        self.editMenu.addAction(self.profilesAction)
        self.editMenu.addAction(self.resourcesAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.preferencesAction)

        self.viewMenu = self.menuBar().addMenu("&View")
        self.viewMenu.addAction(self.dataBrowserAction)
        self.viewMenu.addAction(self.loggerAction)

        self.sequenceMenu = self.menuBar().addMenu("&Sequence")
        self.sequenceMenu.addAction(self.startAction)
        self.sequenceMenu.addAction(self.suspendAction)
        self.sequenceMenu.addAction(self.stopAction)
        self.sequenceMenu.addSeparator()
        self.sequenceMenu.addAction(self.alignmentAction)

        self.toolsMenu = self.menuBar().addMenu("&Tools")
        self.toolsMenu.addAction(self.recoverAction)
        self.toolsMenu.addAction(self.boxLightAction)
        self.toolsMenu.addAction(self.identifyAction)

        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.contentsAction)
        self.helpMenu.addSeparator()
        self.helpMenu.addAction(self.aboutQtAction)
        self.helpMenu.addAction(self.aboutAction)

        # Toolbars

        self.toolBar = self.addToolBar("Toolbar")
        self.toolBar.setObjectName("toolbar")
        self.toolBar.setMovable(False)
        self.toolBar.setFloatable(False)
        self.toolBar.addAction(self.newMeasurementAction)
        self.toolBar.addSeparator()
        self.toolBar.addActions(self.viewMenu.actions())
        self.toolBar.addSeparator()
        self.toolBar.addActions(self.sequenceMenu.actions())
        self.toolBar.addSeparator()
        self.toolBar.addActions(self.toolsMenu.actions())
        spacer = QtWidgets.QWidget(self.toolBar)
        spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        self.toolBar.addWidget(spacer)
        self.toolBarStateLabel = QtWidgets.QLabel()
        self.toolBarStateLabel.setStyleSheet("QLabel{margin-right: 4px; font-weight: bold; color: blue;}")
        self.toolBar.addWidget(self.toolBarStateLabel)

        # Sequence

        self.dashboardWidget = DashboardWidget(self.context, self)
        self.setCentralWidget(self.dashboardWidget)

        self.sequenceController = SequenceController(self.context, self)
        self.sequenceController.failed.connect(self.context.handle_exception)

        # Data Browser

        self.dataBrowserWindow = DataBrowserWindow(self)
        self.dataBrowserWindow.hide()
        self.dataBrowserWindow.setRootPath(self.dashboardWidget.outputPath())
        self.dataBrowserWindow.visibilityChanged.connect(self.dataBrowserAction.setChecked)
        self.dashboardWidget.outputPathChanged.connect(self.dataBrowserWindow.setRootPath)

        # Status bar

        self.messageLabel = QtWidgets.QLabel()
        self.statusBar().addPermanentWidget(self.messageLabel)

        self.progressBar = QtWidgets.QProgressBar()
        self.progressBar.setFixedWidth(300)
        self.statusBar().addPermanentWidget(self.progressBar)

        # Signals

        self.context.message_changed.connect(self.setMessage)
        self.context.progress_changed.connect(self.setProgress)
        self.context.exception_raised.connect(self.showException)
        self.context.suspended.connect(self.setSuspended)

        # States

        self.idleState = QtCore.QState()
        self.idleState.entered.connect(self.enterIdleState)

        self.runningState = QtCore.QState()
        self.runningState.entered.connect(self.enterRunningState)

        self.abortingState = QtCore.QState()
        self.abortingState.entered.connect(self.enterAbortingState)

        # Transitions

        self.idleState.addTransition(self.startRequested, self.runningState)

        self.runningState.addTransition(self.stopRequested, self.abortingState)
        self.runningState.addTransition(self.sequenceController.finished, self.idleState)

        self.abortingState.addTransition(self.sequenceController.finished, self.idleState)

        # State machine

        self.stateMachine = QtCore.QStateMachine()
        self.stateMachine.addState(self.idleState)
        self.stateMachine.addState(self.runningState)
        self.stateMachine.addState(self.abortingState)
        self.stateMachine.setInitialState(self.idleState)

        self.stateMachine.start()

    def readSettings(self):
        try:
            settings = QtCore.QSettings()
            settings.beginGroup("mainwindow")

            geometry = settings.value("geometry", QtCore.QByteArray(), QtCore.QByteArray)
            if not self.restoreGeometry(geometry):
                self.resize(800, 600)

            state = settings.value("state", QtCore.QByteArray(), QtCore.QByteArray)
            self.restoreState(state)

            settings.endGroup()
        except Exception as exc:
            logger.exception(exc)

        self.dashboardWidget.readSettings()
        self.dataBrowserWindow.readSettings()

    def syncSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("mainwindow")

        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("state", self.saveState())

        settings.endGroup()

        self.dashboardWidget.syncSettings()
        self.dataBrowserWindow.syncSettings()

    # Lock

    def isLocked(self) -> bool:
        return self.property("locked") or False

    def setLocked(self, state: bool) -> None:
        self.setProperty("locked", state)

    # Plugins

    def installPlugin(self, plugin):
        try:
            self.plugins.append(plugin)
            plugin.install(self)
        except Exception as exc:
            logger.exception(exc)

    def uninstallPlugin(self, plugin):
        if plugin in self.plugins:
            try:
                plugin.uninstall(self)
                self.plugins.remove(plugin)
            except Exception as exc:
                logger.exception(exc)

    # Shutdown

    def shutdown(self):
        self.dashboardWidget.shutdown()
        self.sequenceController.shutdown()
        self.stateMachine.stop()

    # Logger

    def addLogger(self, logger: logging.Logger) -> None:
        self.loggerWidget.addLogger(logger)

    def removeLogger(self, logger: logging.Logger) -> None:
        self.loggerWidget.removeLogger(logger)

    # Overloaded slots

    def closeEvent(self, event):
        if self.isLocked():
            self.showLockedNotification()
            event.ignore()
        else:
            if self.confirmShutdown():
                self.dataBrowserWindow.close()
                event.accept()
            else:
                event.ignore()

    # File

    @QtCore.pyqtSlot()
    def newMeasurement(self):
        # dialog = CreateMeasurementDialog(self)
        # dialog.setOutputPath(self.dashboardWidget.outputPath())
        # dialog.setOperatorName(self.dashboardWidget.operatorName())
        # dialog.exec()
        # if dialog.result() == dialog.Accepted:
        #     self.dashboardWidget.setOutputPath(dialog.outputPath())
        #     self.dashboardWidget.setOperatorName(dialog.operatorName())
        result = QtWidgets.QMessageBox.question(
            self,
            "New Measurement",
            "Do you want to prepare a new measurement?\n\nThis will clear all plots and restore default sequence configuration."
        )
        if result == QtWidgets.QMessageBox.Yes:
            self.context.reset()
            self.context.reset_data()
            self.dashboardWidget.reset()
            self.dashboardWidget.updateContext()

    # Edit

    @QtCore.pyqtSlot()
    def showProfiles(self) -> None:
        try:
            # TODO
            currentProfile = self.dashboardWidget.profileComboBox.currentData()
            dialog = ProfilesDialog(self)
            dialog.readSettings()
            dialog.exec()
            dialog.syncSettings()
            # TODO
            with QtCore.QSignalBlocker(self.dashboardWidget.profileComboBox):
                self.dashboardWidget.profileComboBox.clear()
                for profile in readProfiles():
                    self.dashboardWidget.profileComboBox.addItem(profile.get("name", ""), profile)
                self.dashboardWidget.profileComboBox.setCurrentIndex(-1)
                index = self.dashboardWidget.profileComboBox.findData(currentProfile)
                self.dashboardWidget.profileComboBox.setCurrentIndex(index)
        except Exception as exc:
            logger.exception(exc)

    @QtCore.pyqtSlot()
    def showResources(self) -> None:
        try:
            dialog = ResourcesDialog(self)
            dialog.readSettings()
            resources = Settings().resources()
            dialog.setResources(resources)
            dialog.exec()
            dialog.syncSettings()
            if dialog.result() == dialog.Accepted:
                Settings().setResources(dialog.resources())
        except Exception as exc:
            logger.exception(exc)

    @QtCore.pyqtSlot()
    def showPreferences(self) -> None:
        try:
            dialog = PreferencesDialog(self)
            dialog.readSettings()
            dialog.loadValues()
            dialog.exec()
            if dialog.result() == dialog.Accepted:
                dialog.saveValues()
            dialog.syncSettings()
        except Exception as exc:
            logger.exception(exc)

    # View

    def setDataBrowserVisible(self, checked: bool) -> None:
        self.dataBrowserWindow.setVisible(checked)

    def setLoggerVisible(self, checked: bool) -> None:
        self.loggerDockWidget.setVisible(checked)

    # Sequence

    def setSuspended(self):
        self.suspendAction.setEnabled(True)

    def toggleSuspend(self, checked) -> None:
        if checked:
            self.suspendAction.setEnabled(False)
            self.context.request_suspend()
        else:
            self.context.request_continue()

    def showAlignmentDialog(self) -> None:
        self.dashboardWidget.updateContext()
        name = self.dashboardWidget.sensorProfileName()
        dialog = AlignmentDialog(self.context)
        try:
            dialog.setSensorName(name)
            dialog.setPadfile(self.context.padfile)
            dialog.readSettings()
            dialog.setLightsOn()
            dialog.setJoystickEnabled(False)
            dialog.exec()
            dialog.syncSettings()
            self.handleAutoStart()
        except Exception as exc:
            logger.exception(exc)
        finally:
            dialog.shutdown()

    def showRecoverStation(self) -> None:
        dialog = RecoverDialog(self.context)
        dialog.run()

    def showIdentify(self) -> None:
        try:
            station = self.context.station
            station.open_resources()
            station.check_identities()
            station.close_resources()
        except Exception as exc:
            logger.exception(exc)

    def toggleBoxLight(self, state) -> None:
        try:
            station = self.context.station
            if state:
                station.box_switch_lights_on()
            else:
                station.box_switch_lights_off()
        except Exception as exc:
            logger.exception(exc)

    # Help

    def showContents(self) -> None:
        contentsUrl = QtWidgets.QApplication.instance().property("ContentsUrl")  # type: ignore
        if contentsUrl:
            webbrowser.open(contentsUrl)

    def showAboutQt(self) -> None:
        QtWidgets.QMessageBox.aboutQt(self, "About Qt")

    def showAbout(self) -> None:
        version = QtWidgets.QApplication.applicationVersion()  # type: ignore
        QtWidgets.QMessageBox.about(self, "About", f"
{APP_TITLE}

Version {version}

{APP_DECRIPTION}

{APP_COPY}

{APP_LICENSE}
")

    def showLockedNotification(self) -> None:
        QtWidgets.QMessageBox.warning(self, "Active Measurement", "A measurement is active. Stop the measurement before quitting the application.")

    def confirmShutdown(self) -> bool:
        result = QtWidgets.QMessageBox.question(self, "Quit Application?", "Do you want to quit the Application?")
        return result == QtWidgets.QMessageBox.Yes

    def confirmReset(self):
        self.context.reset()
        if self.context.data:
            result = QtWidgets.QMessageBox.question(self, "Re-measure?", "Do you want to merge new data with existing data?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
            if result != QtWidgets.QMessageBox.Yes:
                return False
        return True

    # Toolbar

    def setToolBarState(self, text) -> None:
        self.toolBarStateLabel.setText(text)

    def clearToolBarState(self) -> None:
        self.toolBarStateLabel.clear()

    # Status bar

    def setMessage(self, message: str) -> None:
        self.messageLabel.setText(message)

    def setProgress(self, minimum: int, maximum: int, value: int) -> None:
        self.progressBar.setRange(minimum, maximum)
        self.progressBar.setValue(value)
        self.progressBar.show()

    def clearProgress(self) -> None:
        self.progressBar.setRange(0, 1)
        self.progressBar.setValue(0)
        self.progressBar.hide()

    def checkEnvironment(self, data: dict) -> bool:
        minTemperature, maxTemperature = self.dashboardWidget.temperatureRange()
        minHumidity, maxHumidity = self.dashboardWidget.humidityRange()

        temperature = data.get("pt100_1")
        if temperature is None:
            QtWidgets.QMessageBox.warning(self, "Temperature", f"Chuck temperature not available.")
            return False

        if temperature < minTemperature or temperature > maxTemperature:
            QtWidgets.QMessageBox.warning(self, "Temperature", f"Chuck temperature out of range ({formatTemperature(temperature)}).")
            return False

        temperature2 = data.get("box_temperature")
        if temperature2 is None:
            QtWidgets.QMessageBox.warning(self, "Temperature", f"Box temperature not available.")
            return False

        if temperature < minTemperature or temperature2 > maxTemperature:
            QtWidgets.QMessageBox.warning(self, "Temperature", f"Box temperature out of range ({formatTemperature(temperature2)}).")
            return False

        humidity = data.get("box_humidity")
        if humidity is None:
            QtWidgets.QMessageBox.warning(self, "Humidity", f"Box humidity not available.")
            return False

        # Disabled, wait for humidity after door open/close in sequence
        # if humidity < minHumidity or humidity > maxHumidity:
        #     QtWidgets.QMessageBox.warning(self, "Humidity", f"Box humidity out of range ({formatHumidity(humidity)}).")
        #     return False

        return True

    # Actions

    def handleAutoStart(self) -> None:
        """Auto starts measurements if requested by context."""
        if self.context.auto_start_measurement:
            self.context.auto_start_measurement = False

            progress = QtWidgets.QProgressDialog("Auto Start Measurements...", "Cancel", 0, 5, self)
            progress.setValue(0)

            def updateProgress():
                counter = progress.value() + 1
                progress.setValue(counter)

            timer = QtCore.QTimer()
            timer.timeout.connect(updateProgress)
            timer.start(1000)  # 1000 ms = 1 second

            progress.exec()

            if not progress.wasCanceled():
                self.startAction.trigger()

    @QtCore.pyqtSlot()
    def requestStart(self):
        if not self.dashboardWidget.sensorName().strip():
            QtWidgets.QMessageBox.warning(self, "Missing Sensor Name", "No sensor name is set.")
            return
        if not self.dashboardWidget.outputPath().strip():
            QtWidgets.QMessageBox.warning(self, "Missing Output Path", "No output path is set.")
            return
        if not self.dashboardWidget.operatorName().strip():
            QtWidgets.QMessageBox.warning(self, "Missing Operator Name", "No operator name is set.")
            return
        # Check environment limits
        data = self.context.station.box_environment()
        if not self.checkEnvironment(data):
            return
        if not self.confirmReset():
            return
        self.startRequested.emit()

    @QtCore.pyqtSlot()
    def requestStop(self):
        self.stopRequested.emit()

    # States

    @QtCore.pyqtSlot()
    def enterIdleState(self):
        self.newMeasurementAction.setEnabled(True)
        self.profilesAction.setEnabled(True)
        self.resourcesAction.setEnabled(True)
        self.preferencesAction.setEnabled(True)
        self.startAction.setEnabled(True)
        self.suspendAction.setEnabled(False)
        self.suspendAction.setChecked(False)
        self.stopAction.setEnabled(False)
        self.alignmentAction.setEnabled(True)
        self.recoverAction.setEnabled(True)
        self.boxLightAction.setEnabled(True)
        self.dashboardWidget.setLocked(False)
        self.dashboardWidget.setInputsLocked(False)
        self.context.reset()
        self.clearToolBarState()
        self.clearProgress()
        self.setLocked(False)

        if self.dashboardWidget.wasEnvironOutOfBounds():
            QtWidgets.QMessageBox.warning(self, "Environment", "Temperature/Humidity limits exceeded durring past measurement.")

    @QtCore.pyqtSlot()
    def enterRunningState(self):
        self.setLocked(True)
        self.newMeasurementAction.setEnabled(False)
        self.profilesAction.setEnabled(False)
        self.resourcesAction.setEnabled(False)
        self.preferencesAction.setEnabled(False)
        self.startAction.setEnabled(False)
        self.suspendAction.setEnabled(True)
        self.suspendAction.setChecked(False)
        self.stopAction.setEnabled(True)
        self.alignmentAction.setEnabled(False)
        self.recoverAction.setEnabled(False)
        self.boxLightAction.setEnabled(False)
        self.boxLightAction.setChecked(False)
        self.loggerWidget.showRecentRecords()
        self.dashboardWidget.setLocked(True)
        self.dashboardWidget.setInputsLocked(True)
        self.dashboardWidget.updateContext()
        self.sequenceController.setSequence(self.dashboardWidget.sequence())
        self.sequenceController.start()
        self.setToolBarState("Running...")

    @QtCore.pyqtSlot()
    def enterAbortingState(self):
        self.startAction.setEnabled(False)
        self.suspendAction.setEnabled(False)
        self.suspendAction.setChecked(False)
        self.stopAction.setEnabled(False)
        self.context.request_abort()
        self.setToolBarState("Aborting...")

    @QtCore.pyqtSlot(Exception)
    def showException(self, exc):
        details = "".join(traceback.format_tb(exc.__traceback__))
        dialog = QtWidgets.QMessageBox(self)
        dialog.setWindowTitle("Exception occured")
        dialog.setIcon(dialog.Critical)
        dialog.setText(format(exc))
        dialog.setDetailedText(details)
        dialog.setStandardButtons(dialog.Ok)
        dialog.setDefaultButton(dialog.Ok)
        # Fix message box width
        spacer = QtWidgets.QSpacerItem(448, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        dialog.layout().addItem(spacer, dialog.layout().rowCount(), 0, 1, dialog.layout().columnCount())
        dialog.exec()


import json
f = open('data.json')
data = json.load(f)
# print(data[0]["batters"]["batter"][2]["id"])
# print(data[0]["topping"][2]["type"])

def sample():
   # try :
        if id=="0001":
            if val :
                if int(val) in range(1001,1010):
                    s=int(val[-1])-1
                    print(data[0]["batters"]["batter"][int(s)]["type"])
                else:
                    if int(val) in range(5001,5010):
                        s=int(val[-1])-1
                        print(data[0]["topping"][int(s)]["type"])

        if id=="0002":
            if val :
                if int(val) in range(1001,1999):
                    s=int(val[-1])-1
                    print(data[1]["batters"]["batter"][int(s)]["type"])
                else:
                    if int(val) in range(5001,6000):
                        s=int(val[-1])-1
                        print(data[1]["topping"][int(s)]["type"])

        if id=="0003":
            if val:
                if int(val) in range(1001,1999):
                    s=int(val[-1])-1
                    print(data[2]["batters"]["batter"][int(s)]["type"])
                else:
                    if int(val) in range(5001,6000):
                        s=int(val[-1])-1
                        print(data[2]["topping"][int(s)]["type"])

   # except IndexError:
      #  print("not valid")

id=input("Enter the ID: ")
print(id,"Valid Data")

val=input("Enter the VALUE id: ")

sample()


#! usr/bin/python
import pytz, time
import speech_recognition as sr
import pyaudio
import threading
import subprocess
from multiprocessing import process
from voiceCommands import *

def engine(source, r):
    audio=r.listen(source)
    test=None
    test=str(r.recognize(audio))
    switch(test)

def main():
    r=sr.Recognizer()
    r.energy_threshold=800
    r.pause_threshold=0.3
    r.quiet_duration=0.3
    with sr.Microphone() as source:
        try:
             t=threading.Thread(target=engine(source,r))
             t.daemon=True
             #engine(source,r)
             t.start()
        except LookupError:
            print("No voice detected")

if __name__ == "__main__":main()


from django.conf import settings
import random
import pycountry
import numpy as np

from constant.models import YardBucket, FeetBucket
from core.models import PRACTICE_TYPES, DeltaShotReport, Practice
from profiles.models import ClubBag

def pick_random_distances(putting=False, practice=None, driver=None, clubs=None):
    # get existing dist_list
    # --- e.g: 75, 128, 103, 155, 189, 140, 170, 105, 200, 240, 85, 110, 185, 133, 160, 350, 70, 162, 120, 130
    if practice is None:
        existing_dist_list = []
    else:
        existing_dist_list = DeltaShotReport.objects.filter(practice=practice).values_list('distance', flat=True)
        if len(existing_dist_list) > 0:
            existing_dist_list = list(set(existing_dist_list))

    # pick_number e.g: [1,2,2,2,1,1,2]
    if putting:
        buckets = FeetBucket.objects.all()
    else:
        buckets = YardBucket.objects.all()

    # get new distance in every bucket
    new_dist = {}
    final_dist_list = []
    clubs_list = clubs.all()
    club_dist_list = []
    rest_dist_list = []

    if len(clubs.all()):
        for club in reversed(clubs_list):
            club_dist_list.append(club.avg_dist)
        for i in range(len(club_dist_list)-1):
            random_choose_dist = random.choice(list(range(club_dist_list[i], club_dist_list[i+1])))
            if random_choose_dist >= 75:
                rest_dist_list.append(random_choose_dist)

    for bucket in buckets:
        new_dist[bucket.pk] = bucket.pick_dists(existing_dist_list, driver)

    # randomize by bucket order
    bucket_order = list(buckets.values_list('pk', flat=True))
    random.shuffle(bucket_order)

    while True:
        old_count = len(final_dist_list)
        for bucket_pk in bucket_order:
            try:
                final_dist_list.append(new_dist[bucket_pk].pop())
            except IndexError:  # pop from empty list
                pass
        if len(final_dist_list) >= settings.PICK_COUNT:
            break
        if len(final_dist_list) == old_count:  # not be appended anymore
            break

    final_dist_list = final_dist_list + rest_dist_list
    random.shuffle(final_dist_list)

    # if length > PICK_COUNT, randomly pop
    _length = len(final_dist_list)
    if _length > settings.PICK_COUNT:
        poped_random_index_list = random.sample(range(_length), _length - settings.PICK_COUNT)
        poped_random_index_list.sort(reverse=True)
        for index in poped_random_index_list:
            final_dist_list.pop(index)

    return final_dist_list

def pick_standard_putts(practice=None):
    if practice is None:
        existing_dist_list = []
    else:
        existing_dist_list = practice.get_existing_dist_list()

    buckets = FeetBucket.objects.filter(max__lte=25)  # NOTE: hardcode 25
    # get new distance in every bucket
    new_dist = {}
    for bucket in buckets:
        new_dist[bucket.pk] = bucket.pick_standard_dists(existing_dist_list)

    # randomize by bucket order
    bucket_order = list(buckets.values_list('pk', flat=True))
    random.shuffle(bucket_order)

    final_dist_list = []
    for bucket_pk in bucket_order * 3:  # NOTE: hardcoded 3.
        try:
            final_dist_list.append(new_dist[bucket_pk].pop())

        except IndexError:  # pop from empty list
            pass

    # if length > PICK_COUNT, randomly pop
    _length = len(final_dist_list)
    if _length > settings.PICK_COUNT:
        poped_random_index_list = random.sample(range(_length), _length - settings.PICK_COUNT)
        for index in poped_random_index_list:
            final_dist_list.pop(index)

    return final_dist_list

def pick_chip_distances(practice=None):
    return random.sample(set(range(5, 25)), 10)

def pick_pitch_distances(practice=None):
    return random.sample(set(range(25, 75)), 10)

def get_blocked_bin(user=None):
    """
    :return: bin for block practice. NOTE: first bin (check again later.)
    """
    if user is None:
        return None

    bin_list = YardBucket.get_hist_bin()
    practice_list = Practice.objects.filter(
        user=user,
        practice_type__in=[PRACTICE_TYPES.random, PRACTICE_TYPES.block, PRACTICE_TYPES.serial, PRACTICE_TYPES.custom]
    )

    block_bin = None
    yards = YardBucket.objects.all()

    for yard in yards:
        all_shots1 = DeltaShotReport.objects.filter(
            practice__in=practice_list).filter(distance__in=range(yard.min, yard.max)).order_by("-reported_at")
        all_shots = all_shots1[0:10]
        all_dists = [shot.distance for shot in all_shots]
        hit_dists = [shot.distance for shot in all_shots if shot.hit == 0]

        total_hist, bins = np.histogram(all_dists, bins=bin_list)
        hit_hist, bins = np.histogram(hit_dists, bins=bin_list)

        for h, t, b in zip(hit_hist, total_hist, bin_list):
            if t != 0:
                if t < 10 or h / t < 0.3:
                    continue

                # if len(hit_dists):
                #     if sum(hit_dists) / len(hit_dists) > 224:
                #         block_bin = YardBucket.objects.get(min=196)
                #         break

                block_bin = YardBucket.objects.get(min=b)
                break

        if block_bin is not None:
            break

    return block_bin

def pick_custom_distances(min, max):
    if min == max:
        return [min for i in range(10)]
    try:
        dist_set = list(range(min, max+1))
        if len(dist_set) < 10:
            dist_set = (10//len(dist_set) + 1) * dist_set
        return random.sample(dist_set, 10)
    except ValueError as e:
        return []

def is_full_swing(p_type):
    """
    :param p_type: practice_type
    :return: True/False
    """
    if p_type in [PRACTICE_TYPES.random, PRACTICE_TYPES.warmup,
                  PRACTICE_TYPES.serial, PRACTICE_TYPES.block,
                  PRACTICE_TYPES.custom]:
        return True
    else:
        return False

def is_short_game(p_type):
    """
    :param p_type: practice_type
    :return: True/False
    """
    if p_type in [PRACTICE_TYPES.chip, PRACTICE_TYPES.pitch]:
        return True
    else:
        return False

def get_country_list():
    """Return a list of ISO 3166-1 Alpha 2 country codes."""

    countries0 = [(x.alpha_2, x.name)for x in pycountry.countries]
    countries_sorted = sorted(countries0, key=lambda x: x[1])
    country_list = [
        ("", "--- Select country ---"),
    ]
    country_list.extend(countries_sorted)
    return country_list

def groupby2dict(grouped_df):
    levels = len(grouped_df.index.levels)
    dicts = [{} for i in range(levels)]
    last_index = None

    for index, value in grouped_df.itertuples():

        if not last_index:
            last_index = index

        for (ii, (i, j)) in enumerate(zip(index, last_index)):
            if not i == j:
                ii = levels - ii - 1
                dicts[:ii] = [{} for _ in dicts[:ii]]
                break

        for i, key in enumerate(reversed(index)):
            dicts[i][key] = value
            value = dicts[i]

        last_index = index

    return dicts[-1]