import threading

import cv2 as cv
import config
import math
import logging
import numpy as np
import statistics

logger = logging.getLogger(__name__)

class Tracker:

    def __init__ (self, debug):
        self.debug = debug
        self.processingEnded = True
        self.processingSucceeded = False
        self.failInARow = 0
        self.successInARow = 0
        self.movingAvarageLength = 4
        self.processingTime = 0
        self.targetValid = False
        self.startTime = cv.getTickCount() / cv.getTickFrequency()
        self.targetStatus = 'initial'
        self.target = {
            'ex': 'start',
            'roi_start': (0,0),
            'roi_end':(config.FRAME_SIZE_X  - 1, config.FRAME_SIZE_Y - 1),
            'status': 'initial',
            'timeStamp': self.startTime
        }
        self.predictedTarget = {
            'roi_start': (0,0),
            'roi_end':(config.FRAME_SIZE_X - 1, config.FRAME_SIZE_Y - 1),
            'status':'initial'
        }
        self.smoothedTarget = {
            'roi_start': (0, 0),
            'roi_end': (config.FRAME_SIZE_X - 1, config.FRAME_SIZE_Y - 1),
            'xList': [],
            'yList': [],
            'sideAngleList': [],
            'upAngleList': [],
            'timeStampList':[],
            'rangeList':[]
        }

        self.sideAngleGraph=self.createGraphCanvas(640,512).copy()
        self.xGraph = self.createGraphCanvas(640, 512).copy()

    def get_target(self, frame):
        if (self.processingEnded):
            smoothedTarget = self.smoothTarget(self.target, self.movingAvarageLength, self.processingSucceeded)

            if (self.processingSucceeded):
                self.failInARow = 0
                self.successInARow += 1
                if (self.successInARow > 4):
                    self.targetValid = True
                    for key in smoothedTarget: self.predictedTarget[key] =  smoothedTarget[key]
                self.processingSucceeded = False
            else:
                cv.putText(frame, self.target['ex'], (50, 20), cv.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2)
                self.failInARow += 1
                self.successInARow = 0
                if (self.failInARow > 90 and self.targetValid):
                    self.targetValid = False
                    logger.info('switching valid to false')

            tracker_thread = threading.Thread(
                target=self.findSquare,
                args=(frame.copy(), config.LOWER_BLUE, config.UPPER_BLUE,))
            # logger.info('starting findSquare method')
            self.processingEnded = False
            self.processingTime = 0
            tracker_thread.start()

        if (self.targetValid):
            contourColor = (150, 0, 0) if self.predictedTarget['status'] == 'updated' else (0, 150, 150)
            cv.drawContours(frame, [self.predictedTarget['contour']], -1, contourColor, 5)
            centerColor = (0, 150, 0) if self.predictedTarget['status'] == 'updated' else (150, 0, 0)
            #logger.info('status:' + self.targetStatus + ', center: ' + str(self.predictedTarget['center']) + "color: " + str(centerColor))
            cv.circle(frame, self.predictedTarget['center'], 5, centerColor, -1)
            cv.putText(frame, str(int(self.predictedTarget['range'])), (config.FRAME_SIZE_X//2 - 20,config.FRAME_SIZE_Y - 50), cv.FONT_HERSHEY_COMPLEX, 1, 0, 2)

        if self.predictedTarget['status'] != 'initial':  self.updatePrediction(self.predictedTarget)
        logger.info(self.predictedTarget)

            # plot graph
            # ------------

            #self.plotGraphPoint(self.sideAngleGraph, 'sideAngle', graphSpreadFactorX =10, graphSpreadFactorY=500, shiftYStart=-400)
            #self.plotGraphPointFromArray(self.xGraph, 'center',0, graphSpreadFactorX=10, graphSpreadFactorY=0.3,shiftYStart=0)

        cv.putText(frame, str(self.successInARow), (50, 100), cv.FONT_HERSHEY_COMPLEX, 1, 0, 2)
        cv.putText(frame, str(self.failInARow), (125, 100), cv.FONT_HERSHEY_COMPLEX, 1, 0, 2)
        cv.putText(frame, str(self.processingTime), (200, 100), cv.FONT_HERSHEY_COMPLEX, 1, 0, 2)
        cv.putText(frame, str(len(self.smoothedTarget['timeStampList'])), (275, 100), cv.FONT_HERSHEY_COMPLEX, 1, 0, 2)
        cv.rectangle(frame, self.target['roi_start'], self.target['roi_end'], (100, 100, 100), 2)

        self.processingTime += 1
        return frame

    def createGraphCanvas (self, sizeX, sizeY):
        graph = np.zeros([sizeY, sizeX, 3], dtype=np.uint8)
        graph.fill(255)
        cv.putText(graph, 'raw', (50, 20), cv.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0), 2)
        cv.putText(graph, 'smoothed', (50, 100), cv.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
        cv.putText(graph, 'predicted', (50, 180), cv.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2)
        return graph

    def plotGraphPoint(self, graph, patameter, graphSpreadFactorX, graphSpreadFactorY, shiftYStart):
        currentTime = (cv.getTickCount() / cv.getTickFrequency() - self.startTime)
        graphTime = int(currentTime * graphSpreadFactorX) % graph.shape[1]
        shiftY = shiftYStart - 200 * (graphTime //  graph.shape[1])

        cv.circle(graph, (graphTime,
                  int(self.target[patameter] * graphSpreadFactorY + shiftY)), 5, (255, 0, 0),-1)

        cv.circle(graph, (graphTime,
                  int(self.smoothedTarget[patameter] * graphSpreadFactorY + shiftY)), 3,(0, 255, 0), -1)

        cv.circle(graph, (graphTime,
                  int(self.predictedTarget[patameter] * graphSpreadFactorY + shiftY)), 1,(0, 0, 255), -1)

        cv.imshow(patameter, graph)
        cv.waitKey(1)

    def plotGraphPointFromArray (self, graph, patameter, index, graphSpreadFactorX, graphSpreadFactorY, shiftYStart):
        currentTime = (cv.getTickCount() / cv.getTickFrequency() - self.startTime)
        graphTime = int(currentTime * graphSpreadFactorX) %  graph.shape[1]
        shiftY = shiftYStart - 200 * (graphTime //  graph.shape[1])

        cv.circle(graph, (graphTime,
                int(self.target[patameter][index] * graphSpreadFactorY + shiftY)), 5,(255, 0, 0), -1)

        cv.circle(graph, (graphTime,
                int(self.smoothedTarget[patameter][index]  * graphSpreadFactorY + shiftY)), 3,(0, 255, 0), -1)

        cv.circle(graph, (graphTime,
                int(self.predictedTarget[patameter][index]  * graphSpreadFactorY + shiftY)), 1,(0, 0, 255), -1)

        cv.imshow(patameter, graph)
        cv.waitKey(1)

    def findSquare (self, originalFrame, lowerColorTreshold, upperColorTreshold, ):
        img = originalFrame.copy()
        if (self.debug):
            cv.imshow('process', img)
            print('img')
            cv.waitKey(1)
            while True:
                if cv.waitKey(1) & 0xFF == ord('x'):
                    break

        if (not self.targetValid):  #(not self.targetValid):
            roi = img
            self.target['roi_start'] = (0, 0)
            self.target['roi_end'] = (config.FRAME_SIZE_X-1, config.FRAME_SIZE_Y-1)
        else:
            margin = int(config.FRAME_SIZE_Y / 10 * (self.failInARow+1))
            xMin = min(self.target['downLeftCor'][0], self.target['upLeftCor'][0]) - margin
            if (xMin < 0): xMin =0

            xMax = max(self.target['downRightCor'][0], self.target['upRightCor'][0]) + margin
            if (xMax > config.FRAME_SIZE_X-1): xMax = config.FRAME_SIZE_X-1

            yMin = min(self.target['upRightCor'][1], self.target['upLeftCor'][1]) - margin
            if (yMin < 0): yMin = 0

            yMax = max(self.target['downRightCor'][1] , self.target['downLeftCor'][1])+ margin
            if (yMax > config.FRAME_SIZE_Y-1): yMax = config.FRAME_SIZE_Y-1

            roi_mask = np.zeros(img.shape[:2], dtype="uint8")
            cv.rectangle(roi_mask, (xMin, yMin), (xMax, yMax), 255, -1)
            roi = cv.bitwise_and(img, img, mask=roi_mask)
            self.target['roi_start'] = (xMin,yMin)
            self.target['roi_end'] = (xMax,yMax)
            if (self.debug):
                cv.imshow('process', roi)
                print('roi')
                cv.waitKey(1)
                while True:
                    if cv.waitKey(1) & 0xFF == ord('x'):
                        break

        # Convert BGR to HSV
        hsv = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
        if (self.debug):
            cv.imshow('process', hsv)
            print('hsv')
            cv.waitKey(1)
            while True:
                if cv.waitKey(1) & 0xFF == ord('x'):
                    break

        # Threshold the HSV image to get only blue colors
        mask = cv.inRange(hsv, lowerColorTreshold, upperColorTreshold)
        if (self.debug):
            cv.imshow('process', mask)
            print('mask')
            cv.waitKey(1)
            while True:
                if cv.waitKey(1) & 0xFF == ord('x'):
                    break

        contours, _ = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
        timeStamp = cv.getTickCount()/cv.getTickFrequency()
        if (self.debug):
            print('found', len (contours), 'contours')
            cv.drawContours(img, contours, -1, (0, 0, 255), 5)
            cv.imshow('process', img)
            #cv.drawContours(mask, contours, -1, (255, 0, 255), 5)
            #cv.imshow('process', mask)
            print('draw contours')
            cv.waitKey(1)
            while True:
                if cv.waitKey(1) & 0xFF == ord('x'):
                    break
        if (self.debug): print('contours:')

        lastTarget = self.target.copy()
        lastTimeStamp = lastTarget['timeStamp']
        duration = timeStamp - lastTimeStamp
        highestExLevel = 0
        exMessage = ''
        for contour in contours:
            aContour = self.analyze_contour(contour, lastTarget, duration).copy()
            if (self.debug): print(aContour)
            if (not aContour['valid']):
                if (aContour['exLevel'] > highestExLevel):
                    exMessage   = aContour['ex']
                    highestExLevel = aContour['exLevel']
            else:
                for key in aContour: self.target[key] = aContour[key]

                self.target['timeStamp'] = timeStamp
                self.target['ex'] = ''
                self.target['exLevel'] = -1

                self.processingSucceeded = True
                if (self.debug):
                    cv.drawContours(img, [self.target['contour']], -1, (0, 255, 0), 5)
                    cv.circle(img, self.target['center'], 5, (255, 0, 255), -1)
                    for i in range(4):
                        print('i:', i)
                        print('self.targetContour corner:', (self.target['contour'][i][0][0]))
                        cx, cy = self.target['contour'][i][0]
                        cv.putText(img, str(i), (cx, cy), cv.FONT_HERSHEY_COMPLEX, 1, 0, 2)
                    cv.imshow('process', img)
                    print('shaw result')
                    cv.waitKey(1)
                    while True:
                        if cv.waitKey(1) & 0xFF == ord('x'):
                            break
                break

        if not self.processingSucceeded:
            self.target['ex'] = exMessage
            self.target['exLevel'] = highestExLevel

        if (self.debug) : cv.destroyWindow('process')
        cv.waitKey(1)
        self.processingEnded = True
        return (self.target)

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

    def analyze_contour (self, contour, lastTarget, duration):

        aContour ={'ex': 'no contours', 'exLevel': 0}
        try:
            epsilon = 0.1 * cv.arcLength(contour, True)
            approx = cv.approxPolyDP(contour, epsilon, True)
        except:
            aContour['valid'] = False
            aContour['ex'] = 'failed in contour smoothing'
            aContour['exLevel'] += 1
            return aContour.copy()

        if (len(approx) != 4):
            aContour['valid'] = False
            aContour['ex'] = 'no square contours'
            aContour['exLevel'] += 1
            return aContour.copy()

        aContour['area'] = cv.contourArea(approx)
        if (aContour['area'] < 500):
            aContour['ex'] = 'contours too small'
            aContour['exLevel'] += 1
            aContour['valid'] = False
            return aContour.copy()

        aContour ['contour'] = self.arrangeContour(approx)
        c = aContour ['contour']
        (x1,y1) = c[0][0]
        (x2,y2) = c[1][0]
        (x3,y3) = c[2][0]
        (x4,y4) = c[3][0]

        #   1   3
        #   2   4

        aContour['upLeftCor'] = (x1,y1)
        aContour['downLeftCor'] = (x2, y2)
        aContour['upRightCor'] = (x3, y3)
        aContour['downRightCor'] = (x4, y4)
        xCenter = int((x1+x4)/2)
        yCenter = int((y1+y4)/2)
        aContour['center'] = (xCenter, yCenter)

        if self.cropped(aContour):
            aContour['valid'] = False
            aContour['ex'] = 'cropped'
            aContour['exLevel'] += 1
            return aContour.copy()

        if lastTarget['status'] == 'initial' or not lastTarget['valid']:
            aContour['xSpeed'] = 0
        else:
            aContour['xSpeed'] = (xCenter - lastTarget['center'][0]) / duration

       # aContour['xSpeed'] =(0 if lastTarget['status'] == 'initial' or not lastTarget['valid'] else
       #                        (xCenter - lastTarget['center'][0]) / duration)

        aContour['ySpeed'] =(0 if lastTarget['status'] == 'initial' or not lastTarget['valid'] else
                              (yCenter - lastTarget['center'][1]) / duration)

        if y2 == y1 or y4 == y3 or x3 == x1 or x4 == x2:
            aContour['valid'] = False
            aContour['ex'] = 'not a square'
            aContour['exLevel'] += 1
            return aContour.copy()

        leftSlope = (x2-x1)/(y2-y1)
        rightSlope = (x4-x3)/(y4-y3)
        upperSlope = (y3-y1)/(x3-x1)
        lowerSlope = (y4-y2)/(x4-x2)

      #  slopeLimit = 0.3
      #  if (leftSlope>slopeLimit or rightSlope > slopeLimit or upperSlope >slopeLimit or lowerSlope > slopeLimit):
      #      aContour['valid'] = False
      #      aContour['ex'] = 'slope value outside range'
      #      return aContour.copy()

        aContour['verticalSlopesDiff'] = abs(leftSlope - rightSlope)
        if (aContour['verticalSlopesDiff']  > 0.3 ):
            aContour['valid'] = False
            aContour['ex'] = 'vertical slope diff outside range'
            aContour['exLevel'] += 1
            return aContour.copy()

        aContour['horizontalSlopesDiff'] = abs(upperSlope - lowerSlope)
        if (aContour['horizontalSlopesDiff'] > 0.3 ):
            aContour['valid'] = False
            aContour['ex'] = 'horizontal slope diff outside range'
            aContour['exLevel'] += 1
            return aContour.copy()

        aContour['upperLength'] = int(math.sqrt((y3-y1)**2 + (x3-x1)**2))
        aContour['lowerLength'] = int(math.sqrt ((y4-y2)**2 + (x4-x2)**2))
        aContour['leftLength'] = int(math.sqrt((y1 - y2) ** 2 + (x2 - x1) ** 2))
        aContour['rightLength'] = int(math.sqrt((y3 - y4) ** 2 + (x3 - x4) ** 2))

        aContour['upAngle'] = aContour['upperLength'] / aContour['lowerLength']
        aContour['upAngleSpeed'] = (0 if lastTarget['status'] == 'initial' or not lastTarget['valid'] else
                                      (aContour['upAngle'] - lastTarget['upAngle']) / duration)

        aContour['sideAngle'] = aContour['rightLength'] / aContour['leftLength']
        aContour['sideAngleSpeed'] = (0 if lastTarget['status'] == 'initial' or not lastTarget['valid'] else
                                      (aContour['sideAngle'] - lastTarget['sideAngle']) / duration)

        if ( aContour['upAngle'] < 0.8 or aContour['upAngle']  > 1.2):
            aContour['valid'] = False
            aContour['ex'] = 'horizontal sides ratio out of range'
            aContour['exLevel'] += 1
            return aContour.copy()

        #aContour['area'] = (aContour['upperLength'] + aContour['lowerLength'])**2 / 4
        if (self.targetValid):
            areaShpill = 0.2 * (self.failInARow + 1)
            if (self.target['area'] / aContour['area'] < (1 - areaShpill) or self.target['area'] / aContour['area'] > (1 + areaShpill)):
                aContour['valid'] = False
                aContour['ex'] = 'area change is out of range: ' + str(areaShpill)
                aContour['exLevel'] += 1
                return aContour.copy()

        aContour['range'] = (math.sqrt(config.FRAME_SIZE_AREA / aContour['area']))*10
        aContour['rangeSpeed'] = (0 if lastTarget['status'] == 'initial' or not lastTarget['valid'] else
                               (aContour['range'] - lastTarget['range']) / duration)

   #     M = cv.moments(approx)
    #    if M['m00'] != 0.0:
     #       x = int(M['m10'] / M['m00'])
      #      y = int(M['m01'] / M['m00'])
       #     centerCordinates = (x, y)
        #    aContour['center'] = centerCordinates
        #else:
        #    aContour['valid'] = False
        #    aContour['ex'] = 'contour moment failed '
        #    aContour['exLevel'] += 1
        #    return aContour.copy()

        aContour['valid'] = True
        return aContour.copy()

    def arrangeContour (self, sContour):
        sContour = self.sortCorners(sContour, 0, 0, 4)
        sContour = self.sortCorners(sContour, 1, 0, 2)
        sContour = self.sortCorners(sContour, 1, 2, 4)
        return sContour.copy()

    def sortCorners(self, sContour, col, start,end):
        for i in range (start, end-1):
            min = sContour[i][0][col]
            for j in range (i+1,end):
                if(sContour[j][0][col] < min):
                    temp = sContour[i].copy()
                    sContour[i] = sContour[j].copy()
                    sContour[j] = temp.copy()
        return sContour.copy()

    def cropped(self, target):
        xMin = min(target['downLeftCor'][0], target['upLeftCor'][0])
        if (xMin <= 0): return True

        xMax = max(target['downRightCor'][0], target['upRightCor'][0])
        if (xMax >= config.FRAME_SIZE_X - 1): return True

        yMin = min(target['upRightCor'][1], target['upLeftCor'][1])
        if (yMin <= 0): return True

        yMax = max(target['downRightCor'][1], target['downLeftCor'][1])
        if (yMax >= config.FRAME_SIZE_Y - 1): return True

        return False

    def updatePrediction (self, _target):
        target = _target.copy()
        self.predictedTarget['status'] = 'predicted'

        timeStamp = cv.getTickCount()/cv.getTickFrequency()
        duration = (timeStamp - target['timeStamp'])
        (x, y) = target['center']
        self.predictedTarget['center'] = (
        int(x + duration * target['xSpeed']), int(y + duration * target['ySpeed']))
        self.predictedTarget['sideAngle'] = (target['sideAngle'] + duration * target['sideAngleSpeed'])
        self.predictedTarget['upAngle'] = (target['upAngle'] + duration * target['upAngleSpeed'])
        self.predictedTarget['timeStamp'] = timeStamp
        self.predictedTarget['range'] = (target['range'] + duration * target['rangeSpeed'])

        return self.predictedTarget

    def smoothTarget(self, target, requiredLength, processingSucceeded):

        if processingSucceeded:
            self.smoothedTarget['timeStampList'].append(target['timeStamp'])
            self.smoothedTarget['xList'].append(target['center'][0])
            self.smoothedTarget['yList'].append(target['center'][1])
            self.smoothedTarget['sideAngleList'].append(target['sideAngle'])
            self.smoothedTarget['upAngleList'].append(target['upAngle'])
            self.smoothedTarget['rangeList'].append(target['range'])

        length = len(self.smoothedTarget['timeStampList'])
        if  (processingSucceeded and length > requiredLength) or (not processingSucceeded and length > 0):
            self.smoothedTarget['timeStampList'].pop(0)
            self.smoothedTarget['xList'].pop(0)
            self.smoothedTarget['yList'].pop(0)
            self.smoothedTarget['sideAngleList'].pop(0)
            self.smoothedTarget['upAngleList'].pop(0)
            self.smoothedTarget['rangeList'].pop(0)
            length -=1

        if (length < 2):
            self.smoothedTarget['status'] = 'initial'
            return target

        duration = self.smoothedTarget['timeStampList'][-1] - self.smoothedTarget['timeStampList'][0]

        smoothedTargetX = statistics.mean(self.smoothedTarget['xList'])
        self.smoothedTarget['xSpeed'] = (self.smoothedTarget['xList'][-1] - self.smoothedTarget['xList'][0])/duration

        smoothedTargetY = statistics.mean(self.smoothedTarget['yList'])
        self.smoothedTarget['ySpeed'] = (self.smoothedTarget['yList'][-1] - self.smoothedTarget['yList'][0]) / duration

        self.smoothedTarget['center'] = (int(smoothedTargetX), int(smoothedTargetY))

        self.smoothedTarget['sideAngle'] = statistics.mean(self.smoothedTarget['sideAngleList'])
        self.smoothedTarget['sideAngleSpeed'] = (self.smoothedTarget['sideAngleList'][-1] - self.smoothedTarget['sideAngleList'][0]) / duration

        self.smoothedTarget['upAngle'] = statistics.mean(self.smoothedTarget['upAngleList'])
        self.smoothedTarget['upAngleSpeed'] = (self.smoothedTarget['upAngleList'][-1] - self.smoothedTarget['upAngleList'][0]) / duration

        self.smoothedTarget['range'] = statistics.mean(self.smoothedTarget['rangeList'])
        self.smoothedTarget['rangeSpeed'] = (self.smoothedTarget['rangeList'][-1] -
                                                   self.smoothedTarget['rangeList'][0]) / duration

        self.smoothedTarget['contour'] = target['contour']
        self.smoothedTarget['timeStamp'] = self.smoothedTarget['timeStampList'][-1]
        self.smoothedTarget['status'] = 'updated'
        return self.smoothedTarget


# Chiffrement de césar/
Message = "ak"#"yirb"#"uahhks"

#key = 3

alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

def chiffrement(m):
    Mes_crypte = ""
    Ek = 0
    m = m.lower()
    m_list = list(m)
    for i in m_list:
        if i == ' ':
            a = m_list.index(i)
            m_list.pop(a)
    m_new = ""
    for i in m_list:
        m_new += i
    for i in m_new:
        ind = alphabet.index(i)
        Ek = (ind + key) % 26
        Mes_crypte += alphabet[Ek]
    print("Le message crypté est : "+Mes_crypte)

def dechiffrement(c):
    Dk = 0
    Mes_decrypt = ""
    for i in c:
        ind = alphabet.index(i)
        Dk = (ind - key) % 26
        Mes_decrypt += alphabet[Dk]
    print("Le message décrypté est : "+Mes_decrypt)

# Trouver la clé

for i in range(0, 26):
    key = i
    dechiffrement(Message)
    print("Le clé est : "+str(i))
    print("*********---------************")
    print()

dechiffrement(Message)


"""
2 Exercise
----------

Data capture of all users
"""
# Personal information user 1
print(f"\nUser 1")
user1_name_lastname = input("enter your name and last name: ")
user1_ocupation = input("enter your occupation: ")
user1_age = int(input("enter your age: "))
user1_city = input("enter your city: ")
user1_contact_number = int(input("enter your contact number: "))
user1_email = input("enter your email: ")

# Personal information user 2
print(f"\nUser 2")
user2_name_lastname = input("enter your name and last name: ")
user2_ocupation = input("enter your occupation: ")
user2_age = int(input("enter your age: "))
user2_city = input("enter your city: ")
user2_contact_number = int(input("enter your contact number: "))
user2_email = input("enter your email: ")

# Personal information user 3
print(f"\nUser 3")
user3_name_lastname = input("enter your name and last name: ")
user3_ocupation = input("enter your occupation: ")
user3_age = int(input("enter your age: "))
user3_city = input("enter your city: ")
user3_contact_number = int(input("enter your contact number: "))
user3_email = input("enter your email: ")

# Store of each user's information in an immutable structure
user1 = (user1_name_lastname, user1_ocupation, user1_age, user1_city, user1_contact_number, user1_email)

user2 = (user2_name_lastname, user2_ocupation, user2_age, user2_city, user2_contact_number, user2_email)

user3 = (user3_name_lastname, user3_ocupation, user3_age, user3_city, user3_contact_number, user3_email)

# Store the captured information in a mutable structure
users = [list(user1), list(user2), list(user3)]

# Display on screen the information of each user
print(f"\n\n\tUsers Personal Information\n\nUser 1")
# name and lastname
print(f"name and last name:\t{users[0][0]} \
    \noccupation:\t\t{users[0][1]} \
    \nage:\t\t\t{users[0][2]} \
    \ncity:\t\t\t{users[0][3]} \
    \ncontact number:\t\t{users[0][4]} \
    \nemail:\t\t\t{users[0][5]}")
# user 2
print(f"\nUser 2\nname and last name:\t{users[1][0]} \
    \noccupation:\t\t{users[1][1]} \
    \nage:\t\t\t{users[1][2]} \
    \ncity:\t\t\t{users[1][3]} \
    \ncontact number:\t\t{users[1][4]} \
    \nemail:\t\t\t{users[1][5]}")
# user 3
print(f"\nUser 3\nname and last name:\t{users[2][0]} \
    \noccupation:\t\t{users[2][1]} \
    \nage:\t\t\t{users[2][2]} \
    \ncity:\t\t\t{users[2][3]} \
    \ncontact number:\t\t{users[2][4]} \
    \nemail:\t\t\t{users[2][5]}")


#from msilib.schema import Error
import requests
from bs4 import BeautifulSoup
import re
import json
from openpyxl import load_workbook
from datetime import datetime
import time
from telegram import Bot

import logging
import os

try:
    import config
except:
    pass

try:
    token = os.environ.get('TOKEN')
except:
    print('TOKEN is not set!')

bot = Bot(token=token)
logging.basicConfig(level=logging.INFO, filename='output.log', filemode='a', format='%(asctime)s %(levelname)s - %(message)s', datefmt='%d-%b-%y %I:%M:%S %p')

def get_url(model, serial):
    if model.lower() == 'thinkpad e14':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-edge-laptops/thinkpad-e14-type-20ra-20rb/20ra/20ra003ssg/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad e490':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-edge-laptops/thinkpad-e490-type-20n8-20n9/20n8/20n8005psg/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad l13':
        brand, url = 'lenovo', ['https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l13-type-20r3-20r4/20r3/20r3000vsg/{}/warranty'.format(serial), 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l13-type-20r3-20r4/20r4/20r4cto1ww/{}/warranty'.format(serial)]
    elif model.lower() == 'thinkpad l380':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l380-type-20m5-20m6/20m6/20m6cto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad l390':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l390-type-20nr-20ns/20ns/20nscto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad l480':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l480-type-20ls-20lt/20lt/20ltcto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad l490':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l490-type-20q5-20q6/20q6/20q6cto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad x280':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-x-series-laptops/thinkpad-x280-type-20kf-20ke/20ke/20kesbu300/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad x390':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-x-series-laptops/thinkpad-x390/20q1/20q1cto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad l14':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-l-series-laptops/thinkpad-l14-type-20u1-20u2/20u2/20u2cto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad x13':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/sg/en/products/laptops-and-netbooks/thinkpad-x-series-laptops/thinkpad-x13-type-20t2-20t3/20t3/20t3cto1ww/{}/warranty'.format(serial)
    elif model.lower() == 'thinkbook 13s g2':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkbook-series/thinkbook-13s-g2-itl/20v9/20v9005ksb/{}/warranty'.format(serial)
    elif model.lower() == 'thinkpad x13 g2':
        brand, url = 'lenovo', 'https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-x-series-laptops/thinkpad-x13-yoga-gen-2-type-20wk-20wl/20wl/20wls10p00/{}/warranty'.format(serial)
    elif model.lower() == 'acer':
        brand, url = 'acer', 'http://support.acer.com.sg/support/checkwarrantyresults.asp'
    else:
        brand, url = -1, -1
    return brand, url

def get_warranty(url):
    if isinstance(url, list) is False: #not a list
        warranty = lenovo_bs4(url)
    else:
        for warrantyurl in url: #multiple url provided. Go thru list to make sure warranty url returns valid warranty date
            warranty = lenovo_bs4(warrantyurl)
            if  warranty != -1:
                break
    return warranty

def lenovo_bs4(url):
    r = requests.get(url)
    data = r.text
    soup = BeautifulSoup(data, 'html.parser')
    script = soup.find_all('script')
    for sr in script:
        try:
            if 'var ds_warranties' in sr.string:
                warranty_string = sr.string
                ds_warranties = re.findall('window.ds_warranties \|\| ({[\w\W]+});', warranty_string)[0]
                js = json.loads(ds_warranties)
                warranty = js.get('BaseUpmaWarranties')[0].get('End')
                break
            else:
                warranty = -1
        except TypeError:
            pass
    return warranty

def get_acer_warranty(serial):
    url = 'http://support.acer.com.sg/support/checkwarrantyresults.asp'
    formData = {
        '__VIEWSTATE': 'wEPDwUJNDA4Mzc4NTUxZGQazEtBaHJpnK06W95ZaNAwjHPGdC3C2rmOWwCv9qkuBw==',
        'pserialno': serial
    }

    r = requests.post(url, data = formData)
    data = r.text
    soup = BeautifulSoup(data, 'html.parser')
    row = soup.find_all('td')
    for i in range(len(row)):
        try:
            if row[i].strong.text == 'Onsite Expiry:': #take date of onsite expiry as warranty end date
                warranty = row[i+1].text
                break
        except:
            warranty = -1 #error or page not found
    return warranty

def format_date(brand, date_str):
    try:
        if brand == 'lenovo':
            unformatted = datetime.strptime(date_str,'%Y-%m-%d')
            formatted_date = unformatted.strftime('%d %b %Y')
        else:
            unformatted = datetime.strptime(date_str, '%d %B, %Y')
            formatted_date = unformatted.strftime('%d %b %Y')
        return formatted_date
    except ValueError:
        logging.error('{} does not contain a valid date format'.format(date_str))
        return 'Invalid date format'

def generate_output(row):
    try:
        serial = row[0].value.strip()
        model = row[1].value
        brand, url = get_url(model, serial)
        if url == -1 or brand == -1:
            row[2].value = 'Cannot find model'
            print('{} - Cannot find model'.format(serial))
        elif brand == 'lenovo':
            warranty_date = get_warranty(url)
            output_date = format_date(brand, warranty_date)
            row[2].value = output_date
            print('{} - {}'.format(serial, output_date))
        else: #acer brand
            warranty_date = get_acer_warranty(serial)
            if warranty_date == -1:
                output_date = 'Not Found'
            elif warranty_date.lower() == 'not valid': #warranty page returns Not Valid which most likely is expired
                output_date = 'Expired'
            else:
                output_date = format_date(brand, warranty_date)
            row[2].value = output_date
            print('{} - {}'.format(serial, output_date))
        return 1
    except IndexError as e:
        return -1
    except Exception as e:
        print('Unexpected error: {}'.format(e))
        return -2

def main(chat_id):
    wb = load_workbook(filename='input.xlsx')
    list_of_sheetnames = wb.sheetnames
    #sheet = wb['Sample serial number']
    first_sheet_name = list_of_sheetnames[0] #get the name of 1st sheet
    sheet = wb[first_sheet_name] #set it to sheet variable for openpyxl to work on it
    num_rows = len(sheet['A']) -1 #to factor in top row which serves as title
    logging.info('Received input of {} rows.'.format(num_rows))
    bot.send_message(chat_id=chat_id,text='Rows detected: {}'.format(num_rows))
    noError = 0
    noSuccess = 0
    # set how often a notification of the rows being processed is shown. If more then 300 rows, will notify every 100 rows, else every 50 rows
    if num_rows > 300:
        process_noti = 100
    else:
        process_noti = 50
    for row in sheet.iter_rows(min_row=2, max_col=3):
        index = int(row[0].row) - 1 # the row that it is currently on. Minus 1 to factor in top row which serves as title
        if row[0].value == '':
            result = 0
        else:
            result = generate_output(row)
        if index % process_noti == 0 : #output message every 50 rows
            bot.send_message(chat_id=chat_id, text='Processing {}/{}'.format(index, num_rows))
        if result == 1:
            noSuccess+=1
        elif result == -1:    #IndexError during function run
            print('{} - IndexError, retrying..'.format(row[0].value))
            time.sleep(5)   #sleep 5 seconds before re-trying function call
            result2 =generate_output(row)   #attempt to re-run function to get warranty
            if result2 == -1:   #if still IndexError, will output to output file
                print('{} - Index Error on 2nd try'.format(row[0].value))
                row[2].value = 'Index Error'
                logging.error('{} index error after 2nd try'.format(row[0].value))
                noError+=1
        elif result == -2:
            print('{} - Unexpected Error'.format(row[0].value))
            row[2].value = 'Unexpected Error'
            logging.error('{} - Unexpected Error'.format(row[0].value))
            noError+=1
        elif result == 0:
            pass
        else:
            print('{} - This should not happen - Invalid error code'.format(row[0].value))
            row[2].value = 'This should not happen - Invalid error code'
            logging.error('{} - This should not happen'.format(row[0].value))

    wb.save('result.xlsx')
    print('Output created..')
    resultSummary = 'Success: {}    Error: {}'.format(noSuccess, noError)
    logging.info(resultSummary)
    bot.send_message(chat_id=chat_id, text = resultSummary)


'''
Code for studying newton's laws using sr04 sensor
Logs data from various sensors.
Author  : Jithin B.P, jithinbp@gmail.com
Date    : Sep-2019
License : GNU GPL version 3
'''
import sys,time
t = time.time()

from utils import *
from PyQt5 import QtGui, QtCore, QtWidgets

import pyqtgraph as pg

import math, os.path, struct
from collections import OrderedDict

from layouts import ui_newtonslaws

from layouts.gauge import Gauge
import functools
from functools import partial

import numpy as np
import math
import numpy.linalg

print(time.time()-t)
colors=['#00ff00','#ff0000','#ffff80',(10,255,255)]+[(50+np.random.randint(200),50+np.random.randint(200),150+np.random.randint(100)) for a in range(10)]

Byte =     struct.Struct("B") # size 1
ShortInt = struct.Struct("H") # size 2
Integer=   struct.Struct("I") # size 4

############# MATHEMATICAL AND ANALYTICS ###############

#TODO

############# MATHEMATICAL AND ANALYTICS ###############

class Expt(QtWidgets.QMainWindow, ui_newtonslaws.Ui_MainWindow):
	def __init__(self, device=None):
		super(Expt, self).__init__()
		self.setupUi(self)

		self.p = device

		self.rec = True #Recording enabled

		#Define some keyboard shortcuts for ease of use
		self.shortcutActions={}
		self.shortcuts={" ":self.toggleRecord}
		for a in self.shortcuts:
			shortcut = QtGui.QShortcut(QtGui.QKeySequence(a), self)
			shortcut.activated.connect(self.shortcuts[a])
			self.shortcutActions[a] = shortcut

		self.T = 0
		self.start_time = time.time()
		self.points=0
		self.time = np.empty(300)
		self.pcurve_data = np.empty(300)
		self.vcurve_data = []
		self.acurve_data = []

		self.pcurve = self.graphPosition.plot(pen=colors[0])
		self.graphPosition.setRange(xRange=[-5, 0])

		self.pcurve2 = self.graphResults.plot(pen=colors[0],name='Position')
		self.graphResults.setLabel('left', "Position", units='cm',color='#00ff00')

		self.graphResults.viewBoxes=[]

		## Add secondary Axis for Velocity
		self.p3 = pg.ViewBox()
		ax3 = pg.AxisItem('right')
		self.graphResults.plotItem.layout.addItem(ax3, 2, 3)
		self.graphResults.plotItem.scene().addItem(self.p3)
		ax3.linkToView(self.p3)
		self.p3.setXLink(self.graphResults)
		ax3.setZValue(-10000)
		ax3.setLabel('Velocity', color=colors[1])
		self.p3.setGeometry(self.graphResults.plotItem.vb.sceneBoundingRect())
		self.p3.linkedViewChanged(self.graphResults.plotItem.vb, self.p3.XAxis)
		self.graphResults.viewBoxes.append(self.p3)
		self.vcurve = pg.PlotDataItem(pen=colors[1],name='Velocity')
		self.p3.addItem(self.vcurve)

		## Add third Axis for Acceleration
		self.p2 = pg.ViewBox()
		ax2 = pg.AxisItem('right')
		self.graphResults.plotItem.layout.addItem(ax2, 2, 4)
		self.graphResults.plotItem.scene().addItem(self.p2)
		ax2.linkToView(self.p2)
		self.p2.setXLink(self.graphResults)
		ax2.setZValue(-10000)
		ax2.setLabel('Acceleration', color=colors[2])
		self.p2.setGeometry(self.graphResults.plotItem.vb.sceneBoundingRect())
		self.p2.linkedViewChanged(self.graphResults.plotItem.vb, self.p2.XAxis)
		self.graphResults.viewBoxes.append(self.p2)
		self.acurve = pg.PlotDataItem(pen=colors[2],name='Acceleration')
		self.p2.addItem(self.acurve)

		Callback = functools.partial(self.updateViews,self.graphResults)
		self.graphResults.getViewBox().sigStateChanged.connect(Callback)

		self.graphResults.setRange(xRange=[0, 5])

		self.region = pg.LinearRegionItem()
		self.region.setBrush([255,0,50,50])
		self.region.setZValue(10)
		for a in self.region.lines: a.setCursor(QtGui.QCursor(QtCore.Qt.SizeHorCursor));
		self.graphPosition.addItem(self.region, ignoreBounds=False)
		self.region.setRegion([-3,-.5])

		#cross hair
		self.vLine = pg.InfiniteLine(angle=90, movable=False)
		self.hLine = pg.InfiniteLine(angle=0, movable=False)
		self.graphResults.addItem(self.vLine, ignoreBounds=True)
		self.graphResults.addItem(self.hLine, ignoreBounds=True)
		self.graphResults.setTitle('Pause acquisition to view derivates here',justify='left')
		self.resultslabel = self.graphResults.plotItem.titleLabel
		self.proxy = pg.SignalProxy(self.graphResults.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)

		# Populate contents of samplesBox(For moving average) and polyBox(Degree of polynomial)
		self.samplesBox.addItems(['30']+[str(a*5) for a in range(1,20)])
		self.polyBox.addItems([str(a) for a in range(2,7)])
		self.samplesBox.setCurrentIndex(5)
		self.samplesBox.setCurrentIndex(0)

		self.gauge = Gauge(self)
		self.gauge.setObjectName("distance")
		self.gauge.set_MinValue(0)
		self.gauge.set_MaxValue(50)
		self.gaugeLayout.addWidget(self.gauge)

		self.startTime = time.time()
		self.timer = QtCore.QTimer()
		self.timer.timeout.connect(self.updateEverything)
		self.timer.start(2)

	def mouseMoved(self,evt):
		pos = evt[0]  ## using signal proxy turns original arguments into a tuple
		vb = self.graphResults.plotItem.vb
		if vb.sceneBoundingRect().contains(pos):
			mousePoint = vb.mapSceneToView(pos)
			index = int(np.abs(self.time - mousePoint.x()).argmin()) #(np.abs(arr-val)).argmin()
			if index > 0 and index < len(self.vcurve_data):
				self.resultslabel.setText("x=%0.3f,   P=%0.1f cm,V=%0.2f,   A=%0.2f" % (mousePoint.x(), self.pcurve_data[index], self.vcurve_data[index], self.acurve_data[index]))
			self.vLine.setPos(mousePoint.x())
			self.hLine.setPos(mousePoint.y())

	def updateViews(self,plot):
			for a in plot.viewBoxes:
				a.setGeometry(plot.getViewBox().sceneBoundingRect())
				a.linkedViewChanged(plot.plotItem.vb, a.XAxis)

	def updateEverything(self):
		if self.rec:
			D = self.p.sr04_distance()
			if D is None: return
			self.gauge.update_value(D)
			self.T = time.time() - self.start_time
			self.time[self.points] = self.T
			self.pcurve_data[self.points] = D
			self.pcurve.setData(self.time[:self.points],self.pcurve_data[:self.points])
			self.pcurve.setPos(-self.T, 0)

			self.points+=1
			if self.points >= self.time.shape[0]-1:
				tmp = self.time
				self.time = np.empty(self.time.shape[0] * 2) #double the size
				self.time[:tmp.shape[0]] = tmp
				tmp = self.pcurve_data
				self.pcurve_data = np.empty(self.pcurve_data.shape[0] * 2) #double the size
				self.pcurve_data[:tmp.shape[0]] = tmp

	### ANALYSIS : https://dsp.stackexchange.com/a/9512
	### Accepted answer on using moving averages for this particular problem.

	def sg_filter(self,x, m, k=0):
		"""
		x = Vector of sample times
		m = Order of the smoothing polynomial
		k = Which derivative
		"""
		mid = int(len(x) / 2)
		a = x - x[mid]
		expa = lambda x: list(map(lambda i: i**x, a))
		mp = list(map(expa, range(0,m+1)))
		A = np.r_[mp].transpose()
		Ai = np.linalg.pinv(A)
		return Ai[k]

	def smooth(self,x, y, size=5, order=2, deriv=0):
		if deriv > order:
			print( "deriv must be <= order")
			return None

		n = len(x)
		m = size
		result = np.zeros(n)
		for i in range(m, n-m):
			start, end = i - m, i + m + 1
			f = self.sg_filter(np.array(x[start:end]), order, deriv)
			result[i] = np.dot(f, np.array(y[start:end]))

		if deriv > 1:
			result *= math.factorial(deriv)

		return result

	def redoAnalysis(self,s):
		if not self.rec: #Analysis mode
			self.analysis()

	def analysis(self):
		self.recordBox.setChecked(False)
		self.setRecord(False)

		T = np.array(self.time[:self.points])
		pos = np.array(self.pcurve_data[:self.points])

		if self.splineBox.currentIndex()!=0:
			z = np.polyfit(T,pos,10)
			zp = np.poly1d(z)
			pos = zp(T)

		self.pcurve2.setData(T,pos)
		smoothing_size = int(self.samplesBox.currentText()) # points for smoothing
		order = int(self.polyBox.currentText()) #  degree of polynomial

		params = (T, pos, smoothing_size, order)
		V = self.smooth(*params, deriv=1) #1st derivative
		A = self.smooth(*params, deriv=2) #2nd derivative

		self.vcurve_data = V
		self.acurve_data = A

		#self.pcurve2.setData(T,pos)
		self.vcurve.setData(T,V)
		self.acurve.setData(T,A)
		self.graphResults.autoRange()
		self.p2.autoRange()
		self.p3.autoRange()

	def clearAll(self):
		self.T = 0
		self.start_time = time.time()
		self.points=0
		self.time = np.empty(300)
		self.pcurve_data = np.empty(300)
		self.vcurve_data = []
		self.acurve_data = []

		self.graphPosition.setRange(xRange=[-5, 0])
		self.pcurve.setPos(0, 0)

		for a in [self.pcurve,self.pcurve2,self.vcurve,self.acurve]:
			a.clear()

	def setRecord(self,val):
		self.rec = val
		if self.rec:
			self.clearAll()
			self.resultslabel.setText('Acquiring data...')
			self.splitter.setSizes([200,100])
		else:
			self.splitter.setSizes([100,200])
	def toggleRecord(self):
		if self.rec:
			self.recordBox.setChecked(False)
			self.setRecord(False)
			self.analysis()
		else:
			self.recordBox.setChecked(True)
			self.setRecord(True)

if __name__ == '__main__':
	import eyes17.eyes
	dev = eyes17.eyes.open()
	app = QtWidgets.QApplication(sys.argv)

	# translation stuff
	lang=QtCore.QLocale.system().name()
	t=QtCore.QTranslator()
	t.load("lang/"+lang, os.path.dirname(__file__))
	app.installTranslator(t)
	t1=QtCore.QTranslator()
	t1.load("qt_"+lang,
	        QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath))
	app.installTranslator(t1)
	mw = Expt(dev)
	mw.show()
	sys.exit(app.exec_())


from tkinter import *

root = Tk()

class Stopwatch(Frame):

    def __init__(self,master=None):
        Frame.__init__(self,master)
        self.grid()
        self.widgets()
        self.running = False
        self.timer = [0,0,0]
        self.timeString = str(self.timer[0]) + ':' + str(self.timer[1]) + ':' + str(self.timer[2])
        self.update_time()

    def widgets(self):
        self.timeFrame = LabelFrame(root,
                                    text='Time Frame',
                                    width=1200)

        self.timeFrame.grid(row=0,
                            column=0,
                            sticky=W)

        self.resetButton = Button(self.timeFrame,
                                  text='Reset',
                                  command=self.resetTime)

        self.resetButton.grid(row=2,
                              column=1)

        self.pauseButton = Button(self.timeFrame,
                                  text='Pause',
                                  command=self.pause)

        self.pauseButton.grid(row=1,
                              column=1)

        self.startButton = Button(self.timeFrame,
                                  text='Start',
                                  command=self.start)

        self.startButton.grid(row=0,
                              column=1)

        self.show = Label(self.timeFrame,
                          text='00:00:00',
                          font=30)

        self.show.grid(row=0,
                       column=0)

    def update_time(self):

        if self.running == True:

            self.timer[2] += 1

            if (self.timer[2] >= 100):
                self.timer[2] = 0
                self.timer[1] += 1

            if (self.timer[1] >= 60):
                self.timer[0] += 1
                self.timer[1] = 0

            self.timeString = str(self.timer[0]) + ':' + str(self.timer[1]) + ':' + str(self.timer[2])
            self.show.config(text=self.timeString)
        root.after(10, self.update_time)

    def start(self):
        self.running = True

    def pause(self):
        self.running = False

    def resetTime(self):
        self.running = False
        self.timer = [0,0,0]
        self.show.config(text='00:00:00')

Stopwatch(root)

root.mainloop()


from json import loads
from functools import cmp_to_key
from math import prod

def comp(left, right):
    left, right = left[::-1], right[::-1]
    while left and right:
        a, b = left.pop(), right.pop()
        if isinstance(a, int) and isinstance(b, int):
            if a != b:
                return -1 if a < b else 1
            continue
        if isinstance(a, list) and isinstance(b, list):
            result = comp(a, b)
            if not result:
                continue
            return result
        a, b = ([a], b) if isinstance(a, int) else (a, [b])
        left.append(a)
        right.append(b)
    if not left and not right:
        return 0
    return -1 if right else 1

def main():
    with open("day13/input.txt", mode="r", encoding="utf-8") as file:
        dividers = [[[2]], [[6]]]
        lines = [line.rstrip() for line in file]
        packets = [[loads(a) for a in b] for b in zip(lines[::3], lines[1::3])]
        packets_flat = dividers + [packet for pair in packets for packet in pair]
        packets_flat.sort(key=cmp_to_key(comp))

        print("1:", sum(i for i, (a, b) in enumerate(packets, 1) if comp(a, b) == -1))
        print("2:", prod(packets_flat.index(d) + 1 for d in dividers))

if __name__ == "__main__":
    main()


# coding:utf8
import sys

import cv2
import numpy as np
import matplotlib as plt
# import PyQt5
import matplotlib.pyplot as plt

def preprocess(gray):
    # 1. Sobel算子，x方向求梯度
    sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize=3)
    # 2. 二值化
    ret, binary = cv2.threshold(sobel, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)

    # 3. 膨胀和腐蚀操作的核函数
    element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 9))
    element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (24, 6))

    # 4. 膨胀一次，让轮廓突出
    dilation = cv2.dilate(binary, element2, iterations=1)

    # 5. 腐蚀一次，去掉细节，如表格线等。注意这里去掉的是竖直的线
    erosion = cv2.erode(dilation, element1, iterations=1)

    # 6. 再次膨胀，让轮廓明显一些
    dilation2 = cv2.dilate(erosion, element2, iterations=2)

    # 7. 存储中间图片
    # cv2.imwrite("binary.png", binary)
    # cv2.imwrite("dilation.png", dilation)
    # cv2.imwrite("erosion.png", erosion)
    # cv2.imwrite("dilation2.png", dilation2)

    return dilation2

def findTextRegion2(img):
    region = []

    # 1. 查找轮廓
    contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # 2. 筛选那些面积小的
    for i in range(len(contours)):
        cnt = contours[i]
        # 计算该轮廓的面积
        area = cv2.contourArea(cnt)

        # 面积小的都筛选掉
        if (area < 1000):
            continue

        # 轮廓近似，作用很小
        epsilon = 0.001 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)

        # 找到最小的矩形，该矩形可能有方向
        rect = cv2.minAreaRect(cnt)
        print
        "rect is: "
        print
        rect

        # box是四个点的坐标
        box = cv2.cv.BoxPoints(rect)
        box = np.int0(box)

        # 计算高和宽
        height = abs(box[0][1] - box[2][1])
        width = abs(box[0][0] - box[2][0])

        # 筛选那些太细的矩形，留下扁的
        if (height > width * 1.2):
            continue

        region.append(box)
    return region

def findTextRegion(org, img):
    region = []

    # 1. 查找轮廓
    _, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    maxArea = 0
    maxContour = 0

    for i in range(len(contours)):
        cnt = contours[i]
        # 计算该轮廓的面积
        area = cv2.contourArea(cnt)
        if area > maxArea:
            maxArea = area
            maxContour = cnt

    maxRect = cv2.boundingRect(maxContour);
    x, y, w, h = cv2.boundingRect(maxContour)
    cv2.rectangle(org, (x, y), (x + w, y + h), (255, 255, 0), 2)

    # cv2.imshow("img", org)
    # cv2.waitKey(0)

    return x, y, w, h

def detect(img):
    # 1.  转化成灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 2. 形态学变换的预处理，得到可以查找矩形的图片
    dilation = preprocess(gray)

    # 3. 查找和筛选文字区域
    x, y, w, h = findTextRegion(gray, dilation)

    # cropImg = gray[y:y + h, x:x + w]
    # cv2.imshow("img", cropImg)
    # cv2.waitKey(0)
    return x, y, w, h

def draw_rec(image, x, y, width, height, label, number):
    (H, W) = image.shape[:2]
    rec = (H, W)
    COLORS = np.random.randint(0, 255, size=2, dtype="uint8")
    color = [255,0]

    (w, h) = (width, height)
    # (x, y) = (int((rec[1] - width) / 2), int((rec[0] - height) / 2))

    cv2.rectangle(img=image, pt1=(x, y), pt2=(x + w, y + h), color=color[0], thickness=2)

    # text = "{}:{:.3f}".format(label, number)
    # cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_COMPLEX, fontScale=0.5, color=color[1], thickness=1)

if __name__ == '__main__':
    # 读取文件
    imagePath = "1.png"  # sys.argv[1]
    img = cv2.imread(imagePath)
    x, y, w, h = detect(img)
    draw_rec(img, x, y, w, h, 0, 0)
    # cv2.resize(img, (1122, 540))
    cv2.namedWindow("wid", cv2.WINDOW_NORMAL)
    cv2.resizeWindow("wid", 1122, 540)
    # a = cv2.getWindowImageRect('wid')

    # img2 = img[:, :, ::-1]
    # plt.imshow(img2)
    # plt.show()

    cv2.imshow("wid", img)
    cv2.waitKey(0)


from pyramid.response import Response
from pyramid.view import view_config

from sqlalchemy.exc import DBAPIError
from sqlalchemy import func

from ..models.mymodel import TblPatient
from collections import OrderedDict

import logging
log = logging.getLogger(__name__)

@view_config(route_name='home', renderer='../templates/mytemplate.jinja2')
def my_view(request):
	try:
		group_of_age = ['1-17','18-44','45-64','65-84','85-95']
		result = OrderedDict([])
		race = ('white', 'black', 'hispanic', 'asian pacific islander', 'native american', 'other', 'missing')

		for each_group in group_of_age:
			result[each_group] = OrderedDict([])
			split_ = each_group.split('-')
			min_ = int(split_[0])
			max_ = int(split_[1])

			query_result = request.dbsession.query(TblPatient.patient_race, func.count(TblPatient.patient_race)).group_by(TblPatient.patient_race).filter(TblPatient.patient_age >= min_, TblPatient.patient_age <= max_).all()
			tpg = 0

			for row in query_result:
				tpg += row[1]
				result[each_group][row[0]] = row[1]

			result[each_group]['total'] = tpg

	except Exception as e:
		log.exception(str(e))
		return {'code': 'error', 'message': str(e), 'content' : ''}
	return {'code': 'ok', 'message': '', 'content' : result, 'race' : race}

db_err_msg = """\
Pyramid is having a problem using your SQL database.  The problem
might be caused by one of the following things:

1.  You may need to run the "initialize_kamdabase3_db" script
	to initialize your database tables.  Check your virtual
	environment's "bin" directory for this script and try to run it.

2.  Your database server may not be running.  Check that the
	database server referred to by the "sqlalchemy.url" setting in
	your "development.ini" file is running.

After you fix the problem, please restart the Pyramid application to
try it again.
"""


#!/usr/bin/env python3
"""
This script is designed as a sandbox for testing different
ensembling and post-processing steps.
Edit to create the ensembles / weights / post-processing
pipelines you want to try, then run to produce results for each.
"""

import numpy as np
import pandas as pd
import sys, os
import csv
from time import strftime
import matplotlib.pyplot as plt
from orchestra import *
from evaluation import *
from post_processing import *
from predict_multiview import *

base_dir = "kits19/data"

# Syntax for predict_multiview is:
#   predict_multiview(img_vol, weights_file, orientation='axial', t_thresh=0.1, k_thresh=0.2)
models_to_check = [
    # [   # "score1"
    #     lambda img_vol: predict_multiview(
    #         img_vol, weights_file="ensemble_weights/unet_axial_e98.h5"
    #     ),
    #     lambda img_vol: predict_multiview(
    #         img_vol, weights_file="ensemble_weights/unet_axial_KT-T_e200.h5"
    #     ),
    # ]
    # [   # "score2" and "final"
    #     lambda img_vol: predict_multiview(
    #         img_vol, weights_file="ensemble_weights/unet_axial_e150.h5"
    #     ),
    #     lambda img_vol: predict_multiview(
    #         img_vol, weights_file="ensemble_weights/unet_axial_KT-T_e205.h5"
    #     ),
    # ],
]
model_descriptions = [
    # "2axial_score1",
    # "2axial_score2",
    # "2axial_final",
]
coefs_to_check = [
    # [(1, 1), (1, 1)],
    # [(1, 1), (1, 1)],
    # [(1, 1), (1, 1)],
]

post_proc_to_check = [
    # lambda seg: post_process_kt_t( # "score1"
    #     seg,
    #     k_steps=[],
    #     t_steps=[
    #         lambda seg: fill_gaps(seg),
    #         lambda seg: fill_gaps(seg, axis=1),
    #         lambda seg: fill_gaps(seg, axis=2),
    #     ],
    #     kt_steps=[lambda seg: keep_largest(seg)],
    #     do_intersect=True,
    # ),
    # lambda seg: post_process_kt_t( # "score2"
    #     seg,
    #     k_steps=[],
    #     t_steps=[
    #         lambda seg: fill_large_gaps(seg, axis="all"),
    #         lambda seg: fill_objects(seg, axis="all"),
    #         lambda seg: keep_largest(seg, n=5),
    #     ],
    #     kt_steps=[lambda seg: keep_largest(seg)],
    #     do_intersect=True,
    # ),
    # lambda seg: post_process_kt_t_2(  # score2-with-kidney-preserving "keep_largest" pproc + singleslice removal  (final-score)
    #     seg,
    #     k_steps=[],
    #     t_steps=[
    #         lambda seg: fill_large_gaps(seg, axis="all"),
    #         lambda seg: fill_objects(seg, axis="all"),
    #         lambda seg: remove_single_slice_objects(seg),
    #         lambda seg: keep_largest(seg, n=5),
    #     ],
    #     kt_steps=[lambda seg, vol_kt: keep_largest_intersecting_K(seg, vol_kt)],
    #     do_intersect=True,
    # ),
]

def compare_segs(gt_vol, pred_vol):
    return  # Stand-alone -- do not vis. Comment this line to re-enable
    import matplotlib.pyplot as plt

    # Find maximal segmentation slice and use that...
    has_segs = np.argmax([v.sum() for v in gt_vol])
    plt.figure(figsize=(15, 3))
    plt.subplot(1, 6, 1)
    plt.imshow(pred_vol[has_segs].astype(int), cmap="Set1")
    plt.subplot(1, 6, 2)
    plt.imshow((pred_vol[has_segs] == 1).astype(int), cmap="gray")
    plt.subplot(1, 6, 3)
    plt.imshow((pred_vol[has_segs] == 2).astype(int), cmap="gray")
    plt.subplot(1, 6, 4)
    plt.imshow(gt_vol[has_segs].astype(int), cmap="Set1")
    plt.subplot(1, 6, 5)
    plt.imshow((gt_vol[has_segs] == 1).astype(int), cmap="gray")
    plt.subplot(1, 6, 6)
    plt.imshow((gt_vol[has_segs] == 2).astype(int), cmap="gray")
    plt.show()

def check_dice_on_list(
    patient_list,
    vis=True,
    report_after_n=None,
    orchestra=None,
    post_proc=None,
    save_masks=None,
):
    if orchestra is not None:
        ensemble = orchestra

    def print_summary(p_list, dice_list):
        print(
            "Averages for N={} volumes: KT: {:.3f} std {:.3f} TU: {:.3f} std {:.3f}\n".format(
                len(p_list),
                dice_list[:, 1].mean(),
                dice_list[:, 1].std(ddof=1),
                dice_list[:, 2].mean(),
                dice_list[:, 2].std(ddof=1),
            )
        )

    if report_after_n is None:
        report_after_n = []
    elif type(report_after_n) != type([]):
        report_after_n = [report_after_n]
    all_dice = []
    for pid_idx, pid in enumerate(patient_list):
        print(
            "Predicting for case {} ({} of {})".format(
                pid, (pid_idx + 1), len(patient_list)
            )
        )
        dirname = "case_{:05d}".format(pid)
        dirpath = os.path.join(base_dir, dirname)
        img_file = os.path.join(dirpath, "imaging.nii.gz")
        seg_file = os.path.join(dirpath, "segmentation.nii.gz")
        img_vol, affine = ensemble.load_nifti(img_file)
        pred_vol = ensemble.analyze_volume(img_vol)
        if post_proc is not None:
            print("Applying post processing...")
            pred_vol = post_proc(pred_vol)
        del img_vol
        if save_masks is not None:
            pred_file = "prediction_{:05d}.nii.gz".format(pid)
            ensemble.save_mask(pred_file, pred_vol, affine)
        gt_seg, _ = ensemble.load_nifti(seg_file, is_mask=True)
        kt_dice, t_dice = kt_dice_score(gt_seg, pred_vol)
        print("KT Dice: {}, TU Dice: {}".format(kt_dice, t_dice))
        all_dice.append([pid, kt_dice, t_dice])

        if (vis is True) or (type(vis) == type([]) and pid_idx in vis):
            compare_segs(gt_seg, pred_vol)
        del gt_seg
        del pred_vol

        # If we need an early report, do so now:
        if (pid_idx + 1) in report_after_n:
            print_summary(patient_list[: (pid_idx + 1)], np.asarray(all_dice))

    all_dice = np.array(all_dice)

    print_summary(patient_list, all_dice)

    return all_dice

def write_report(fname, score_data):
    """
    Writes avg, stddev of dice scores, then scores for each patient.
    """
    # {'pid':all_dice[:,0],'kt_dice':all_dice[:,1], 't_dice':all_dice[:,2]}
    score_data = np.asarray(score_data)
    kt_avg = score_data[:, 1].mean()
    kt_std = score_data[:, 1].std(ddof=1)
    t_avg = score_data[:, 2].mean()
    t_std = score_data[:, 2].std(ddof=1)
    with open(fname, "w") as fout:
        writer = csv.writer(fout)
        writer.writerow(["", "pid", "kt_dice", "t_dice"])
        writer.writerow(["avg", "*", round(kt_avg, 3), round(t_avg, 3)])
        writer.writerow(["stddev", "*", round(kt_std, 3), round(t_std, 3)])
        for idx, row in enumerate(score_data):
            writer.writerow([idx + 1, row[0], row[1], row[2]])

def run_multiple_scenarios(patient_list, save_masks=None):
    # First with unity weights
    print("\n\nUnity weights.")
    for idx, models in enumerate(models_to_check):
        report_name = "model_{}_unity.csv".format(model_descriptions[idx])
        if not os.path.isfile(report_name):
            coefs = coefs_to_check[idx]
            if post_proc_to_check is not None:
                post_proc = post_proc_to_check[idx]
            else:
                post_proc = None
            ## Orchestra:
            # Orchestra(models, weights, datapath, outpath, unity_weights=True)
            ensemble = Orchestra(
                models, coefs, "", outpath=save_masks if save_masks is not None else ""
            )
            all_dice = check_dice_on_list(
                patient_list,
                vis=False,
                report_after_n=[10],
                orchestra=ensemble,
                post_proc=post_proc,
                save_masks=save_masks,
            )
            write_report(report_name, all_dice)
    # Now with non-unity weights (Uncomment to try this option.)
    # print("\n\nNon-Unity weights.")
    # for idx, models in enumerate(models_to_check):
    #     report_name = "model_set_{}_NONunity.csv".format(idx)
    #     if not os.path.isfile(report_name):
    #         coefs = coefs_to_check[idx]
    #         ## Orchestra:
    #         # Orchestra(models, weights, datapath, outpath, unity_weights=True)
    #         ensemble = Orchestra(models, coefs, "", "", unity_weights=False)
    #         all_dice = check_dice_on_list(patient_list, vis=False, report_after_n=[10], orchestra=ensemble)
    #         write_report("model_set_{}_NONunity.csv".format(idx), all_dice)
    # Now with unity weights but lower threshold:
    # print("\n\nUnity weights, threshold = K=0.20,T=.15")
    # for idx, models in enumerate(models_to_check):
    #     if len(models) < 2:
    #         continue
    #     report_name = "model_set_{}_unity_ktt-20_tut-15_postproc.csv".format(idx)
    #     if not os.path.isfile(report_name):
    #         coefs = coefs_to_check[idx]
    #         post_proc = post_proc_to_check[idx]
    #         ## Orchestra:
    #         # Orchestra(models, weights, datapath, outpath, unity_weights=True)
    #         ensemble = Orchestra(models, coefs, "", "")
    #         ensemble.k_thresh = 0.20
    #         ensemble.t_thresh = 0.15
    #         all_dice = check_dice_on_list(
    #             patient_list,
    #             vis=False,
    #             report_after_n=[10],
    #             orchestra=ensemble,
    #             post_proc=post_proc,
    #         )
    #         write_report(
    #             "model_set_{}_unity_ktt-20_tut-15_postproc.csv".format(idx), all_dice
    #         )

def load_cases():
    test_list = pd.read_csv("test_files_with_mask_class_info_sorted_by_pid.csv")

    test_list.head()

    patient_list = test_list.pid.unique()
    print(patient_list)
    return patient_list

def main():
    args = get_args()
    global base_dir
    base_dir = args.base_dir

    patient_list = load_cases()
    run_multiple_scenarios(patient_list, save_masks=args.save_masks)

def get_args():
    import argparse

    # construct the argument parser and parse the arguments
    ap = argparse.ArgumentParser(prog="{0}".format(os.path.basename(sys.argv[0])))

    ap.add_argument(
        "--base-dir",
        type=str,
        required=False,
        default="data",
        help="Path to the directory containing the image and mask subdirectories.",
    )

    ap.add_argument(
        "--save-predictions",
        dest="save_masks",
        type=str,
        required=False,
        help="Path to a directory where predicted masks should be output; if not provided, masks will not be saved.",
    )

    return ap.parse_args()

if __name__ == "__main__":
    main()


from PySide2.QtWidgets import QApplication, QMainWindow

from levyrekisteri import Ui_LevyRekisteri
#Uuden ikkunan takia
from tallenna import Ui_Tallenna
#from tallenna_gui import Tallenna_gui

#Luokka sisältää kaikki Levyn tiedot

class Levy(object):
    def __init__(self,Artisti="",LevynNimi="",JulkV="",Levy_Yht="",Painos=""):
        super().__init__()

        self.ArtistinNimi = Artisti
        self.LevynNimi = LevynNimi
        self.JulkaisuVuosi = JulkV
        self.Levy_yhtio = Levy_Yht
        self.Painos = Painos


import numpy as np
import matplotlib.pyplot as plt

###################Calcul et tracé de la fonction##############################
t=np.linspace(0,0.2,1000)
xi_d=0.2
w0_d=204
K_d=2
s=K_d*(1-np.exp(-w0_d*xi_d*t)/np.sqrt(1-xi_d**2)*np.cos(w0_d*np.sqrt(1-xi_d**2)*t\
                                                      -np.arctan(xi_d/np.sqrt(1-xi_d**2))))
fig1=plt.figure('Réponse 1')
plt.plot(t,s)
plt.plot(t,s[-1]*np.ones(len(t)),'-.',c='#1f77b4')
plt.plot(t,0.95*s[-1]*np.ones(len(t)),'--',c='#1f77b4')
plt.plot(t,1.05*s[-1]*np.ones(len(t)),'--',c='#1f77b4')
plt.savefig('img/fig01.png')
###############################################################################

#Question 2:
K=s[-1]

#Question 3:
def recherche(s):
    val=s[0]
    for i in range(len(s)):
        if s[i] > val:
            val=s[i]
    return val

#Question 4:
i=0
while s[i]!=recherche(s):
    i+=1
Tp=2*t[i]

#Question 5:
def D(xi):
    return 100*np.exp(-xi*np.pi/np.sqrt(1-xi**2))

xi=0.7
while D(xi)<100*(recherche(s)-s[-1])/s[-1]:
    xi+=-0.1

w0=2*np.pi/(Tp*np.sqrt(1-xi**2))

print("Données",K_d,xi_d,w0_d)
print("Identifications",K,xi,w0)

i=-1
while s[i]<1.05*s[-1] and s[i]>0.95*s[-1]:
    i+=-1
t5=t[i]

def tr5(xi,w0):
    return np.log(20)/(xi*w0)

if abs(t5-tr5(xi,w0))<0.1:
    print("OK")
else:
    print("KO")

print("Le temps de réponse est %s secondes" % format(t5))
t5=tr5(xi,w0)
print("Le temps de réponse est %s secondes" % format(t5))
plt.plot([t[i],t[i]],[1.1*s[-1],0.9*s[-1]])

fig2=plt.figure('Réponse 2')

while t5>0.025:
    xi+=10**(-3)
    s=K*(1-np.exp(-w0*xi*t)/np.sqrt(1-xi**2)*np.cos(w0*np.sqrt(1-xi**2)*t-np.arctan(xi/np.sqrt(1-xi**2))))
    i=-1
    while s[i]<1.05*s[-1] and s[i]>0.95*s[-1]:
        i+=-1
    t5=t[i+1]

print("Pour xi=%.3f, le temps de réponse est %s secondes" % ((xi),(t5)))
plt.plot([t[i],t[i]],[1.1*s[-1],0.9*s[-1]])
plt.plot(t,s)
plt.plot(t,0.95*s[-1]*np.ones(len(t)))
plt.plot(t,1.05*s[-1]*np.ones(len(t)))

print((2**20-1)/5)
print(2**(-3),2**(-1),2**(-25))


#!/usr/bin/python

import os, sys, socket, getpass
from lib import fstab, ping, sge, network, hosts, nodelist

#   Make sure the script is being run as root
whoami = getpass.getuser()

if whoami != 'root':
    print('This script must be run as root')
    sys.exit()
else:
    print('Running as root... OK')

# Get the name of the headnode... THIS NODE
headnode = socket.gethostname()

print("We'll need an IP address where the new node may be reached to get started.")
print("Login to the node and type `ifconfig` if you don't know it.")
tmp_ip = raw_input("IP Address: ")

if not ping.host_alive(tmp_ip):
    print("%s is not reachable. Exiting.")
    sys.exit()

#   Read in node list, sorting by IP
#   Determin next IP to use
print("Reading node list...")
nodes = nodelist.read_nodelist()
if len(nodes) > 0:
    num = (int(nodes[-1][-3:])+1)
    new_ip = "10.0.0." + str(num)
    new_node = "minion" + str(num)
else:
    new_ip = "10.0.0.101"
    new_node = "minion101"

#   Update the hosts file
print("Updating hosts file...")
hosts.add_node(headnode, nodes, new_ip, new_node, tmp_ip)

#   Set the hostname
print("Updating hostname...")
hosts.set_hostname(new_node, tmp_ip)

#   Generate network interfaces file for the new node
#   Replace the new nodes network interfaces file
print("Updating network interfaces...")
network.add_interface(new_ip, tmp_ip)

#   Fetch the new nodes fstab file
print("Adding mount for home directories...")
fstab.add_home(tmp_ip)

#   Finally, add the new node to the node list
print("Adding node to nodelist...")
nodelist.add_node(new_node)

#   Reboot node
print("Rebooting node...")
ping.reboot(tmp_ip)

#   Add this node to grid engine
print("Adding node to grid engine...")
sge.add_node(new_node, raw_input("How many cores does %s have?: " % new_node))

#   Perform final reboot
print("Going for last reboot...")
ping.reboot(new_ip)

#   Show the host list
os.system("sudo -u sgeadmin qhost")

print("done.")


#%%
# Carl Klier - Graph Coloring Annealing for Network Science Final Exam
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import random
import math
import json
import sys

# nodes = ['a', 'b', 'c']
# edges = [('a', 'b'), ('a', 'c'), ('b', 'c')]

# nodes = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']
# edges = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('c', 'd'), ('b', 'e'),
#               ('e', 'f'), ('d', 'f'), ('e', 'g'), ('g', 'h'), ('f', 'h'), ('g', 'i'),
#                ('i', 'j'), ('h', 'j'), ('i', 'k'), ('k', 'l'), ('j', 'l')]

# building Graph 1
with open('deezer.json') as f:
  data = json.load(f)
G = nx.Graph()
G.add_edges_from(data["0"])

nodes = list(G.nodes)
edges = list(G.edges)

# print("nodes: ", nodes)
# print("edges: ", edges)
# sys.exit()

# G = nx.Graph()
# G.add_nodes_from(nodes)
# G.add_edges_from(edges)

colors = ['Red', 'Green']
temperature = 5

random.seed(100)

def get_initial_color_mapping(nodes):
  color_mapping = {}
  for node in nodes:
    color_mapping[node] = colors[0]
  return color_mapping

color_mapping = get_initial_color_mapping(nodes)

def update_color_mapping(node, color):
  color_mapping[node] = color

def get_energy_value(color, neighbors):
  energy = 0
  for neighbor in neighbors:
    if color == color_mapping[neighbor]:
      energy = energy + 1
  return energy

# def get_next_energy_value(node, current_energy):
#   neighbors = list(G.neighbors(node))
#   current_color = color_mapping[node]
#   lowest_energy = get_energy_value(current_color, neighbors)
#   lowest_energy_color = current_color
#   for color in colors:
#     possible_lowest_energy = get_energy_value(color, neighbors)
#     if possible_lowest_energy < lowest_energy:
#       lowest_energy = possible_lowest_energy
#       lowest_energy_color = color
#   update_color_mapping(node, color)

plt.figure(figsize=(50, 50))
print("initial color mapping: ", color_mapping)
nx.draw_networkx(G, with_labels=True, node_color=list(color_mapping.values()))
plt.show()

for i in range(100):
  node_to_pick = random.randint(0, len(nodes) - 1)
  color_to_pick = random.randint(0, len(colors) - 1)
  node = nodes[node_to_pick]
  color = colors[color_to_pick]

  neighbors = list(G.neighbors(node))
  current_color = color_mapping[node]
  current_energy_value = get_energy_value(current_color, neighbors)
  proposed_energy_value = get_energy_value(color, neighbors)

  exponential = math.exp((-proposed_energy_value + current_energy_value)/temperature)
  accept_prob = min(1, exponential)
  sample = random.random()
  if sample < accept_prob:
    update_color_mapping(node, color)

plt.figure(figsize=(50, 50))
print("final color mapping: ", color_mapping)
nx.draw_networkx(G, with_labels=True, node_color=list(color_mapping.values()))
plt.show()

#%%


import sys
sys.path.append('/Users/admin/Documents/Diploma/tcn/')

import argparse
import tensorflow as tf
import numpy as np
from tf_version.model import TCN
from utils import data_generator, get_logger

logger = get_logger("music_test_tensorflow")

parser = argparse.ArgumentParser(description='Sequence Modeling - Polyphonic Music')

parser.add_argument('--dropout', type=float, default=0.25,
                    help='dropout applied to layers (default: 0.25)')
parser.add_argument('--clip', type=float, default=0.2,
                    help='gradient clip, -1 means no clip (default: 0.2)')
parser.add_argument('--epochs', type=int, default=100,
                    help='upper epoch limit (default: 100)')
parser.add_argument('--ksize', type=int, default=5,
                    help='kernel size (default: 5)')
parser.add_argument('--levels', type=int, default=4,
                    help='# of levels (default: 4)')
parser.add_argument('--log-interval', type=int, default=100, metavar='N',
                    help='report interval (default: 100')
parser.add_argument('--lr', type=float, default=1e-3,
                    help='initial learning rate (default: 1e-3)')
parser.add_argument('--optim', type=str, default='Adam',
                    help='optimizer to use (default: Adam)')
parser.add_argument('--nhid', type=int, default=150,
                    help='number of hidden units per layer (default: 150)')
parser.add_argument('--data', type=str, default='Piano',
                    help='the dataset to run (default: Piano)')
parser.add_argument('--seed', type=int, default=1111,
                    help='random seed (default: 1111)')

if __name__ == '__main__':
    args = parser.parse_args()

    # Piano roll size
    output_size = 88
    X_train, X_valid, X_test = data_generator(dataset=args.data, framework="tf")
    train_idx_list = np.arange(len(X_train), dtype="int32")

    n_outputs = args.nhid
    num_channels = [args.nhid] * args.levels
    kernel_size = args.ksize
    padding = kernel_size - 1
    dropout = args.dropout
    lr = args.lr
    training_steps = args.epochs

    tf.reset_default_graph()
    with tf.Graph().as_default() as g:

        X = tf.placeholder("float", [1, None, output_size], name="X_placeholder")
        Y = tf.placeholder("float", [None, output_size], name="Y_placeholder")
        is_training = tf.placeholder("bool")
        learning_rate = tf.placeholder(tf.float32, shape=[])

        model = TCN(
            output_size=output_size,
            num_channels=num_channels,
            kernel_size=kernel_size,
            dropout=dropout
        )

        output = model(X, training=is_training)
        predictions = tf.squeeze(model(inputs=X, training=is_training))
        loss_op = tf.losses.log_loss(labels=Y, predictions=predictions)
        output = tf.squeeze(predictions)
        loss = -tf.trace(
                         tf.matmul(Y, tf.transpose(tf.log(output))) +
                         tf.matmul((1 - Y), tf.transpose(tf.log(1 - output)))
                         )
        loss = tf.convert_to_tensor(loss)

        with tf.name_scope("optimizer"):
            optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
            # Gradient clipping
            # gvs = optimizer.compute_gradients(loss)
            # capped_gvs = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gvs]
            # train_op = optimizer.apply_gradients(capped_gvs)
            train_op = optimizer.minimize(loss_op)

        saver = tf.train.Saver()
        init = tf.global_variables_initializer()

    with tf.Session(graph=g) as sess:
        # Run the initializer
        max_loss = 10 ** 100
        vloss_list = []
        sess.run(init)
        np.random.shuffle(train_idx_list)
        for ep in range(1, training_steps + 1):
            logger.info("Epoch: {}".format(ep))
            logger.info("Training...")
            for idx in train_idx_list:
                data_line = X_train[idx]
                batch_x = np.expand_dims(data_line[:-1].astype(dtype=np.float32), axis=0)
                batch_y = data_line[1:].astype(dtype=np.float32)
                sess.run(train_op, feed_dict={X: batch_x, Y: batch_y, is_training: True, learning_rate: lr})

            total_loss = 0
            logger.info("Validating...")
            for data_line in X_valid:
                batch_x = np.expand_dims(data_line[:-1].astype(dtype=np.float32), axis=0)
                batch_y = data_line[1:].astype(dtype=np.float32)
                #loss = sess.run(loss_op, feed_dict={X: batch_x, Y: batch_y, is_training: False})
                loss_ = sess.run(loss, feed_dict={X: batch_x, Y: batch_y, is_training: False})
                total_loss += loss_
                vloss_list.append(loss_)

            if ep > 10 and total_loss > max(vloss_list[-3:]):
                lr /= 10
                logger.info("Update learning rate. New lr: {}".format(lr))

            logger.info("Loss: {}".format(total_loss))

        logger.info("Training is done!")
        logger.info("Testing...")
        total_loss = 0
        for data_line in X_test:
            batch_x = np.expand_dims(data_line[:-1].astype(dtype=np.float32), axis=0)
            batch_y = data_line[1:].astype(dtype=np.float32)
            loss = sess.run(loss_op, feed_dict={X: batch_x, Y: batch_y, is_training: False})
            total_loss += loss

        logger.info("Loss: {}".format(total_loss))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PlantModelLib.Bettina import Bettina
import numpy as np

class BettinaNetwork(Bettina):
    def __init__(self, args):
        """
        Plant model concept.
        This module inherits the tree growth functionality of Bettina, but can account for resource loss and water
        transfer through root grafts.
        MRO: BettinaNetwork, Bettina, TreeModel, object
        Args:
            args: BettinaNetwork module specifications from project file tags
        """
        super().__init__(args=args)
        self.getInputParameters(args=args)

    def progressPlant(self, tree, aboveground_resources, belowground_resources):
        """
        Manage growth procedures for a timestep --- read tree geometry and parameters, schedule computations, and update tree geometry and survival
        Args:
            tree (dict): tree object
            aboveground_resources (float): above-ground resource growth reduction factor
            belowground_resources (float): below-ground resource growth reduction factor
        Sets:
            dictionary containing network information
        """
        network = tree.getNetwork()
        ## counter to track or define the time required for root graft formation,
        # if -1 no root graft formation takes place at the moment
        self.rgf = network['rgf']
        self.partner = network['partner']
        self.potential_partner = network['potential_partner']
        self.psi_osmo = network["psi_osmo"]

        # parameters for rgf variant "V2_adapted"
        self.r_gr_min = network['r_gr_min']
        self.r_gr_rgf = network['r_gr_rgf']
        self.l_gr_rgf = network['l_gr_rgf']
        self.weight_gr = network['weight_gr']

        self.name = str(tree.group_name) + str(tree.plant_id)
        # Simple bettina tree progress
        super().progressPlant(tree=tree,
                             aboveground_resources=aboveground_resources,
                             belowground_resources=belowground_resources)

        network['variant'] = self.variant
        network['rgf'] = self.rgf
        network['potential_partner'] = self.potential_partner
        network['partner'] = self.partner
        network['node_degree'] = len(self.partner)

        # parameters for rgf variant "V2_adapted"
        network['r_gr_min'] = self.r_gr_min
        network['r_gr_rgf'] = self.r_gr_rgf
        network['l_gr_rgf'] = self.l_gr_rgf
        network['weight_gr'] = self.weight_gr  # only required for csv
        # output

        tree.setNetwork(network=network)
        if self.survive == 1:
            tree.setSurvival(1)
        else:
            tree.setSurvival(0)

    def treeGrowthWeights(self):
        """
        Calculate the growth weights for distributing biomass increment to the tree geometries.
        See `pyMANGA.PlantModelLib.Bettina.Bettina`
        If resources for root graft formation are required, the respective module is called.
        Sets:
            multiple float
        """
        if self.variant == 'V2_adapted':
            self.treeGrowthWeightsV2()
        else:
            super().treeGrowthWeights()

    def growthResources(self):
        """
        Calculate the available resources and the biomass increment.
        See `pyMANGA.PlantModelLib.Bettina.Bettina`
        If resources for root graft formation are required, the respective module is called.
        Sets:
            multiple float
        """
        super().growthResources()
        if self.variant == "V0_instant":
            self.rootGraftFormationV0()
        if self.variant == "V1_fixed":
            self.rootGraftFormationV1()

    def treeGrowthWeightsV2(self):
        """
        Calculate the growth weights for distributing biomass increment to the tree geometries.
        See `pyMANGA.PlantModelLib.Bettina.Bettina`
        In addition, the function calls the root graft formation function, if the tree is currently
        in the process of root graft formation and root graft formation follows variant V2.
        Sets:
            multiple float
        """
        # Simple bettina get growth weigths
        super().treeGrowthWeights()

        # If self.r_gr_min exists the tree is currently in the process of rgf
        if self.r_gr_min:
            self.rootGraftFormationV2()
        else:
            self.weight_gr = 0

    def rootGraftFormationV0(self):
        """
        Manage root graft formation.
        Immediate root grafting, i.e. roots in contact are immediately grafted and no resources are required.
        Sets:
            multiple float
        """
        if self.rgf != -1:
            self.rgf = -1
            self.partner.append(self.potential_partner)
            self.potential_partner = []

    def rootGraftFormationV1(self):
        """
        Manage root graft formation.
        Growth reduction during root graft formation.
        It is assumed that the process will take 2 years.
        Sets:
            multiple float
        """
        if self.rgf != -1:
            self.grow = self.grow * (1 - self.f_growth)
            self.rgf = self.rgf + 1
            if round(self.rgf * self.time / 3600 / 24 / 365, 3) >= 2:
                self.rgf = -1
                self.partner.append(self.potential_partner)
                self.potential_partner = []

    def rootGraftFormationV2(self):
        """
        Manage root graft formation.
        Girth growth reduction during root graft formation.
        The duration of the process is variable, depending on the size and resource availability of the plants involved.        Sets:
            multiple float
        """
        # check if rgf is finished
        if self.r_gr_rgf >= self.r_gr_min[0]:
            # Append partner
            self.partner.append(self.potential_partner)
            # Reset rgf parameters
            self.r_gr_rgf = 0.004
            self.rgf = -1
            # analyse rgf duration
            self.potential_partner = []
            self.r_gr_min = []
        else:
            # if rgf is not finished, the grafted roots must grow
            self.weight_gr = self.weight_girthgrowth * self.f_growth
            inc_r_gr = (self.weight_gr * self.grow /
                        (2 * np.pi * self.l_gr_rgf * self.r_gr_rgf))
            self.r_gr_rgf += inc_r_gr
            # reduce girth growth factor
            self.weight_girthgrowth = self.weight_girthgrowth * (1 -
                                                                 self.f_growth)
            self.rgf += 1

    def getInputParameters(self, args):
        tags = {
            "prj_file": args,
            "required": ["type", "variant", "f_growth"]
        }
        super().getInputParameters(**tags)


# Dado um número n inteiro e positivo, dizemos que ele é perfeito se for igual à
# soma de seus divisores positivos diferentes de n. Construa um programa que leia
# um número do usuário e verifica se ele é perfeito ou não.

# O número 6 é perfeito, pois os divisores são: 1, 2 e 3, que somados tem seis
# como resultado.

n = int(input('Informe um número: '))
soma=0

for count in range(1,n):
    if n % count== 0:
        soma += count

if (n == soma):
    print(f'O número "{n}" perfeito!')
else:
    print(f'O número "{n}" não é perfeito!')


import re, email.utils
n = int(input())
format_emaildata=[]
emaildata = [input() for _ in range(n)]
for i in emaildata:
	x = email.utils.parseaddr(i)
	format_emaildata.append(x)

pattern = re.compile(r'(^[a-zA-Z][a-zA-Z0-9\-+._]+@[a-z]+\.[a-z]+$)')
print('\n')
for i in format_emaildata:
	match = pattern.search(i[1])
	#print(i, match)
	if match:
		print(email.utils.formataddr(i))


from ...core.message import (
    MessageType,
    MessageRequest,
    MessageResponse,
    MessageBody
)

class Message13Base(MessageRequest):
    def __init__(self, protocol_version, message_type, body_type):
        super().__init__(
            device_type=0x13,
            protocol_version=protocol_version,
            message_type=message_type,
            body_type=body_type
        )

    @property
    def _body(self):
        raise NotImplementedError

class MessageQuery(Message13Base):
    def __init__(self, protocol_version):
        super().__init__(
            protocol_version=protocol_version,
            message_type=MessageType.query,
            body_type=0x24)

    @property
    def _body(self):
        return bytearray([
            0x00, 0x00, 0x00, 0x00
        ])

class MessageSet(Message13Base):
    def __init__(self, protocol_version):
        super().__init__(
            protocol_version=protocol_version,
            message_type=MessageType.set,
            body_type=0x00)
        self.brightness = None
        self.color_temperature = None
        self.effect = None
        self.power = None

    @property
    def _body(self):
        body_byte = 0x00
        if self.power is not None:
            self.body_type = 0x01
            body_byte = 0x01 if self.power else 0x00
        elif self.effect is not None and self.effect in range(1, 6):
            self.body_type = 0x02
            body_byte = self.effect + 1
        elif self.color_temperature is not None:
            self.body_type = 0x03
            body_byte = self.color_temperature
        elif self.brightness is not None:
            self.body_type = 0x04
            body_byte = self.brightness
        return bytearray([body_byte, 0x00, 0x00, 0x00])

class MessageMainLightBody(MessageBody):
    def __init__(self, body):
        super().__init__(body)
        self.brightness = self.read_byte(body, 1)
        self.color_temperature = self.read_byte(body, 2)
        self.effect = self.read_byte(body, 3) - 1
        if self.effect > 5:
            self.effect = 1
        '''
        self.rgb_color = [self.read_byte(body, 5),
                          self.read_byte(body, 6),
                          self.read_byte(body, 7)]
        '''
        self.power = self.read_byte(body, 8) > 0

class MessageMainLightResponseBody(MessageBody):
    def __init__(self, body):
        super().__init__(body)
        self.control_success = body[1] > 0

class Message13Response(MessageResponse):
    def __init__(self, message):
        super().__init__(message)
        if self.body_type == 0xa4:
            self.set_body(MessageMainLightBody(super().body))
        elif self.message_type == MessageType.set and self.body_type > 0x80:
            self.set_body(MessageMainLightResponseBody(super().body))
        self.set_attr()


from datasets import load_dataset, load_metric
from transformers import AutoTokenizer, HfArgumentParser, TrainingArguments, AutoModelForTokenClassification, Trainer, \
    DataCollatorForTokenClassification
import json
import numpy as np
from bert_crf import BertCRF
from dataclasses import field, dataclass
from typing import Optional

# 加载标签列表
with open("sample_data/labels.json", "rt") as fp:
    lable_dict = json.load(fp)
label_names = list(dict(lable_dict).keys())

# 使用seqeval作为metric
metric = load_metric("seqeval")

def compute_metrics(p):
    """
    计算metrics，包括准确率、召回率、F1，以及各个分类的F1
    """
    predictions, labels = p
    # predictions = np.argmax(predictions, axis=2)

    predictions = predictions.astype(int)
    labels = labels
    mask = np.where(labels >= 0, 0, -100)
    predictions = predictions + mask

    # 忽略-100的label
    true_predictions = [
        [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    true_labels = [
        [label_names[l] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]

    results = metric.compute(predictions=true_predictions, references=true_labels)
    flattened_results = {
        "overall_precision": results["overall_precision"],
        "overall_recall": results["overall_recall"],
        "overall_f1": results["overall_f1"],
        "overall_accuracy": results["overall_accuracy"],
    }
    for k in results.keys():
        if (k not in flattened_results.keys()):
            flattened_results[k + "_f1"] = results[k]["f1"]

    return flattened_results

def align_labels(tokenizer, label_dict):
    """
    对文本进行tokenize，并且将标注与token进行对齐
    """

    def _align_labels(sample):
        tokenized_inputs = tokenizer(sample["text"], truncation=True, is_split_into_words=False)
        tokens = [tokenizer.convert_ids_to_tokens(input_id) for input_id in tokenized_inputs["input_ids"]]
        labels = []
        # label_json = json.loads(sample["label"])
        for i, (token, label_text) in enumerate(zip(tokens, sample["label"])):
            # 获取每个token的偏移量，类似[(0,0),(1,2),(2,3),(0,0)]的格式，其中第一个token为[CLS]，最后一个为[SEP]
            offsets = tokenized_inputs.encodings[i].offsets
            label_ids = [0] * len(offsets)
            # 将[CLS]和[SEP]的标签设置为-100
            label_ids[0] = 0
            label_ids[-1] = 0
            if label_text is not None:
                # 加载标签数据
                label_items = json.loads(label_text)
                for item in label_items:
                    # 对于每个标注，计算标注对应token的label。
                    # 标注格式为{"start": 12, "end": 14, "text": "陈醋", "labels": ["PRD"]}
                    # 标注格式和label-studio导出CSV格式的样本保持一致
                    start = 0
                    try:
                        while offsets[start][0] < item["start"] or offsets[start][1] == 0:
                            start += 1
                        end = start
                        while offsets[end][1] <= item["end"] and offsets[end][1] != 0:
                            end += 1
                        if offsets[end - 1][1] < item["end"]:
                            end += 1
                        label_b = label_dict['B-' + item["labels"][0]]
                        label_i = label_dict['I-' + item["labels"][0]]
                        cur_label = label_b
                        for j in range(start, end):
                            # Tokenizer 可能出现某个token从属于上一个的情况，这类token会以##开头
                            if j > start and not token[j].startswith("##"):
                                cur_label = label_i
                            label_ids[j] = cur_label
                    except Exception as err:
                        pass
            labels.append(label_ids)
        tokenized_inputs["labels"] = labels
        return tokenized_inputs

    return _align_labels

@dataclass
class TrainingDataArguments:
    """
    Arguments pertaining to what sample_data we are going to input our model for training and eval.
    """

    train_data_file: str = field(
        default=None, metadata={"help": "The input training sample_data file (a text file)."}
    )
    eval_data_file: Optional[str] = field(
        default=None,
        metadata={
            "help": "An optional input evaluation sample_data file to evaluate the perplexity on (a text file)."},
    )

if __name__ == "__main__":
    # 解析训练参数
    parser = HfArgumentParser((TrainingDataArguments, TrainingArguments))
    training_data_args, training_args = parser.parse_args_into_dataclasses()
    training_args.seed = 12345

    # 从之前训练的checkpoint恢复
    # training_args.resume_from_checkpoint = "output/checkpoint-4000"

    # 设置gradient_checkpointing为True，可以减少显存占用，但是由于梯度数据会写入硬盘暂存，训练速度会有所降低
    training_args.gradient_checkpointing = False

    # 加载训练和测试样本，由于示例样本比较少，train和eval用了同一个文件，实际使用时指定正确的文件即可
    # datafiles = {"train": "sample_data/brand_samples.csv"}
    # train_ds = load_dataset("csv", data_files=datafiles, split="train[:20480]")
    # eval_ds = load_dataset("csv", data_files=datafiles, split="train[-10240:]")
    datafiles = {'train': training_data_args.train_data_file}
    if training_args.do_eval and training_data_args.eval_data_file is not None:
        datafiles['eval'] = training_data_args.eval_data_file
        eval_ds = load_dataset("csv", data_files=datafiles, split="eval")
    train_ds = load_dataset("csv", data_files=datafiles, split="train")

    tokenizer = AutoTokenizer.from_pretrained("youzanai/bert-product-title-chinese", truncation=True)
    # 对样本文本进行tokenize，并且将标签和token进行对齐
    map_func = align_labels(tokenizer, lable_dict)
    train_ds = train_ds.map(map_func, batched=True)
    train_ds.set_format(columns=["input_ids", "attention_mask", "labels"])
    if training_args.do_eval:
        eval_ds = eval_ds.map(map_func, batched=True)
        eval_ds.set_format(columns=["input_ids", "attention_mask", "labels"])
    else:
        eval_ds = None
    # Padding训练样本，标签使用-100进行padding，可以避免标签产生loss
    data_collator = DataCollatorForTokenClassification(tokenizer, padding="max_length", max_length=128,
                                                       label_pad_token_id=-100)
    # 加载预训练模型
    model = BertCRF.from_pretrained(
        "youzanai/bert-product-title-chinese", num_labels=9)

    # 冻结预训练参数
    for param in model.bert.parameters():
        param.requires_grad = False

    # 使用Huggingface官方的trainer进行模型的finetune
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_ds,
        eval_dataset=eval_ds,
        data_collator=data_collator,
        compute_metrics=compute_metrics
    )
    train_result = trainer.train()

    # 保存模型
    trainer.save_model()
    metrics = train_result.metrics
    trainer.log_metrics("train", metrics)
    trainer.save_metrics("train", metrics)
    trainer.save_state()