from urllib.request import urlopen
import re
import pyproteins.container.customCollection
import pyproteins.container.Core
import pyproteinsext.pfam as pfam
import json

import gzip

from xml.etree.ElementTree import parse, dump, fromstring, register_namespace, ElementTree, tostring

from os.path import expanduser

PfamEntrySet = None
uniprotEntrySet = None

PfamCache = None

from json import JSONEncoder

import pyproteins.utils.make_json_serializable
#def _default(self, obj):
#    return getattr(obj.__class__, "toJSON", _default.default)(obj)
#
#_default.default = JSONEncoder.default
#JSONEncoder.default = _default

## Returns a list of all keyword in collection, along with the list of uniprotObj featuring them
def keyWordChart(uniprotObjIter, kwType='GO'):
    def kwMapper(obj, _type) :
        if _type == 'GO':
            return obj.GO
        raise TypeError("implement other KW plz")

    kwChart = {}
    for uniprotObj in uniprotObjIter:
        for kwObj in  kwMapper(uniprotObj, kwType):
            if kwObj not in kwChart:
                kwChart[kwObj] = []
            kwChart[kwObj].append(uniprotObj)

    return sorted([ (k,v) for k,v in kwChart.items() ], key=lambda x : len(x[1]), reverse=True)

# Give link to uniprot Collection to allow proxy settings
# and cache setting for it and pfam
def getUniprotCollection ():
    global uniprotEntrySet
    if not uniprotEntrySet:
        uniprotEntrySet = EntrySet()

    return uniprotEntrySet

#def setCache(location):
#    print location
#    uniprotEntrySet.setCache(location=location)

#def proxySetting(**kwargs):
#    proxySetting(**kwargs)

def getPfamCollection ():
    global PfamEntrySet
    if not PfamEntrySet:
        home = expanduser("~")
        PfamCacheDefault = home
        PfamEntrySet = pfam.EntrySet(PfamCacheDefault)

    return PfamEntrySet

def proxySetting(**param):
    pyproteins.container.Core.proxySetting(param)

'''
    TODO Isoform, minimal -> affects the fasta sequence
                 Need to check isoform data xml structure, sequence variant specs of Uniprot
'''

def strip(string):
    subString = re.search(".xml$", string)
    if subString:
        return string.split(".")[0]

    return None

def capture(string):
    subString = re.search("[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}", string)
    if subString:
        return subString.group()

    return None

def isValidID(string):
    if re.match("^[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}$", string):
        return True
    if re.match("^[A-Z]{3}[0-9]{5}$", string):
        return True
    return False

class fetchEntries():
    def __init__(self, list):
        self.list = list
        self.entrySet = [Entry(id = id) for id in self.list]
    def __iter__(self):
        for entry in self.entrySet:
            yield entry

''' Collection of uniprot entries
customary cache location is "/Users/guillaumelaunay/work/data/uniprot"
'''
class EntrySet(pyproteins.container.customCollection.EntrySet):

    def __init__(self, **kwargs):
        self.ns = '{http://uniprot.org/uniprot}'

        home = expanduser("~")
        cachePath = home
        self.isXMLCollection = False
        self.isRedisCollection = False
        if 'streamXML' or 'collectionXML' in kwargs:
            self.isXMLCollection = True
            if 'streamXML' in kwargs:
                self.etree = parse(kwargs['streamXML'])
            elif 'collectionXML' in kwargs:
                if kwargs['collectionXML'].endswith('.gz'):
                    with gzip.open(kwargs['collectionXML'], 'r') as gz:
                        self.etree = parse(gz)
                else:
                    self.etree = parse(kwargs['collectionXML'])

            self.etree_root = self.etree.getroot()
            self.index = None
          #  print(f"==> {type(self.etree_root)} {type(self.etree)} <==")

        super().__init__(collectionPath=cachePath, constructor=Entry, typeCheck=isValidID, indexer=strip)
        if 'collectionXML' in kwargs:
            print (f"Acknowledged {len(self)} entries {kwargs['collectionXML']}")

    def __len__(self):
        if self.isXMLCollection:
            return len(list([ _ for _ in self ]))
        return super().__len__()

    def keys(self):
        """ Returns uniprot identifiers within collection as a generator """
        if self.isXMLCollection:
            for entry in self.etree_root.findall(f"{self.ns}entry"):
                for alt_acc in entry.findall(f"{self.ns}accession"):
                    yield(alt_acc.text)
        else :
            return super().keys()

    def has(self, id):
        if self.isXMLCollection:
            if self.index is None:
                self.index = {}
                for acc in self.keys():
                    self.index[acc] = True
            return id in self.index
        else :
            return super().has()

    def __iter__(self):
        if self.isXMLCollection:
            for entry in self.etree_root.findall(f"{self.ns}entry"):
                uniprotID = entry.find(f"{self.ns}accession").text
                yield Entry(uniprotID, xmlEtreeHandler = entry, xmlNS = self.ns)

    def get(self, uniprotID):
        if self.isXMLCollection:
            for entry in self.etree_root.findall(f"{self.ns}entry"):
                for acc in entry.findall(f"{self.ns}accession"):
                    if acc.text == uniprotID: # entry is the node matching provided UNIPROT accessor
                        return Entry(uniprotID, xmlEtreeHandler = entry, xmlNS = self.ns)
            return None
        else :
            print(f"Looking in XML files/dir collection for {uniprotID}")
            return super().get(uniprotID, xmlNS = self.ns)

    def serialize(self, ext=''):
        global PfamCache
        print ("serializing uniprot collection")
        super().serialize(ext=ext)
        if PfamCache:
            print ("serializing pfam collection")
            getPfamCollection().serialize(ext=ext)

    @property
    def taxids(self):
        taxids = set()
        for e in self:
            taxids.add(e.taxid)
        return list(taxids)

class Entry(pyproteins.container.Core.Container):
    ## Wrap and split kwargs
    # Re-encode etree xpath like search
    # https://docs.python.org/3/library/xml.etree.elementtree.html#example
    # //div[@id='..' and @class='...]

    def __repr__(self):
        asStr = f"{self.id}:{self.AC}\n"
        asStr += f"{self.name}:{self.fullName}({self.geneName})\n"

        if self.STRING_ID:
            asStr += f"STRING_ID:{self.STRING_ID}\n"

        asStr += f"taxid:{self.taxid}:{self.lineage}\n"
        asStr += f"KW:{self.KW}\n"
        asStr += f"GO:{self.GO}\n"

        return asStr

    def _buildXpath(self, xpath, **kwargs):

        _xpath_ = xpath.replace("/",f"/{self._ns}")
        _xpath_ += ' and '.join( [ f"[@{k}='{v}']" for k,v in kwargs.items() ] )

        if not ( _xpath_.startswith('/') or _xpath_.startswith('./') ) :
            _xpath_ = f"{self._ns}{_xpath_}"

        #print(f"building xpath from {xpath} to {_xpath_}")
        return _xpath_

    def _xmlAttrib(self, key, elem=None):
        if elem is None:
            elem = self.xmlHandler
        return elem.attrib[key]

    def _xmlFind(self, tag, elem=None, **kwargs):
        if elem is None:
            elem = self.xmlHandler
        if len(kwargs.keys()) > 1:
            raise(f"Asking for Too many attributes ({len(kwargs.keys())}) in tag xpath search")

        xpathStr = self._buildXpath(tag, **kwargs)
        return elem.find(xpathStr)

    def _xmlFindAll(self, tag, elem=None, **kwargs):
        if elem is None:
            elem = self.xmlHandler

        xpath = self._buildXpath(tag, **kwargs)
        return elem.findall(xpath)

    def __init__(self, id, baseUrl="http://www.uniprot.org/uniprot/", fetchable= True, fileName=None, xmlEtreeHandler=None, xmlNS=None):
        if not id:
            raise TypeError('identifier is empty')
        super().__init__(id, url=baseUrl + str(id) + '.xml', fileName=fileName)
        #pyproteins.container.Core.Container.__init__(self, id, url=baseUrl + str(id) + '.xml', fileName=fileName)

        if not xmlNS is None:
            self._ns = xmlNS

        if not xmlEtreeHandler is None:
            self.xmlHandler = xmlEtreeHandler
        else:
            print("Search for", f"{xmlNS}entry")
            self.xmlHandler = self.getXmlHandler(fetchable=fetchable).find(f"{xmlNS}entry")

        if self.xmlHandler is None:
            return None

        self.name = self._xmlFind("./name").text

        if not self._xmlFind("./protein/recommendedName/fullName") is None:
            self.fullName = self._xmlFind("./protein/recommendedName/fullName").text
        else:
            self.fullName = self.name

        self.geneName =  None
        e = self._xmlFind("gene")
        if not e is None:
           self.geneName = self._xmlFind("./name", elem=e).text

        self.STRING_ID = None
        e = self._xmlFind("./dbReference", type="STRING")
        if not e is None:
            self.STRING_ID = self._xmlAttrib('id', elem=e)

        self.parseAC()
        self.parseLineage()
        self.parseKW()
        self.parseGO()
        self.parseSequence()
        self.parse_subcellular_location()

# Following oarsing stages are disabled since we got rid of bs4
# Need to port them  to lxml, making use of xpath syntax
    def PARSER_TO_PORT_TO_ETREE(self):

        self.parseSse()
        self.Ensembl = self.parseEnsembl()
        self.GeneID = self.parseGeneID()
        self.parseSequence()
        self.parsePDB()
        self.parseMIM()
        self.parseDI()
        self.parseORPHA()
        self.xref = self.get_xref()
        self.parseInterpro()

    def __hash__(self):
        return hash(self.id)

    def __copy__(self):
        return self

    def __deepcopy__(self, memo):
        return self

    def __eq__ (self, other):
        return self.id == other.id

    def toJSON(self):
        #print ('toJSON')
        #asDict = {}
        #for k in self.__dict__.keys():
        #    if k == 'xmlHandler':
        ##        continue
        #    if k != 'GO':
        #        continue
        #    asDict[str(k)] = getattr(self, k)
        #    print(asDict)

        container = {}
        for k, v in self.__dict__.items():
            if k == 'name':
                container[k] = v
            if k == 'GO':
                container[k] = [ go.__dict__ for go in v ]
            if k == 'id':
                container[k] = v
            if k == 'geneName':
                container[k] = v
            if k == 'fullName':
                container[k] = v
            if k == 'taxid':
                container[k] = v
        return container

    def parseAC(self):
        self.AC = [ e.text for e in  self._xmlFindAll("./accession") ]

    def parseLineage(self):
        self.lineage = [ e.text for e in  self._xmlFindAll("./organism/lineage/taxon") ]
        e = self._xmlFind('organism/dbReference', type='NCBI Taxonomy')
        self.taxid = self._xmlAttrib('id', elem=e)

    def parseMIM(self):
        self.MIM = []
        for e in self.xmlHandler.find_all("dbReference", type="MIM"):
            if str(e.parent.name) == 'entry':
                self.MIM.append(MimKW(e))

    def parseDI(self):
        self.DI = []
        for e in self.xmlHandler.find_all("disease"):
            self.DI.append(DI(e))

    def parseGO(self):
        self.GO = []
        for e in self._xmlFindAll("dbReference", type="GO"):
            self.GO.append(GoKW(
                self._xmlAttrib('id', elem=e),
                self._xmlAttrib('value', elem = self._xmlFind("property", elem=e, type="term") ),
                self._xmlAttrib('value', elem = self._xmlFind("property", elem=e, type="evidence") )
            ))

    def parseORPHA(self):
        self.ORPHA = []
        for e in self.xmlHandler.find_all("dbReference", type="Orphanet"):
            if str(e.parent.name) == 'entry':
                self.ORPHA.append(OrphaKW(e))

    def parsePDB(self):
        self.pdbRef = []
        for e in self.xmlHandler.find_all("dbReference", type="PDB"):
            self.pdbRef.append(PDBref(e))

    def parseDomain(self):
        try:
            self.domains=getPfamCollection().map(uniprotID=self.id)
        except:
            self.domains=[]

        #self.domains = []
        #for e in self.xmlHandler.find_all("feature", type="domain"):
        #    buf = Domain(e, self.id)
        #    if buf.description:
        #        self.domains.append(Domain(e, self.id))
        #for e in self.xmlHandler.find_all("feature", type="repeat"):
        #    buf = Domain(e, self.id)
        #    if buf.description:
        #        self.domains.append(Domain(e, self.id))
        #if not self.domains:
        #    print "No domain data found for " + self.id + ", attempting pfam"
        #    try :
        #        self.domains = getPfamCollection().map(uniprotID=self.id)
        #    except ValueError as msg:
        #        print ("Could not bind uniprot to its pfam ressources reason\n" + str(msg))

    def parseSse(self):
        self.sse = []
        for e in self.xmlHandler.find_all("feature", type="strand"):
            self.sse.append(Sse(e))
        for e in self.xmlHandler.find_all("feature", type="turn"):
            self.sse.append(Sse(e))
        for e in self.xmlHandler.find_all("feature", type="helix"):
            self.sse.append(Sse(e))

    def parseKW(self):
        self.KW = []

        for e in self._xmlFindAll("./keyword"):
            self.KW.append( UniprotKW(self._xmlAttrib('id', elem=e), e.text) )

    def parseSequence(self):
        self.sequence = self._xmlFind("./sequence").text.replace("\n", "").replace(" ", "")
        #self.sequence = Sequence(self.xmlHandler.find("sequence", {"length" : True}))
    #    pass

    def parseEnsembl(self):
        #Search for Ensembl id : ENSGXXXXXXXXXXX
        Ensembl_id = []
        for e in self.xmlHandler.find_all("dbReference", type="Ensembl"):
            for e_gene_id in e.find_all('property',type='gene ID'):
                if e_gene_id["value"] not in Ensembl_id:
                    Ensembl_id.append(e_gene_id["value"])
        return Ensembl_id

    def parseGeneID(self):
        GeneID= []
        for e_gene_id in self.xmlHandler.find_all("dbReference", type="GeneID"):
            if e_gene_id["id"] not in GeneID:
                GeneID.append(e_gene_id["id"])
        return GeneID

    def parse_subcellular_location(self):
        self.subcellular_location = []
        for citation in self._xmlFindAll('comment', type="subcellular location"):
            for sl in self._xmlFindAll('subcellularLocation', elem = citation):
                for location in self._xmlFindAll('location', elem = sl):
                    self.subcellular_location.append(location.text)
        # except :
        #     print("WARNING", self.AC, "subcellular location can't be parsed")
        #     print(citation, sl, location)

        # self.subcellular_location = []
        # citations = self._xmlFindAll("comment", type="subcellular location")
        # for citation in citations:
        #     annotation = self._xmlFind('subcellularLocation/location', elem=citation)

        #     # if not annotation:
        #     #     print("no annotation", self.AC, annotation)
        #     # else:
        #     try:
        #         self.subcellular_location.append(annotation.text)
        #     except:
        #         print("WARNING", self.AC, "subcellular location can't be parsed")
        #         self.subcellular_location = ['WARNING']

    def get_xref(self):
        # print("GET XREF")
        dic_xref = {'EMBL': {}, 'RefSeq': {}}
        # Search EMBL
        for e in self.xmlHandler.find_all("dbReference", type="EMBL"):
            if str(e.parent.name) == 'entry':
                for e_prot_id in e.find_all('property',type='protein sequence ID'):
                    dic_xref["EMBL"][e["id"]] = e_prot_id["value"]
        # Search RefSeq
        for e in self.xmlHandler.find_all("dbReference", type="RefSeq"):
            if str(e.parent.name) == 'entry':
                for e_prot_id in e.find_all('property',type='nucleotide sequence ID'):
                    dic_xref["RefSeq"][e_prot_id["value"]] = e["id"]
        return dic_xref

    def parseInterpro(self):
        self.Interpro = []
        for e in self.xmlHandler.find_all("dbReference", type = "InterPro"):
            for name in e.find_all("property", type = "entry name"):
                self.Interpro.append(Interpro(e))

    @property
    def fasta(self):
        return '>' + str(self.id) + ' ' + str(self.name) + '\n' + str(self.sequence)

    def peptideSeed(self):
        return {
            'id' : self.id,
            'desc' : self.name,
            'seq' : str(self.sequence)
        }

        #self.parseIsoform()

    def hasKW(self, keyword):
        if keyword.upper() in (kw.id.upper() for kw in self.KW):
            return True
        return False

    @property
    def isGOannot(self):
        return not len(self.GO) == 0

    def hasGO(self, keyword):
        if keyword.upper() in (kw.id.upper() for kw in self.GO):
            return True
        return False

    def hasMIM(self, keyword):
        if keyword.upper() in (kw.id.upper() for kw in self.MIM):
            return True
        return False

    def hasORPHA(self, keyword):
        if keyword.upper() in (kw.id.upper() for kw in self.ORPHA):
            return True
        return False

    def hasDI(self, keyword):
        if keyword.upper() in (kw.id.upper() for kw in self.DI):
            return True
        return False

    # returns a position information container
    def pos(self, i, lookup=False):
        if  i < 1 or i >= len(self.sequence):
            raise IndexError("sequence index \""  + str(i) + "\" is out of range (" + str(len(self.sequence)) + ")")
        d = self._domainFlyCast(position=i)
        if len(d) > 1:
            raise ValueError("\"" + self.id + "\" lays more than one domain \"" + str(len(d)) +
                             "\" at position " + str(i)  )
        domain="SomeDefault"
        if d:
            domain = d[0]
        else:
            l = self._domainFlyCast(NterLookup=i)
            r = self._domainFlyCast(CterLookup=i)
            if l and r:
                domain = { 'outOfBounds' : 'hinge' }
            elif l:
                domain = { 'outOfBounds' : 'Cter' }
            elif r:
                domain = { 'outOfBounds' : 'Nter' }
            else:
                print ("Not a hinge a Cter or a Nter at " + str(i) + " in " + self.id)
        return Position(i, domain=domain, sse=self._getSse(i), letter=self.sequence[i])

    def _domainFlyCast(self, **kwargs):
        if not self.domains:
            raise ValueError("No domain data available for " + self.id)
            #return None
        if 'position' in kwargs:
            results = [ d._dict for d in self.domains if d.owns(kwargs['position']) ]
            return results
        if 'NterLookup' in kwargs:
            for i in range (int(kwargs['NterLookup']), 0, -1):
                d = self._domainFlyCast(position=i)
                if d:
                    return d#d[0]
        if 'CterLookup' in kwargs:
            for i in range (int(kwargs['CterLookup']), len(self.sequence) + 1):
                d = self._domainFlyCast(position=i)
                if d:
                    return d#d[0]

        return None

    def _getSse(self, position):
        i = int(position)
        if not self.sse:
            return None
        for d in self.sse:
            if d.begin <= i <= d.end:
                return d.type
        return "coil"

# Custom encoder for uniprot entity
class EntryEncoder(json.JSONEncoder):
    def default(self, entryObj):
        if isinstance(entryObj, pyproteins.container.Core.Container):
            container = {}
            for k, v in entryObj.__dict__.items():
                if k == 'name':
                    container[k] = v
                if k == 'GO':
                    container[k] = [ go.__dict__ for go in v ]
                if k == 'id':
                    container[k] = v
                if k == 'geneName':
                    container[k] = v
                if k == 'fullName':
                    container[k] = v
            return container
        # Error
        return json.JSONEncoder.default(self, entryObj)

class Position ():
    def __init__(self, number, **kwargs):
        self.number = number
        self.domain = kwargs['domain'] if 'domain' in kwargs else None

        self.sse = kwargs['sse'] if 'sse' in kwargs else None
        self.letter = kwargs['letter'] if 'letter' in kwargs else None

    def __repr__(self):
        return self.letter + str(self.number) + "(" + str(self.sse) + ") : " + str(self.domain)

class Sequence():
    def __init__(self, e):
        if not e:
            raise ValueError("Cant parse sequence")
        self.string = e.text.replace("\n", "").replace(" ", "")
        self.mass =e._xmlAttrib('mass')
    def __len__(self):
        return len(self.string)
    def __getitem__(self, i):
        if isinstance(i, slice):
            newSlice = slice(i.start - 1, i.stop - 1)
            return self.string[newSlice]
        else:
            if i < 1 or i > len(self):
                raise IndexError(str(i) + " out of range [1-" + self(len(self)) + "]")
            return self.string[i - 1]

    def __repr__(self):
        return self.string

class Domain(object):
    def __init__(self, xmlHandler, id):
        self.begin = [int(e['position']) for e in xmlHandler.find_all('begin')]
        self.end = [int(e['position']) for e in xmlHandler.find_all('end')]
        if len(self.begin) != len(self.end):
            raise ValueError("Number of end/stop differs " + str(self.begin) + "/" + str(self.end))
        if 'description' not in xmlHandler:
            #print "Warning Domain w/in \"" + id + "\" does not feature any description"
            self.description = None
        else:
            self.description = xmlHandler['description']
        self.carriedBy = id

    def __eq__(a, b):
        if a.id != b.id:
            return False
        if a.begin != b.begin:
            return False
        if a.end != b.end:
            return False
        if a.description != b.description:
            return False

        return True

    def __repr__(self):
        b = [ str (self.begin[i]) + "-" + str(self.end[i]) for i,e  in enumerate (self.begin) ]
        return "\"" + self.description + "\"\t" + ",".join(b) + "\n"

    def owns(self, position):
        try :
            j = int(position)
        except ValueError as err:
            print ("Improper amino acid position \"" + str(position) + "\"")
            return False

        for i, e  in enumerate (self.begin):
            if self.begin[i] <= j <= self.end[i]:
                return True

        return False

    @property
    def _dict(self):
        return self.__dict__

class Sse():
    def __init__(self, e):
        self.begin = int(e.find('begin')['position'])
        self.end =  int(e.find('end')['position'])
        self.type = e['type']
    def __repr__(self):
        return "SSE:" + self.type + " " + str(self.begin) + "-" + str(self.end)

class annotTerm:
    def __init__(self):
        pass
    def __hash__(self):
        return hash(str(self.id))
    def __eq__(self, other):
        return hash(self) == hash(other)

class GoKW(annotTerm):
    def __init__(self, id,term, evidence):
        self.id = id
        self.term = term
        self.evidence = evidence
    def __repr__(self):
        return self.id + ":" + self.term + "{" + self.evidence + "}"

class MimKW(annotTerm):
    def __init__(self, e):
        self.id = e['id']
        self.value = e.find('property', type='type')['value']
    def __repr__(self):
        return self.id + ":" + self.value

class OrphaKW(annotTerm):
    def __init__(self, e):
        self.id = e['id']
        self.type = e.find("property")['type']
        self.value = e.find('property')['value']

    def __repr__(self):
        return self.id + ": (" + self.type + ")" + self.value

class DI(annotTerm):
    def __init__(self, e):
        self.id = e['id']
        self.name = e.find('name').string
        acronym = e.find('acronym')
        self.acronym = acronym.string if acronym else 'NA'
        self.description = e.find('description').string
    def __repr__(self):
        return self.id + ":" + self.name + " (" + self.acronym + ") {" + self.description + "}"

class PDBref():
    def __init__(self, e):
        self.id = e['id']
        self.method = e.find('property', type='method')['value'] if e.find('property', type='method') else None
        self.resolution = e.find('property',type='resolution')['value'] if e.find('property', type='resolution') else None
        self.chains = e.find('property',type='chains')['value'] if e.find('property', type='chains') else None

    def __repr__(self):
        string = "PDB_id: " + self.id
        elements = []
        if self.method:
            elements.append("method: " + self.method)
        if self.resolution :
            elements.append("resolution: " + self.resolution)
        if self.chains:
            elements.append("chains: " + self.chains)
        if elements:
            return string + ':{' + ','.join(elements)  + '}'

        return string

class UniprotKW():
    def __init__(self, id, text):
        self.id = id
        self.term = text
    def __repr__(self):
        return self.id + ":" + self.term

class Interpro():
    def __init__(self,e):
        self.id = e["id"]
        self.getName(e)

    def __repr__(self):
        return self.id + ":" + self.name

    def getName(self,e):
        for name in e.find_all("property", type = "entry name"):
           self.name = name["value"]

class Genome():
    def __init__(self,xmlHandler):
        self.searchEMBL(xmlHandler)
        self.searchRefSeq(xmlHandler)

    def searchEMBL(self,xmlHandler):
        self.EMBLRef=[]
        self.EMBLProteinRef=[]
        for e in xmlHandler.find_all("dbReference", type="EMBL"):
            if str(e.parent.name) == 'entry':
                self.EMBLRef.append(e['id'])
                for e_prot_id in e.find_all('property',type='protein sequence ID'):
                    self.EMBLProteinRef.append(e_prot_id['value'])

    def searchRefSeq(self,xmlHandler):
        self.RefSeqRef=[]
        self.RefSeqProteinRef=[]
        for e in xmlHandler.find_all("dbReference", type="RefSeq"):
            if str(e.parent.name) == 'entry':
                self.RefSeqProteinRef.append(e['id'])
                for e_prot_id in e.find_all('property',type='nucleotide sequence ID'):
                    self.RefSeqRef.append(e_prot_id['value'])


# © Designed and Developed by Mehmet Güdük.
# © Licensed with GPL-3.0 License, Author is Mehmet Güdük.

import re
import sys
import random
from datetime import datetime
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QMessageBox, QInputDialog
from PyQt5.QtCore import QTimer, QDate
from interface import Ui_MainWindow
from database_functions import *
from default_deck import *

class myApp(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # We are connecting to the necessary databases here.
        DATABASE_CONNECT()
        DATABASE_CONNECT_for_logs()

        "__________CREATING_DEFAULT_DECK__________"
        """This section includes commands that creates a default deck for first start."""
        if DATABASE_user_deleted_default_tool() != "DELETED" and DATABASE_is_default_created() != True:
            DATABASE_create_deck("German Top200 Words (Default Deck)")
            for item in default_list:
                card = item.split("-")
                DATABASE_inserting_default_list(card[0], card[1])

        "__________STARTING_WITH_APP__________"
        """This section includes commands that should start with application."""
        self.ui.tableCards.horizontalHeader().setSectionResizeMode(1)

        self.ui.tableWidget_creation_card_list.horizontalHeader().setSectionResizeMode(1)
        self.ui.tableWidget_creation_card_list.setHorizontalHeaderLabels(["Front","Back"])
        self.ui.tableWidget_for_total_studied.horizontalHeader().setSectionResizeMode(1)
        self.ui.tableWidget_for_total_studied.setHorizontalHeaderLabels(["Total studied time"])
        self.ui.tableWidget_for_last_studied.horizontalHeader().setSectionResizeMode(1)
        self.ui.tableWidget_for_last_studied.setHorizontalHeaderLabels(["Last studied time"])
        self.ui.tableWidget_for_records.horizontalHeader().setSectionResizeMode(1)
        self.ui.tableWidget_for_records.setHorizontalHeaderLabels(["Deck name","Studied time"])
        self.update_deck_list()
        self.ui.label_4.setOpenExternalLinks(True)
        self.ui.label_6.setOpenExternalLinks(True)
        self.stopwatch_for_study()
        self.setting_current_time_to_datetime_edit()
        self.ui.label_3.setHidden(1)

        "__________SIGNALS__________"
        """This section includes signals that program will use."""
        self.ui.btnCreateDeck.clicked.connect(self.create_new_deck)
        self.ui.btnRenameDeck.clicked.connect(self.rename_deck)
        self.ui.btnDeleteDeck.clicked.connect(self.delete_deck)
        self.ui.btnSortDecks.clicked.connect(self.sort_deck_names)
        self.ui.btnDeleteCard.clicked.connect(self.delete_card_from_list_and_database)
        self.ui.btnCreateCard.clicked.connect(self.changing_page_to_card_creation)
        self.ui.btn_creation_clear.clicked.connect(self.clear_text_edits)
        self.ui.btn_add_the_card_below.clicked.connect(self.add_the_card_below)
        self.ui.btn_creation_remove_card.clicked.connect(self.remove_selected_card_from_list)
        self.ui.btn_creation_cancel.clicked.connect(self.cancel_without_saving_card_list)
        self.ui.btn_creation_add_all_to_the_deck.clicked.connect(self.add_cards_to_database)
        self.ui.btn_start_study.clicked.connect(self.show_card_for_study)
        self.ui.btnRed.clicked.connect(self.red_clicked)
        self.ui.btnOrange.clicked.connect(self.orange_clicked)
        self.ui.btnYellow.clicked.connect(self.yellow_clicked)
        self.ui.btnGreen.clicked.connect(self.green_clicked)
        self.ui.btnBlue.clicked.connect(self.blue_clicked)
        self.ui.btnShow.clicked.connect(self.show_back)
        self.ui.btn_homepage_deckcars.clicked.connect(self.tool_bar_deckandcard)
        self.ui.btn_homepage_study.clicked.connect(self.tool_bar_study)
        self.ui.btn_stop_studying.clicked.connect(self.stop_button_worked)
        self.ui.btn_start_study.clicked.connect(self.start_button_worked)
        self.ui.btnSortDecks_2.clicked.connect(self.sorting_combobox_for_statistics)
        self.ui.btn_clear_day.clicked.connect(self.clearday_worked_for_logs)
        self.ui.btn_log_record.clicked.connect(self.remove_log_worked_for_logs)
        self.ui.comboBox_for_statistic.currentIndexChanged.connect(self.when_user_select_a_deck_from_combobox_statistic)
        self.ui.comboBox_deck_select.currentTextChanged.connect(self.clear_labels_when_deckname_change)
        self.ui.listDeck.clicked.connect(self.update_card_table)
        self.ui.tableCards.itemDoubleClicked.connect(self.edit_card_by_list_tool)
        self.ui.tableCards.itemChanged.connect(self.edit_card_by_list)
        self.ui.tableWidget_creation_card_list.itemDoubleClicked.connect(self.space_character_blocker_tool)
        self.ui.tableWidget_creation_card_list.itemChanged.connect(self.space_character_blocker)
        self.ui.dateTimeEdit_for_statistics.dateChanged.connect(self.datetime_input_catches)
        self.ui.tableWidget_for_records.clicked.connect(self.selecting_log_from_tablewidget)

        "__________TOOLBAR_AND_ACTIONS__________"
        """This section includes commands that work with toolbar."""
        self.ui.actionHomePage.triggered.connect(self.tool_bar_homepage)
        self.ui.actionDeckandCard.triggered.connect(self.tool_bar_deckandcard)
        self.ui.actionStudy.triggered.connect(self.tool_bar_study)
        self.ui.actionStatistics.triggered.connect(self.tool_bar_statistics)

    ############################################################################################################################################
    # This method includes commands that must run before the program closes.
    # Asks the users if they really wants to close application.
    # If user's answer is 'Yes' it stops studying and database connections.
    def closeEvent(self, event):
        reply = QMessageBox.question(self, 'LearnQuick', 'Are you sure you want to close LearnQuick?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.stop_button_worked()
            DATABASE_DISCONNECT()
            DATABASE_DISCONNECT_for_logs()
            event.accept()
        else:
            event.ignore()
    ############################################################################################################################################

    "_______________________________________________________________HOME PAGE__________________________________________________________________"
    """This section includes commands to use on Homepage"""

    ############################################################################################################################################
    # This method catches a signal if user click 'Home' section from toolbar.
    # The main goal of this method is that, changing current page to 'Homepage'.
    def tool_bar_homepage(self):
        self.stop_button_worked()
        self.ui.stackedWidget_MainApp.setCurrentIndex(0)
    ############################################################################################################################################

    "__________________________________________________________DECK AND CARDS PAGE_____________________________________________________________"
    """This section includes commands to use on Deck and Cards"""

    ############################################################################################################################################
    # This method catches a signal if user click 'Deck and Cards' section from toolbar.
    # The main goal of this method is that, changing current page to 'Deck and Cards'.
    def tool_bar_deckandcard(self):
        self.stop_button_worked()
        self.ui.stackedWidget_MainApp.setCurrentIndex(1)
        self.ui.label_shows_card_back.clear()
        self.ui.label_shows_card_front.clear()
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Create Deck' button.
    # The main goal of this method is that, creating new deck with a deckname input from user.
    def create_new_deck(self):
        if(re.compile('[@_!#$%^&*()<>?/\|}{~:]').search(self.ui.typeDeckName.text()) == None):
            if self.ui.typeDeckName.text() == "" or self.ui.typeDeckName.text().startswith(" "):
                QMessageBox.warning(self, "Invalid Deck Name", "Deck name can't be empty and can't start with space character.", QMessageBox.Ok)
            elif self.ui.typeDeckName.text() in table_list:
                label = "{} is already exists.".format(self.ui.typeDeckName.text())
                QMessageBox.warning(self, "Invalid Deck Name", label, QMessageBox.Ok)
            elif self.ui.typeDeckName.text()[0].isdigit() == True:
                QMessageBox.warning(self, "Invalid Deck Name", "Deck name can't start with number", QMessageBox.Ok)
            else:
                DATABASE_create_deck(self.ui.typeDeckName.text())
                self.update_deck_list()
        else:
            QMessageBox.warning(self, "Invalid Deck Name", "Deck name cannot contain special characters.", QMessageBox.Ok)
    ############################################################################################################################################
    # This method catches a signal if user click 'Rename Deck' button.
    # The main goal of this method is that, changing deck's name with a deckname input from user.
    def rename_deck(self):
        if self.ui.typeDeckName.text() == "":
            QMessageBox.warning(self, "Invalid Value", "Please select or type the name of the deck.", QMessageBox.Ok)
        elif self.ui.typeDeckName.text() not in table_list:
            label = "There is no deck named {}".format(self.ui.typeDeckName.text())
            QMessageBox.warning(self, "Invalid Deck Name", label, QMessageBox.Ok)
        else:
            new_deckname, ok = QInputDialog.getText(self, "Edit Deck", "New Deck Name")
            if new_deckname in table_list:
                label = "{} is already exists.".format(new_deckname)
                QMessageBox.warning(self, "Invalid Deck Name", label, QMessageBox.Ok)
            elif new_deckname.startswith(" ") or new_deckname == "":
                QMessageBox.warning(self, "Invalid Deck Name", "New deck name can't be empty and can't start with space character.", QMessageBox.Ok)
            elif (re.compile('[@_!#$%^&*()<>?/\|}{~:]').search(new_deckname) != None):
                QMessageBox.warning(self, "Invalid Deck Name", "New deck name cannot contain special characters.", QMessageBox.Ok)
            elif new_deckname[0].isdigit() == True:
                QMessageBox.warning(self, "Invalid Deck Name", "New deck name can't start with number", QMessageBox.Ok)
            else:
                if self.ui.typeDeckName.text() == "German Top200 Words (Default Deck)":
                    DATABASE_user_deleted_default()
                DATABASE_rename_deck(self.ui.typeDeckName.text(), new_deckname)
                DATABASE_change_decknames_from_db(self.ui.typeDeckName.text(), new_deckname)
                self.datetime_input_catches()
                self.update_deck_list()
    ############################################################################################################################################
    # This method catches a signal if user click 'Delete Deck' button.
    # The main goal of this method is that, deleting deck.
    def delete_deck(self):
        if self.ui.typeDeckName.text() == "":
            QMessageBox.warning(self, "Invalid Value", "Please select or type the name of the deck.", QMessageBox.Ok)
        elif self.ui.typeDeckName.text() not in table_list:
            label = "There is no deck named {}".format(self.ui.typeDeckName.text())
            QMessageBox.warning(self, "Invalid Deck Name", label, QMessageBox.Ok)
        else:
            label = "Do you really want to delete {}".format(self.ui.typeDeckName.text())
            question = QMessageBox.question(self,"Delete Deck", label, QMessageBox.Yes | QMessageBox.No)
            if question == QMessageBox.Yes:
                if self.ui.typeDeckName.text() == "German Top200 Words (Default Deck)":
                    DATABASE_user_deleted_default()
                DATABASE_delete_deck(self.ui.typeDeckName.text())
                DATABASE_delete_logs_when_a_deck_deleted(self.ui.typeDeckName.text())
                self.ui.tableCards.clear()
                self.ui.tableCards.setHorizontalHeaderLabels(["Front","Back"])
                self.ui.typeDeckName.clear()
                self.ui.tableCards.setRowCount(0)
                self.datetime_input_catches()
                self.update_deck_list()
    ############################################################################################################################################
    # This method catches a signal if user click 'Sort Decks' button.
    # The main goal of this method is that, sorting decks (items) in QListWidget.
    def sort_deck_names(self):
        self.ui.listDeck.sortItems()
    ############################################################################################################################################

    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, inserting decknames into QListWidget.
    def update_deck_list(self):
        table_list.clear()
        self.ui.listDeck.clear()
        DATABASE_get_decks()
        for table in table_list:
            if table != "sqlite_sequence":
                self.ui.listDeck.addItem(table)
    ############################################################################################################################################
    # This method catches a signal if user click a deck (item) from QListWidget.
    # The main goal of this method is that, inserting all cards into QTableWidget.
    def update_card_table(self):
        currently_selected_deck_name = self.ui.typeDeckName.text()
        card_list.clear()
        self.ui.tableCards.clear()
        DATABASE_get_cards(currently_selected_deck_name)
        tablerow = 0
        self.ui.tableCards.setRowCount(len(card_list))
        for i in card_list:
            self.ui.tableCards.setItem(tablerow, 0, QtWidgets.QTableWidgetItem(i[1]))
            self.ui.tableCards.setItem(tablerow, 1, QtWidgets.QTableWidgetItem(i[2]))
            tablerow += 1
        self.ui.tableCards.setHorizontalHeaderLabels(["Front","Back"])
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Delete Card' button.
    # The main goal of this method is that, deleting selected card from list and database.
    def delete_card_from_list_and_database(self):
        selected_deckname = self.ui.typeDeckName.text()
        current_row = self.ui.tableCards.currentRow()
        if selected_deckname == "":
            QMessageBox.warning(self, "Card Error", "Please select a deck from deck list.", QMessageBox.Ok)
        elif current_row == -1 and self.ui.tableCards.rowCount() != 0:
            QMessageBox.warning(self, "Card Error", "Please select a card from card list.", QMessageBox.Ok)
        elif current_row == -1 and self.ui.tableCards.rowCount() == 0:
            QMessageBox.warning(self, "Card Error", "There are no cards in the deck that can be deleted.", QMessageBox.Ok)
        else:
            selected_front =  (self.ui.tableCards.item(current_row, 0).text())
            selected_back = (self.ui.tableCards.item(current_row, 1).text())
            DATABASE_delete_card(selected_deckname, selected_front, selected_back)
            self.update_card_table()
    ############################################################################################################################################
    # This method catches a signal if user doubleclick a card (item) in QTableWidget.
    # The main goal of this method is that, saving old front and back sides of card before editing.
    def edit_card_by_list_tool(self):
        try:
            global current_front
            global current_back
            current_front = (self.ui.tableCards.item(self.ui.tableCards.currentRow(), 0).text())
            current_back = (self.ui.tableCards.item(self.ui.tableCards.currentRow(), 1).text())
        except:
            pass
    ############################################################################################################################################
    # This method catches a signal if user change a card (item) from QTableWidget.
    # The main goal of this method is that, ensuring the changes made by the user are applied to QTableWidget and database.
    def edit_card_by_list(self):
        try:
            deckname = self.ui.typeDeckName.text()
            new_front = (self.ui.tableCards.item(self.ui.tableCards.currentRow(), 0).text()).strip() # değişenin front'i
            new_back = (self.ui.tableCards.item(self.ui.tableCards.currentRow(), 1).text()).strip() # değişenin back'i
            DATABASE_get_fronts_and_backs(deckname)
            x=0
            for front, back in zip(fronts_list, backs_list):
                if front == new_front and back == new_back:
                    label = "Front: {}\nBack: {}\nis already exists.".format(new_front, new_back)
                    QMessageBox.warning(self, "Invalid Card", label, QMessageBox.Ok)
                    self.update_card_table()
                    x=1
                    break
            if x==0:
                if new_front == "":
                    self.update_card_table()
                elif new_back == "":
                    self.update_card_table()
                else:
                    DATABASE_update_card(deckname, current_front, current_back, new_front, new_back)
                    self.update_card_table()
        except:
            pass
    ############################################################################################################################################

    "___________________________________________________________CARD CREATION PAGE_____________________________________________________________"
    """This section includes commands to use on Card Creation Page"""

    ############################################################################################################################################
    # This method catches a signal if user click 'Create Card' button.
    # The main goal of this method is that, changing current page to 'Card Creation Page'.
    def changing_page_to_card_creation(self):
        if self.ui.typeDeckName.text() == "":
            QMessageBox.warning(self, "Invalid Deck Name", "Please select or type the name of the deck.", QMessageBox.Ok)
        elif self.ui.typeDeckName.text() not in table_list:
            label = "There is no deck named {}".format(self.ui.typeDeckName.text())
            QMessageBox.warning(self, "Invalid Deck Name", label, QMessageBox.Ok)
        else:
            label = "{}".format(self.ui.typeDeckName.text())
            self.ui.lbl_title_creation_cardname.setText(label)
            self.ui.tableWidget_creation_card_list.setHorizontalHeaderLabels(["Front","Back"])
            self.ui.stackedWidget_MainApp.setCurrentIndex(3)
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Clear' button.
    # The main goal of this method is that, clearing labels in card creation page.
    def clear_text_edits(self):
        self.ui.textEdit_creation_back.clear()
        self.ui.textEdit_creation_front.clear()
    ############################################################################################################################################
    # This method catches a signal if user click 'Add the card below' button.
    # The main goal of this method is that, inserting card into QTableWidget as an item.
    def add_the_card_below(self):
        front = self.ui.textEdit_creation_front.toPlainText().strip()
        back = self.ui.textEdit_creation_back.toPlainText().strip()
        value = self.ui.tableWidget_creation_card_list.rowCount()
        self.clear_text_edits()
        if front == "":
            QMessageBox.warning(self, "Invalid Value", "Front can't be empty.", QMessageBox.Ok)
        elif back == "":
            QMessageBox.warning(self, "Invalid Value", "Back can't be empty.", QMessageBox.Ok)
        elif front == "" and back == "":
            QMessageBox.warning(self, "Invalid Value", "Front and Back can't be empty.", QMessageBox.Ok)
        else:
            self.ui.tableWidget_creation_card_list.insertRow(value)
            self.ui.tableWidget_creation_card_list.setItem(value, 0, QtWidgets.QTableWidgetItem(front))
            self.ui.tableWidget_creation_card_list.setItem(value, 1, QtWidgets.QTableWidgetItem(back))
            self.ui.tableWidget_creation_card_list.setHorizontalHeaderLabels(["Front","Back"])
            self.ui.textEdit_creation_front.setFocus()
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Remove Card' button.
    # The main goal of this method is that, removing selected card (item) from QTableWidget.
    def remove_selected_card_from_list(self):
        current_row = self.ui.tableWidget_creation_card_list.currentRow()
        self.ui.tableWidget_creation_card_list.removeRow(current_row)
    ############################################################################################################################################
    # This method catches a signal if user click 'Cancel' button.
    # The main goal of this method is that, discarding all progress while creating card and turning back to 'Deck and Cards' page.
    def cancel_without_saving_card_list(self):
        if self.ui.tableWidget_creation_card_list.rowCount() > 0:
            question = QMessageBox.question(self,"Delete Deck", "Do you really want to discard all cards that you want to create?", QMessageBox.Yes | QMessageBox.No)
            if question == QMessageBox.Yes:
                self.ui.tableWidget_creation_card_list.clear()
                self.ui.tableWidget_creation_card_list.setHorizontalHeaderLabels(["Front","Back"])
                self.ui.tableWidget_creation_card_list.setRowCount(0)
                self.clear_text_edits()
                self.ui.stackedWidget_MainApp.setCurrentIndex(1)
        else:
            self.clear_text_edits()
            self.ui.stackedWidget_MainApp.setCurrentIndex(1)
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Add all to the deck' button.
    # The main goal of this method is that, inserting all cards(items in QTableWidget) into database and turning back to 'Deck and Cards' page.
    def add_cards_to_database(self):
        global creation_fronts_list
        global creation_backs_list
        creation_fronts_list = []
        creation_backs_list = []
        DATABASE_get_fronts_and_backs(self.ui.typeDeckName.text())
        for index in range(self.ui.tableWidget_creation_card_list.rowCount()):
            global creation_front
            global creation_back
            creation_front = self.ui.tableWidget_creation_card_list.item(index, 0).text()
            creation_back = self.ui.tableWidget_creation_card_list.item(index, 1).text()
            if self.is_there_same_one_on_the_list() == True:
                label = "You tried to add same card more than one. For this reason program added the card once.\n{} (FRONT)\n{} (BACK)".format(creation_front, creation_back)
                QMessageBox.information(self, "Card Error", label , QMessageBox.Ok)
            elif self.is_there_same_one_in_the_database() == True:
                label = "Same card is already in the deck.\n{} (Front)\n{} (Back)".format(creation_front, creation_back)
                QMessageBox.information(self, "Card Error", label, QMessageBox.Ok)
            else:
                creation_fronts_list.append(creation_front)
                creation_backs_list.append(creation_back)
                DATABASE_create_card(self.ui.typeDeckName.text(),creation_front,creation_back)
        self.ui.tableWidget_creation_card_list.clear()
        self.ui.tableWidget_creation_card_list.setRowCount(0)
        self.ui.tableWidget_creation_card_list.setHorizontalHeaderLabels(["Front","Back"])
        self.ui.textEdit_creation_back.clear()
        self.ui.textEdit_creation_front.clear()
        self.update_card_table()
        self.ui.stackedWidget_MainApp.setCurrentIndex(1)
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, trying to find same card in QTableWidget before inserting.
    def is_there_same_one_on_the_list(self):
        for listed_front, listed_back in zip(creation_fronts_list, creation_backs_list):
            if listed_front == creation_front and listed_back == creation_back:
                return True
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, trying to find same card in database before inserting.
    def is_there_same_one_in_the_database(self):
        for db_listed_front, db_listed_back in zip(fronts_list, backs_list):
            if db_listed_front == creation_front and db_listed_back == creation_back:
                return True
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user doubleclick to QTableWidget.
    # The main goal of this method is that, saving current front and back sides of card before space_character_blocker method.
    def space_character_blocker_tool(self):
        global current_front_2
        global current_back_2
        current_front_2 = (self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 0).text())
        current_back_2 = (self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 1).text())
    ############################################################################################################################################
    # This method catches a signal if user change a card(item) in QTableWidget.
    # The main goal of this method is that, making sure user's input is not empty and has not useless space characters.
    def space_character_blocker(self):
        try:
            new_front_2 = self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 0).text().strip()
            new_back_2 = self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 1).text().strip()
            if new_front_2 == "":
                self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 0).setText(current_front_2)
            elif new_back_2 == "":
                self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 1).setText(current_back_2)
            else:
                self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 0).setText(new_front_2)
                self.ui.tableWidget_creation_card_list.item(self.ui.tableWidget_creation_card_list.currentRow(), 1).setText(new_back_2)
        except:
            pass
    ############################################################################################################################################

    "_______________________________________________________________STUDY PAGE_________________________________________________________________"
    """This section includes commands to use on Study Page"""

    ############################################################################################################################################
    # This method catches a signal if user click 'Study' section from toolbar.
    # The main goal of this method is that, changing current page to 'Study Page'.
    def tool_bar_study(self):
        if len(table_list) <= 1:
            QMessageBox.information(self, "Deck Error", "Please create a deck first and add cards in it.", QMessageBox.Ok)
        else:
            self.get_decks_to_study_combobox()
            self.clear_lcd_numbers()
            self.ui.stackedWidget_MainApp.setCurrentIndex(2)
    ############################################################################################################################################

    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, inserting all decks into QComboBox.
    def get_decks_to_study_combobox(self):
        self.ui.comboBox_deck_select.clear()
        for deck in table_list:
            if deck != "sqlite_sequence":
                self.ui.comboBox_deck_select.addItem(deck)
    ############################################################################################################################################
    # This method works with other methods and cathes a signal if user click 'Start' button.
    # The main goal of this method is that, while considering chance showing cards to user with label texts.
    def show_card_for_study(self):
        self.ui.comboBox_deck_select.setDisabled(1)
        if self.is_deck_empty_in_study_page()== False:
            deckname = self.ui.comboBox_deck_select.currentText()
            DATABASE_get_all_reds_into_list(deckname)
            DATABASE_get_all_oranges_into_list(deckname)
            DATABASE_get_all_yellows_into_list(deckname)
            DATABASE_get_all_greens_into_list(deckname)
            DATABASE_get_all_blues_into_list(deckname)
            self.lcd_numbers()

            self.ui.progressBar.setEnabled(1)
            self.progress_bar_for_studying()
            while True:
                rand = random.randrange(1,101)
                global selected
                if rand >= 1 and rand <= 35 and len(red_list) != 0:
                    selected = random.choice(red_list)
                    self.ui.label_shows_card_front.setText(selected[1])
                    break
                elif rand >= 36 and rand <= 60 and len(orange_list) != 0:
                    selected = random.choice(orange_list)
                    self.ui.label_shows_card_front.setText(selected[1])
                    break
                elif rand >= 61 and rand <= 80 and len(yellow_list) != 0:
                    selected = random.choice(yellow_list)
                    self.ui.label_shows_card_front.setText(selected[1])
                    break
                elif rand >= 81 and rand <= 95 and len(green_list) != 0:
                    selected = random.choice(green_list)
                    self.ui.label_shows_card_front.setText(selected[1])
                    break
                elif rand >= 96 and rand <= 100 and len(blue_list) != 0:
                    selected = random.choice(blue_list)
                    self.ui.label_shows_card_front.setText(selected[1])
                    break
        else:
            self.ui.comboBox_deck_select.setEnabled(1)
            label = "There are no cards in {}".format(self.ui.comboBox_deck_select.currentText())
            QMessageBox.information(self, "Deck Error", label, QMessageBox.Ok)
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Red' button.
    # The main goal of this method is that, changing card's property to red.
    def red_clicked(self):
        if self.ui.label_shows_card_front.text() != "":
            DATABASE_reset_colors_for_card(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            DATABASE_setting_red(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            self.show_card_for_study()
            self.ui.label_shows_card_back.clear()
    ############################################################################################################################################
    # This method catches a signal if user click 'Orange' button.
    # The main goal of this method is that, changing card's property to orange.
    def orange_clicked(self):
        if self.ui.label_shows_card_front.text() != "":
            DATABASE_reset_colors_for_card(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            DATABASE_setting_orange(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            self.show_card_for_study()
            self.ui.label_shows_card_back.clear()
    ############################################################################################################################################
    # This method catches a signal if user click 'Yellow' button.
    # The main goal of this method is that, changing card's property to yellow.
    def yellow_clicked(self):
        if self.ui.label_shows_card_front.text() != "":
            DATABASE_reset_colors_for_card(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            DATABASE_setting_yellow(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            self.show_card_for_study()
            self.ui.label_shows_card_back.clear()
    ############################################################################################################################################
    # This method catches a signal if user click 'Green' button.
    # The main goal of this method is that, changing card's property to green.
    def green_clicked(self):
        if self.ui.label_shows_card_front.text() != "":
            DATABASE_reset_colors_for_card(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            DATABASE_setting_green(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            self.show_card_for_study()
            self.ui.label_shows_card_back.clear()
    ############################################################################################################################################
    # This method catches a signal if user click 'Blue' button.
    # The main goal of this method is that, changing card's property to blue.
    def blue_clicked(self):
        if self.ui.label_shows_card_front.text() != "":
            DATABASE_reset_colors_for_card(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            DATABASE_setting_blue(self.ui.comboBox_deck_select.currentText(), selected[1], selected[2])
            self.show_card_for_study()
            self.ui.label_shows_card_back.clear()
    ############################################################################################################################################
    # This method catches a signal if user click 'Show' button.
    # The main goal of this method is that, showing back side of the card to user.
    def show_back(self):
        if self.ui.label_shows_card_front.text() != "":
            self.ui.label_shows_card_back.setText(selected[2])
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, checking the deck's inside empty or not.
    def is_deck_empty_in_study_page(self):
        try:
            DATABASE_get_cards(self.ui.comboBox_deck_select.currentText())

            if len(card_list) == 0:
                return True
            else:
                return False
        except sqlite3.OperationalError:
            pass
    ############################################################################################################################################
    # This method catches a signal if user change current in QComboBox.
    # The main goal of this method is that, clearing labels and setting color buttons disable while conmsidering deck's inside.
    def clear_labels_when_deckname_change(self):
        self.ui.label_shows_card_front.clear()
        self.ui.label_shows_card_back.clear()
        if self.is_deck_empty_in_study_page()== True:
            self.ui.btnRed.setDisabled(1)
            self.ui.btnOrange.setDisabled(1)
            self.ui.btnYellow.setDisabled(1)
            self.ui.btnGreen.setDisabled(1)
            self.ui.btnBlue.setDisabled(1)
            self.ui.btnShow.setDisabled(1)
        elif self.is_deck_empty_in_study_page()== False:
            self.ui.btnRed.setEnabled(1)
            self.ui.btnOrange.setEnabled(1)
            self.ui.btnYellow.setEnabled(1)
            self.ui.btnGreen.setEnabled(1)
            self.ui.btnBlue.setEnabled(1)
            self.ui.btnShow.setEnabled(1)
    ############################################################################################################################################

    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, setting card count into QLCDNumber of corresponding color.
    def lcd_numbers(self):
        self.ui.lcdNumber_Red.display(len(red_list))
        self.ui.lcdNumber_Orange.display(len(orange_list))
        self.ui.lcdNumber_Yellow.display(len(yellow_list))
        self.ui.lcdNumber_Green.display(len(green_list))
        self.ui.lcdNumber_Blue.display(len(blue_list))
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, setting all QLCDNumbers to zero.
    def clear_lcd_numbers(self):
        self.ui.lcdNumber_Red.display(0)
        self.ui.lcdNumber_Orange.display(0)
        self.ui.lcdNumber_Yellow.display(0)
        self.ui.lcdNumber_Green.display(0)
        self.ui.lcdNumber_Blue.display(0)
    ############################################################################################################################################

    ############################################################################################################################################
    # This method catches a signal if user click 'Start' button.
    # The main goal of this method is that, starting the time for user's studytime statistics and saving current date.
    def start_button_worked(self):
        self.flag = True
        self.current_deck_name_for_timing = self.ui.comboBox_deck_select.currentText()
        self.study_starting_time = datetime.strftime(datetime.now(), "%d %B %Y")
    ############################################################################################################################################
    # This method catches a signal if user click 'Stop' button.
    # The main goal of this method is that, stoping the time for user's studytime statistics and clearing labels.
    def stop_button_worked(self):
        self.ui.progressBar.setDisabled(1)
        self.ui.progressBar.setValue(0)
        self.ui.label_shows_card_front.clear()
        self.ui.label_shows_card_back.clear()
        self.clear_lcd_numbers()
        if 0 <= float(self.ui.label_3.text()) < 1:
            self.flag = False
            self.count = 0
            self.ui.label_3.setText(str(self.count))
        else:
            self.flag = False
            self.sending_logs_to_db()
            self.count = 0
            self.ui.label_3.setText(str(self.count))
        self.ui.comboBox_deck_select.setEnabled(1)
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, to act as a stopwatch.
    def stopwatch_for_study(self):
        self.count = 0
        self.flag = False
        self.ui.label_3.setText(str(self.count))
        timer = QTimer(self)
        timer.timeout.connect(self.stopwatch_for_study_tool)
        timer.start(100)
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, to act as a stopwatch.
    def stopwatch_for_study_tool(self):
        if self.flag:
            self.count+= 1
            text = str(self.count / 10)
            self.ui.label_3.setText(text)
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, showing a progress bar according to blue card count.
    def progress_bar_for_studying(self):
        card_count = len(red_list) + len(orange_list) + len(yellow_list) + len(green_list) + len(blue_list)
        try:
            value= (len(blue_list)/card_count) * 100
            self.ui.progressBar.setValue(int(value))
        except ZeroDivisionError:
            self.ui.progressBar.setValue(0)
    ############################################################################################################################################

    "_______________________________________________________________STATISTICS_________________________________________________________________"
    """This section includes commands to use on Statistics"""

    ############################################################################################################################################
    # This method catches a signal if user click 'Statistics' section from toolbar.
    # The main goal of this method is that, changing current page to 'Statistics'.
    def tool_bar_statistics(self):
        self.stop_button_worked()
        self.get_decks_to_combobox_for_statistics()
        self.reset_card_count_labels()
        self.ui.stackedWidget_MainApp.setCurrentIndex(4)
        self.datetime_input_catches()
        self.database_index_no = ""
    ############################################################################################################################################

    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, inserting all decknames into QComboBox.
    def get_decks_to_combobox_for_statistics(self):
        self.ui.comboBox_for_statistic.clear()
        for deck in table_list:
            if deck != "sqlite_sequence":
                self.ui.comboBox_for_statistic.addItem(deck)
        self.ui.comboBox_for_statistic.setCurrentIndex(-1)
    ############################################################################################################################################
    # This method catches a signal if user click 'Sort' button.
    # The main goal of this method is that, sorting decks(items) in QComboBox.
    def sorting_combobox_for_statistics(self):
        table_list.sort(key = lambda x : x.lower())
        self.get_decks_to_combobox_for_statistics()
        self.reset_card_count_labels()
    ############################################################################################################################################
    # This method catches a signal if user change current index in QComboBox.
    # The main goal of this method is that, showing all stats to the user according to deckname that user has selected.
    def when_user_select_a_deck_from_combobox_statistic(self):
        try:
            DATABASE_get_all_reds_into_list(self.ui.comboBox_for_statistic.currentText())
            DATABASE_get_all_oranges_into_list(self.ui.comboBox_for_statistic.currentText())
            DATABASE_get_all_yellows_into_list(self.ui.comboBox_for_statistic.currentText())
            DATABASE_get_all_greens_into_list(self.ui.comboBox_for_statistic.currentText())
            DATABASE_get_all_blues_into_list(self.ui.comboBox_for_statistic.currentText())
        except:
            pass
        total_card_count = len(red_list) + len(orange_list) + len(yellow_list) + len(green_list) + len(blue_list)
        self.ui.lbl_red_count.setText(str(len(red_list)))
        self.ui.lbl_orange_count.setText(str(len(orange_list)))
        self.ui.lbl_yellow_count.setText(str(len(yellow_list)))
        self.ui.lbl_green_count.setText(str(len(green_list)))
        self.ui.lbl_blue_count.setText(str(len(blue_list)))
        self.ui.lbl_total_count.setText(f"Total\n{str(total_card_count)}")

        gl_one = DATABASE_getting_last_studied_time(self.ui.comboBox_for_statistic.currentText())
        if gl_one == None:
            self.ui.tableWidget_for_last_studied.clear()
            self.ui.tableWidget_for_last_studied.setRowCount(1)
            self.ui.tableWidget_for_last_studied.setItem(0,0, QtWidgets.QTableWidgetItem(""))
            self.ui.tableWidget_for_last_studied.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)
            self.ui.tableWidget_for_last_studied.setHorizontalHeaderLabels(["Last studied time"])
        else:
            dt = datetime.strptime(gl_one, '%d/%m/%Y')
            dt = datetime.strftime(dt, "%d %B %Y")
            self.ui.tableWidget_for_last_studied.clear()
            self.ui.tableWidget_for_last_studied.setRowCount(1)
            self.ui.tableWidget_for_last_studied.setItem(0,0, QtWidgets.QTableWidgetItem(dt))
            self.ui.tableWidget_for_last_studied.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)
            self.ui.tableWidget_for_last_studied.setHorizontalHeaderLabels(["Last studied time"])

        i = DATABASE_getting_total_studied_time(self.ui.comboBox_for_statistic.currentText())
        if i == 0:
            self.ui.tableWidget_for_total_studied.clear()
            self.ui.tableWidget_for_total_studied.setRowCount(1)
            self.ui.tableWidget_for_total_studied.setItem(0,0, QtWidgets.QTableWidgetItem(""))
            self.ui.tableWidget_for_total_studied.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)
            self.ui.tableWidget_for_total_studied.setHorizontalHeaderLabels(["Total studied time"])
        else:
            seconds = int(i)
            hours = divmod(seconds,3600)[0]
            minutes = divmod(seconds-(hours*3600),60)[0]
            kalan_seconds = seconds-((hours*3600)+(minutes*60))
            if kalan_seconds == 0 and hours == 0 and minutes == 0:
                time_label_one = ""
            elif hours > 1:
                time_label_one = f"{hours} Hours {minutes} Minutes {kalan_seconds} Seconds"
            elif hours == 1:
                time_label_one = f"{hours} Hour {minutes} Minutes {kalan_seconds} Seconds"
            elif hours == 0 and minutes > 1:
                time_label_one = f"{minutes} Minutes {kalan_seconds} Seconds"
            elif hours == 0 and minutes == 1:
                time_label_one = f"{minutes} Minute {kalan_seconds} Seconds"
            elif hours == 0 and minutes == 0:
                time_label_one = f"{kalan_seconds} Seconds"
            self.ui.tableWidget_for_total_studied.clear()
            self.ui.tableWidget_for_total_studied.setRowCount(1)
            self.ui.tableWidget_for_total_studied.setItem(0,0, QtWidgets.QTableWidgetItem(time_label_one))
            self.ui.tableWidget_for_total_studied.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)
            self.ui.tableWidget_for_total_studied.setHorizontalHeaderLabels(["Total studied time"])
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, clearing color card counts from labels.
    def reset_card_count_labels(self):
        self.ui.lbl_red_count.setText("")
        self.ui.lbl_orange_count.setText("")
        self.ui.lbl_yellow_count.setText("")
        self.ui.lbl_green_count.setText("")
        self.ui.lbl_blue_count.setText("")
        self.ui.lbl_total_count.setText("")
    ############################################################################################################################################

    ############################################################################################################################################
    # This method works when the application open.
    # The main goal of this method is that, setting today's date to QDateTimeEdit.
    def setting_current_time_to_datetime_edit(self):
        current_time = str(datetime.now()).split(" ")[0].split("-")
        date = QDate(int(current_time[0]), int(current_time[1]), int(current_time[2]))
        self.ui.dateTimeEdit_for_statistics.setDate(date)
    ############################################################################################################################################
    # This method catches a signal if user change date from QDateTimeEdit.
    # The main goal of this method is that, showing all logs to the user according to date that user has selected.
    def datetime_input_catches(self):
        input_datetime_for_logs = self.ui.dateTimeEdit_for_statistics.text()
        logs = DATABASE_get_logs_from_db(input_datetime_for_logs)
        if logs == None:
            self.ui.tableWidget_for_records.clear()
            self.ui.tableWidget_for_records.setRowCount(0)
        else:
            self.ui.tableWidget_for_records.clear()
            tablerow = 0
            self.ui.tableWidget_for_records.setRowCount(len(logs))
            for i in logs:
                self.ui.tableWidget_for_records.setItem(tablerow, 0, QtWidgets.QTableWidgetItem(i[1]))
                self.ui.tableWidget_for_records.setItem(tablerow, 1, QtWidgets.QTableWidgetItem(i[2]))
                self.ui.tableWidget_for_records.setItem(tablerow, 2, QtWidgets.QTableWidgetItem(str(i[0])))
                tablerow += 1
        self.ui.tableWidget_for_records.hideColumn(2)
        self.ui.tableWidget_for_records.setHorizontalHeaderLabels(["Deck name","Study time","id"])
    ############################################################################################################################################
    # This method works with other methods.
    # The main goal of this method is that, sending time value to the database after user's click to stop button when studying.
    def sending_logs_to_db(self):
        starting_time_for_logs = datetime.strptime(self.study_starting_time, '%d %B %Y')
        starting_time_for_logs = datetime.strftime(starting_time_for_logs, "%d/%m/%Y")
        current_deckname_for_logs = self.ui.comboBox_deck_select.currentText()
        seconds = float(self.ui.label_3.text())
        seconds = int(str(seconds).split(".")[0])
        hours = divmod(seconds,3600)[0]
        minutes = divmod(seconds-(hours*3600),60)[0]
        kalan_seconds = seconds-((hours*3600)+(minutes*60))
        if kalan_seconds == 0 and hours == 0 and minutes == 0:
            time_label_two = "No info"
        elif hours > 1:
            time_label_two = f"{hours} Hours {minutes} Minutes {kalan_seconds} Seconds"
        elif hours == 1:
            time_label_two = f"{hours} Hour {minutes} Minutes {kalan_seconds} Seconds"
        elif hours == 0 and minutes > 1:
            time_label_two = f"{minutes} Minutes {kalan_seconds} Seconds"
        elif hours == 0 and minutes == 1:
            time_label_two = f"{minutes} Minute {kalan_seconds} Seconds"
        elif hours == 0 and minutes == 0:
            time_label_two = f"{kalan_seconds} Seconds"
        DATABASE_creating_table_for_day(starting_time_for_logs)
        DATABASE_insert_log_inside_table(starting_time_for_logs, current_deckname_for_logs, time_label_two)
    ############################################################################################################################################
    # This method catches a signal if user click 'Clear day' button.
    # The main goal of this method is that, removing all logs in that selected day.
    def clearday_worked_for_logs(self):
        if self.ui.tableWidget_for_records.rowCount() == 0:
            QMessageBox.warning(self, "Clear Day", f"There is no log for {self.ui.dateTimeEdit_for_statistics.text()}.", QMessageBox.Ok)
        else:
            label = f"Do you really want to clear {self.ui.dateTimeEdit_for_statistics.text()}?"
            question = QMessageBox.question(self,"Clear Day", label, QMessageBox.Yes | QMessageBox.No)
            if question == QMessageBox.Yes:
                DATABASE_clear_day_log(self.ui.dateTimeEdit_for_statistics.text())
                self.datetime_input_catches()
                self.when_user_select_a_deck_from_combobox_statistic()
    ############################################################################################################################################
    # This method catches a signal if user click a log(item) in QTableWidget.
    # The main goal of this method is that, saving current row's data before removing.
    def selecting_log_from_tablewidget(self):
        current_row_for_logs = self.ui.tableWidget_for_records.currentRow()
        self.database_index_no =  (self.ui.tableWidget_for_records.item(current_row_for_logs, 2).text())
    ############################################################################################################################################
    # This method catches a signal if user click 'Remove log' button.
    # The main goal of this method is that, removing selected log from QTableWidget and database.
    def remove_log_worked_for_logs(self):
        if self.database_index_no != "":
            question = QMessageBox.question(self,"Remove Log", "Do you really want to remove selected log?", QMessageBox.Yes | QMessageBox.No)
            if question == QMessageBox.Yes:
                DATABASE_remove_record_from_db(self.ui.dateTimeEdit_for_statistics.text(), self.database_index_no)
                self.datetime_input_catches()
                self.when_user_select_a_deck_from_combobox_statistic()
        else:
            QMessageBox.warning(self, "Remove Log", "Please select a log from list.", QMessageBox.Ok)
        self.database_index_no = ""
    ############################################################################################################################################

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    win = myApp()
    win.show()
    sys.exit(app.exec_())


# Always prefer setuptools over distutils
from setuptools import setup, find_packages

# To use a consistent encoding
from codecs import open
from os import path

# The directory containing this file
HERE = path.abspath(path.dirname(__file__))

# Get the long description from the README file
with open(path.join(HERE, 'README.md'), encoding='utf-8') as f:
    long_description = f.read()

# This call to setup() does all the work
setup(
    name="opensea",
    version="0.2.0",
    description="wrapper library for opensea api",
    long_description=long_description,
    long_description_content_type="text/markdown",
    # url="https://opensea.readthedocs.io/", # TASKETE KUDASAI :(
    author="Ankush Singh",
    author_email="ankush4singh@gmail.com",
    license="MIT",
    classifiers=[
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3",
        "Operating System :: OS Independent"
    ],
    packages=find_packages(),
    include_package_data=True,
    install_requires=["requests"]
)


# https://leetcode.com/problems/target-sum/
# Question : You are given an integer array nums and an integer target.
# You want to build an expression out of nums by adding one of the symbols '+' and '-'
# before each integer in nums and then concatenate all the integers.
# For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and
# concatenate them to build the expression "+2-1".
# Return the number of different expressions that you can build, which evaluates to target.
#
# Question Type : ShouldSee
# Used : Make dp of size -total to +total
#        Run 2 loops, 1 for all elements and 1 for all possible total
#        After the loop, get result for target from dp.
#           Choose element and the next count.
#        Note that we are using 2 dp arrays here
# Logic: dp[-nums[0]] = 1
#        dp[nums[0]] += 1
#        for i in range(1, n):
#           next = defaultdict(int)
#           for k in range(-total, total + 1, 1):
#               next[k + nums[i]] += dp[k]
#               next[k - nums[i]] += dp[k]
#           dp = next
#        return dp.get(target, 0)
# Complexity : O(n*m) n is no of elements, m is sum of elements

from collections import defaultdict

def find_number(nums, target):
    total = sum(nums)
    n = len(nums)
    dp = defaultdict(int)

    dp[-nums[0]] = 1
    dp[nums[0]] += 1

    for i in range(1, n):
        next = defaultdict(int)
        for k in range(-total, total + 1, 1):
            next[k + nums[i]] += dp[k]
            next[k - nums[i]] += dp[k]
        dp = next

    return dp.get(target, 0)

if __name__ == "__main__":
    nums = [1, 1, 1, 1, 1]
    target = 3
    print(find_number(nums, target))

    nums = [2, 1]
    target = 1
    print(find_number(nums, target))

    nums = [0, 0, 0, 0, 0, 0, 0, 0, 1]
    target = 1
    print(find_number(nums, target))


value=int(input('Enter the value you want to get a factor of it: '))
i=1
sum=0
count=0
while i<=value:
    if value%i==0:
        print(i,"is a factor")
        count+=1
        sum+=i
    i+=1
print('total no. of factors and sum are',count, sum,'respectively')


import datetime

class TimeCounter:

    def __init__(self):

        self.start_time = datetime.datetime.now()
        [self.t_hr, self.t_min, self.t_sec] =self.start_time.hour,self.start_time.minute,self.start_time.second

        self.old_t_hr = self.t_hr
        self.old_t_min = self.t_min
        self.old_t_sec = self.t_sec

        str_time_start = datetime.datetime.now()

    def get_time_str(self):
        #global str_time_start, str_time, old_str_time

        old_t_sec = self.t_sec
        [self.t_hr, self.t_min, self.t_sec] = datetime.now().hour, datetime.now().minute, datetime.now().second
        str_to_return = 0
        if not old_t_sec == self.t_sec:
            str_to_return = "{0}:{1}.{2}".format(self.t_hr, self.t_min, self.t_sec)

        return str_to_return

    def get_time_diff(self):
        now_time = datetime.datetime.now()

        old_t_sec = self.t_sec
        [self.t_hr, self.t_min, self.t_sec] = now_time.hour, now_time.minute, now_time.second
        str_to_return = 0
        if not old_t_sec == self.t_sec:
            hr_diff = self.t_hr - self.old_t_hr
            min_diff = self.t_min - self.old_t_min
            sec_diff = self.t_sec - self.old_t_sec

            str_to_return = datetime.timedelta(hours=hr_diff, minutes=min_diff, seconds=sec_diff)

        return str_to_return

    def make_two_digit_num(int_to_check):
        str_temp = '{}'.format(int_to_check)
        if int_to_check < 10: str_temp = '0{}'.format(int_to_check)
        return str_temp


'''
MPH to Knots
Write a function to convert a speed in miles per hour to knots.
(1 mph = 0.8689762 knots)
For example,
5 mph = 4.344881 knots

'''

def mph_to_knots(mph):

    knots = 1.15078 * mph             #conversion MPH to Knots

    if mph >= 0:                        #speed values converted -> if positive
        return f"{knots} Knots"
    else:
        return "Better type correct speed value!"  #wrong values given


import xml.etree.ElementTree as ET
import urllib.request, urllib.error, urllib.parse
import logging
import re
from copy import deepcopy

class OIMVOInfo(object):
    """
    Class to obtain and store VO info from OIM
    """

    oim_url = "https://topology.opensciencegrid.org/vosummary/xml?summary_attrs_showfield_of_science=on&all_vos=on&active=on&active_value=1&oasis_value=1&sort_key=name"

    # Put any non-nested element before the last item in this list.  The last
    # item is a dict that holds all nested elements and the structure
    wanted_attributes = ['LongName', 'Name',        # These two are examples - we don't actually need them
                         {'FieldsOfScience':        # We actually want these
                              {'PrimaryFields': 'Field',
                               'SecondaryFields': 'Field'
                               }
                          }
                         ]
    vodict = {}

    def __init__(self, url=None):
        if url is not None:
            self.oim_url = url

        self.xmlfile = self.__get_oim_info()
        if self.xmlfile:
            self.__get_vo_info()

    def __get_oim_info(self):
        """
        Download VO info from OIM
        :return file oim_xml: File-like object with OIM VO info in XML format
        """
        header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' }

        try:
            req = urllib.request.Request(self.oim_url, headers=header)
            oim_xml = urllib.request.urlopen(req)
            logging.info('Downloaded OIM VO info')
        except (urllib.error.HTTPError, urllib.error.URLError) as e:
            logging.error(e)
            return None
        return oim_xml

    def __get_vo_info(self):
        """
        Parse XML file and store VO Information we want in vodict

        :return: None
        """
        pathattr = re.compile('^.//(\w+)$')     # Top level elements
        pathattr_rec = re.compile('^.//.+/(\w+)/.+$')   # Nested Elements

        root = ET.parse(self.xmlfile)
        root = root.getroot()

        for voelt in root:
            VOName = voelt.find('Name').text.lower()
            if VOName not in self.vodict:
                self.vodict[VOName] = {}
            cur_dict = self.vodict[VOName]

            # Top level xpaths
            xpaths = ['.//' + attr for attr in self.wanted_attributes[:-1]]

            # Nested xpaths
            def recurse_attrs(cur_level, cur_list=[], final_list=[]):
                if not isinstance(cur_level, dict):   # We're at end of nesting
                    cur_list.append(cur_level)
                    final_list.append(cur_list)     # Add the list to the final output
                else:       # Process current level, go down to next level
                    for key in cur_level:
                        nowlist = deepcopy(cur_list)
                        nowlist.append(key)
                        recurse_attrs(cur_level[key], nowlist, final_list)
                return final_list

            xpath_list_raw = recurse_attrs(self.wanted_attributes[-1])

            xpaths.extend(['.//' + '/'.join(pathlist)
                           for pathlist in xpath_list_raw])

            # Look for info in XML file
            for path in xpaths:
                # Grab whichever regex returns a match
                m = [_f for _f in (pat.match(path) for pat in (pathattr, pathattr_rec)) if _f]
                assert len(m) == 1  # One or other should be a match, not both

                if m:
                    key = m[0].group(1)
                else:
                    continue    # Skip this key
                lst = [elt.text for elt in voelt.findall(path)] # Search XML file using xpath

                if len(lst) > 1:
                    cur_dict[key] = lst  # Add to dict
                elif len(lst) == 1:
                    cur_dict[key] = lst[0]

        logging.info('Constructed OIM VO dictionary')
        return

    def parse_doc(self, doc):
        """
        Parse corrected raw non-Payload doc, write VO FOS into the
        OIM_FieldofScience field.

        :param dict doc: GRACC Record
        :return dict doc:  GRACC record with OIM_FOS either added or corrected
        """
        if 'ResourceType' in doc and doc['ResourceType'] != 'Payload':
            if 'VOName' in doc and doc['VOName'].lower() in self.vodict:
                curvodict = self.vodict[doc['VOName'].lower()]
                if 'PrimaryFields' in curvodict:
                    newfos = curvodict['PrimaryFields']
                    doc['OIM_FieldOfScience'] = newfos
                    logging.info('Set FOS to {0}'.format(newfos))
        return doc

if __name__ == '__main__':
    v = OIMVOInfo()

    testdoc = {
            "OIM_Organization": "Georgia Institute of Technology",
            "ResourceType": "Batch",
            "VOName": "osg",
            "RawProjectName": "VERITAS",
            "OIM_Department": "School of Physics & Center for Relativistic Astrophysics",
            "ReportableVOName": "osg",
            "RawVOName": "/osg/LocalGroup=users",
            "ProjectName": "VERITAS",
            "OIM_FieldOfScience": "Astrophysics",
        }
    print(v.parse_doc(testdoc))


# Exercício Python 089: Crie um programa que leia nome e duas notas de vários alunos e
# guarde tudo em uma lista composta. No final, mostre um boletim contendo a
# média de cada um e permita que o usuário possa mostrar as notas de cada aluno
# individualmente.
lisgeral = []
while True:
    nome = str(input('Nome do aluno: '))
    n1 = float(input('1ª NOTA: '))
    n2 = float(input('2ª NOTA: '))
    media = (n1 + n2) / 2
    lisgeral.append([nome, [n1, n2], media])
    resposta = ' '
    while resposta not in 'SN':
        resposta = str(input('Cadastrar novo aluno? [S/N]: ')).strip().upper()[0]
    if resposta == 'N':
        break
print(30*'-+')
for i, aluno in enumerate(lisgeral):
    print(f'{i:>2} - {aluno[0]:<10} {aluno[2]:>10.2f}')
print(30*'-+')
escolha = 0
while True:
    print('Digite apenas o número do aluno.')
    escolha = int(input('Ver nota de qual alono [999 para finalizar pesquisa]: '))
    print(30*'-+')
    if escolha == 999:
        break
    if escolha <= len(lisgeral):
        print(f'Note de {lisgeral[escolha][0]}, são: {lisgeral[escolha][1]}')


import sys

# Devuelve la máxima suma del array
def kadane(values):
    n = len(values)
    max_sum = float('-inf')
    current_sum = 0
    for i in range(n):
        current_sum += values[i]
        if current_sum < 0:
            current_sum = 0  # Como es menor que 0 lo reiniciamos
            max_sum = max(max_sum, values[i])  # values[i] >= current_sum
        else:
            max_sum = max(max_sum, current_sum)
    return max_sum

# Devuelve el máximo subRectángulo
def maxSubRectangle(mat, n):
    max_rect = float('-inf')
    for left in range(n):
        values = [0] * n
        for right in range(left, n):
            for row in range(n):
                if right == left:
                    values[row] = mat[row][right]  # llenamos el array con los valores iniciales
                else:
                    values[row] += mat[row][right]  # Calculamos el array actual

            max_rect = max(max_rect, kadane(values))

    return max_rect

if __name__ == "__main__":
    for line in sys.stdin:
        n = int(line)
        mat = []
        # Rellenamos la matriz con los datos de entrada
        for i in range(n):
            row = list(map(int, input().split()))
            mat.append(row)

        print(maxSubRectangle(mat, n))

Problemas/A por el botin!/Soluciones/python.py


from django.shortcuts import render

from django.contrib.auth.models import User
from django.views.generic import TemplateView

from report.utils import convert_to_64
from report.report import report
from core.models import Person

class Index(TemplateView):
    template_name = "index.html"

def exportUsersPDF(request):
    """Example of ExportPDF"""
    users = User.objects.all()

    users_list = []
    for user in users:
        users_list.append({
            'name': user.first_name,
            'username': user.username
        })

    person = Person.objects.filter(id=1).first()
    data = {
        'users': users_list,
        'image': convert_to_64(person.image.url)
    }

    return report(request, 'users', data)