#!/usr/bin/env python
# coding: utf-8

import pickle
import numpy as np

def load_data_and_save(dir_path, filenames):
    data = import_data(dir_path)
    return data
    #pdata = organize_pdata(data)

    #del data
    #zdata = organize_zdata(pdata)

    #save_obj(pdata, filenames[0])
    #save_obj(zdata, filenames[1])

def save_obj(obj, name ):
    with open('./' + name + '.pkl', 'wb') as f:
        pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)

def load_obj(name ):
    with open('./' + name + '.pkl', 'rb') as f:
        return pickle.load(f)

def gen_X_matrix(ddict, zone):
    mods = ['ADC','CDI','HBV']
    features = ['orig','mean','std','skew','kurt']
    X = {}
    Y = []

    for mod in mods:
        for feat in features:
            X[mod+'_'+feat] = []

    for p in ddict:
        gs = pdata[p][zone]['GS'][0::3] # Take every third element (there are duplicates because of the modes)
        for slice in range(0, len(pdata[p][zone]['ADC']['orig'])):
            if pdata[p][zone]['GS'][slice] > 6:
                pCa = '1'
            else:
                pCa = '0'
            Y.extend(pCa*len(pdata[p][zone][mod][feat][slice]))
            for mod in mods:
                for feat in features:
                    X[mod+'_'+feat].extend(pdata[p][zone][mod][feat][slice])
        """
        pdata[p][zone]['GS']
        for i in range(0,len(pdata[p][zone]['ADC'])):
            if pdata[p][zone]['GS'][i] > 6:
                pCa = '1'
            else:
                pCa = '0'

            # Append if num samples match for each mod
            if len(pdata[p][zone]['ADC'][i]) == len(pdata[p][zone]['CDI'][i]) == len(pdata[p][zone]['HBV'][i]):
                Y.extend(pCa*len(pdata[p][zone]['ADC'][i]))
                for mod in mods:
                    X[mod].extend(pdata[p][zone][mod][i])
 #               for j in range(0, len(pdata[p][zone]['ADC'][i])):
 #                   if len(pdata[p][zone]['ADC'][i][j]) == len(pdata[p][zone]['CDI'][i][j]) == len(pdata[p][zone]['HBV'][i][j]):
 #                       dat = [d for d in pdata[p]['PZ']['ADC'][i][j] if d != 0]
 #                       Y.extend(pCa*len(dat))
 #                       for mod in mods:
 #                           data = [d for d in pdata[p]['PZ'][mod][i][j] if d != 0]
 #                           X[mod].extend(data)
         """
    return X, Y

def organize_pdata(data):
    pdata = {}
    categories = ['ADC','CDI','HBV','GS']
    features = ['orig','mean','std','skew','kurt']

    PZ_zones = [1, 4, 5, 7, 9, 10]
    TZ_zones = [2, 3]
    CZ_zones = [6, 8]

    for patient in data:
        pid = patient['id']
        pdata[pid] = {}
        pdata[pid]['PZ'] = {}
        pdata[pid]['TZ'] = {}
        pdata[pid]['CZ'] = {}
        for cat in categories[:-1]:
            for zone in pdata[pid]:
                pdata[pid][zone][cat] = {}
                pdata[pid][zone]['GS'] = []
                for feat in features:
                    pdata[pid][zone][cat][feat] = []

        # check if the segmentation map has the same number of slices as the mri volume
        if patient['zone_map'].shape[-1] != patient['ADC'].shape[-1]:# and patient['zone_map'].shape != patient['CDI'].shape and patient['zone_map'].shape != patient['CDI'].shape:
            continue

        for slice_index in range(patient['ADC'].shape[-1]):

            # To trim the slices

            # Check if the slice contains the prostate
            if np.max(patient['mask'][:,:,slice_index]) > 0:
                #binary_mask = patient['mask'][:,:,slice_index] == 255  # create a binary mask of the slice
                zone_map = patient['zone_map'][:,:,slice_index] > 0
                trimmed_zone_map, ix, jx, iy, jy = trim_zeros_2D(zone_map)

                # For each modality compute feature extraction algorithms
                # apply the mask to the slice and trim zeros
                for mod in categories[:-1]:
                    mod_slice = (patient[mod][:,:,slice_index] * zone_map)[ix:jx+1,iy:jy+1]
                    mean,std,skew,kurt = feat_extraction_1d(mod_slice)

                    # To trim the zones
                    for zone_index in range(10):

                    # check the zone map to see if the slice contains the zone
                        if zone_index + 1 in patient['zone_map'][:,:,slice_index]:

                            # extract zone GS and maps into a dictionary
                            bin_mask = trimmed_zone_map == zone_index + 1  # create a binary mask

                            mod_crop = mod_slice[bin_mask == 1]
                            mean_crop = mean[bin_mask == 1]
                            std_crop = std[bin_mask == 1]
                            skew_crop = skew[bin_mask == 1]
                            kurt_crop = kurt[bin_mask == 1]

                            gs = patient['maxGleason_map'][slice_index][zone_index]

                            # add data to correct zone
                            zindex = zone_index + 1
                            if zindex in PZ_zones:
                                pr_zone = 'PZ'
                            elif zindex in TZ_zones:
                                pr_zone = 'TZ'
                            else:
                                pr_zone = 'CZ'

                            pdata[pid][pr_zone][mod]['orig'].append(np.array(mod_crop))
                            pdata[pid][pr_zone][mod]['mean'].append(np.array(mean_crop))
                            pdata[pid][pr_zone][mod]['std'].append(np.array(std_crop))
                            pdata[pid][pr_zone][mod]['skew'].append(np.array(skew_crop))
                            pdata[pid][pr_zone][mod]['kurt'].append(np.array(kurt_crop))
                            pdata[pid][pr_zone]['GS'].append(gs)

                    """
                    # ADC crops
                    adc_crop = patient['ADC'][:,:,slice_index][bin_mask == 1]
                    #adc_mask = patient['ADC'][:,:,slice_index] * binary_mask  # apply the mask to the slice
                    #adc_crop = trim_zeros_2D(adc_mask)  # trim the slice to the dimensions of the prostate zone

                    # CDI crops
                    cdi_crop = patient['CDI'][:,:,slice_index][bin_mask == 1]
                    #cdi_mask = patient['CDI'][:,:,slice_index] * binary_mask  # apply the mask to the slice
                    #cdi_crop = trim_zeros_2D(cdi_mask)  # trim the slice to the dimensions of the prostate zone

                    # HBV crops
                    hbv_crop = patient['HBV'][:,:,slice_index][bin_mask == 1]
                    #hbv_mask = patient['HBV'][:,:,slice_index] * binary_mask  # apply the mask to the slice
                    #hbv_crop = trim_zeros_2D(hbv_mask)  # trim the slice to the dimensions of the prostate zone

                    # GS scores
                    gs = patient['maxGleason_map'][slice_index][zone_index]

                    pdata[pid][pr_zone]['ADC'].append(np.array(adc_crop))
                    pdata[pid][pr_zone]['CDI'].append(np.array(cdi_crop))
                    pdata[pid][pr_zone]['HBV'].append(np.array(hbv_crop))
                    pdata[pid][pr_zone]['GS'].append(gs)
                    """
    return pdata

def organize_zdata(pdata):
    # zone format of pdata
    zdata = {}
    zones = ['PZ','CZ','TZ']
    for zone in zones:
        zdata[zone] = {}
    for patient in pdata:
        for zone in pdata[patient]:
            zdata[zone][patient] = pdata[patient][zone]

    return zdata


# ffmpeg only backend

import locale
import logging
import re
from subprocess import PIPE, Popen

logger = logging.getLogger(__name__)

class VideoEncodingError(Exception):
    def __init__(self, *args, **kwargs):
        self.message = args[0]
        super(VideoEncodingError, self).__init__(*args, **kwargs)

RE_TIMECODE = re.compile(r"time=(\d+:\d+:\d+.\d+)")
console_encoding = locale.getdefaultlocale()[1] or "UTF-8"

class FFmpegBackend(object):
    name = "FFmpeg"

    def __init__(self):
        pass

    def _spawn(self, cmd):
        try:
            return Popen(
                cmd,
                shell=False,
                stdin=PIPE,
                stdout=PIPE,
                stderr=PIPE,
                close_fds=True,
            )
        except OSError as e:
            raise VideoEncodingError("Error while running ffmpeg", e)

    def _check_returncode(self, process):
        ret = {}
        stdout, stderr = process.communicate()
        ret["code"] = process.returncode
        return ret

    def encode(self, cmd):
        process = self._spawn(cmd)
        buf = output = ""
        while True:
            out = process.stderr.read(10)

            if not out:
                break
            try:
                out = out.decode(console_encoding)
            except UnicodeDecodeError:
                out = ""
            output = output[-500:] + out
            buf = buf[-500:] + out
            try:
                line, buf = buf.split("\r", 1)
            except BaseException:
                continue

            progress = RE_TIMECODE.findall(line)
            if progress:
                progress = progress[0]
            yield progress

        process_check = self._check_returncode(process)
        if process_check["code"] != 0:
            raise VideoEncodingError(output[-1000:])  # output could be huge

        if not output:
            raise VideoEncodingError("No output from FFmpeg.")

        yield output[-1000:]  # output could be huge


import os
import socket
import sys
import time

from PyQt5 import uic
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, relative_path)

form = resource_path('QuizClient.ui')
form_class = uic.loadUiType(form)[0]

class connectQuizServer(QThread):
    sig_msg = pyqtSignal(str)
    sig_err = pyqtSignal(str)

    def __init__(self, parent):
        super().__init__(parent)

        self.HOST = ''
        self.PORT = 0
        self.msg = ''
        self.tmpSocket = ''

    def setInfo(self, host, port):
        self.HOST = host
        self.PORT = port

    def sendMsg(self, sock, msg):
        try:
            sock.send(msg.encode())
        except Exception as e:
            print('sendMsg: ' + e)
            pass

    def rcvMsg(self, sock):
        while True:
            try:
                data = sock.recv(1024)
                if not data:
                    break

                self.sig_msg.emit(data.decode())
                print(data.decode())
            except:
                pass

    def run(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            self.tmpSocket = sock
            try:
                sock.connect((self.HOST, self.PORT))
                if self.msg != '':
                    msg = self.msg
                    self.sendMsg(sock, msg)
                    self.msg = ''

                while True:
                    try:
                        data = sock.recv(1024)
                        if not data:
                            break

                        self.sig_msg.emit(data.decode())
                        print(data.decode())
                    except Exception as e:
                        pass
            except Exception as e:
                self.sig_err.emit(str(e))
                print('connect exception')

    def stop(self):
        if self.isRunning():
            self.sendMsg(self.tmpSocket, '/quit')
            self.tmpSocket = ''
            self.terminate()

class clientWindow(QMainWindow, form_class):
    isMe = False

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.setFixedSize(490, 570)
        self.username = ''
        self.quizSvr = connectQuizServer(self)
        self.quizSvr.sig_msg.connect(self.incomingChat)
        self.quizSvr.sig_err.connect(self.errorChat)

        self.pb_connect.clicked.connect(self.runQuizSvr)
        self.pb_tbClear.clicked.connect(self.clearText)
        self.le_inputText.returnPressed.connect(self.sendMsg)
        self.lbl_question.setStyleSheet('color: red')

    def sendMsg(self):
        if self.quizSvr.isRunning():
            self.quizSvr.sendMsg(self.quizSvr.tmpSocket, self.le_inputText.text())
            self.le_inputText.clear()

            if self.lbl_question.text() != '' and self.isMe:
                self.isMe = False
                self.le_inputText.setEnabled(False)

    def runQuizSvr(self):
        host = self.le_hostip.text()
        port = int(self.le_hostport.text())
        self.username = self.le_username.text()
        if host and port:
            if self.username:
                if self.quizSvr.isRunning():
                    self.quizSvr.msg = '/quit'
                    try:
                        self.quizSvr.stop()
                    except Exception as e:
                        self.quizSvr.terminate()
                        pass
                    print('연결이 종료되었습니다.')
                    self.incomingChat('연결이 종료되었습니다.\n')
                    self.lbl_question.setText('')
                    self.le_inputText.setEnabled(True)
                    self.le_hostip.setEnabled(True)
                    self.le_hostport.setEnabled(True)
                    self.le_username.setEnabled(True)
                    self.pb_connect.setText('연결')
                else:
                    self.quizSvr.setInfo(host, port)
                    self.quizSvr.msg = self.username
                    self.quizSvr.start()
                    self.le_hostip.setEnabled(False)
                    self.le_hostport.setEnabled(False)
                    self.le_username.setEnabled(False)
                    self.pb_connect.setText('연결 끊기')

            else:
                self.tb_content.append('이름을 입력해주세요.')
                print('이름을 입력해주세요.')
        else:
            self.tb_content.append('IP와 포트를 입력해주세요.')

    def clearText(self):
        self.tb_content.clear()

    @pyqtSlot(str)
    def incomingChat(self, msg):
        text = ''
        if msg.find('*Q:') != -1:
            self.lbl_question.setText(msg.replace('*Q:', ''))
            if msg.replace('*Q:', '') != '':
                self.le_inputText.setEnabled(False)
        elif msg.find('*출제자:') != -1:
            presenter = msg.replace('*출제자:', '')
            text = '출제자는 초성(또는 단어)을 입력하여 퀴즈를 시작해주세요. (출제자: ' + presenter + ')'
            if presenter != self.username:
                self.le_inputText.setEnabled(False)
            else:
                self.le_inputText.setEnabled(True)
        elif msg.find('*/next:') != -1:
            if msg.replace('*/next:', '') != self.username:
                self.isMe = False
                self.le_inputText.setEnabled(False)
            else:
                self.isMe = True
                self.le_inputText.setEnabled(True)
        elif msg.find('*/reset:') != -1:
            self.le_inputText.setEnabled(True)
        elif msg.find('*/') != -1:
            text = msg.replace('*/', '')
            # 틀렸으면 다시 오픈
            if text.find('몽총이') != -1 or text.find('틀렸습니다') != -1 or text.find('초성 불가') != -1:
                self.le_inputText.setEnabled(True)
        else:
            text = msg
        # elif msg.find('######## 초성 퀴즈 ########') != -1:
        #     text = msg
        # else:
        #     text = time.strftime('%H:%M:%S', time.localtime(time.time())) + '  ' + msg

        if text:
            self.tb_content.append(text)

    @pyqtSlot(str)
    def errorChat(self, errmsg):
        self.tb_content.append(errmsg)
        self.le_hostip.setEnabled(True)
        self.le_hostport.setEnabled(True)
        self.le_username.setEnabled(True)
        self.pb_connect.setText('연결')

if __name__ == "__main__":
    # QApplication : 프로그램을 실행시켜주는 클래스
    app = QApplication(sys.argv)
    # WindowClass의 인스턴스 생성
    myWindow = clientWindow()
    # 프로그램 화면을 보여주는 코드
    myWindow.show()
    # 프로그램을 이벤트루프로 진입시키는(프로그램을 작동시키는) 코드
    app.exec_()


from chain import *
import os

file_path = os.path.dirname(os.path.abspath(__file__))
# print(file_path)
images_path = os.path.join(file_path, "Testing database")
# print(images_path)
os.chdir(images_path)

files_in_dir = os.listdir()
# print(files_in_dir)
paths = [os.path.join(images_path, x) for x in files_in_dir]

for element in paths[2:-1]:
    tree = Image.open(element)
    imfile = tree.convert("1", dither = Image.NONE)
    c = Chain(imfile)
    print(element)
    c.print_information()


from django.shortcuts import render, redirect
from .forms  import RegisterForm
from django.contrib import messages
from django.contrib.auth import logout

def register(request):
    if request.method == "POST":
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()

            logout(request)
            return redirect("login")
        else:
            messages.error(request, f"Ошибка, перепроверьте вводимые данные.")
            form = RegisterForm()

    form = RegisterForm()
    return render(request, "register/register.html", {"form":form,})


import math
class Solution(object):
    def maximumGap(self, nums):
        ans = 0

        if(len(nums) <2):
            return 0
        global_min = float(min(nums))
        global_max = float(max(nums))

        bucket_interval = round( (global_max - global_min)/len(nums), 1) + 0.1
        buckets = [[] for i in range((len(nums)))]
        pre_max = global_min

        for i in range(len(nums)):
            buckets[int((nums[i] - global_min) / bucket_interval)].append(nums[i])

        for i in range(len(nums)):
            if(len(buckets[i]) > 0):
                ans = max( min(buckets[i]) - pre_max , ans )
                pre_max = max(buckets[i])

        return int(ans)


import sys
import os
import numpy as np
from pyproj import Proj, Transformer, CRS
import multiprocessing as mp
import pandas as pd
import argparse
from tqdm import tqdm

AUSTIN_PROJ4_DESC = "+proj=lcc \
    +lat_1=31.8833333333333 +lat_2=30.1166666666667 +lat_0=29.6666666666667 \
    +lon_0=-100.333333333333 +x_0=2296583.333333 +y_0=9842500 \
    +datum=NAD83 +units=m +no_defs"

ERR_UB = 2000

parser = argparse.ArgumentParser(description="Preprocess the GPS-data")
parser.add_argument('inputDir', type=str, metavar="DirIn", help='Raw dataset Dir')
parser.add_argument('outputDir', type=str, metavar="DirOut", help='Output Dir')
parser.add_argument("--size", type=int, metavar='Size', help='GeoHash Block Size', default=ERR_UB)
parser.add_argument("--thread", type=int, help='Multiprocessing Thread Pool Size', default=4)
parser.add_argument("--mapLbX", type=float, default=30.18)
parser.add_argument("--mapLbY", type=float, default=-97.92)

crsWGS84 = CRS.from_epsg(4326)
crsAustin = CRS.from_proj4(AUSTIN_PROJ4_DESC)
transformer = Transformer.from_crs(crsWGS84, crsAustin)

def applyTransform(wgs):
    return transformer.transform(wgs[0], wgs[1])

def preproc(filePath, fileName, outputPath, tqdmHandle, blockSize):
    stat = pd.DataFrame()   # Collect the statistic data

    # Load the dataset
    tqdmHandle.set_description("Loading Dataset")
    dataset = pd.read_csv(os.path.join(filePath, fileName))
    stat.loc[0, 'RawPings'] = dataset.shape[0]

    # Clean the accuracy - Drop pings with large acc error
    acc = dataset.loc[:, ('horizontal_accuracy', 'vertical_accuracy')].fillna(np.inf)
    err = np.linalg.norm(acc, ord=2, axis=1)
    errPings = np.logical_or(err == 0, err > ERR_UB)
    dataset.drop(dataset[errPings].index, inplace=True, axis=0)

    # Sort the data according to the time
    dataset = dataset.sort_values(by='location_at')

    # Get the map boundary
    wgsMapLb = np.zeros(2)
    wgsMapLb[0] = args.mapLbX
    wgsMapLb[1] = args.mapLbY
    mapLb = np.array(transformer.transform(wgsMapLb[0], wgsMapLb[1]))

    # Convert to local cartesian coordinate
    tqdmHandle.set_description("Convert the CRS")
    pos = dataset.loc[:, ("latitude", "longitude")]
    with mp.Pool(args.thread) as pool:
        localPos = pool.map(applyTransform, pos.values)
    localPos = np.array(localPos)

    # Get the geohash coordinate
    geohashBlk = np.around((localPos - mapLb) / blockSize).astype(np.int)

    # Remove blocks out of boundary
    validIdx = np.all(geohashBlk >= 0, axis=1)
    oobCount = np.sum(~validIdx)
    dataset = dataset[validIdx]
    tqdmHandle.set_description(f"{oobCount} pings out of boundary!")
    localPos = localPos[validIdx, :]
    geohashBlk = geohashBlk[validIdx, :]

    # Insert new data
    dataset.insert(7, 'x', localPos[:, 0])
    dataset.insert(8, 'y', localPos[:, 1])
    dataset.insert(9, 'bx', geohashBlk[:, 0])
    dataset.insert(10, 'by', geohashBlk[:, 1])
    outputPath = os.path.join(outputPath, fileName)
    tqdmHandle.set_description(f"Writing CSV to {outputPath}")
    # Export the CSV and write the stat
    dataset.to_csv(outputPath)
    stat.loc[0, 'Filename'] = fileName
    stat.loc[0, 'FilteredPings'] = dataset.shape[0]
    stat.loc[0, 'OOBPings'] = oobCount
    stat.loc[0, 'AccErrPings'] = np.sum(errPings)
    stat.loc[0, 'latitudeLb'] = wgsMapLb[0]
    stat.loc[0, 'longitudeLb'] = wgsMapLb[1]
    stat.loc[0, 'xLb'] = mapLb[0]
    stat.loc[0, 'yLb'] = mapLb[1]
    return stat

if __name__ == '__main__':
    args = parser.parse_args()
    blockSize = np.array((args.size, args.size))
    os.makedirs(args.outputDir, exist_ok=True)

    # Find all the CSV files
    csvFiles = []
    for path, _, files in os.walk(args.inputDir):
        for file in files:
            if file[-4:] == '.csv':
                csvFiles.append((path, file))
    print(f"Found {len(csvFiles)} files under {args.inputDir}")

    # Run!
    fileLoader = tqdm(csvFiles)
    stat = None
    for p, f in fileLoader:
        ret = preproc(p, f, args.outputDir, fileLoader, blockSize)
        if stat is None:
            stat = ret
        else:
            stat = pd.concat((stat, ret), ignore_index=True)

    # Export the stat data
    statPath = os.path.join(args.outputDir, "stat.csv")
    print(f"Export the stat to {statPath}")
    stat.to_csv(statPath)
    print("Done")


from PyQt5 import QtWidgets, QtGui, QtCore
import sys
import layout.main, layout.edit_clients, layout.report
from inspect import getsourcefile
import os.path

### if you want to load for ui files but i haven't figured out how to package and call
### those when you aren't in local directory
#Ui_MainWindow, QtBaseClass = uic.loadUiType('layout_main.ui')
#Ui_dlgEditClients, QtBaseClass = uic.loadUiType('layout_edit_clients.ui')
#Ui_dlgReport, QtBaseClass = uic.loadUiType('layout_report.ui')

class NotesTextEdit(QtWidgets.QPlainTextEdit):
    """
    Subclass text field to use focus events
    """
    lostFocus = QtCore.pyqtSignal('QString')

    def focusOutEvent(self, event):
        self.lostFocus.emit(self.toPlainText())
        super(NotesTextEdit, self).focusOutEvent(event)

class Storage:
    """
    Data interface
    """

    class DbConCursor(object):

        """
        Context manager for database connection cursor
        """
        def __init__(self):

            # get the path of the script that launched the application
            self.app_path = os.path.dirname(os.path.abspath(getsourcefile(lambda: 0)))

            import sqlite3
            try:
                self.dbcon = sqlite3.connect(os.path.join(self.app_path, 'timemachine.db'))
            except sqlite3.Error as e:
                raise e

        def __enter__(self):
            return self.dbcon.cursor()

        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.dbcon:
                self.dbcon.commit()
                self.dbcon.close()

    def __init__(self):
        self.create()

    def create(self):
        """
        Create db
        """
        with self.DbConCursor() as dbc:
            dbc.execute('''CREATE TABLE IF NOT EXISTS
                              clients(clientId INTEGER NOT NULL PRIMARY KEY, clientName)''')
            dbc.execute('''CREATE TABLE IF NOT EXISTS
                              worked(clientId integer NOT NULL, dateWorkedInt text, secondsWorked float,
                              FOREIGN KEY (clientId) REFERENCES clients(clientId) )''')
            dbc.execute('''CREATE TABLE IF NOT EXISTS
                              notes(dateNote text, note)''')

    def get_clients(self):
        """
        :return: dictionary of client names; client ids
        """
        import sqlite3

        try:
            with self.DbConCursor() as dbc:
                dbc.row_factory = sqlite3.Row
                dbc.execute('SELECT * from clients')
                clients = dbc.fetchall()
                return clients
        except Exception as e:
            print("Storage error: {}".format(e))

    def add_client(self, client_name):
        """
        Add client
        :param client_name:
        :return: new client_id
        """
        with self.DbConCursor() as dbc:
            dbc.execute("INSERT INTO clients (clientName) VALUES (?)", ([client_name]))
            new_client_id = dbc.lastrowid

        return new_client_id

    def remove_client(self, client_name):
        """
        Remove client
        :param client_name:
        :return: nothing
        """
        with self.DbConCursor() as dbc:
            # get clientId with clientName
            dbc.execute("SELECT clientId from clients WHERE clientName = ?", ([client_name]))
            ret_val = dbc.fetchone()

            if ret_val:
                client_id = ret_val[0]
                # remove client records for worked table
                dbc.execute("DELETE FROM worked WHERE clientID = ?", ([client_id]))
                # remove parent record from clients table
                dbc.execute("DELETE FROM clients WHERE clientID = ?", ([client_id]))

    def update_time(self, client_id, seconds_worked):
        """
        Update time worked for client
        :param client_id:
        :param seconds_worked:
        :return: nothing
        """
        import datetime
        with self.DbConCursor() as dbc:
            dbc.execute('''SELECT secondsWorked from worked
                              WHERE ClientID = ? and dateWorkedInt = ?''',
                           (client_id, datetime.datetime.now().strftime("%Y%m%d")))
            prev_worked = dbc.fetchone()

            if prev_worked is None:
                dbc.execute('''INSERT INTO worked (clientId, dateWorkedInt, secondsWorked)
                               VALUES (?,?,?)''',
                               (client_id, datetime.datetime.now().strftime("%Y%m%d"), seconds_worked))
            else:
                dbc.execute('''UPDATE worked SET secondsWorked = ? WHERE ClientID = ? and dateWorkedInt = ?''', (
                    (prev_worked[0] + seconds_worked), client_id, datetime.datetime.now().strftime("%Y%m%d")))

    def update_client(self, client_id, client_name):
        """
        Update client name
        :param client_id:
        :param client_name:
        :return: void
        """
        with self.DbConCursor() as dbc:
            dbc.execute("UPDATE clients SET clientName = ? WHERE clientId = ?", ([client_name, client_id]))

    def get_note(self, date_str):
        """
        Retrieve any notes for the date
        """
        notes_txt = ''

        with self.DbConCursor() as dbc:
            dbc.execute("SELECT note FROM notes WHERE dateNote == ?", ([date_str]))
            ret_val = dbc.fetchone()
            if ret_val:
                notes_txt = ret_val[0]

        return notes_txt

    def update_note(self, notes_txt):
        """
        Update day notes
        :param notes_txt:
        :return: void
        """
        import datetime
        current_date_str = datetime.datetime.now().strftime("%Y%m%d")

        with self.DbConCursor() as dbc:
            dbc.execute("SELECT dateNote FROM notes WHERE dateNote == ?", ([current_date_str]))
            note_exists = dbc.fetchone()

            if note_exists:
                if len(notes_txt):
                    dbc.execute("UPDATE notes SET note = ? WHERE dateNote = ?", (notes_txt, current_date_str))
                else:
                    dbc.execute("DELETE FROM notes WHERE dateNote = ?", ([current_date_str]))
            else:
                dbc.execute("INSERT INTO notes (dateNote, note) VALUES (?,?)", (current_date_str, notes_txt))

    def get_report(self, date_str):
        """
        Get worked data for date string
        :param date_str:
        :return: list of records
        """
        with self.DbConCursor() as dbc:
            # get clientId with clientName
            dbc.execute("""SELECT clients.clientName, (worked.secondsWorked/3600),
                              (SELECT SUM(worked.secondsWorked/3600) FROM worked WHERE dateWorkedInt = ?) AS totalHours
                              FROM clients INNER JOIN worked ON clients.clientId = worked.clientId
                              WHERE worked.dateWorkedInt = ?""", (date_str, date_str))

            ret_val = dbc.fetchall()
            return ret_val

class ClientTimer:
    """
    Timer for hours worked
    """
    def __init__(self, storage):

        self.storage = storage
        self.activeClientId = 0  # who we are timing
        self.datetimeStart = 0.0  # when we started

    def update(self, client_id):
        """
        Update Timer

        if clientId is 0 the timer should go off on active client
            b) storage.write(activeClientId, minutes(now - start time))
            c) start time = 0
        else
            if activeClientId = 0
                first client and need to start timer
                a) start time = now
                b) activeClientId = clientId
                c) storage.write

            else need to switch clients
                a) storage.write(activeClientId, minutes(now - start time)
                b) start time = now
                c) activeClientId = clientId

        """
        import datetime

        #   save
        if self.activeClientId != 0:
            self.storage.update_time(self.activeClientId,
                                     (datetime.datetime.now() - self.datetimeStart).total_seconds())

        # update timer values
        if client_id == 0:
            self.datetimeStart = 0
            self.activeClientId = 0
        else:
            self.datetimeStart = datetime.datetime.now()
            self.activeClientId = client_id

    def close(self):
        """
        Close down timer and save
        """

        import datetime

        if self.activeClientId != 0:
            self.storage.update_time(self.activeClientId,
                                     (datetime.datetime.now() - self.datetimeStart).total_seconds())
        self.datetimeStart = 0
        self.activeClientId = 0

class DlgEditClients(QtWidgets.QDialog, layout.edit_clients.Ui_dlgEditClients):
    """
    Dialog for editing clients
    """
    def __init__(self, parent=None):
        QtWidgets.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.storage = parent.storage

        for clientId, clientName in self.storage.get_clients():
            item = QtWidgets.QListWidgetItem(clientName)
            item.setData(QtCore.Qt.UserRole, clientId)
            item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
            self.clientListWidget.addItem(item)

        self.btnInsert.clicked.connect(self.add_item)
        self.btnRemove.clicked.connect(self.remove_items)
        self.clientListWidget.itemDoubleClicked[QtWidgets.QListWidgetItem].connect(self.edit_item)
        self.clientListWidget.itemChanged[QtWidgets.QListWidgetItem].connect(self.save_changed_item)

    @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
    def save_changed_item(self, current_item):
        """
        Save inline edit of client name
        :param current_item:
        :return:
        """
        client_id = current_item.data(QtCore.Qt.UserRole)
        new_client_name = current_item.text()

        self.storage.update_client(client_id, new_client_name)
        self.parentWidget().update_client_button(client_id, new_client_name)

    @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
    def edit_item(self, item):
        """
        Bring up inline editor in listwidget
        :param item:
        :return:
        """
        self.clientListWidget.editItem(item)

    @QtCore.pyqtSlot()
    def add_item(self):
        """
        Add new client
        :return:
        """
        if len(self.inputNewClient.text()):
            self.clientListWidget.addItem(self.inputNewClient.text())
            new_client_id = self.storage.add_client(self.inputNewClient.text())
            self.parentWidget().add_client_button(new_client_id, self.inputNewClient.text())

    @QtCore.pyqtSlot()
    def remove_items(self):
        """
        Remove selected clients
        :return:
        """
        for item in self.clientListWidget.selectedItems():
            self.clientListWidget.takeItem(self.clientListWidget.row(item))
            self.storage.remove_client(item.text())
            self.parentWidget().remove_client_button(item.text())

class DlgReport(QtWidgets.QDialog, layout.report.Ui_dlgReport):
    """
    Dialog for viewing work report
    """
    def __init__(self, parent=None):
        QtWidgets.QDialog.__init__(self)
        self.setupUi(self)
        self.storage = parent.storage
        # set handler when date is clicked
        self.calendarWidget.clicked[QtCore.QDate].connect(self.generate_report)
        # on init, select current date and display that as default
        self.generate_report(QtCore.QDate.currentDate())

    @QtCore.pyqtSlot("QDate")
    def generate_report(self, date):
        """
        Display work for date
        :param date:
        :return:
        """
        # QtWidgets.QMessageBox.information(self,"QCalendarWidget Date Selected",date.toString("yyyyMMdd"))
        work_report = self.storage.get_report(date.toString("yyyyMMdd"))

        if len(work_report):
            self.lblReport.setText("")
            for clientName, hoursWorked, totalHours in work_report:
                self.lblReport.setText(self.lblReport.text() + "{} : {:04.2f} hours\n".format(clientName, hoursWorked))

            self.lblReport.setText(
                self.lblReport.text() + "======================\nTotal : {:04.2f}\n".format(totalHours))

        else:
            self.lblReport.setText("No work data for " + date.toString())

        # Any notes?
        notes = self.storage.get_note(date.toString("yyyyMMdd"))
        if len(notes):
            self.lblReport.setText(self.lblReport.text() + "\n====================== Notes:\n{}".format(notes))

class TimeMachineApp(QtWidgets.QMainWindow, layout.main.Ui_MainWindow):
    def __init__(self, parent=None):
        super(TimeMachineApp, self).__init__(parent)
        self.setupUi(self)
        self.actionExit.triggered.connect(QtWidgets.qApp.quit)
        self.actionEdit_Clients.triggered.connect(self.edit_clients)
        self.actionReport.triggered.connect(self.report)

        # init storage
        self.storage = Storage()

        # runtime layout
        self.runtime_layout()

        # init timer
        self.clientTimer = ClientTimer(self.storage)

        # session persistence
        self.settings = QtCore.QSettings()

        # done
        self.statusBar().showMessage('Ready')

    @QtCore.pyqtSlot(int)
    def client_button_group_toggled(self, client_id):
        """
        Respond to client button change
        """
        self.clientTimer.update(client_id)
        if client_id != 0:
            self.statusBar().showMessage('Timer Started')
        else:
            self.statusBar().showMessage('Timer Stopped')

    def runtime_layout(self):
        """
        Client buttons must be dynamic
        This is a function that clears the client buttons if needed
        then adds client buttons to the form
        """

        # ####   add client buttons from storage list #####

        #   init logical container for client buttons; not sure if this is worth the trouble except for id feature
        self.clientButtonGroup = QtWidgets.QButtonGroup(self)

        self.add_client_button(0, 'Off')
        # add client buttons for each client
        for clientId, clientName in self.storage.get_clients():
            self.add_client_button(clientId, clientName)

        # make logical container toggle
        self.clientButtonGroup.setExclusive(True)
        self.clientButtonGroup.buttonPressed[int].connect(self.client_button_group_toggled)

        # notes editor
        self.notesVerticalLayout.addWidget(QtWidgets.QLabel("Day Notes:"))
        notesTextEdit = NotesTextEdit()
        notesTextEdit.lostFocus.connect(self.save_notes)
        # insert notes from storage
        notes = self.storage.get_note(QtCore.QDate.currentDate().toString("yyyyMMdd"))
        if notes:
            notesTextEdit.setPlainText(notes)
        self.notesVerticalLayout.addWidget(notesTextEdit)

    def add_client_button(self, client_id, client_name):
        """
        Adds new client button to button group layout
        :param client_id:
        :param client_name:
        :return:
        """
        btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
        btn.setText(client_name)
        btn.setCheckable(True)
        if client_id == 0:
            btn.setChecked(True)

        self.clientBtnVerticalLayout.addWidget(btn)
        self.clientButtonGroup.addButton(btn, client_id)

    def update_client_button(self, client_id, client_name):
        """
        Update client button text
        :param client_id:
        :param client_name:
        :return:
        """
        btn = self.clientButtonGroup.button(client_id)
        btn.setText(client_name)

    def remove_client_button(self, client_name):
        """
        Remove client button to button group layout
        :param client_name:
        :return:
        """
        for i in reversed(range(self.clientBtnVerticalLayout.count())):
            widget_to_remove = self.clientBtnVerticalLayout.itemAt(i).widget()
            if widget_to_remove.text() == client_name:
                # remove it from the layout list
                self.clientBtnVerticalLayout.removeWidget(widget_to_remove)
                # remove it from the gui
                widget_to_remove.setParent(None)

    @QtCore.pyqtSlot('QString')
    def save_notes(self, notes_txt):
        """
        Save notes to storage
        """
        self.storage.update_note(notes_txt)

    @QtCore.pyqtSlot()
    def edit_clients(self):
        """
        Launch client list editor
        :return:
        """
        DlgEditClients(self).exec_()

    @QtCore.pyqtSlot()
    def report(self):
        """
        Launch time report
        :return:
        """
        DlgReport(self).exec_()

    @QtCore.pyqtSlot(QtWidgets.QMainWindow, QtGui.QCloseEvent)
    def closeEvent(self, event):
        """
        override close event
        :param event:
        :return:
        """
        self.close()

    @QtCore.pyqtSlot()
    def close(self):
        """
        QApplication.aboutToQuit() doesn't send event and i haven't figured out how to
        so i resort to chaining closeEvent() -> close() like this for now.
        i could connect to clientTimer.clost() but i left it this way in case i wanted
        to do something else like confirm user close...
        :return:
        """
        self.clientTimer.close()

    def show_first_time(self):
        """
        The first time we identify db has no clients
        """
        if self.settings.value('first_time_run', True, type=bool):

            # no clients in db
            if not len(self.storage.get_clients()):

                msg_button_reply = QtWidgets.QMessageBox.question(
                                    self,
                                    'Time Machine',
                                    'Time Machine has no clients yet.\nAdd Clients now?',
                                    QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes,
                                    QtWidgets.QMessageBox.Yes )

                if msg_button_reply == QtWidgets.QMessageBox.Yes:
                    self.edit_clients()

        # update settings; no longer the first time
        self.settings.setValue('first_time_run', False)
        # this writes to native storage
        del self.settings

def main():

    app = QtWidgets.QApplication(sys.argv)
    # app style
    app.setStyle("plastique")
    # app setting in general but specifically so that call to QSettings() uses meaningful path for settings
    app.setOrganizationName("jling_projects")
    app.setApplicationName("Time Machine")

    appWin = TimeMachineApp()
    # connect main window close and app close events
    app.aboutToQuit.connect(appWin.close)

    try:
        appWin.show()
        appWin.show_first_time()
        # disable resizing or stretching of main window
        appWin.setFixedSize(appWin.size());

    except Exception as e:
        QtWidgets.QMessageBox.information("QCalendarWidget Date Selected", e)

    sys.exit(app.exec_())

if __name__ == '__main__':
    main()


import os
import datetime
import numpy as np
import pandas as pd

from matplotlib import pyplot as plt
from matplotlib.ticker import MultipleLocator, FixedLocator
# ---------------------------
def plot_data(ax, label, c, filename, n_trial=30, time_shift=0,  ls="-", alpha=0.2, linewidth = None):
    for trial in range(n_trial):
        print(filename.replace("##", str(trial)))
        data = pd.read_csv(filename.replace("##", str(trial)), sep="\t")
        data = data.loc[~np.isnan(data['s'])]
        if linewidth is None:
            ax.plot(data['window_end']+time_shift, data['s'], alpha=alpha, label=label, c=c, ls=ls)
        else:
            ax.plot(data['window_end']+time_shift, data['s'], alpha=alpha, label=label, c=c, ls=ls, linewidth=linewidth)
        label = None
    return

def plot_statevar(ax, label, c, filename, time_shift=0,  ls="-", alpha=0.7, names = ["t", "S", "E", "I", "R"]):
    statevar = pd.read_csv(filename, sep="\t", comment="#", names=names)
    if "I" in names:
        ax.plot(statevar['t']+time_shift, statevar['I'], alpha=alpha, label=label, c=c, ls=ls)
    else:
        print(statevar)
        ax.plot(statevar['t'] + time_shift, statevar['I_l'] + statevar['I_h'], alpha=alpha, label=label, c=c, ls=ls)

def run(baseline_statevar,baseline_dense,baseline_sparse,baseline_full,
        highR0, highR0_dense, transhet, transhet_dense,
        reduced1p1, reduced1p1_dense, reduced0p75, reduced0p75_dense, figname):

    plt.rcParams.update({'font.size': 10})
    fig, axes = plt.subplots(2, 4, figsize=[13.5, 5], dpi=300)

    # ---------------------------
    ## Panel A, B: dense vs. sparse vs. full
    plot_statevar(axes[0, 0], 'dense',   "k",       baseline_statevar)
    plot_data    (axes[1, 0], 'dense',   "k",       baseline_dense)
    plot_data    (axes[1, 0], 'sparse',  "#9e9e9e", baseline_sparse)
    plot_data    (axes[1, 0], 'full',    "k"      , baseline_full, ls = "--", n_trial = 1, alpha = 1)

    ## inset - same data but zoomed out
    left, bottom, width, height = [0.07, 0.33, 0.075, 0.12]
    inset = fig.add_axes([left, bottom, width, height])

    plot_data    (inset, 'dense',   "k",       baseline_dense)
    plot_data    (inset, 'sparse',  "#9e9e9e", baseline_sparse)
    plot_data    (inset, 'full',    "k"      , baseline_full, ls = "--", n_trial = 1, alpha = 1)

    inset.patch.set_alpha(0.8)
    inset.set_xlim([-25, 205])
    inset.xaxis.set_ticklabels([])

    inset.yaxis.set_major_locator(FixedLocator([]))
    inset.set_ylim([-10, 2750])
    inset.yaxis.set_major_locator(FixedLocator([200, 1500, 2500]))

    inset.tick_params(axis = 'y', direction = 'in', pad = -67, labelsize = 6, labelright = True, labelleft = False, right = False, left = True)

    # ---------------------------
    ## Panel C, D: high R0 vs. low R0 - todo
    plot_statevar(axes[0, 1], r'R$_{0}$ = 1.6', "k",       baseline_statevar)
    plot_data    (axes[1, 1], r'R$_{0}$ = 1.6', "k",       baseline_dense)

    plot_statevar(axes[0, 1], r'R$_{0}$ = 2.0', "#318fdb", highR0)
    plot_data    (axes[1, 1], r'R$_{0}$ = 2.0', "#318fdb", highR0_dense)

    # ---------------------------
    # Panel E, F: transmission heterogeneity
    statevar_1 = pd.read_csv(baseline_statevar, sep="\t", comment="#", names=["t", "S", "E",  "I", "R"])
    statevar_2 = pd.read_csv(transhet, sep="\t", comment="#",names=["t", "S", "E", "I_l", "I_h", "R"])
    time_shift =  statevar_1['t'][np.argmax(statevar_1['I'])] - \
                  statevar_2['t'][np.argmax(statevar_2['I_l'] + statevar_2['I_h'])]

    plot_statevar(axes[0, 2], 'No TH',       "k",       baseline_statevar)
    plot_data    (axes[1, 2], 'No TH',       "k",       baseline_dense)

    plot_statevar(axes[0, 2], 'TH'          , "#80b564", transhet,       ls = "-.", names = ["t", "S", "E", "I_l", "I_h", "R"])
    plot_statevar(axes[0, 2], 'TH (shifted)', "#80b564", transhet,       time_shift= time_shift, names = ["t", "S", "E", "I_l", "I_h", "R"])
    plot_data    (axes[1, 2], 'TH (shifted)', "#80b564", transhet_dense, time_shift= time_shift)

    # ---------------------------
    # ## Panel G, H: reduced R0
    plot_statevar(axes[0, 3], r'R$_{0}$ unchanged',       "k",       baseline_statevar)
    plot_data    (axes[1, 3], r'R$_{0}$ unchanged',       "k",       baseline_dense)

    plot_statevar(axes[0, 3], r'R$_{0}$ reduced to 1.1' , "#ff5f43", reduced1p1)
    plot_data    (axes[1, 3], r'R$_{0}$ reduced to 1.1' , "#ff5f43", reduced1p1_dense)
    plot_statevar(axes[0, 3], r'R$_{0}$ reduced to 0.75', "#fcb708", reduced0p75)
    plot_data    (axes[1, 3], r'R$_{0}$ reduced to 0.75', "#fcb708", reduced0p75_dense)
    #

    # ---------------------------
    # set axis limit
    for i in range(4):
        axes[0, i].set_xlim([0, 200]); axes[0, i].set_ylim([0, 10000]);
        axes[1, i].set_xlim([0, 200]); axes[1, i].set_ylim([0, 225]);
        axes[0, i].ticklabel_format(axis='y', style="sci", scilimits=(3, 4))

        if i != 0:
            axes[0, i].legend(loc="upper right")
        axes[1, i].set_xlabel('Days post index case')

    axes[0, 0].set_ylabel("Number of\ninfected individuals")
    axes[1, 0].set_ylabel("Number of\nsegregating sites")

    ## add subplot label
    labels = ['ACEG'.lower(), 'BDFH'.lower()]
    for i, label_row in enumerate(labels):
        for j, l in enumerate(label_row):
            if j == 0:
                x_loc = -0.4
                axes[i][j].text(-0.27, 1.1, l, transform=axes[i][j].transAxes, fontsize=16, fontweight='bold', va='top', ha='left')
            else:
                x_loc = -0.3
                axes[i][j].text(-0.23, 1.1, l, transform=axes[i][j].transAxes, fontsize=16, fontweight='bold', va='top', ha='left')
            continue

    ## save figure
    fig.tight_layout(pad=0.5)
    fig.savefig(f'manuscript figures/pdf/{figname}.pdf')
    fig.savefig(f'manuscript figures/png/{figname}.png')

    fig.clf()
    plt.cla()

    return

if __name__ == "__main__":
    import os
    import argparse

    if os.getcwd().find("manuscript_figure_script") == -1:
        parser = argparse.ArgumentParser()
        parser.add_argument('--figname', type=str, default="figure3")

        parser.add_argument('--baseline_statevar',  type=str, required=True)
        parser.add_argument('--baseline_dense',     type=str, required=True)
        parser.add_argument('--baseline_sparse',    type=str, required=True)
        parser.add_argument('--baseline_full',      type=str, required=True)

        parser.add_argument('--highR0',         type=str, required=True)
        parser.add_argument('--highR0_dense',   type=str, required=True)

        parser.add_argument('--transhet',       type=str, required=True)
        parser.add_argument('--transhet_dense', type=str, required=True)

        parser.add_argument('--reduced1p1',         type=str, required=True)
        parser.add_argument('--reduced1p1_dense',   type=str, required=True)
        parser.add_argument('--reduced0p75',        type=str, required=True)
        parser.add_argument('--reduced0p75_dense',  type=str, required=True)

        args = parser.parse_args()

    else:
        os.chdir(os.getcwd().replace("manuscript_figure_script", ""))
        parser = argparse.ArgumentParser()
        args = parser.parse_args()
        args.figname = "figure1"

        args.baseline_statevar = "simulated_data/simpleSEIR_R0=16e-1_mu2e-1/seed20230201_statevar.tsv"
        args.baseline_dense    = "simulated_data/simpleSEIR_R0=16e-1_mu2e-1/seed20230201_unif40_win4/seed230207_segsites.tsv"
        args.baseline_sparse   = "simulated_data/simpleSEIR_R0=16e-1_mu2e-1/seed20230201_unif20_win4/seed230207_segsites.tsv"
        args.baseline_full     = "simulated_data/simpleSEIR_R0=16e-1_mu2e-1/seed20230201_full_win4/seed230201_segsites.tsv"

        args.highR0            = "simulated_data"
        args.highR0_dense      = "simulated_data"

        args.transhet          = "simul_dat"
        args.transhet_dense    = "simul_dat"

        args.reduced1p1        = "simul_dat"
        args.reduced1p1_dense  = "simul_dat"
        args.reduced0p75       = "simul_dat"
        args.reduced0p75_dense = "simul_dat"

    print(args)
    run(args.baseline_statevar,
        args.baseline_dense,
        args.baseline_sparse,
        args.baseline_full,

        args.highR0,
        args.highR0_dense,
        args.transhet,
        args.transhet_dense,

        args.reduced1p1,
        args.reduced1p1_dense,
        args.reduced0p75,
        args.reduced0p75_dense,

        args.figname)


import math
import random

from src.ImageLibrary import IMAGES

from src.Sound import SND

FLYING_FRAMES = 30
START_X = 400
START_Y = 30

ID_ALLOC = [1]

SICK_TREAT_TIME = 30 * 6
SAD_TREAT_TIME = 30 * 7
ANGRY_TREAT_TIME = 30 * 8
CRAZY_TREAT_TIME = 30 * 9
UNKNOWN_TREAT_TIME = 30 * 6

STARTING_TTL = 30 * 40

AILMENTS = 'sick sad angry crazy'.split(' ')

class Device:
	def __init__(self, playboard, game_time, device_type, x, y, ailment):
		self.id = ID_ALLOC[0]
		ID_ALLOC[0] += 1
		self.start = game_time
		self.end = None
		self.response_time = 0
		self.type = 'device'
		self.device_type = device_type # { 'phone', 'tablet', 'laptop' }
		self.x = START_X
		self.y = START_Y
		self.landing_x, self.landing_y = playboard.get_random_open_tile()
		self.landing_x = self.landing_x * 32 + 16
		self.landing_y = self.landing_y * 32 + 16
		self.ttl = STARTING_TTL

		self.ailment = ailment # { 'sick', 'sad', 'angry', 'crazy', 'dead', 'unknown' }
		self.actual_ailment = random.choice(AILMENTS)
		self.unknown_discovery_counter = 0
		self.state = 'flying' # { 'flying', 'ailed', 'treated', 'new', 'dead' }
		self.resolution = None
		self.state_counter = 0
		self.playboard = playboard
		self.model = playboard.model
		self.replaced = False
		self.treatment_ratio = 1.0

	def start_treatment(self):
		self.state = 'treated'
		self.state_counter = 0
		ratio = 1.0
		for furn in self.playboard.model.furniture:
			if furn[0] == '4':
				ratio *= 1.03
			elif furn[0] == '1':
				dx = self.x - (furn[1] + .5) * 32
				dy = self.y - (furn[2] + .5) * 32
				dist = (dx ** 2 + dy ** 2) ** .5
				if dist <= 32 * 3:
					ratio *= 1.02
			elif furn[0] == '3':
				#print furn, self.x, self.y
				dx = self.x - (furn[1] + .5) * 32
				dy = self.y - (furn[2] + .5) * 32
				dist = (dx ** 2 + dy ** 2) ** .5
				#print 'distance', dist
				if dist <= 32 * 3:
					ratio *= 1.10

		#print ratio
		self.treatment_ratio = ratio

	def update(self):
		self.state_counter += 1
		self.ttl -= 1
		if self.state == 'flying':
			self.ttl = STARTING_TTL # still immune to the aging process while flying through the air
			progress = 1.0 * self.state_counter / FLYING_FRAMES
			antiprogress = 1 - progress
			self.x = int(START_X * antiprogress + self.landing_x * progress)
			self.y = int(START_Y * antiprogress + self.landing_y * progress)
			if self.state_counter == FLYING_FRAMES:
				self.x = self.landing_x
				self.y = self.landing_y
				self.state = 'ailed'
				self.state_counter = 0
				if self.ailment == 'dead':
					self.state = 'dead'
		elif self.state == 'dead':
			if not self.replaced:
				self.replaced = True
				t = self.device_type
				self.resolution = 'ordered'
				if t == 'phone':
					if self.model.inventory_phones > 0:
						self.resolution = 'replaced'
						self.model.inventory_phones -= 1
					else:
						self.model.special_order_phone()
				elif t == 'tablet':
					if self.model.inventory_tablets > 0:
						self.resolution = 'replaced'
						self.model.inventory_tablets -= 1
					else:
						self.model.special_order_tablet()
				elif t == 'laptop':
					if self.model.inventory_laptops > 0:
						self.resolution = 'replaced'
						self.model.inventory_laptops -= 1
					else:
						self.model.special_order_laptop()

		elif self.state == 'ailed':
			self.response_time += 1
			if self.ailment == 'unknown':
				for staff in self.playboard.model.staff:
					dx = staff.x - self.x
					dy = staff.y - self.y
					if dx ** 2 + dy ** 2 < 32 ** 2:
						self.unknown_discovery_counter += 1

				if self.unknown_discovery_counter >= UNKNOWN_TREAT_TIME:
					self.ailment = self.actual_ailment
		elif self.state == 'treated':
			self.response_time += 1
			if self.ailment == 'sick':
				treat_time = SICK_TREAT_TIME
			elif self.ailment == 'sad':
				treat_time = SAD_TREAT_TIME
			elif self.ailment == 'angry':
				treat_time = ANGRY_TREAT_TIME
			elif self.ailment == 'crazy':
				treat_time = CRAZY_TREAT_TIME
			elif self.ailment == 'dead':
				treat_time = 9999999 # won't happen
			elif self.ailment == 'unknown':
				treat_time = UNKNOWN_TREAT_TIME

			if self.state_counter * self.treatment_ratio >= treat_time:
				self.state = 'new'
				self.resolution = 'treated'
				self.state_counter = 0
				SND.play_device_fix()

		if self.ttl < 0 and self.state != 'dead':
			self.state = 'dead'
			self.state_counter = 0

	def render(self, rc, render_list):
		sort_key = self.y * 1000000

		progress_bar = None

		if self.state == 'flying':
			mid = FLYING_FRAMES // 2
			zeroToOne = 1.0 - ((self.state_counter - mid) ** 2.0) / (mid ** 2.0)
			py_offset = int(zeroToOne * 80)
			self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '.png'), sort_key, self.x, self.y - py_offset)
		elif self.state == 'ailed':
			self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '.png'), sort_key, self.x, self.y)
			ailment = IMAGES.get('devices/' + self.ailment + '.png')
			py_offset = int(math.sin(3.14159 + self.state_counter * 2 * 3.14159 / 150) * 10 + 30)
			self.draw_image(render_list, ailment, sort_key + 1, self.x, self.y - py_offset)

			if self.ailment == 'unknown' and self.unknown_discovery_counter > 0:
				progress_bar = [
					(0, 128, 255),
					self.unknown_discovery_counter,
					UNKNOWN_TREAT_TIME
				]
		elif self.state == 'treated':
			if self.ailment == 'sick':
				treatment_time = SICK_TREAT_TIME
				self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '.png'), sort_key, self.x, self.y)
				self.draw_image(render_list, IMAGES.get('treatments/iv_rack.png'), sort_key - 1, self.x - 16, self.y,)
			elif self.ailment == 'sad':
				treatment_time = SAD_TREAT_TIME
				self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '_cucumber.png'), sort_key, self.x, self.y)
			elif self.ailment == 'angry':
				treatment_time = ANGRY_TREAT_TIME
				self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '_headphones.png'), sort_key, self.x, self.y)
			elif self.ailment == 'crazy':
				treatment_time = CRAZY_TREAT_TIME
				self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '_straightjacket.png'), sort_key, self.x, self.y)
			else:
				raise Exception("No rendering code for ailment treatment.")

			counter = int(self.treatment_ratio * self.state_counter)
			if counter > treatment_time: counter = treatment_time

			progress_bar = [
				(0, 255, 0),
				self.state_counter,
				treatment_time
			]
		else:
			self.draw_image(render_list, IMAGES.get('devices/' + self.device_type + '.png'), sort_key, self.x, self.y)

		if progress_bar != None:
			numerator = progress_bar[1]
			treatment_time = progress_bar[2]
			color = progress_bar[0]
			progress = 1.0 * numerator / treatment_time
			if progress > 1.0:
				progress = 1.0

			width = 50
			height = 8
			x = self.x - 12
			y = self.y + 8
			self.draw_rectangle(render_list, sort_key, x, y, width, height, (100, 100, 100))
			self.draw_rectangle(render_list, sort_key, x, y, int(width * progress), height, color)

		if self.state == 'ailed' or self.state == 'treated':
			progress = 1.0 * self.ttl / STARTING_TTL
			color = (0, 180, 0)
			if progress > .5:
				if progress > .75:
					color = (0, 100, 200)
				else:
					color = (0, 180, 0)
			elif progress > .25:
				color = (255, 255, 0)
			elif progress > .1:
				color = (255, 128, 0)
			else:
				color = (255, 0, 40)

			width = 50
			height = 8
			x = self.x - 12
			y = self.y
			self.draw_rectangle(render_list, sort_key, x, y, width, height, (100, 100, 100))
			self.draw_rectangle(render_list, sort_key, x, y, int(width * progress), height, color)

	def draw_image(self, rl, img, sort, x, y):
		rl.append(('I', sort, img, x - img.get_width() // 2, y - img.get_height()))

	def draw_rectangle(self, rl, sort, x, y, width, height, color):
		rl.append(('R', sort, x, y, width, height, color))


from tensorhive.core import ssh
from tensorhive.core.ssh import HostsConfig, ProxyConfig, Hostname, Username
from pssh.clients.native import ParallelSSHClient
from typing import List, Optional, Dict, Iterator, Tuple
import time
from datetime import datetime
import logging
log = logging.getLogger(__name__)

__author__ = '@micmarty'
__all__ = ['ExitCodeError', 'SpawnError', 'spawn', 'terminate', 'running', 'fetch_log']
"""
This module provides functionality for spawning commands on host machines via ssh.
It's divided into 3 parts:
    1) command builder(s):
        Classes that hold a set of commands (as strings) which can be launched by task executor
        in order to achieve some result, e.g. `spawn` and `terminate`
        You can implement your own command builder that uses different backend, currently it's `screen` program.

    2) task executor:
        Launches commands provided by command builder.
        It contains core logic for e.g. spawning, terminating processes on remote hosts via ssh.

    3) stateless API functions:
        Provides high-level interface for operations on remote host, like:
        * spawning, terminating processes
        * getting info about running processes
        * log fetching

Author's note:
    1) The ONLY recommended way to use this module from outside is by using
    exposed API functions (see __all__)
    # TODO Write tests
"""

# Custom exception names
class ExitCodeError(AssertionError):
    pass

class SpawnError(Exception):
    pass

class ScreenCommandBuilder:
    """Set of configurable commands built on top of `screen` program."""

    @staticmethod
    def spawn(command: str,
              session_name: str,
              capture_output: bool = True,
              custom_log_name: Optional[str] = None,
              keep_alive: bool = False) -> str:
        """Command that runs inside daemonized screen session.

        Command is put into background manually (-D + & instead of simply using -d) -> we want to know the PID
        Ways to capture output:
            * standard way: keep session alive, user resumes and inspects it by himself.
                May need to use some workarounds if we want to know when command stops.
            * alternative way: screen should run ``script some_output.log -c "sleep 20; echo TensorHive"`
            * tee way (implemented here): redirect command's stdout + stderr from within `screen` with `tee`
                When used with -i, --ignore-interrupts options it won't accept SIGINT so that
                the main command will catch it instead of `tee`. Use case for this is when your command
                prints something important after SIGINT and you want to see it + put that into log file.
            * the screen way: use `screen -L -Logfile name.log`, unfortunately
                              not all `screen` versions supports that feature.

        Side effects (incompatible with capture_output=True):
            * keep_alive will prevent saving command's output to log file.
            It is advised to keep this turned off, unless you really want to keep session
            alive even when the command has finished execution - sometimes useful!

        Note: `custom_log_name` argument will be ignored when `capture_output=False`

        """
        if capture_output:
            if keep_alive:
                log.debug('Forcing keep_alive=False - incompatible with capture_output=True')
                keep_alive = False

            if custom_log_name:
                create_logfile_command = ScreenCommandBuilder.custom_log_file(custom_log_name)
            else:
                create_logfile_command = ScreenCommandBuilder.tmp_log_file()
            # | -> stdout only, |& -> stdout + stderr (Bash 4), 2>&1 (old Bash)
            capturing_command = '|& tee --ignore-interrupts $({})'.format(create_logfile_command)

        return 'screen -Dm -S {sess_name} bash -c "{cmd}{keep_alive} {log}" {to_bg}'.format(
            sess_name=session_name,  # will help distinguishing between TensorHive and user's sessions
            cmd=command,
            log=capturing_command if capture_output else '',  # see docstring
            keep_alive='; exec sh' if keep_alive else '',  # see docstring
            to_bg='& echo $!'  # force printing process pid
        )

    @staticmethod
    def mkdir(target_dir: str) -> str:
        """Command that creates any inexisting directory given in path"""
        return 'mkdir --parents ' + target_dir

    @staticmethod
    def tmp_log_file() -> str:
        """Command that creates tmp file for storing log content.

        When command is executed it will print path to newly created file.
        Target directory will be created automatically when executing.
        Current implementation uses week number for easier differentiation.
        """
        target_dir = '~/TensorHiveLogs'
        week_no = datetime.today().strftime('%U')  # type: str
        name_template = 'week_' + week_no + '_XXXX'  # at least 3 'X' are required by mktemp

        mkdir_cmd = ScreenCommandBuilder.mkdir(target_dir)
        mktemp_cmd = 'mktemp {dir}/{templ} --suffix .log'.format(dir=target_dir, templ=name_template)
        return mkdir_cmd + ' && ' + mktemp_cmd

    @staticmethod
    def custom_log_file(filename: str, target_dir: str = '~/TensorHiveLogs') -> str:
        """Command that prints path to custom log file (`filename`)

        Note: if log file already exists `tee` will overwrite it!
        Target directory will be created automatically when executing, but
        log file won't be created (`touch`ed), assuming that `tee` will create it when executing.
        """
        mkdir_cmd = ScreenCommandBuilder.mkdir(target_dir)
        echopath_cmd = 'echo {dir}/{name}.log'.format(dir=target_dir, name=filename)
        return mkdir_cmd + ' && ' + echopath_cmd

    @staticmethod
    def interrupt(pid: int) -> str:
        """Command that sends SIGINT to screen session. Should be used to gracefully terminate running command."""
        return 'screen -S {} -X stuff "^C"'.format(pid)

    @staticmethod
    def terminate(pid: int) -> str:
        """Command that terminates screen session using only pid (we don't need the full name: 1234.tensorhive)."""
        return 'screen -X -S {} quit'.format(pid)

    @staticmethod
    def kill(pid: int) -> str:
        """Command that kills screen session by pid.

        It should also wipe dead sessions. Note that `kill` exit code is returned!
        """
        return 'kill -9 {}; KILL_EXIT=$?; screen -wipe; (exit $KILL_EXIT)'.format(pid)

    @staticmethod
    def get_active_sessions(grep_pattern: str) -> str:
        """Fetches the full names of screen sessions matching given grep pattern."""
        return 'screen -ls | cut -f 2 | sed -e "1d;$d" | grep -e "{}"'.format(grep_pattern)

class Task:
    """Represents task executed on one machine."""

    __slots__ = ['hostname', 'command', 'pid', '_command_builder']

    def __init__(self, hostname: str, command: str = None, pid: int = None):
        self.hostname = hostname
        self.command = command
        self.pid = pid
        # Here you can replace with your own backend/command builder
        self._command_builder = ScreenCommandBuilder

    def spawn(self, client: ParallelSSHClient, name_appendix: Optional[str] = None) -> int:
        """Spawns command via ssh client.
        Returns:
            pid of the process
        """
        sess_name = 'tensorhive_task'
        log_name = None
        if name_appendix:
            sess_name += '_' + name_appendix
            log_name = 'task_' + name_appendix

        command = self._command_builder.spawn(
            self.command, session_name=sess_name, capture_output=True, custom_log_name=log_name, keep_alive=False)
        output = ssh.run_command(client, command)
        stdout = ssh.get_stdout(host=self.hostname, output=output)

        if not stdout:
            reason = output[self.hostname].exception
            raise ValueError('Unable to acquire pid from empty stdout, reason: {}'.format(reason))
        # FIXME May want to decouple it somehow
        # FIXME pop() may theoretically fail (never stumbled upon this issue)
        pid = stdout.split().pop()
        self.pid = int(pid)
        return self.pid

    """
    Author's note: These three methods below are nearly identical, but I wanted
    to keep them separate in order to easily extend their logic in the future.
    """

    def terminate(self, client: ParallelSSHClient) -> int:
        """Terminates the task using it's pid.

        Returns exit code of the operation
        """
        assert self.pid, 'You must first spawn the task or provide pid manually.'
        command = self._command_builder.terminate(self.pid)
        output = ssh.run_command(client, command)
        exit_code = output[self.hostname].exit_code
        return exit_code

    def interrupt(self, client: ParallelSSHClient) -> int:
        """Interrupts the task gracefully by sending SIGINT signal

        Returns exit code of the operation
        """
        assert self.pid, 'You must first spawn the task or provide pid manually.'
        command = self._command_builder.interrupt(self.pid)
        output = ssh.run_command(client, command)
        exit_code = output[self.hostname].exit_code
        return exit_code

    def kill(self, client: ParallelSSHClient) -> int:
        """Kills the task using it's pid.

        Returns exit code of the operation
        """
        assert self.pid, 'You must first spawn the task or provide pid manually.'
        command = self._command_builder.kill(self.pid)
        output = ssh.run_command(client, command)
        exit_code = output[self.hostname].exit_code
        return exit_code

def spawn(command: str, host: Hostname, user: Username, name_appendix: Optional[str] = None) -> int:
    """Stateless, high-level interface for spawning process on remote host.

    name_appendix: string that will be attached to session name and log file name
        Example: appendix='99' will produce session='tensorhive_task_99', log='task_99.log'
    Returns pid of new process
    """
    config, pconfig = ssh.build_dedicated_config_for(host, user)
    client = ssh.get_client(config, pconfig)
    task = Task(host, command)
    try:
        pid = task.spawn(client, name_appendix)
    except ValueError as e:
        raise SpawnError('{} on {}@{} failed: {}'.format(command, user, host, e))
    else:
        log.debug('Command spawned, pid: {}'.format(pid))
        return pid

def terminate(pid: int, host: Hostname, user: Username, gracefully: Optional[bool] = True) -> int:
    """Stateless, high-level interface for terminating process on remote host.

    gracefully: determines how task will be stopped
        False:  SIGKILL - almost guaranteed termination, aggressive
        None:   SIGTERM - works in most cases, but rather aggresive
        True:   SIGINT - often does not work, but only this method allows for capturing logs when program is closing
    Returns exit code of termination operation, not running process
    """
    config, pconfig = ssh.build_dedicated_config_for(host, user)
    client = ssh.get_client(config, pconfig)
    task = Task(host, pid=pid)

    if gracefully is None:
        exit_code = task.terminate(client)
    elif gracefully is False:
        exit_code = task.kill(client)
    else:
        exit_code = task.interrupt(client)
    return exit_code

def running(host: Hostname, user: Username) -> List[int]:
    """Stateless, high-level interface for getting a list of running processes on remote host.

    Ignores sessions with names other than `pattern`
    Returns a list of pids
    """
    config, pconfig = ssh.build_dedicated_config_for(host, user)
    client = ssh.get_client(config, pconfig)
    pattern = '.*tensorhive_task.*'
    command = ScreenCommandBuilder.get_active_sessions(pattern)
    output = ssh.run_command(client, command)
    stdout = ssh.get_stdout(host, output)
    if not stdout:
        return []

    # '4321.foobar_session' -> 4321
    pid_from_session_name = lambda name: int(name.split('.')[0])
    pids = [pid_from_session_name(line) for line in stdout.split('\n')]
    log.debug('Running pids: {}'.format(pids))
    return pids

def fetch_log(host: Hostname, user: Username, task_id: int, tail: bool = False) -> Tuple[Iterator[str], str]:
    """Stateless, high-level interface for fetching log files from remote host.

    Seeks and reads files located under specific folder on remote host.
    Re-raises pssh exceptions.
    tail: whether to include full content or only last 10 lines
    """
    # TODO Path should be configurable from config files (see config.py)
    path = '~/TensorHiveLogs/task_{}.log'.format(task_id)
    program = 'tail' if tail else 'cat'
    command = '{} {}'.format(program, path)

    config, pconfig = ssh.build_dedicated_config_for(host, user)
    client = ssh.get_client(config, pconfig)
    output = ssh.run_command(client, command)

    if output[host].exception:
        # Propagage ssh exception
        raise output[host].exception
    if output[host].exit_code != 0:
        raise ExitCodeError(path)
    return output[host].stdout, path


from django.shortcuts import render

# Create your views here.

from .models import LbBiOrderFlow as LbOf
from django.http import HttpResponse, JsonResponse
from django.db.models import Q
import json
from django.core.paginator import Paginator, InvalidPage, EmptyPage
import datetime

# Create your views here.
def orderflow(request):
    """课程"""
    course_datetime1 = request.GET.get("course_datetime1")
    course_datetime2 = request.GET.get("course_datetime2")
    # course_state = request.GET.get('course_state')
    each_page = 30
    if course_datetime1 or course_datetime2:
        aQ1 = Q()
        aQ2 = Q()
        list1 = []
        if course_datetime1 or course_datetime2 is not None:

            start_date = datetime.datetime.strptime(course_datetime1, "%Y-%m-%d")
            end_data = datetime.datetime.strptime(course_datetime2, "%Y-%m-%d") + datetime.timedelta(days=1)

            print(start_date)
            aQ1.add(Q(course_datetime__range=(start_date, end_data)), Q.OR)
            aQ2.add(Q(course_datetime__range=(start_date, end_data)), Q.AND)
            list1.append(course_datetime1)
        print(aQ1)
        print(list1)
        page = int(request.GET.get('page', '1'))
        if len(list1) == 1:
            all_orders = LbOf.objects.filter(aQ1)
            if page == -1:
                p_num = LbOf.objects.filter(aQ1).count()
                print(p_num)
                contacts = all_orders
            else:
                paginator = Paginator(all_orders, each_page)
                p_num = paginator.count
                print(p_num)
                try:
                    contacts = paginator.page(page)
                    print(contacts)
                except(EmptyPage, InvalidPage):
                    contacts = paginator.page(paginator.num_pages)
        else:
            all_orders = LbOf.objects.filter(aQ2)
            if page == -1:
                p_num = LbOf.objects.filter(aQ2).count()
                print(p_num)
                contacts = all_orders
            else:
                paginator = Paginator(all_orders, each_page)
                p_num = paginator.count
                print(p_num)
                try:
                    contacts = paginator.page(page)
                    print(contacts)
                except(EmptyPage, InvalidPage):
                    contacts = paginator.page(paginator.num_pages)
    else:
        try:
            all_orders = LbOf.objects.all()
        except LbOf.DoesNotExist:
            return HttpResponse(status=404)
        page = int(request.GET.get('page', '1'))
        if page == -1:
            return JsonResponse({'code': 400, 'msg': '数据量太多，请输入查询时间'})
        else:
            paginator = Paginator(all_orders, each_page)
            p_num = paginator.count
            print(p_num)
            try:
                contacts = paginator.page(page)
                print(contacts)
            except(EmptyPage, InvalidPage):
                contacts = paginator.page(paginator.num_pages)
    resultList = []
    print(contacts)
    for index in contacts:
        resultList += [{
            "combo_name": index.combo_name,
            "class_number": index.class_number,
            "partial_delivery": index.partial_delivery,
            "pending_delivery": index.pending_delivery,
            "waiting_network_test": index.waiting_network_test,
            "waiting_garden_confirm": index.waiting_garden_confirm,
            "all_delivery": index.all_delivery,
            "order_complete": index.order_complete,
            "no_plan": index.no_plan,
            "is_plan": index.is_plan,
            "class_corrected_number": index.class_corrected_number,
            "arranged_formal_courses": index.arranged_formal_courses,
            "confirm_time": index.confirm_time,
        }]

    # 返回值
    # response = JsonResponse(resultList, safe=False)
    # response.status_code = 500  自定义响应码
    return JsonResponse({"code": 200, "msg": "操作成功", "total": p_num, "data": resultList})


import csv, requests
from .models import Location, EntryDate, create_friendly_name, create_hash
from .models import County, ProvinceState, CountryRegion, CaseType
from .models import CountEntry, CountIncreaseEntry, CountPercentIncreaseEntry
from .coronaVars import county_key, province_key, country_key, lat_key
from .coronaVars import long_key, us_keys, global_keys, csv_global_urls, csv_us_urls
from .coronaVars import get_file, case_status_type_names
from celery import shared_task
from celery.signals import worker_ready
from datetime import datetime as dt
from datetime import timedelta as td
from hashlib import sha256

def get_state_location(locs, state, case_status_type_id):
    state_hash = create_hash(state.province_state)
    if state_hash not in locs:
        state = Location(
            province_state = state,
            friendly_name = state.province_state,
            friendly_hash = state_hash
        )
        state.save()
        state.case_types.add(case_status_type_id)
        return state
    locs[state_hash].case_types.add(case_status_type_id)
    return locs[state_hash]

def get_region_location(locs, region, case_status_type_id):
    region_hash = create_hash(region.region_country)
    if region_hash not in locs:
        region = Location(
            region_country = region,
            friendly_name = region.region_country,
            friendly_hash = region_hash

        )
        region.save()
        region.case_types.add(case_status_type_id)
        return region
    locs[region_hash].case_types.add(case_status_type_id)
    return locs[region_hash]

# csv_file: csv.DictReader
# case_status_type: string, one of ['confirmed', 'deaths', 'recovered']
def update_database(csv_file, case_status_type_id, column_keys, row_start):

    # hashtable the relevant objects for O(1) lookups
    csts = {cst.case_type : cst for cst in  CaseType.objects.all() }

    # if this case status type doesn't exist create it
    if case_status_type_id not in csts:
        case_status_type_id = CaseType(case_type=case_status_type_id)
        case_status_type_id.save()
    # otherwise grab the object from the hash table
    else:
        case_status_type_id = csts[case_status_type_id]

    # for each row in the csv file
    for row in csv_file:
        locs = { loc.friendly_hash: loc for loc in Location.objects.all() }
        province_state = { ps.province_state : ps for ps in ProvinceState.objects.all() }
        country_region = { cr.region_country : cr for cr in CountryRegion.objects.all() }
        counties = { cnty.county : cnty for cnty in County.objects.all() }

        # not all rows have a county entry
        county = None

        # if there is a county key present in the keys sent to this function
        if county_key in column_keys:
            # assign the couny accordingly
            county = row[column_keys[county_key]]

        # otherwise get values from row with keys sent to this function
        province = row[column_keys[province_key]]
        region = row[column_keys[country_key]]
        lat = row[column_keys[lat_key]]
        lon = row[column_keys[long_key]]

        # if there was a county
        if county:
            # include it in the friendly name
            friendly_name = create_friendly_name(province, region, county=county)
        else:
            # don't include it
            friendly_name = create_friendly_name(province, region)

        # create the friendly hash for this location for lookup in hash table
        friendly_hash = create_hash(friendly_name)

        # if the region isn't in the region hash table
        if region not in country_region:
            # create it
            region = CountryRegion(region_country=region)
        else:
            # grab it
            region = country_region[region]

        # try to add this case type to the region
        region.save()
        region.case_types.add(case_status_type_id)

        # get or create a location object that is only associated with this region
        region_location = get_region_location(locs, region, case_status_type_id)

        if province != '':
            #if the state isn't in the state hash table
            if province not in province_state:
                # create it
                province = ProvinceState(
                    province_state = province,
                    region_country = region
                )
            else:
                # grab it
                province = province_state[province]

            # try to add this case type to the state
            province.save()
            province.case_types.add(case_status_type_id)

            # get or create a location object that is only assicated with this state
            state_location = get_state_location(locs, province, case_status_type_id)

        else:
            province = None
            state_location = None

        # if there was a county
        if county:
            # if the county isn't in the county hash table
            if county not in counties:
                # create it
                county = County(
                    county = county,
                    province_state = province,
                    region_country = region
                )
            else:
                # grab it
                county = counties[county]

            # try to add this case type to this county
            county.save()
            county.case_types.add(case_status_type_id)

        # if this location not in the hash table of locations
        if friendly_hash not in locs:
            # if there was a county
            if county:
                # create a location with a county
                location = Location(
                    county = county,
                    province_state = province,
                    region_country = region,
                    latitude = lat,
                    longitude = lon,
                    friendly_name = friendly_name,
                    friendly_hash = friendly_hash
                )
            else:
                # create a location with a null county
                location = Location(
                    province_state = province,
                    region_country = region,
                    latitude = lat,
                    longitude = lon,
                    friendly_name = friendly_name,
                    friendly_hash = friendly_hash
                )
        else:
            # grab it
            location = locs[friendly_hash]

        # try to add this case type to this location
        location.save()
        location.case_types.add(case_status_type_id)

        # get number entries of this type for this location
        locations_entries = CountEntry.objects.filter(date__location=location, date__case_status_type_id=case_status_type_id)
        num_historic_db_entries = len(locations_entries)

        # get all actual entries on this row
        list_row = [ item for item in row.items() ][row_start:]

        # if the number of entries for this row is equal to the number in the database
        if num_historic_db_entries == len(list_row):
            # no new entries, nothing to do
            print('no updates for', case_status_type_id)
            continue

        one_day = td(days=1)

        # for each new entry
        for entry in list_row[num_historic_db_entries:]:

            the_date = dt.strptime(entry[0], '%m/%d/%y').date()
            previous_date = (dt.strptime(entry[0], '%m/%d/%y') - one_day).date()

            # try to get the previous date of this location for calculations if it exists
            try:
                previous_entry_date = EntryDate.objects.get(
                    date=previous_date,
                    location=location,
                    case_status_type_id=case_status_type_id
                )
                previous_date_count = previous_entry_date.countentry
                divisor = previous_date_count
                if int(divisor) == 0:
                    divisor = 100
            # it doesn't exist, fallback to default values
            except Exception as e:
                print(e)
                previous_date_count = 0
                divisor = 100

            # create a new EntryDate
            date = EntryDate(
                date = the_date,
                location = location,
                case_status_type_id = case_status_type_id
            )
            date.save()

            # crete a new CountEntry
            count_entry = CountEntry(
                date = date,
                value = int(entry[1])

            )
            count_entry.save()

            # create a new CountIncreaseEntry
            count_inc_entry = CountIncreaseEntry(
                date = date,
                value =  int(entry[1]) - int(previous_date_count)
            )
            count_inc_entry.save()

            # create a new CountPercentIncreaseEntry
            count_perc_inc_entry = CountPercentIncreaseEntry(
                date = date,
                value = 100*(int(count_inc_entry)/int(divisor))
            )
            count_perc_inc_entry.save()

            # if processing a US file

            # UPDATE STATE
            # get state location associated previous EntryDate object count
            if state_location:
                try:
                    previous_state_date = EntryDate.objects.get(
                        date=previous_date,
                        location=state_location,
                        case_status_type_id=case_status_type_id
                    )
                    previous_state_date_count = previous_state_date.countentry
                    state_divisor = previous_state_date_count
                    if int(state_divisor) == 0:
                        state_divisor = 100
                except Exception as e:
                    print(e)
                    previous_state_date_count = 0
                    state_divisor = 100

                # get or create the current state location EntryDate object
                try:
                    state_date = EntryDate.objects.get(
                        location=state_location,
                        date=date.date,
                        case_status_type_id = case_status_type_id
                        )
                except Exception as e:
                    print(e)
                    state_date = EntryDate(
                        date = the_date,
                        location = state_location,
                        case_status_type_id = case_status_type_id
                    )
                    state_date.save()

                # get and update or create the current state location CountEntry
                try:
                    state_date.countentry.value += count_entry.value
                    state_date.countentry.save()
                except Exception as e:
                    print(e)
                    state_count_entry = CountEntry(
                        date = state_date,
                        value = int(entry[1])

                    )
                    state_count_entry.save()

                # get and update or create the current state location CountIncreaseEntry
                try:
                    state_date.countincreaseentry.value += count_inc_entry.value
                    state_date.countincreaseentry.save()
                    state_inc_entry = state_date.countincreaseentry
                except Exception as e:
                    print(e)
                    state_inc_entry = CountIncreaseEntry(
                        date = state_date,
                        value = count_inc_entry.value
                    )
                    state_inc_entry.save()

                # get and update or create the current state location CountPercentIncreaseEntry
                try:
                    state_date.countpercentincreaseentry.value = 100*(int(state_inc_entry)/int(state_divisor))
                    state_date.countpercentincreaseentry.save()
                except Exception as e:
                    print(e)
                    state_perc_inc_entry = CountPercentIncreaseEntry(
                        date = state_date,
                        value = count_perc_inc_entry.value
                    )
                    state_perc_inc_entry.save()

                # UPDATE REGION
                if not county:
                    try:
                        previous_region_date = EntryDate.objects.get(
                            date=previous_date,
                            location=region_location,
                            case_status_type_id = case_status_type_id
                        )
                        previous_region_date_count = previous_region_date.countentry
                        region_divisor = previous_region_date_count
                        if int(region_divisor) == 0:
                            region_divisor = 100
                    except Exception as e:
                        print(e)
                        previous_region_date_count = 0
                        region_divisor = 100

                    try:
                        region_date = EntryDate.objects.get(
                            location=region_location,
                            date=date.date,
                            case_status_type_id=case_status_type_id
                            )
                    except Exception as e:
                        print(e)
                        region_date = EntryDate(
                            date = the_date,
                            location = region_location,
                            case_status_type_id = case_status_type_id
                        )
                        region_date.save()

                    try:
                        region_date.countentry.value += count_entry.value
                        region_date.countentry.save()
                    except Exception as e:
                        print(e)
                        region_count_entry = CountEntry(
                            date = region_date,
                            value = int(entry[1])
                        )
                        region_count_entry.save()

                    try:
                        region_date.countincreaseentry.value += count_inc_entry.value
                        region_date.countincreaseentry.save()
                        region_inc_entry = region_date.countincreaseentry
                    except Exception as e:
                        print(e)
                        region_inc_entry = CountIncreaseEntry(
                            date = region_date,
                            value = count_inc_entry.value
                        )
                        region_inc_entry.save()

                    try:
                        region_date.countpercentincreaseentry.value = 100*(int(region_inc_entry)/int(region_divisor))
                        region_date.countpercentincreaseentry.save()
                    except Exception as e:
                        print(e)
                        region_perc_inc_entry = CountPercentIncreaseEntry(
                            date = region_date,
                            value = count_perc_inc_entry.value
                        )
                        region_perc_inc_entry.save()

            print('new {status_type} historical entry {num} for {date} {location}'.format(
                status_type=case_status_type_id,
                num=entry[1],
                date=entry[0],
                location=location.friendly_name))

@shared_task
def do_data_update():
    csv_global_files = [get_file(csv_url) for csv_url in csv_global_urls]
    csv_us_files = [get_file(csv_url) for csv_url in csv_us_urls]

    for case_status_type_name, csv_file in zip(case_status_type_names, csv_global_files):
        update_database(csv_file, case_status_type_name, global_keys, 4)

    for case_status_type_name, csv_file in zip(case_status_type_names[:2], csv_us_files):
        if case_status_type_name == 'recovered':
            update_database(csv_file, case_status_type_name, us_keys, 11)
        else:
            update_database(csv_file, case_status_type_name, us_keys, 12)

# @worker_ready.connect
# def update_data(sender=None, conf=None, **kwargs):
#     do_data_update()


"""
https://codeforces.com/group/MWSDmqGsZm/contest/219432/problem/Y

 """

"""
Errors I digested:

1.
The error message you're encountering, "IndexError: list assignment index out of range," suggests that you're trying to assign a value to an index in a list that doesn't exist yet. This typically happens when you try to assign a value to an index that is greater than or equal to the current length of the list.

In the context of your code, it seems like you are attempting to assign a value to fibo[0], but fibo is either an empty list or has not been defined yet. To fix this issue, you need to create a list and then assign values to its elements. Here's an example of how to create a list to store Fibonacci numbers and initialize the first element to 0:

2.
It seems like you're encountering an issue where you're getting the string representation of a function object instead of the expected result. This typically happens when you try to print or use a function without actually calling it.

If you see <function function_name at memory_address>, it means you are referring to the function itself without calling it.
 """

num = int (input())
fibo=[0,1]

for i in range(2,num):
    fibo.append(fibo[i-1] + fibo[i-2])

for val in fibo[:num]:  #শুধু [:num]
    print(val,end=' ')

# [:num] এইটা লিখলে ইনপুট যদি 1 দিই তাহলে আউটপুট শুধু ১ টা ইলিমেন্টের জন্য দেখানোর কথা। আর এভাবে না লিখলে ইনপুট 1 এর জন্য আউটপুটে 0 1 দেখাচ্ছিলো।


class produto:
    def __init__(self, nome, preco):
        self.nome = nome
        self.preco = preco

class livro(produto):
    def __init__(self, nome, preco, autor):
        super().__init__(nome, preco)
        self.autor = autor

class eletronico(produto):
    def __init__(self, nome, preco, voltagem):
        super().__init__(nome, preco)
        self.voltagem = voltagem

livro = livro("O pequeno príncipe", 15.22, "Antoine de Saint-Exupéry")
eletronico = eletronico("Microondas", 1154.75, "220V")

print("Livro:", livro.nome, "- Autor:", livro.autor, "- Preço:", livro.preco)
print("Eletrônico:", eletronico.nome, "- Voltagem:", eletronico.voltagem, "- Preço:", eletronico.preco)


import matplotlib.pyplot as plt
import numpy as np
import serial
import time

Fs = 300

t = np.arange(0,10,1/10)

x = np.zeros(100)
y = np.zeros(100)
z = np.zeros(100)

serdev = '/dev/ttyACM0'

s = serial.Serial(serdev,115200)

for i in range(300):
    line=s.readline()
    #print(line)
    if(i%3 == 0): x[int(i/3)] = float(line)
    elif(i%3 == 1): y[int(i/3)] = float(line)
    elif(i%3 == 2): z[int(i/3)] = float(line)

disa = np.zeros(100)
disb = np.zeros(100)
dis = np.zeros(100)
great =  np.zeros(100)
for i in range(100):
    tu = 0.05
    if (i==0):
        disa[i]=0
        disb[i]=0
    else:
        disa[i]=disa[i-1]+x[i-1]*9.8/2*0.1*0.1
        disb[i]=disb[i-1]+y[i-1]*9.8/2*0.1*0.1
    dis[i] = np.sqrt(disa[i]*disa[i]+disb[i]*disb[i])
    if dis[i] > tu:   great[i] = 1
    else:   great[i] = 0

fig, ax = plt.subplots(2, 1)

ax[0].plot(t,x, label = 'x')
ax[0].plot(t,y, label = 'y')
ax[0].plot(t,z, label = 'z')
ax[0].set_xlabel('Time')
ax[0].set_ylabel('Acc Vector')
ax[0].legend()

ax[1].stem(t,great)
ax[1].set_xlabel('Time')
ax[1].set_ylabel('displacement larger than 5')

plt.show()

s.close()


from re import sub
from django.db import connection
from .models import AlbumDetails, SongDescription

def get_music_menu_album_list(language):
    return AlbumDetails.objects.filter(language=language) \
        .select_related('album') \
        .order_by('-album__release_date') \
        .only(
            'album__id', 'album__release_date',
            'title', 'slug', 'album_id'
        )

def get_all_songs(language, album_id=None):
    songs = SongDescription.objects.select_related('song').filter(language=language)
    songs = songs.order_by('-song__album_id', 'song__album_order', 'song__release_date')
    songs = songs.only(
        'song__id',
        'song__album_id',
        'song__original_title',
        'song__length',
        'song__slug',
        'song__is_hidden',
        'title'
    )

    if album_id:
        songs.filter(song__album_id=album_id)

    return songs

def get_album_language_urls(album_id):
    albums = AlbumDetails.objects.filter(album_id=album_id).only('album_id', 'language', 'slug')

    slugs = {}
    for album in albums:
        slugs[album.language] = '%d-%s/' % (album.album_id, album.slug)
    return slugs

def get_album_durations():
    with connection.cursor() as cursor:
        cursor.execute(
            'SELECT \
                TIME(SUM(STRFTIME("%s", length)), "unixepoch") AS album_length, \
                album_id \
            FROM music_song \
            WHERE is_hidden = 0 \
            GROUP BY album_id'
        )

        return [
            {'album_id': row[1], 'album_duration': sub('00:0?', '', row[0])} for row in cursor.fetchall()
        ]


# -*- coding: utf-8 -*-
"""
This module provides support for converting mime-types and extensions of RDF serializations that are
supported by the LoD app.

Serializations formats that are not in EXTENSION_TO_MIME_TYPE cannot be used during the 303 content negotiation and
data rendering.
"""
from .mimeparse import best_match as lib_best_match

# SPARQL results
JSON_MIME = "application/sparql-results+json"
XML_MIME = "application/sparql-results+xml"

# support RDF mime-types
HTML_MIME = "text/html"
N3_MIME = "text/n3"
TURTLE_MIME = "text/turtle"
RDFXML_MIME = "application/rdf+xml"
NTRIPLES_MIME = "application/n-triples"
JSONLD_MIME = "application/json"
NQUADS_MIME = "application/n-quads"

EXTENSION_TO_MIME_TYPE = {"rdf": RDFXML_MIME, "n3": N3_MIME, "nt": NTRIPLES_MIME, "turtle": TURTLE_MIME,
                          "ttl": TURTLE_MIME, "json-ld": JSONLD_MIME, "json": JSONLD_MIME, 'nquads': NQUADS_MIME}
MIME_TYPE_TO_EXTENSION = dict(list(map(
        reversed,
        [(ext, mime) for ext, mime in list(EXTENSION_TO_MIME_TYPE.items()) if ext not in ['ttl', 'json']]
)))

def mime_to_extension(mime_type):
    """
    Convert mime-type from accept-headings to file extension.
    """
    if mime_type in MIME_TYPE_TO_EXTENSION:
        return MIME_TYPE_TO_EXTENSION[mime_type]
    return "rdf"

def extension_to_mime(extension):
    """Convert file extension to mime-type.

    Some have to be rewritten because the MIME type does not correspond to the file extension.
    """
    if extension == 'ttl':
        extension = 'turtle'
    if extension == 'json':
        extension = 'json-ld'
    if extension == 'nq':
        extension = 'nquads'
    if extension in EXTENSION_TO_MIME_TYPE:
        return extension, EXTENSION_TO_MIME_TYPE[extension]
    return "xml", RDFXML_MIME

def result_extension_to_mime(extension):
    """Convert result extension to MIME type.

     Some have to be rewritten rewritten because the MIME type does not correspond to the file extension.
    """
    if extension == 'xml':
        return XML_MIME
    if extension == 'json':
        return JSON_MIME
    if extension == 'html':
        return HTML_MIME
    return EXTENSION_TO_MIME_TYPE[extension]

def best_match(candidate, header):
    if header and 'text/html' in header:
        return ''
    if header:
        return lib_best_match(candidate, header)
    return None


from fastapi import FastAPI
from connexion import DB

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

# aux infos d'un restaurant à partir de son id,
@app.get("/api/info/{id}")
async def get_info_by_id(id=None):
    return DB.get_info_by_id(id)

# à la liste des noms de restaurants à partir du type de cuisine,
@app.get("/api/name/{type_cuisine}")
async def get_resto_by_type(type_cuisine: str):
    return DB.get_name_by_type(type_cuisine)

# au nombre d'inspection d'un restaurant à partir de son id restaurant,
@app.get("/api/inspection/{idrestaurant}")
async def get_inspection_by_idrestaurant(idrestaurant: int):
    return DB.get_nb_inspec_by_id(idrestaurant)

# les noms des 10 premiers restaurants d'un grade donné.
@app.get("/api/classement/{grade}")
async def get_classement_by_grade(grade: str):
    return DB.get_top_10(grade)

# http://localhost:88/api/info/50041578
# http://localhost:88/api/name/Asian
# http://localhost:88/api/inspection/40859175
# http://localhost:88/api/classement/B