# -*- coding: utf-8 -*-
"""
Created on Tue Jul 30 10:52:58 2013

@author: pmaurier
"""

import os, sys
from datetime import date
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QAbstractItemView

saved_columns_width = QtCore.QSettings("XL-ant", "Oryx")

# Define function to import external files when using PyInstaller.
def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)

Ui_MainWindow = uic.loadUiType(resource_path('inventory.ui'))[0]
# from inventoryUI import Ui_MainWindow

from printlist import createpdf

import os

from config import *
from formatting import *
from datafilehandling import *

# Create the data file if it doesn't exists
open(FILENAME, 'a').close()

# Create autosaves dir if it doesn't exists
if not os.path.exists(AUTOSAVE_DIR):
    os.makedirs(AUTOSAVE_DIR)

# Constant for the sorting role in the Standard Items
SORT_ROLE = QtCore.Qt.UserRole + 1

###########################################

############## Main Window ################

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        super(Ui_MainWindow, self).__init__()

        self.setupUi(self)
        self.initUI()

        self.current_num = self.eyeglassesNum.value()
        self.modif = False

        # Data Structure (Name, formating function, get function,
        #                 set function, default value)
        self.data_structure = [
            ['Num', lambda n: str(int(n)), self.eyeglassesNum.value,
             self.eyeglassesNum.setValue, 1],
            ['OD Sph', formatSph, self.rSphereSpin.value,
              self.rSphereSpin.setValue, DEFAULT_SPHERE],
            ['OD Cyl', formatCyl, self.rCylSpin.value,
             self.rCylSpin.setValue, DEFAULT_CYL],
            ['OD Axe', formatAxis, self.rAxisSpin.value,
             self.rAxisSpin.setValue, DEFAULT_AXIS],
            ['OD Add', formatSph, self.rAddSpin.value,
             self.rAddSpin.setValue, DEFAULT_ADD],
            ['OG Sph', formatSph, self.lSphereSpin.value,
             self.lSphereSpin.setValue, DEFAULT_SPHERE],
            ['OG Cyl', formatCyl, self.lCylSpin.value,
             self.lCylSpin.setValue, DEFAULT_CYL],
            ['OG Axe', formatAxis, self.lAxisSpin.value,
             self.lAxisSpin.setValue, DEFAULT_AXIS],
            ['OG Add', formatSph, self.lAddSpin.value,
             self.lAddSpin.setValue, DEFAULT_ADD],
            ['Foyer', formatType, self.getAddType, self.setAddType, 0],
            ['Mont', formatFrame, self.getFrame, self.setFrame, 0],
            ['Teinte', formatSun, self.getSolar, self.setSolar, 0],
            ['Com', lambda n: str(n),
             self.getComment, self.setComment, ''],
            ['Statut', formatStock, lambda: 1, lambda n: n, 1],
            ['Date Mvmt', formatDate, self.getDateMvmt,
             self.setDateMvmt, date.today()],
            ]

        self.data = dict()
        self.model = QtGui.QStandardItemModel(self)

        self.data = self.loadDataFromCsv()

        self.displayData(self.data)
        self.new()

    def initUI(self):
        """ create actions and default values of the interface """
        # List actions and create the menubar
        self.exitAction.triggered.connect(QtWidgets.qApp.quit)
        self.saveAction.triggered.connect(self.save)
        self.deleteAction.triggered.connect(self.delete)
        self.newAction.triggered.connect(self.new)
        self.backupAction.triggered.connect(backup)
        self.restoreAction.triggered.connect(self.restoreAndShow)
        self.printAction.triggered.connect(lambda: createpdf(self.model))
        self.printButton.clicked.connect(lambda: createpdf(self.model))

        # Numbering/Actions panel
        self.eyeglassesNum.valueChanged.connect(self.eyeglassesNumModified)

        self.current_num = self.eyeglassesNum.value()
        self.eyeglassesNum.noWarn = False

        self.saveButton.clicked.connect(self.saveAction.trigger)
        self.deleteButton.clicked.connect(self.deleteAction.trigger)
        self.newButton.clicked.connect(self.newAction.trigger)
        self.backupButton.clicked.connect(self.backupAction.trigger)
        self.restoreButton.clicked.connect(self.restoreAction.trigger)

        # Right Eye
        # Correction
        self.rSphereSpin.setMaximum(MAX_SPHERE)
        self.rSphereSpin.setMinimum(MIN_SPHERE)
        self.rSphereSpin.setSingleStep(STEP_SPHERE)
        self.rSphereSpin.setValue(DEFAULT_SPHERE)
        self.rSphereSpin.valueChanged.connect(self.modified)

        self.rCylSpin.setMaximum(MAX_CYL)
        self.rCylSpin.setMinimum(MIN_CYL)
        self.rCylSpin.setSingleStep(STEP_CYL)
        self.rCylSpin.setValue(DEFAULT_CYL)
        self.rCylSpin.valueChanged.connect(self.modified)

        self.rLabelCylPos.setVisible(False)
        self.rLabelCylPos.setPalette(RED_PALETTE)

        self.rAxisSpin.setMaximum(MAX_AXIS)
        self.rAxisSpin.setMinimum(MIN_AXIS)
        self.rAxisSpin.setSingleStep(STEP_AXIS)
        self.rAxisSpin.setValue(DEFAULT_AXIS)
        self.rAxisSpin.valueChanged.connect(self.modified)

        self.r90Button.clicked.connect(self.r90Action)
        self.rFineCheckbox.stateChanged.connect(self.rFineChanged)

        self.rAddSpin.setMaximum(MAX_ADD)
        self.rAddSpin.setMinimum(MIN_ADD)
        self.rAddSpin.setSingleStep(STEP_ADD)
        self.rAddSpin.setValue(DEFAULT_ADD)
        self.rAddSpin.valueChanged.connect(self.enableAddition)

        self.rLinkCheckbox.stateChanged.connect(self.linkChanged)

        self.rCopyButton.clicked.connect(self.copyRtoL)

        # Left Eye
        # Correction
        self.lSphereSpin.setMaximum(MAX_SPHERE)
        self.lSphereSpin.setMinimum(MIN_SPHERE)
        self.lSphereSpin.setSingleStep(STEP_SPHERE)
        self.lSphereSpin.setValue(DEFAULT_SPHERE)
        self.lSphereSpin.valueChanged.connect(self.modified)

        self.lCylSpin.setMaximum(MAX_CYL)
        self.lCylSpin.setMinimum(MIN_CYL)
        self.lCylSpin.setSingleStep(STEP_CYL)
        self.lCylSpin.setValue(DEFAULT_CYL)
        self.lCylSpin.valueChanged.connect(self.modified)

        self.lLabelCylPos.setVisible(False)
        self.lLabelCylPos.setPalette(RED_PALETTE)

        self.lAxisSpin.setMaximum(MAX_AXIS)
        self.lAxisSpin.setMinimum(MIN_AXIS)
        self.lAxisSpin.setSingleStep(STEP_AXIS)
        self.lAxisSpin.setValue(DEFAULT_AXIS)
        self.lAxisSpin.valueChanged.connect(self.modified)

        self.l90Button.clicked.connect(self.l90Action)
        self.lFineCheckbox.stateChanged.connect(self.lFineChanged)

        self.lAddSpin.setMaximum(MAX_ADD)
        self.lAddSpin.setMinimum(MIN_ADD)
        self.lAddSpin.setSingleStep(STEP_ADD)
        self.lAddSpin.setValue(DEFAULT_ADD)
        self.lAddSpin.valueChanged.connect(self.enableAddition)

        self.lLinkCheckbox.stateChanged.connect(self.linkChanged)

        # Addition
        self.addRadioP.toggled.connect(self.modified)
        self.addRadioBF.toggled.connect(self.modified)

        # Comments
        self.commentEdit.textChanged.connect(self.modified)

        # Solar
        self.solarRadioNo.toggled.connect(self.modified)
        self.solarRadioYes.toggled.connect(self.modified)

        # Frame
        self.childRadioNo.toggled.connect(self.modified)
        self.childRadioYes.toggled.connect(self.modified)
        self.childRadioHalf.toggled.connect(self.modified)

        # Table
        self.tableView.doubleClicked.connect(self.selectLine)
        self.tableView.horizontalHeader().sectionResized.connect(self.saveColumnsState)
        self.tableView.setSortingEnabled(True)

    def loadDataFromCsv(self):
        out = loadCsv()
        for key in out:
            for j in range(len(self.data_structure)):
                if j > len(out[key]):
                    out[key].append(0)
        return out

    def closeEvent(self, event):
        """ Reimplementation of the closeEvent
            to warn the user about potential dataloss """
        if self.warnModified() != 'Canceled':
            event.accept()
        else:
            event.ignore()

    def enableAddition(self):
        """ Enable the right addition group
            when addition value is different from 0.00 """
        if (self.rAddSpin.value() == 0) and (self.lAddSpin.value() == 0):
            self.addGroupBox.setDisabled(True)
        else:
            self.addGroupBox.setDisabled(False)

        value = self.sender().value()
        if self.rLinkCheckbox.isChecked() and self.rAddSpin.value() != self.lAddSpin.value():
            if self.sender() == self.rAddSpin:
                self.lAddSpin.setValue(value)
            elif self.sender() == self.lAddSpin:
                self.rAddSpin.setValue(value)
        self.modified()

    def linkChanged(self):
        """ Link the state of the two checkboxes """
        state = self.sender().isChecked()
        self.lLinkCheckbox.setChecked(state)
        self.rLinkCheckbox.setChecked(state)

    def r90Action(self):
        if self.r90Button.text() == "90°":
            self.rAxisSpin.setValue(90)
            self.r90Button.setText("180°")
        else:
            self.rAxisSpin.setValue(180)
            self.r90Button.setText("90°")

    def l90Action(self):
        if self.l90Button.text() == "90°":
            self.lAxisSpin.setValue(90)
            self.l90Button.setText("180°")
        else:
            self.lAxisSpin.setValue(180)
            self.l90Button.setText("90°")

    def rFineChanged(self):
        if self.rFineCheckbox.isChecked():
            self.rAxisSpin.setSingleStep(1)
            self.rAxisSpin.setMinimum(1)
        else:
            self.rAxisSpin.setMinimum(MIN_AXIS)
            self.rAxisSpin.setSingleStep(5)
            self.rAxisSpin.setValue(self.rAxisSpin.value()-self.rAxisSpin.value()%5)

    def lFineChanged(self):
        if self.lFineCheckbox.isChecked():
            self.lAxisSpin.setSingleStep(1)
            self.lAxisSpin.setMinimum(1)
        else:
            self.lAxisSpin.setMinimum(MIN_AXIS)
            self.lAxisSpin.setSingleStep(5)
            self.lAxisSpin.setValue(self.lAxisSpin.value()-self.lAxisSpin.value()%5)

    def copyRtoL(self):
        if (self.rFineCheckbox.isChecked()):
            self.lFineCheckbox.setChecked(True)
        if self.lSphereSpin.value() != self.rSphereSpin.value():
            self.lSphereSpin.setValue(self.rSphereSpin.value())
            self.lSphereSpin.setStyleSheet("background-color: lightgrey")
        if self.lCylSpin.value() != self.rCylSpin.value():
            self.lCylSpin.setValue(self.rCylSpin.value())
            self.lCylSpin.setStyleSheet("background-color: lightgrey")
        if self.lAxisSpin.value() != self.rAxisSpin.value():
            self.lAxisSpin.setValue(self.rAxisSpin.value())
            self.lAxisSpin.setStyleSheet("background-color: lightgrey")
        if self.lAddSpin.value() != self.rAddSpin.value():
            self.lAddSpin.setValue(self.rAddSpin.value())
            self.lAddSpin.setStyleSheet("background-color: lightgrey")

    def modified(self):
        """ Action when a value is modified """
        self.setStatus('Modified')
        self.sender().setStyleSheet("")
        self.modif = True

        self.rLabelCylPos.setVisible(self.rCylSpin.value() > 0)
        self.lLabelCylPos.setVisible(self.lCylSpin.value() > 0)

    def getFirstNewNumber(self):
        """ returns the next available number
            (starting with the current number) """
        n = 1
        while n in self.data:
            if self.data[n][12] == 0:
                break
            n += 1
        return n

    def new(self):
        """ set the number to the smallest available eyeglasses number """
        n = self.getFirstNewNumber()
        self.rSphereSpin.setFocus()
        self.rSphereSpin.selectAll()
        if n != self.current_num:
            self.eyeglassesNum.setValue(n)
            self.reset()
        else:
            self.warnModified()

    def getAddType(self):
        """ returns the code corresponding to the selected radio button
            0 means section is disabled
            1 means Progressive
            2 means Bifocal
        """
        if (self.rAddSpin.value() != 0) or (self.lAddSpin.value() != 0):
            if self.addRadioP.isChecked():
                return 1
            elif self.addRadioBF.isChecked():
                return 2
        else:
            return 0

    def setAddType(self, value):
        """ selects the radio button corresponding to the input code
            0 or 1 selects Progressive
            2 selects Bifocal
        """
        if value == 2:
            self.addRadioBF.setChecked(True)
        else:
            self.addRadioP.setChecked((True))

    def getSolar(self):
        """ returns the code corresponding to the selected radio button
            0 means normal lenses
            1 means solar lenses
        """
        return int(self.solarRadioYes.isChecked())

    def setSolar(self, value):
        """ selects the radio button corresponding to the input code
            0 selects normal lenses
            1 selects solar lenses
        """
        if value == 1:
            self.solarRadioYes.setChecked(True)
        else:
            self.solarRadioNo.setChecked(True)

    def getDateMvmt(self):
        return date.today()

    def setDateMvmt(self, value):
        pass

    def getFrame(self):
        """ returns the code corresponding to the selected radio button
            0 means adult frame
            1 means child frame
            2 means half-lenses
        """
        if self.childRadioYes.isChecked():
            return 1
        elif self.childRadioNo.isChecked():
            return 0
        else:
            return 2

    def setFrame(self, value):
        """ selects the radio button corresponding to the input code
            0 selects adult frame
            1 selects child frame
            2 selects half-lenses
        """
        if value == 1:
            self.childRadioYes.setChecked(True)
        elif value == 0:
            self.childRadioNo.setChecked(True)
        else:
            self.childRadioHalf.setChecked(True)

    def getComment(self):
        """ returns the comment formated to suitable codec """
        return self.commentEdit.toPlainText()

    def setComment(self, value):
        """ write the input value to the comment field """
        self.commentEdit.setPlainText(str(value))

    def convert(self):
        """ convert the data entered if cylinder is positive """
        if self.data_structure[2][2]() > 0:
            self.data_structure[1][3](
                    self.data_structure[1][2]() + self.data_structure[2][2]())
            self.data_structure[2][3](-self.data_structure[2][2]())
            self.data_structure[3][3]((self.data_structure[3][2]() + 90) % 180)

        if self.data_structure[6][2]() > 0:
            self.data_structure[5][3](
                    self.data_structure[5][2]() + self.data_structure[6][2]())
            self.data_structure[6][3](-self.data_structure[6][2]())
            self.data_structure[7][3]((self.data_structure[7][2]() + 90) % 180)

    def save(self):
        """ Save the modifications and write it to the CSV file """
        self.convert()

        item = [QtGui.QStandardItem(str(self.current_num))] \
               + [QtGui.QStandardItem( \
                   self.data_structure[i][1](self.data_structure[i][2]()) \
                   ) for i in range(len(self.data_structure))[1:]]

        # For each item, add the data value to allow correct sorting
        for i, item_data in enumerate(item):
            item_data.setData(self.data_structure[i][2](), SORT_ROLE)

        if self.data_structure[2][2]() == 0:
            item[3].setData(u'000°', QtCore.Qt.DisplayRole)
            item[3].setData(0, SORT_ROLE)

        if self.data_structure[6][2]() == 0:
            item[7].setData(u'000°', QtCore.Qt.DisplayRole)
            item[7].setData(0, SORT_ROLE)

        item[0].setBackground(QtGui.QColor(UNIT_COLORS[int(str(self.current_num)[-1])]))

        item[1].setBackground(RIGHT_COLOR)
        item[2].setBackground(RIGHT_COLOR)
        item[3].setBackground(RIGHT_COLOR)
        item[4].setBackground(RIGHT_COLOR)

        item[5].setBackground(LEFT_COLOR)
        item[6].setBackground(LEFT_COLOR)
        item[7].setBackground(LEFT_COLOR)
        item[8].setBackground(LEFT_COLOR)

        item[13].setBackground(STATUS_COLOR[int(item[13].data())])

        box_label = getBoxNum(int(self.current_num))
        box_item = QtGui.QStandardItem(box_label)
        box_item.setData(box_label)
        item.insert(1, box_item)

        # Alter the existing line or create a new one
        rows = self.model.findItems(str(self.current_num))
        if len(rows) == 0:
            self.model.appendRow(item)
        elif len(rows) == 1:
            row = rows[0].row()
            for column, elem in enumerate(item):
                self.model.setItem(row, column, elem)
        else:
            print("Error when trying to update the TableView")
        # if self.modif:
        self.data = self.loadDataFromCsv()
        self.data[self.current_num] = [self.data_structure[i][2]()
                        for i in range(len(self.data_structure))[1:]]
        if self.data[self.current_num][1] == 0:
            self.data[self.current_num][2] = 0
        if self.data[self.current_num][5] == 0:
            self.data[self.current_num][6] = 0
        writeCsv(self.data)
        autoSave(self.data)
        self.setStatus('Saved')
        self.modif = False

        self.scrollTo(self.current_num)

        self.new()

        self.model.setSortRole(SORT_ROLE)
        self.tableView.sortByColumn(0, QtCore.Qt.AscendingOrder)

    def delete(self):
        """ Delete given entry and write the modification to the CSV file """
        text = (u'Les lunettes numéro '+str(self.current_num)+' '
                u'vont être retirées de l\'inventaire.\n'
                u'\n'
                u'Souhaitez-vous continuer ?')
        question = QtWidgets.QMessageBox.warning(self,
            u'Supprimer les données ?',
            text,
            QtWidgets.QMessageBox.Yes,
            QtWidgets.QMessageBox.No)

        if question == QtWidgets.QMessageBox.Yes:
            if self.current_num in self.data:
                rows = self.model.findItems(str(self.current_num))
                if len(rows) == 1:
                    self.model.takeRow(rows[0].row())
                else:
                    print("Error when trying to find the right row to delete")
                self.data = self.loadDataFromCsv()
                self.data.pop(self.current_num)
                writeCsv(self.data)
                self.setStatus('New')
                self.modif = False
                self.reset()

    def loadData(self):
        """ Load data from the self.data variable
            and display it in the form """
        self.rLinkCheckbox.setChecked(False)
        if self.current_num in self.data:
            self.rFineCheckbox.setChecked(self.data[self.current_num][3] % 5 != 0)
            self.lFineCheckbox.setChecked(self.data[self.current_num][7] % 5 != 0)
            for i, element in enumerate(self.data_structure[1:]):
                element[3](self.data[self.current_num][i])
            self.modif = False
            self.setStatus('Saved')
        else:
            self.reset()

        if self.rAddSpin.value() == self.lAddSpin.value():
            self.rLinkCheckbox.setChecked(True)

        self.boxNum.setText(getBoxNum(self.current_num))

        self.scrollTo(self.current_num)

    def scrollTo(self, number):
        """ scrolls and selects the line corresponding to the input number """
        index_to_scroll = number
        while index_to_scroll not in self.data:
            index_to_scroll -= 1
            if index_to_scroll <= 0:
                self.tableView.scrollToBottom()
                break
        items = self.model.findItems(str(index_to_scroll))
        if len(items) != 0:
            self.tableView.scrollTo(items[0].index())
            self.tableView.selectRow(items[0].index().row())
        else:
            self.tableView.scrollToBottom()

    def selectLine(self):
        """ change the eyeglasses number to the selected line in the table """
        row = self.tableView.selectionModel().currentIndex().row()
        self.eyeglassesNum.setValue(int(self.model.item(row, 0).text()))

    def saveColumnsState(self):
        """ Save width of columns to reload next startup """
        saved_columns_width.setValue("inventory columns", self.tableView.horizontalHeader().saveState())

    def loadColumnsState(self):
        """ Reload columns width from saved value """
        columns_state = saved_columns_width.value("inventory columns", "Not saved")
        if columns_state == "Not saved":
            return
        self.tableView.horizontalHeader().restoreState(columns_state)

    def reset(self):
        """ Reset the values in the form to the default values """
        self.rFineCheckbox.setChecked(False)
        self.lFineCheckbox.setChecked(False)
        for element in self.data_structure[1:]:
            element[3](element[4])
        self.modif = False

        self.setStatus('New')

    def setStatus(self, value):
        """ Display the current status (Saved/Modified/New)
            with different colors """
        if value == 'Saved':
            self.status.setText(u'Enregistré')
            palette = QtGui.QPalette()
            palette.setColor(QtGui.QPalette.Foreground, QtCore.Qt.green)
            self.status.setPalette(palette)
        elif value == 'Modified':
            self.status.setText(u'Modifié')
            palette = QtGui.QPalette()
            palette.setColor(QtGui.QPalette.Foreground, QtCore.Qt.red)
            self.status.setPalette(palette)
        elif value == 'New':
            self.status.setText(u'Nouveau')
            palette = QtGui.QPalette()
            palette.setColor(QtGui.QPalette.Foreground, QtCore.Qt.blue)
            self.status.setPalette(palette)

    def eyeglassesNumModified(self):
        """ Event when num is modified """
        self.eyeglassesNum.setStyleSheet("QSpinBox { background-color: "+QtGui.QColor(UNIT_COLORS[int(str(self.eyeglassesNum.value())[-1])]).name()+"; }")
        self.warnModified()

    def warnModified(self):
        """ Warn the user that unsaved data will be lost if unsaved """
        if not(self.eyeglassesNum.noWarn):
            if self.modif:
                text = (u'Les lunettes '+str(self.current_num)+' '
                        u'n\'ont pas été enregistrées.\n'
                        u'Abandonner les changements ?')
                question = QtWidgets.QMessageBox.warning(self,
                    u'Lunettes Modifiées !',
                    text,
                    QtWidgets.QMessageBox.Yes,
                    QtWidgets.QMessageBox.No)

                if question == QtWidgets.QMessageBox.Yes:
                    self.current_num = self.eyeglassesNum.value()
                    self.loadData()
                    return 'Discarded'
                else:
                    if self.sender() != None:
                        self.eyeglassesNum.noWarn = True
                    self.eyeglassesNum.setValue(self.current_num)
                    return 'Canceled'
            else:
                self.current_num = self.eyeglassesNum.value()
                self.loadData()
        else:
            self.eyeglassesNum.noWarn = False
            return 'Saved'

    def restoreAndShow(self):
        """ Restore data from file and displays it in the table """
        restored_data = restore()
        if restored_data != []:
            self.data = restored_data
            self.displayData(self.data)

    def displayData(self, data):
        """ Displays data in the table """
        self.model.clear()
        labels = [self.data_structure[i][0] for i in range(len(self.data_structure))]
        labels.insert(1, "Boite")
        labels.insert(2, labels.pop(14))
        self.model.setHorizontalHeaderLabels(labels)

        for key, values in data.items():
            row = [key] + values

            items = [QtGui.QStandardItem(self.data_structure[i][1](row[i]))
                         for i in range(len(self.data_structure))]

            # For each item, add the data value to allow correct sorting
            for i, item in enumerate(items):
                item.setData(getData(row[i]), SORT_ROLE)
            items[0].setBackground(QtGui.QColor(UNIT_COLORS[int(str(row[0])[-1])]))

            items[1].setBackground(RIGHT_COLOR)
            items[2].setBackground(RIGHT_COLOR)
            items[3].setBackground(RIGHT_COLOR)
            items[4].setBackground(RIGHT_COLOR)

            items[5].setBackground(LEFT_COLOR)
            items[6].setBackground(LEFT_COLOR)
            items[7].setBackground(LEFT_COLOR)
            items[8].setBackground(LEFT_COLOR)

            items[13].setBackground(STATUS_COLOR[int(row[13])])

            box_label = getBoxNum(int(row[0]))
            box_item = QtGui.QStandardItem(box_label)
            box_item.setData(box_label)
            items.insert(1, box_item)
            items.insert(2, items.pop(14))

            self.model.appendRow(items)

        self.model.setSortRole(SORT_ROLE)

        # self.tableView.resizeColumnsToContents()
        self.tableView.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Interactive)
        self.tableView.horizontalHeader().setStretchLastSection(False)

        self.loadColumnsState()

        self.tableView.setModel(self.model)
        self.tableView.sortByColumn(0, QtCore.Qt.AscendingOrder)

# if __name__ == '__main__':
#     import sys
#     app = QtWidgets.QApplication(sys.argv)
#     mainWin = MainWindow()
#     mainWin.searchWindowButton.hide()
#     mainWin.show()
#     sys.exit(app.exec_())


# -*- coding: utf-8 -*-
import datetime
import json
import logging
import werkzeug
from werkzeug.exceptions import BadRequest
from odoo import SUPERUSER_ID, api, http, _
from odoo import registry as registry_get
from odoo.addons.auth_oauth.controllers.main import OAuthController as Controller
from odoo.addons.web.controllers.main import (login_and_redirect, ensure_db)
from odoo.exceptions import AccessDenied
from odoo.http import request

_logger = logging.getLogger(__name__)

class OAuthController(Controller):

    @http.route('/web/login/sms', type='http', auth='public', website=True, sitemap=False)
    def web_odoo_sms_login(self, **kw):
        """
        短信登录入口,点击后返回到验证码界面
        :param kw:
        :return:
        """
        if request.session.uid:
            request.session.uid = False
        if request.session.login:
            request.session.login = False
        data = {'code_maxlength': 6}   # 验证码默认最大长度
        return request.render('sms_base.sms_login_signup', data)

    @http.route('/web/sms/send/code', type='http', auth="public", website=True, sitemap=False)
    def web_sms_send_code(self, **kw):
        """
        发送验证码
        :param kw:
        :return:
        """
        values = request.params.copy()
        user_phone = values.get('user_phone')
        if not user_phone:
            return json.dumps({'state': False, 'msg': "手机号码不能为空！"})
        _logger.info("手机号码:{}正在尝试发送验证码".format(user_phone))
        # 获取服务商
        partners = request.env['sms.partner'].sudo().get_partners_priority()
        if not partners:
            return json.dumps({"state": False, 'msg': "系统未找到可用的短信服务商，请联系管理员维护！"})
        # 验证员工是否有此手机号
        domain = [('user_phone', '=', user_phone)]
        user = request.env['res.users'].sudo().search(domain, limit=1)
        if not user:
            # 判断系统是否允许自动注册
            sms_auto_login = request.env['ir.config_parameter'].sudo().get_param('sms_base.default_sms_auto_login')
            if not sms_auto_login:
                return json.dumps({'state': False, 'msg': "抱歉，您的手机号未注册，请联系管理员完善信息！"})
            # 创建用户
            user = request.env['res.users'].sudo().create_user_by_sms_login(user_phone)
            # 发送通知短信
            partners.sudo().send_registration_message(user, user_phone)
        # 使用服务商的发送验证码函数
        result = partners.sudo().send_message_code(user, user_phone, 'login')
        if result.get('state'):
            return json.dumps({"state": True, 'msg': "验证码已发送，请注意查收短信！"})
        return json.dumps({"state": False, 'msg': result.get('msg')})

    @http.route('/web/sms/user/login', type='http', auth="public", website=True, sitemap=False)
    def web_sms_user_login(self, **kw):
        """
        验证登录验证码
        :param kw:
        :return:
        """
        values = request.params.copy()
        user_phone = values.get('phone')
        code = values.get('code')
        if not user_phone or not code:
            return json.dumps({'state': False, 'msg': "手机号和验证码不正确！"})
        # 检查验证码和登录手机有效性
        domain = [('phone', '=', user_phone), ('code', '=', code), ('state', '=', 'normal')]
        records = request.env['sms.verification.record'].sudo().search(domain)
        if not records:
            return json.dumps({'state': False, 'msg': "验证码不存在,请重新输入！"})
        # 检查时效
        for record in records:
            if datetime.datetime.now() > record.end_time:
                record.sudo().write({'state': 'invalid'})
                return json.dumps({'state': False, 'msg': "验证码已失效！请重新获取!"})
        records.sudo().write({'state': 'invalid'})
        # 验证通过执行登录操作
        return self.do_post_login(user_phone)

    def do_post_login(self, user_phone):
        """
        执行登录
        :param user_phone:
        :return:
        """
        if request.session.uid:
            request.session.uid = False
        if request.session.login:
            request.session.login = False
        ensure_db()
        dbname = request.session.db
        if not http.db_filter([dbname]):
            return BadRequest()
        registry = registry_get(dbname)
        with registry.cursor() as cr:
            try:
                env = api.Environment(cr, SUPERUSER_ID, {})
                credentials = env['res.users'].sudo().auth_oauth_sms('sms', user_phone)
                cr.commit()
                url = '/web'
                resp = login_and_redirect(*credentials, redirect_url=url)
                if werkzeug.urls.url_parse(resp.location).path == '/web' and not request.env.user.has_group('base.group_user'):
                    resp.location = '/'
                return json.dumps({'state': True, 'msg': "登录成功"})
            except AttributeError:
                return json.dumps({'state': False, 'msg': "未在数据库'%s'上安装auth_signup：oauth注册已取消" % (dbname)})
            except AccessDenied:
                _logger.info('>>>SMS-OAuth2: 访问被拒绝，在存在有效会话的情况下重定向到主页，而未设置Cookie')
                url = "/web/login?oauth_error=3"
                redirect = werkzeug.utils.redirect(url, 303)
                redirect.autocorrect_location_header = False
                return redirect
            except Exception as e:
                return json.dumps({'state': False, 'msg': "OAuth2: %s" % str(e)})


from flask import Flask, jsonify, request
from classifier import classify
from twitter_handler import get_recent_tweets

app = Flask(__name__)

def get_current_mood_percent():
    """Gets the current mood of the internet as a percent"""
    tweets = get_recent_tweets()
    positive_tweets = 0
    negative_tweets = 0
    for t in tweets:
        if classify(t) == 'Positive':
            positive_tweets += 1
        else:
            negative_tweets += 1
    return negative_tweets / ( negative_tweets + positive_tweets )

def convert_to_degress(percent):
    """Converts percent to degrees negativity"""
    return int(percent * 180)

@app.route('/degrees/')
def degrees():
    """View for degrees"""
    return str(convert_to_degress(get_current_mood_percent()))

@app.route('/percent/')
def percent():
    """View for percent"""
    return str(int(get_current_mood_percent() * 100))

@app.route('/')
def index():
    """View for index"""
    return jsonify({'status': 'ok'})

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0')


from .AbstractExporter import AbstractExporter
from ...common.Properties import Properties

from com.sun.star.io import IOException

# An exporter which is configured with a filter name, and
# uses the specified filter to export documents.
class FilterExporter(AbstractExporter):

    filterName = ""
    props = Properties()

    def __init__(self, exporter_):
        super(FilterExporter, self).__init__(exporter_)
        self.filterName = self.getArgument("Filter", exporter_)

    # (non-Javadoc)
    # @see com.sun.star.wizards.web.export.Exporter#export(java.lang.Object, java.io.File, com.sun.star.wizards.web.data.CGSettings, com.sun.star.lang.XMultiServiceFactory)
    def export(self, source, target, xmsf, task):
        result = True
        document = None

        try:
            document = self.openDocument(source, xmsf)
            task.advance(True)
            self.storeToURL1(document, target, self.filterName, self.props.getProperties1())
            task.advance(True)
        except IOException as iox:
            iox.printStackTrace(System.err)
            result = False
            raise iox
        finally:
            self.closeDocument(document, xmsf)
            self.calcFileSize(source, target, xmsf)
            task.advance(True);
        return result


import numpy as np
import cv2

def sketch(image):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gblur = cv2.GaussianBlur(gray, (5,5), 0)
    cedges = cv2.Canny(gblur, 10, 70)
    ret, mask = cv2.threshold(cedges, 70, 255, cv2.THRESH_BINARY_INV)
    return mask

cap = cv2.VideoCapture('data/video/DJI_0001.mp4')

while(cap.isOpened()):
    ret, frame = cap.read()

    cv2.imshow('frame',sketch(frame))
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()


from datetime import date

from oslo_log import log as logging

from trove.common import stream_codecs
from trove.common import utils
from trove.guestagent.common import operating_system
from trove.guestagent.module.drivers import module_driver

LOG = logging.getLogger(__name__)

NR_ADD_LICENSE_CMD = ['nrsysmond-config', '--set', 'license_key=%s']
NR_SRV_CONTROL_CMD = ['/etc/init.d/newrelic-sysmond']

class NewRelicLicenseDriver(module_driver.ModuleDriver):
    """Module to set up the license for the NewRelic service."""

    def get_description(self):
        return "New Relic License Module Driver"

    def get_updated(self):
        return date(2016, 4, 12)

    @module_driver.output(
        log_message='Installing New Relic license key',
        success_message='New Relic license key installed',
        fail_message='New Relic license key not installed')
    def apply(self, name, datastore, ds_version, data_file, admin_module):
        license_key = None
        data = operating_system.read_file(
            data_file, codec=stream_codecs.KeyValueCodec())
        for key, value in data.items():
            if 'license_key' == key.lower():
                license_key = value
                break
        if license_key:
            self._add_license_key(license_key)
            self._server_control('start')
        else:
            return False, "'license_key' not found in contents file"

    def _add_license_key(self, license_key):
        try:
            exec_args = {'timeout': 10,
                         'run_as_root': True,
                         'root_helper': 'sudo'}
            cmd = list(NR_ADD_LICENSE_CMD)
            cmd[-1] = cmd[-1] % license_key
            utils.execute_with_timeout(*cmd, **exec_args)
        except Exception:
            LOG.exception("Could not install license key '%s'",
                          license_key)
            raise

    def _server_control(self, command):
        try:
            exec_args = {'timeout': 10,
                         'run_as_root': True,
                         'root_helper': 'sudo'}
            cmd = list(NR_SRV_CONTROL_CMD)
            cmd.append(command)
            utils.execute_with_timeout(*cmd, **exec_args)
        except Exception:
            LOG.exception("Could not %s New Relic server", command)
            raise

    @module_driver.output(
        log_message='Removing New Relic license key',
        success_message='New Relic license key removed',
        fail_message='New Relic license key not removed')
    def remove(self, name, datastore, ds_version, data_file):
        self._add_license_key("bad_key_that_is_exactly_40_characters_xx")
        self._server_control('stop')


import math
import os
import time

import ffmpeg

def concatFrames():
    os.system("ffmpeg -f concat -safe 0 -i list.txt -c:v 500k -c copy output.webm")
    os.system("ffmpeg -i output.webm -i input.webm -c copy -map 0:v:0 -map 1:a:0 -shortest final.webm")

    os.system('del frames\\*')
    os.system('del frame\\*')
    os.system('del list.txt')

def exportFrames():
    os.system("cd D:\\PyCharm Projects\\Webm trolling")
    frames = os.popen("ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames input.webm").read()
    frameCount = frames.split('\n')[1].split('=')[1]

    probe = ffmpeg.probe('input.webm')
    video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
    width = int(video_stream['width'])
    height = int(video_stream['height'])
    (
        ffmpeg
            .input('input.webm')
            .output('frames\\frames%d.jpg', vframes=frameCount)
            .run()
    )
    amplitude = 0.01
    (
        ffmpeg
            .input('frames\\frames1.jpg')
            .output('frame\\frame1.webm', vcodec='vp8', crf=10)
            .run()
    )
    for x in range(2,int(frameCount)):
        sin = abs(round(math.sin(x * amplitude),2))
        cos = abs(round(math.cos(x * amplitude),2))
        (
            ffmpeg
            .input('frames\\frames{}.jpg'.format(x))
            .filter('scale',sin*width,cos*height)
            .output('frame\\frame{}.webm'.format(x), vcodec='vp8', crf=10)
            .run()
        )
        amplitude += 0.03

    f = open("list.txt",'a')
    for a in range(1,int(frameCount)):
        f.write("file 'frame\\frame{}.webm' \n".format(a))
    f.close()

    concatFrames()

exportFrames()


"""Original minimal designs based around nesting similar shapes."""

from itertools import cycle
import os
from random import random, choice

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

from numpy import pi, sin, cos

# ---------- Design and output parameters ------------
# ... for container shape, outer to all the other shapes:
outer_size = 0.5
outer_centre = (0.5, 0.5)

# ... for the design of inner shapes:
size_ratio_of_next_inner_shape = 0.09  # 0.07 used for stage one
# For the alternative designs as shown in the README gallery, instead set 0.03
number_inner_shapes = 500

# For stage two i.e. 'edge descend', the main designs
padding_factor_for_edge_descend = 0.35

# ... for the (cycling) colours of the shapes:
background_colour = "black"
# For the alternative designs, instead set the above to "white"
# Choose a set of harmonious 'mod' like colours that are bold but not garish
stage_one_colours = [
    "midnightblue",
    "lavender",
    "crimson",
    "dodgerblue",
    "indianred",
]
# For the alternative designs in the README gallery, instead set the above to:
# ["black", "peru", "darkslategrey", "goldenrod", "teal", "wheat"]
stage_one_colour_selector = cycle(stage_one_colours)
stage_two_colours = [
    "darkslategrey",
    "khaki",
    "lightseagreen",
    "azure",
    "indigo",
    "powderblue",
]
stage_two_colour_selector = cycle(stage_two_colours)  # flake8 error skip?

# ... directories to save designs into:
first_level_dir = "img"  # second_level_dirs live separately under this dir
second_level_dirs = [
    "without-random-edge-alignment",
    "with-random-edge-alignment",
]
# For the alternative designs, add "-alt" to the end of both dirs above

# List all designs to create...
number_sides_to_plot_compound = [1, 4, 3, 5]  # 3 <-> 4 for overall symmetry
number_sides_to_plot_as_single = number_sides_to_plot_compound + [7, 12]

# ... with the (empirically determined) mapping to zoom in to achieve a
# close-up on the container shape without any of the background showing.
# Note that the higher the value, the less zoomed in, up to 0.5 which results
# in no zoom-in relative to the standard designs (with 'closeup=False')
zoom_in_factors = {
    1: 0.27,
    3: 0.18,  # note this is also shifted down by ~0.1 later to re-centralise
    4: 0.215,
    5: 0.24,
    7: 0.25,
    12: 0.27,
}

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

def change_centre_variant_2(centre, index):
    """Variant of 'change_centre' function to generate '-2' designs.

    Note the 'index' should be added as an input to change_centre in
    this case to reflect the signature here.

    """
    identical_coor_val = centre[0] + 0.0008 * index
    return (identical_coor_val, identical_coor_val)

def change_centre_variant_3(centre):
    """Variant of 'change_centre' function to generate '-3' designs."""
    return (centre[0] * 0.99, centre[1] * 0.96)

def change_centre(centre, sides, new_size, old_size):
    """Used to generate the main, 'edge descend', designs.

    Note 'new_size' and 'old_size' should be added as an input to
    'change_centre' in this case to reflect the signature here.

    """
    change_in_size = new_size - old_size
    move_centre_by = change_in_size * (1 - padding_factor_for_edge_descend)
    if sides == 1:  # For a circle, the *_size refers to the radii
        # Shift next circle in a random direction by the radius change
        shift_angle_rads = 2 * pi * random()
    else:  # is a RegularPolygon where *_size is distance centre-vertex
        # Possible shift directions are discretised with the number of sides:
        possible_shift_rads = [(2 * pi * N) / sides for N in range(sides)]
        shift_angle_rads = choice(possible_shift_rads)

    new_centre = (
        centre[0] + move_centre_by * cos(shift_angle_rads),
        centre[1] + move_centre_by * sin(shift_angle_rads),
    )
    return new_centre

def make_shape(
    centre, size, sides=1, colour_selector=stage_one_colour_selector
):
    """Make and return a patch defining a positioned polygon to plot."""
    # Could take the (number of) sides -> infinity for sides of a regular
    # polygon to approximate a circle, but better to use actual circular
    # matplotlib patch as a special case:
    if sides == 1:
        size *= 0.9  # make slightly smaller so relatively in size w/ polygons
        return mpatches.Circle(centre, size, facecolor=next(colour_selector))
    elif sides == 2:
        raise ValueError(
            "No two-sided regular polygon, choose another 'sides' value!"
        )
    else:  # regular polygon patch of specified number of sides
        if sides == 3:
            # Make size larger else triangle looks a bit small in relation and
            # also shift downwards to fit larger shape in the axes boundaries
            size *= 1.1
            centre_x, centre_y = centre
            centre = centre_x, centre_y - 0.1
        return mpatches.RegularPolygon(
            centre, sides, size, facecolor=next(colour_selector)
        )

def make_design_patches(sides=1, reposition=False):
    """Return the list of positioned patches defining the design."""
    patch_layers = []

    use_centre = outer_centre  # fixed centre if reposition=False

    # zorder managed naturally via plotting largest first, if did in inverse
    # order would need to use zorder to stop larger shapes covering smaller.
    new_size = outer_size
    for index in range(number_inner_shapes):
        if reposition:
            old_size = new_size
        new_size *= 1 - size_ratio_of_next_inner_shape

        # Process new centre for edge descend:
        if reposition:
            use_centre = change_centre(use_centre, sides, new_size, old_size)

        new_patch = make_shape(use_centre, new_size, sides)
        patch_layers.append(new_patch)

    return patch_layers

def create_design(axes, stage_two, sides=1):
    """Create a design by attaching all underlying patches to axes."""
    for p in make_design_patches(sides=sides, reposition=stage_two):
        axes.add_artist(p)

def plot_and_save(
    use_number_of_sides=1, single=True, closeup=False, stage_two=True
):
    """Plot and save a single or compound 'Edge Descend' design."""
    fig = plt.figure(figsize=(5, 5), facecolor=background_colour)

    if single:
        ax = fig.add_subplot(111, aspect="equal")
        create_design(ax, sides=use_number_of_sides, stage_two=stage_two)
    else:
        # Vary subplot_index prefix appropriate to len(use_number_of_sides)
        for i, set_sides in enumerate(use_number_of_sides):
            subplot_index = 2
            ax = fig.add_subplot(subplot_index, subplot_index, i + 1)
            ax.set_facecolor(background_colour)
            ax.set_axis_off()
            create_design(ax, sides=set_sides, stage_two=stage_two)

    plt.axis("off")

    if single:
        name_prefix = f"single_design_with_{use_number_of_sides}_sides"
    else:
        name_prefix = "compound_design"

    # Create dirs to store the output designs if they do not exist already
    os.makedirs(f"{first_level_dir}", exist_ok=True)
    for directory in second_level_dirs:
        os.makedirs(f"{first_level_dir}/{directory}", exist_ok=True)
        os.makedirs(f"{first_level_dir}/{directory}-closeups", exist_ok=True)

    # Now can plot, save and show the final single or compound design
    use_subdir = second_level_dirs[int(stage_two)]
    directory = f"{first_level_dir}/{use_subdir}"

    if closeup and single:
        # Zoom in on single plots if requested
        zoom_in_factor = zoom_in_factors[use_number_of_sides]
        zoom_in_vals = (0.5 - zoom_in_factor, 0.5 + zoom_in_factor)  # min, max
        if use_number_of_sides == 3:
            shifted_down_zoom_in_vals = (
                0.405 - zoom_in_factor,
                0.405 + zoom_in_factor,
            )
            zoom_in_vals = zoom_in_vals + shifted_down_zoom_in_vals
        else:  # x and y axes min and max are the same
            zoom_in_vals += zoom_in_vals
        ax.axis(zoom_in_vals)

        # For the alternative designs, add "_alt" to the end of the filename
        # before the extension to save to the dirs as in the savde repo state:
        plt.savefig(
            f"{directory}-closeups/{name_prefix}_closeup.png",
            format="png",
            dpi=1000,
            bbox_inches="tight",
        )
        plt.show()
    else:
        #  For the alternative designs, add "_alt" (see above comment)
        plt.savefig(
            f"{directory}/{name_prefix}.png",
            format="png",
            dpi=1000,
        )
        plt.show()

# Create and plot the designs from...
# ...without zooming in:
for number_sides in number_sides_to_plot_as_single:
    plot_and_save(use_number_of_sides=number_sides)
plot_and_save(single=False, use_number_of_sides=number_sides_to_plot_compound)
# ... zooming in to create a close-up where the shapes fill the entire canvas:
for number_sides in number_sides_to_plot_as_single:
    plot_and_save(use_number_of_sides=number_sides, closeup=True)

# TODO: fix triangular 'edge descend' case, where some inner triangles get
# positioned slightly outside the previous larger triangle, but should be
# fully contained. This doesn't seem to be related to the overall shifting
# since it seems to happen even when the shifting is manually disabled...


from uvicorn import run
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from src.api.recommender_api import router as recommender_router
from src.config import \
    ALLOWED_ORIGINS, ALLOWED_HEADERS, ALLOWED_METHODS, ALLOW_CREDENTIALS, DOCS_URL

app = FastAPI(title='AlkohoLove-recommender', docs_url=DOCS_URL, redoc_url=None)

app.add_middleware(
    CORSMiddleware,
    allow_origins=ALLOWED_ORIGINS,
    allow_credentials=ALLOW_CREDENTIALS,
    allow_methods=ALLOWED_METHODS,
    allow_headers=ALLOWED_HEADERS,
)

app.include_router(recommender_router)

if __name__ == '__main__':
    run('main:app', host='127.0.0.1', port=8081, reload=True)


import torch
import torch.nn as nn
import torch.nn.functional as F

class AdversarialLoss(nn.Module):
    def __init__(self, loss_type='hinge', target_real_label=1., target_fake_label=0.):
        super(AdversarialLoss, self).__init__()
        self.loss_type = loss_type
        self.register_buffer('real_label', torch.tensor(target_real_label))
        self.register_buffer('fake_label', torch.tensor(target_fake_label))

        if loss_type == 'nsgan':
            self.criterion = nn.BCELoss()
        elif loss_type == 'lsgan':
            self.criterion = nn.MSELoss()
        elif loss_type == 'hinge':
            self.criterion = nn.ReLU()

    def __call__(self, outputs, is_real: bool, is_discriminator: bool):
        if self.loss_type == 'hinge':
            if is_discriminator:
                if is_real:
                    outputs = -outputs
                return self.criterion(1 + outputs).mean()
            else:
                return (-outputs).mean()
        else:
            labels = (self.real_label if is_real else self.fake_label).expand_as(outputs)
            loss = self.criterion(outputs, labels)
            return loss

class AudioVisualAttentionLoss(nn.Module):
    def __init__(self, avnet):
        super(AudioVisualAttentionLoss, self).__init__()
        for param in avnet.parameters():
            param.requires_grad = False
        self.avnet = avnet
        self.mse_loss = nn.MSELoss()

    def forward(self, image_pred, image_gt, audio):
        av_attmap_pred = self.avnet(image_pred, audio)['av_attmap']
        av_attmap_gt = self.avnet(image_gt, audio)['av_attmap']
        return self.mse_loss(av_attmap_pred, av_attmap_gt)

class L2ContrastiveLoss(nn.Module):
    def __init__(self, margin=1.0):
        super(L2ContrastiveLoss, self).__init__()
        self.margin = margin

    def l2_similarity(self, feature1, feature2):
        feature = feature1.expand(feature1.size(0), feature1.size(0), feature1.size(1)).transpose(0, 1)
        sim_scores = torch.norm(feature - feature2, p=2, dim=2)
        return sim_scores

    def forward(self, feature1, feature2):
        sim_scores = self.l2_similarity(feature1, feature2)
        diagonal_dist = sim_scores.diag()
        cost_s = (self.margin - sim_scores).clamp(min=0)

        # clear diagonals
        mask = torch.eye(sim_scores.size(0)) > .5
        I = mask
        if torch.cuda.is_available():
            I = I.cuda()
        cost_s = cost_s.masked_fill_(I, 0)

        loss = (torch.sum(cost_s ** 2) + torch.sum(diagonal_dist ** 2)) / (2 * feature1.size(0))
        return loss


import os
import datetime as dt
import time
import copy
import urllib.request, json
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import george
from kernel import kernel
from cs import cs_to_stdev, stdev_to_cs

from pandas.io.json import json_normalize

def get_data(url=os.getenv("HISTORY_URI")):
    with urllib.request.urlopen(url) as res:
        data = json.loads(res.read().decode())

    return data

data = get_data()

out = []

for station in data:
    x, y, cslist, sigma = [], [], [], []

    for pt in station['history']:
        tm = (pd.to_datetime(pt[0]) - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s")
        cs = pt[1]
        if cs < 10 and cs != -1:
            continue

        sd = cs_to_stdev(cs, adj100=True)
        fof2, mufd, hmf2 = pt[2:5]

        x.append(tm / 86400.)
        y.append(np.log(mufd))
        cslist.append(cs)
        sigma.append(sd + 1e-3)

    if len(x) < 40:
        continue

    x = np.array(x)
    y = np.array(y)
    ymean = np.mean(y)
    y -= ymean
    cslist = np.array(cslist)
    sigma = np.array(sigma)

    x_train, x_test, y_train, y_test, cs_train, cs_test, sigma_train, sigma_test = train_test_split(
            x, y, cslist, sigma, test_size=0.25)

    gp = george.GP(kernel)
    gp.compute(x_train, sigma_train)

    pred, sd = gp.predict(y_train, x_test, return_var=True)
    sd = sd**0.5

    for i in range(len(pred)):
        print("%d\t%d\t%d\t%f\t%f\t%f" % (
            station['id'],
            x_test[i],
            cs_test[i],
            pred[i],
            y_test[i],
            sd[i],
            )
        )


from collections import deque

food_supplies = [int(x) for x in input().split(", ")]
daily_stamina = deque([int(x) for x in input().split(", ")])
peaks = {80: "Vihren",
         90: "Kutelo",
         100: "Banski Suhodol",
         60: "Polezhan",
         70: "Kamenitza"}
day = 1
peak_values = [80, 90, 100, 60, 70]
conquered_peaks = []
while food_supplies and daily_stamina and peak_values and day <= 7:
    stamina = daily_stamina.popleft()
    food = food_supplies.pop()
    result = stamina + food
    current_peak = peak_values[0]

    if result >= current_peak:
        conquered_peaks.append(peaks[current_peak])
        peak_values.remove(current_peak)
    day += 1
if peak_values:
    print("Alex failed! He has to organize his journey better next time -> @PIRINWINS")
else:
    print("Alex did it! He climbed all top five Pirin peaks in one week -> @FIVEinAWEEK")
if conquered_peaks:
    print("Conquered peaks:")
    for el in conquered_peaks:
        print(el)

solutions of exams - advanced/climb the peaks.py


import numpy as np
import cv2
import imutils
from imutils.video import FPS
from imutils.video import WebcamVideoStream
from imutils.video import FileVideoStream
from tracking_lanes.tracking_lanes import drawing_lane

video_path = r'C:\Users\DucTRung\Documents\data_set\tracking_lane\ivcam_60_fps.avi'

# using read() from VideoCapture
cap = cv2.VideoCapture(1)
print("using VideoCapture")
fps = FPS().start()

while (cap.isOpened()):
    ret, frame = cap.read()
    if ret is True:

        copy_image = np.copy(frame)
        lane_image, _ = drawing_lane(frame, copy_image)

        cv2.imshow('VideoCapture', lane_image)

        if cv2.waitKey(1) & 0xFF == ord('x'):
            break

        fps.update()

    else:
        break

fps.stop()
print('Time:{:.2f}'.format(fps.elapsed()))
print('FPS:{:.2f}'.format(fps.fps()))
print('Number of frames:{:.2f}'.format(fps.fps()*fps.elapsed()))

cap.release()
cv2.destroyAllWindows()

# using WebcamVideoStream
wvs = WebcamVideoStream(src= 1).start()
print("\nusing WebcamVideoStream")
fps = FPS().start()

while True:
    frame = wvs.read()
    if frame is not None:

        copy_image = np.copy(frame)
        lane_image, _ = drawing_lane(frame, copy_image)

        cv2.imshow('WebcamVideoStream', lane_image)

        if cv2.waitKey(1) & 0xFF == ord('x'):
            break

        fps.update()

    else:
        break

fps.stop()
print('Time:{:.2f}'.format(fps.elapsed()))
print('FPS:{:.2f}'.format(fps.fps()))
print('Number of frames:{:.2f}'.format(fps.fps()*fps.elapsed()))

wvs.stop()
cv2.destroyAllWindows()

# using FileVideoStream
fvs = FileVideoStream(1).start()
print('\nusing FileVideoStream')
fps = FPS().start()

while True:
    frame = fvs.read()
    if frame is not None:

        copy_image = np.copy(frame)
        lane_image, _ = drawing_lane(frame, copy_image)

        cv2.imshow('FileVideoStream', lane_image)

        if cv2.waitKey(1) & 0xFF == ord('x'):
            break

        fps.update()

    else:
        break

fps.stop()
print('Time:{:.2f}'.format(fps.elapsed()))
print('FPS:{:.2f}'.format(fps.fps()))
print('Number of frames:{:.2f}'.format(fps.fps()*fps.elapsed()))

fvs.stop()
cv2.destroyAllWindows()


import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """ Uma classe que administra os projeteis disparados pela espaçonave """

    def __init__(self, settings, screen, ship):
        super().__init__()
        self.screen = screen

        # Cria um retângulo para projétil em (0, 0) e, em seguida,
        # define a posição correta
        self.rect = pygame.Rect(
            0, 0, settings.bullet_width, settings.bullet_height
        )
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top

        # armazena a posição do projeto com um valor decimal
        self.y = float(self.rect.y)

        self.color = settings.bullet_color
        self.speed_factor = settings.bullet_speed_factor

    def update(self):
        """ Move o projétil para cima da tela """

        # Atualiza a posição decimal do projetil
        self.y -= self.speed_factor
        # Atualiza a posição do rect
        self.rect.y = self.y

    def draw_bullet(self):
        """ Desenha o projétil na tela """
        pygame.draw.rect(self.screen, self.color, self.rect)


import pymel.core as pm
import maya.OpenMaya

import os
import sys

import v1_core
import v1_shared

from v1_shared.decorators import csharp_error_catcher
from v1_shared.shared_utils import get_first_or_default, get_index_or_default, get_last_or_default

from metadata import meta_network_utils, meta_property_utils
from metadata.network_registry import Network_Registry, Property_Registry

from maya_utils import node_utils
from maya_utils import fbx_wrapper

def get_scene_name():
    '''
    Gets the name of the scene without full path
    '''
    return pm.sceneName().namebase

@csharp_error_catcher
def get_scene_name_csharp(c_object, event_args):
    '''
    get_scene_name(self, c_definition, event_args)
    Event method to get the name of the scene and pass it to C#

    Args:
        c_object (None): Unused
        event_args (StringEventArgs): Used to pass the scene name string back to C#
    '''
    event_args.Value = get_last_or_default(get_scene_name().split("_", 1))

def set_current_frame():
    '''
    Set the current frame to itself to force a scene update
    '''
    # Weird issue, turning off cycle check here prevents warnings of a meaningless cycle
    cycle_check = pm.cycleCheck(q=True, e=True)
    pm.cycleCheck(e=False)

    pm.cycleCheck(e=cycle_check)

def set_current_frame_to_timerange_start():
    pm.currentTime(get_scene_times()[0])

def get_scene_times():
    '''
    Gets all values from the scene time range

    Returns:
        (float, float, float float). The full time range from the scene
    '''
    min_time = pm.playbackOptions(q=True, min=True)
    ast_time = pm.playbackOptions(q=True, ast=True)
    max_time = pm.playbackOptions(q=True, max=True)
    aet_time = pm.playbackOptions(q=True, aet=True)

    return (min_time, ast_time, max_time, aet_time)

def set_scene_times(value_tuple):
    min_time, ast_time, max_time, aet_time = value_tuple
    pm.playbackOptions(min=min_time, ast=ast_time, max=max_time, aet=aet_time)

def get_scene_fps():
    '''
    Gets the FPS that the scene is set to

    Returns:
        float. FPS value
    '''
    time = pm.currentUnit(q=True, t=True)

    if(time == 'game'):
        return 15.0
    elif(time == 'film'):
        return 24.0
    elif(time == 'pal'):
        return 25.0
    elif(time == 'ntsc'):
        return 30.0
    elif(time == 'show'):
        return 48.0
    elif(time == 'palf'):
        return 50.0
    elif(time == 'ntscf'):
        return 60.0
    else:
        print(time)

def set_playback_rate(playback_string):
    '''
    Set the Animation playback rate
    '''
    pm.currentUnit(t=playback_string)

def get_playback_rate():
    '''
    Get the Animation playback rate
    '''
    return pm.currentUnit(q=True, t=True)

def remove_empty_namespaces():
    '''
    Remove all empty namespaces in the scene
    '''
    for ns in pm.system.Namespace(':').listNamespaces():
        if not ns.listNodes():
            ns.remove()

def clean_scene():
    '''
    Clean up a maya scene of orphaned and unused objects
    '''
    # Delete orphaned controller tags
    controller_tags = [x for x in pm.ls(type='controller') if x.exists() and not x.controllerObject.listConnections()]
    if controller_tags:
        pm.delete(controller_tags)
        v1_core.v1_logging.get_logger().info("Scene Cleanup Deleted Controller Tags : \n{0}".format(controller_tags))

    # Delete unused shader nodes
    temp_cube = None
    try:
        # Since Maya 2020 an error is thrown when running Delete Unused Nodes if the StandardSurface default shader is unassigned
        # So we create a temporary object, assign the default StandardSurface material to it, then delete Unused and delete the sphere
        temp_cube = pm.polyCube(name="TEMP_StandardSurface_Assignment")[0]
        temp_standard_shader = pm.sets( renderable=True, noSurfaceShader=True, empty=True, name="standardSurface1SG" )
        standard_material = pm.PyNode("standardSurface1")
        standard_material.outColor >> temp_standard_shader.surfaceShader
        pm.sets(temp_standard_shader, edit=True, forceElement=temp_cube)

        pm.mel.eval('hyperShadePanelMenuCommand("hyperShadePanel1", "deleteUnusedNodes");')
    except Exception as e:
        v1_core.v1_logging.get_logger().info("Failed To Delete Unused Nodes:")
        v1_core.v1_logging.get_logger().info("{0}".format(e))
        pass
    finally:
        if temp_cube:
            pm.delete(temp_cube)

    # Delete empty namespaces
    delete_empty_namespaces()

    # Delete empty Display Layers
    delete_empty_display_layers()

    # Delete RenderLayers that aren't a default layer
    render_layers = [x for x in pm.ls(type='renderLayer') if x != pm.nt.RenderLayer('defaultRenderLayer')]
    if render_layers:
        pm.delete(render_layers)
        v1_core.v1_logging.get_logger().info("Scene Cleanup Deleted RenderLayers : \n{0}".format(render_layers))

    # Delete orphaned groupId nodes
    group_id_nodes = [x for x in pm.ls(type='groupId') if not x.listConnections()]
    if group_id_nodes:
        pm.delete(group_id_nodes)
        v1_core.v1_logging.get_logger().info("Scene Cleanup Deleted GroupID Nodes : \n{0}".format(group_id_nodes))

    # Delete orphaned TimeEditor tracks
    time_editor_tracks = [x for x in pm.ls(type='timeEditorTracks') if not x.listConnections()]
    if time_editor_tracks:
        pm.delete(time_editor_tracks)
        v1_core.v1_logging.get_logger().info("Scene Cleanup Deleted TimeEditorTracks : \n{0}".format(time_editor_tracks))

    # Delete orphaned GraphEditorInfo nodes
    graph_editor_info = [x for x in pm.ls(type='nodeGraphEditorInfo') if not x.listConnections()]
    if graph_editor_info:
        pm.delete(graph_editor_info)
        v1_core.v1_logging.get_logger().info("Scene Cleanup Deleted GraphEditorInfo : \n{0}".format(graph_editor_info))

    # Delete orphaned and unlocked reference nodes
    reference_nodes = [x for x in pm.ls(type='reference') if not x.listConnections() and not x.isLocked()]
    if reference_nodes:
        pm.delete(reference_nodes)
        v1_core.v1_logging.get_logger().info("Scene Cleanup Deleted References : \n{0}".format(reference_nodes))

def delete_empty_namespaces():
    '''
    Remove all empty namespaces from bottom up to remove children namespaces first
    '''
    namespace_list = []
    for ns in pm.listNamespaces( recursive  =True, internal =False):
        namespace_list.append(ns)

    # Reverse Iterate through the contents of the list to remove the deepest layers first
    for ns in reversed(namespace_list):
        if not pm.namespaceInfo(ns, ls=True):
            pm.namespace(removeNamespace = ns, mergeNamespaceWithRoot = True)

def delete_empty_display_layers():
    empty_layer_list = []
    default_display_layer = pm.PyNode('defaultLayer') if pm.objExists('defaultLayer') else None
    for display_layer in pm.ls(type="displayLayer"):
        if display_layer != default_display_layer and not display_layer.listMembers():
            empty_layer_list.append(display_layer)

    pm.delete(empty_layer_list)

def delete_empty_objects():
    pm.delete([x for x in pm.ls(assemblies=True) if not x.getShape() and not x.getChildren()])

def setup_exporter():
    '''
    Quick initial Exporter setup from scene objects.
    '''
    from metadata.network_core import CharacterCore, JointsCore
    from metadata.exporter_properties import ExportDefinition, CharacterAnimationAsset
    character_core_type = Network_Registry().get(CharacterCore)
    joints_core_type = Network_Registry().get(JointsCore)
    export_definition_type = Network_Registry().get(ExportDefinition)
    character_animation_asset_type = Property_Registry().get(CharacterAnimationAsset)

    export_definition_list = meta_network_utils.get_all_network_nodes(export_definition_type)
    current_definition = meta_network_utils.create_from_node(export_definition_list[0]) if export_definition_list else None
    export_definition = current_definition if current_definition else export_definition_type()
    export_definition.set('use_scene_name', True)

    character_list = meta_network_utils.get_all_network_nodes(character_core_type)
    for character_node in character_list:
        character_network = meta_network_utils.create_from_node(character_node)
        joints_network = character_network.get_downstream(joints_core_type)
        root_joint = node_utils.get_root_node(joints_network.get_first_connection(), 'joint')
        character_name = root_joint.namespace().strip(":").upper()

        existing_export_property = meta_property_utils.get_property(root_joint, character_animation_asset_type)
        export_property = existing_export_property if existing_export_property else character_animation_asset_type()
        export_property.set('asset_name', character_name)

        export_property.connect_node(root_joint)
        export_definition.connect_node(export_property.node)

    pm.select(None)

def fix_full_paths():
    '''
    Fixes full paths in references and texture maps so they are relative to Data\
    '''
    nodes = []

    # Creates a variable named fileNodes and is set to list all of the files
    fileNodes = pm.ls(type = "file")
    refNodes = pm.listReferences()

    nodes.extend(fileNodes)
    nodes.extend(refNodes)

    # dataPathNodes is a variable set to an empty array
    dataPathNodes = []

    # looking at a list of nodes within the fileNodes variables
    for node in nodes:
        try:
            # we are getting the fileTextureName from each specific file node and splitting each sub directory in each node
            fileNameArray = node.fileTextureName.get().replace("\\", "/").split("/")
        except:
            fileNameArray = node.unresolvedPath().split("/")

        dataNumb = None

        # look at specific elements inside of the fileName
        for element in fileNameArray:
            # element array is lowercased with .lower()
            # if there is not data found within the elements array skip the file Node
            if element != "Data":
                continue

            dataNumb = fileNameArray.index("Data")

        if not dataNumb:
            continue

        if dataNumb <= 0:
            continue

        newEndPathArray = fileNameArray[dataNumb:]
        newEndPath = "/".join(newEndPathArray)
        newDir = newEndPath

        try:
            node.fileTextureName.set(newDir)
        except:
            node.replaceWith(newDir)

def clean_reference_cameras():
    '''
    Default cameras from referenced scenes can get into animation scenes.  This finds them,
    unflags their default status, and deletes them
    '''
    for cam in pm.ls(type = "camera"):
        try:
            topParent = cam.getParent(generations = -1)
            isStartUp = pm.camera(cam, q = True, startupCamera = True)

            if topParent.isReferenced() and isStartUp:
                pm.camera(cam, e = True, startupCamera = False)
                pm.delete(cam.getParent())
        except:
            continue

def import_file_safe(file_path, fbx_mode = "add", tag_imported = False, keep_scene_time = True, **kwargs):
    '''
    Import FBX files or Maya files safely, and with the ability to return import nodes.

    Note: If an .ma file has plugin attributes from a plugin that the user does not have, the Maya importFile
    command will error and fail to return any values.  If this happens we fall back to comparing pre and post
    import scene object lists to find the imported objects.
    EDGE CASE: This method will fail to return objects if the import process parents the imported nodes into
    existing scene hierarchy

    Args:
        file_path (string): Full path to the file to import
        **kwargs (kwargs): keyword args to pass along to pm.importFile
    '''
    scene_time_tuple = get_scene_times()
    current_time = pm.currentTime()
    current_playback = get_playback_rate()

    import_return = None
    pre_import_list = pm.ls(assemblies = True)
    current_import_mode = fbx_wrapper.FBXImportMode(q=True)
    try:
        filename, extension = os.path.splitext(file_path)
        if extension.lower() == '.ma':
            import_return = pm.importFile(file_path, **kwargs)
        elif extension.lower() == '.fbx':
            fbx_file_path = file_path.replace('\\', '\\\\')

            fbx_wrapper.FBXImportMode(v = fbx_mode)
            fbx_wrapper.FBXImport(f = fbx_file_path)
    except:
        if ".ai_translator" in v1_core.exceptions.get_exception_message():
            v1_core.v1_logging.get_logger().info("Ignoring Maya to Arnold File Import Errors")
        else:
            exception_info = sys.exc_info()
            v1_core.exceptions.except_hook(exception_info[0], exception_info[1], exception_info[2])
    finally:
        fbx_wrapper.FBXImportMode(v = current_import_mode)
        set_playback_rate(current_playback)
        if (keep_scene_time):
            set_scene_times(scene_time_tuple)
            pm.currentTime(current_time)

    if not import_return and kwargs['returnNewNodes'] == True:
        post_import_list = pm.ls(assemblies = True)
        import_parent_list = [x for x in post_import_list if x not in pre_import_list]

        import_return = import_parent_list
        for import_parent in import_parent_list:
            import_return = import_return + import_parent.listRelatives(ad=True)

    if import_return and tag_imported:
        from metadata.network_core import ImportedCore
        imported_core = Network_Registry().get(ImportedCore)()
        relative_path = v1_shared.file_path_utils.full_path_to_relative(file_path)
        imported_core.set('import_path', relative_path)
        imported_core.connect_nodes(import_return)
        import_return.append(imported_core.node)

    return import_return

def export_selected_safe(file_path, **kwargs):
    filename, extension = os.path.splitext(file_path)
    if extension.lower() == '.ma':
        pm.exportSelected(file_path, force = True)
    elif extension.lower() == '.fbx':
        fbx_file_path = file_path.replace("\\", "\\\\")
        fbx_wrapper.FBXExport(f = fbx_file_path, **kwargs)

    v1_core.v1_logging.get_logger().info("export_selected_safe - {0}".format(file_path))


"""Helper functions/classes related to dates/datetimes."""

import datetime as dt
import re
from typing import List

from dateutil.parser import parse as dateutil_parse

from .types import DateLike, assert_never

def parse_date(date: DateLike) -> dt.date:
    """Parses a date string.

    Args:
        date: The date string to parse.

    This function defaults to using dateutil.parser.parse(), but also accepts
    the following special formats for ``date``:

        @t | @today
            Today's date.

        Nd
            N days ago.

        Nw
            N weeks ago.
    """
    if isinstance(date, str):
        date = date.lower()

        match = re.match("^(?P<num>[0-9]+)(?P<ch>d|w)$", date)
        if match:
            num = int(match.group("num"))
            ch = match.group("ch")

            if ch == "d":
                days = num
            else:
                assert ch == "w"
                days = 7 * num

            return dt.date.today() - dt.timedelta(days=days)

    if date in ["@today", "@t"]:
        return dt.date.today()
    elif isinstance(date, str):
        datetime = dateutil_parse(date)
        return datetime.date()
    elif isinstance(date, dt.datetime):
        return date.date()
    elif isinstance(date, dt.date):
        return date
    else:
        assert_never(date)

def parse_daterange(daterange: str) -> List[dt.date]:
    """Transforms a date range specifier into a list of sequential dates.

    Args:
        daterange: A string of the form START_DATE:END_DATE where both
          START_DATE and END_DATE are any value that the parse_date() function
          can parse.
    """
    if ":" not in daterange:
        return [parse_date(daterange)]
    else:
        first_date, last_date = [parse_date(D) for D in daterange.split(":")]

        result = []
        next_date = first_date
        while next_date <= last_date:
            result.append(next_date)
            next_date = next_date + dt.timedelta(days=1)

        return result


from django.shortcuts import render
from django.http import HttpResponse
from scripts.data_handler import data_handler
from collections import OrderedDict
import json

# Create your views here.
def index(request):
	healthy_levels = data_handler.read_healthy_levels()
	healthy_temperature = healthy_levels['temperature']
	healthy_humidity = healthy_levels['humidity']
	healthy_soil_moisture = healthy_levels['soil_moisture']
	healthy_sunlight = healthy_levels['sunlight']

	available_temperatures = data_handler.get_available_temperatures()
	available_humidities = data_handler.get_available_humidities()
	available_soil_moistures = data_handler.get_available_soil_moistures()
	available_sunlights = data_handler.get_available_sunlights()

	for available_temperature in available_temperatures:
		if healthy_temperature == available_temperature[0]:
			healthy_temperature_label = available_temperature[1]
			break

	for available_humidity in available_humidities:
		if healthy_humidity == available_humidity[0]:
			healthy_humidity_label = available_humidity[1]
			break

	for available_soil_moisture in available_soil_moistures:
		if healthy_soil_moisture == available_soil_moisture[0]:
			healthy_soil_moisture_label = available_soil_moisture[1]
			break

	for available_sunlight in available_sunlights:
		if healthy_sunlight == available_sunlight[0]:
			healthy_sunlight_label = available_sunlight[1]
			break

	log_data = data_handler.get_log_data()
	labels = ''
	temperatures = ''
	humidities = ''
	soil_moistures = ''
	sunlights = ''
	for record in log_data:
		labels += str(record) + ','
		temperatures += str(log_data[record]['temperature']) + ','
		humidities += str(log_data[record]['humidity']) + ','
		soil_moistures += str(log_data[record]['soil_moisture']) + ','
		sunlights += str(log_data[record]['sunlight']) + ','

	temperatures = temperatures[:-1]
	humidities = humidities[:-1]
	soil_moistures = soil_moistures[:-1]
	sunlights = sunlights[:-1]
	labels = labels[:-1]

	log_data = data_handler.get_log_data()
	log_data = OrderedDict(log_data)
	log_data = list(log_data.items())
	last_reading = {}
	last_reading_datetime, last_reading_values = log_data[-1]

	legend = data_handler.get_legend()

	last_temperature = data_handler.bucket_to_nominal("temperature", last_reading_values['temperature'])
	last_humidity = data_handler.bucket_to_nominal("humidity", last_reading_values['humidity'])
	last_soil_moisture = data_handler.bucket_to_nominal("soil_moisture", last_reading_values['soil_moisture'])
	last_sunlight = data_handler.bucket_to_nominal("sunlight", last_reading_values['sunlight'])

	mode = data_handler.get_mode()

	current_dc_settings = data_handler.get_dc_settings()
	current_freq_settings = data_handler.get_freq_settings()

	# extract duty cycles for view render
	fan_dc = current_dc_settings["fan_dc"]
	light_dc = current_dc_settings["light_dc"]
	# extract frequencies for view render
	fan_freq = current_freq_settings["fan_freq"]
	light_freq = current_freq_settings["light_freq"]

	length = len(log_data)

	return render(request, 'Dashboard/dashboard.html', {'mode': mode, 'legend': legend, 'last_temperature': last_temperature, 'last_humidity': last_humidity, 'last_soil_moisture': last_soil_moisture, 'last_sunlight': last_sunlight, 'healthy_temperature': healthy_temperature, 'healthy_temperature_label': healthy_temperature_label, 'healthy_humidity': healthy_humidity, 'healthy_humidity_label': healthy_humidity_label, 'healthy_soil_moisture': healthy_soil_moisture, 'healthy_soil_moisture_label': healthy_soil_moisture_label, 'healthy_sunlight': healthy_sunlight, 'healthy_sunlight_label': healthy_sunlight_label, 'labels': labels, 'temperatures': temperatures, 'humidities': humidities, 'soil_moistures': soil_moistures, 'sunlights': sunlights, 'fan_freq':fan_freq, 'light_freq':light_freq, 'fan_dc':fan_dc, 'light_dc':light_dc, 'length':length})


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

# #                                      THE COVID-19 PROJECT                                             #

# In[1]:

import pandas as pd
import numpy
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")

# In[2]:

#To read csv file
pro=pd.read_csv("4. covid_19_data.csv")

# In[3]:

pro

# In[4]:

#To show how many rows and columns
pro.shape

# In[5]:

#count
pro.count()

# In[6]:

pro.isnull().sum()

# In[7]:

#Showing Boolean values by Heat Map
sns.heatmap(pro.isnull())
plt.show()

# In[8]:

#First 5 records
pro.head()

# In[9]:

#Last 5 records
pro.tail()

# In[10]:

#Total records groupby Region
pro.groupby("Region").sum()

# In[11]:

#Top 20 records
pro.groupby("Region").sum().head(20)

# In[12]:

#Total Confirmed cases by Region
pro.groupby("Region")["Confirmed"].sum()

# In[13]:

#Total Confirmed cases by Decending order
pro.groupby("Region")["Confirmed"].sum().sort_values(ascending=False).head(10)

# In[14]:

#Total Confirmed cases,Recovered by region
pro.groupby("Region")["Confirmed","Recovered"].sum()

# In[15]:

#Total Deaths by Regions
pro.groupby("Region")["Deaths"].sum()

# In[16]:

#Total top Deaths cases by Region by ascending order
pro.groupby("Region")["Deaths"].sum().sort_values(ascending=True).head(60)

# In[17]:

# pro[(pro.Confirmed < 10)]

# In[18]:

pro[(~pro.Confirmed < 10)]

# In[19]:

#Top 10 records
pro.head(10)

# In[20]:

#Top 10 States Recovered by ascending order
pro.sort_values(by = ["Recovered"],ascending=True).head(10)

# # Showing the Records by Visualization

# In[21]:

#Confirmed cases vs Recovered cases
a=pro["Confirmed"]
b=pro["Recovered"]
sns.scatterplot(a,b,color="orange",s=500)
plt.show()

# In[22]:

pro.sort_values(by = ["Confirmed"],ascending=True).head(10)

# In[23]:

recovered=pro[1:10]
recovered

# In[24]:

#Recovered cases vs Region
sns.set(rc={"figure.figsize":(10,8)})
sns.barplot(x="Region",y="Recovered",data=recovered)
plt.show()

# In[25]:

deaths=pro[1:12]
deaths

# In[26]:

b=pro["Deaths"]
b

# In[27]:

#Deaths cases vs Region
sns.set(rc={"figure.figsize":(10,8)})
sns.barplot(x="Region",y="Deaths",data=deaths)
plt.show()

# In[28]:

a=pro["Confirmed"]
a

# In[29]:

confirmed=pro[1:10]
confirmed

# In[30]:

#Confirmed cases vs Region
sns.set(rc={"figure.figsize":(10,8)})
sns.lineplot(x="Region",y="Confirmed",data=confirmed)
plt.show()

# In[31]:

top_states_death=pro[0:10]
top_states_death

# In[33]:

#Deaths vs Confirmed
a=pro["Confirmed"]
b=pro["Deaths"]
sns.scatterplot(a,b,color="red",s=1080)
plt.show()

# In[34]:

#Region vs Recovered
sns.set(rc={"figure.figsize":(10,10)})
sns.lineplot(x="Region",y="Recovered",data=top_states_death)
plt.show()

# In[35]:

top_states_deaths=pro[1:15]
top_states_deaths

# In[36]:

#Deaths vs Recovered
sns.set(rc={"figure.figsize":(10,10)})
sns.barplot(x="Deaths",y="Recovered",data=top_states_deaths)
plt.show()

# #                                               #  THE END                                          #


from SSINS import SS
from SSINS import Catalog_Plot as cp
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('obs')
parser.add_argument('inpath')
parser.add_argument('outpath')
args = parser.parse_args()

read_kwargs = {'ant_str': 'cross',
               'file_type': 'uvfits'}

MF_kwargs = {'sig_thresh': 5,
             'shape_dict': {'TV6': [1.74e8, 1.81e8],
                            'TV7': [1.81e8, 1.88e8],
                            'TV8': [1.88e8, 1.95e8]},
             'N_thresh': 20}

ss = SS(obs=args.obs, inpath=args.inpath, outpath=args.outpath, read_kwargs=read_kwargs,
        bad_time_indices=[0, -1, -2, -3])
ss.MF_prepare(**MF_kwargs)
cp.MF_plot(ss.MF)


import os
import pandas as pd
import numpy as np
import pyarrow
import glob
import geopandas as gpd
import multiprocessing
import timeit
import ogr
import gdal
import shutil
from matplotlib import pyplot
import matplotlib.pyplot as plt
import haversine as hs  # this is for distance calculation between two points (pip install haversine)
import pysheds
# import xarray as xr
from pysheds.grid import Grid  # use conda install -c conda-forge pysheds
from src.createOutput import (
    outputDir,
    find_watershed_outlet,
    general_purpose_watershed,
    create_FlowAccu_tif_file,
    finding_NDAMS
)

## defining paths for shapefiles
## shapefiles are in different folders (different categories)

path_dict = {}
# path_dict['output_dir'] = r"/data/fzr5082/"
path_dict['output_dir'] = r"/scratch/fzr5082"   # r"/data/fzr5082/"
path_dict['path_shp'] = r"/data/wxt42/hydroDL_reservoir_data/total_shp/CONUS_basin_shp/"  # shapefiles directory
# path_dict['path_shp'] = r"/data/wxt42/raw_data/shape_file/SRB_HUC10_all/"  # shapefiles directory
path_dict['path_dams_shp'] = r"/data/fzr5082/NID/maj_dams_shp/us-dams.shp"  # dams point shapefile
path_dict['path_dams_info'] = r"/data/fzr5082/NID/NID2019_U.feather"  # dams excel file information
path_dict['gages_path'] = r"/data/wxt42/hydroDL_reservoir_data/gagesII_9322_point_shapefile/gagesII_9322_sept30_2011.shp"  # streamflow gauges point shapefile
path_dict['topography_path'] = r"/data/shared_data/NED_10m/"
# path_dict['topography_path'] = r"/data/shared_data/DEM/SRTMGL1v003_30m/"
# Do we need Flow accumulation (DEM part) part? If yes --> needDEM = True, if No --> needDEM = False
needDEM = True
# creating directories
path_dict = outputDir(path_dict)

## Reading shapefiles directory
# dirs_shp = os.listdir(path_shp)

# It is a shapefile that illustrates each dam as a point
dams_shp = gpd.read_file(path_dict['path_dams_shp'])
dams_shp_prj = dams_shp.to_crs('epsg:4269')  # Do projection for the shapefile
## some information of the dams in dams_shp is in the following file (dams_info)
dams_info = pd.read_feather(path_dict['path_dams_info'])
dams_info_m = dams_info.loc[dams_info['OTHERSTRUCTUREID']!=dams_info['OTHERSTRUCTUREID']].reset_index(drop=True)
dams_info_gdf = gpd.GeoDataFrame(
    dams_info_m, geometry=gpd.points_from_xy(dams_info_m.LONGITUDE, dams_info_m.LATITUDE))

dams_info_gdf.set_crs(epsg=4269, inplace=True)
print("dams information file was read")
### remove maj dams that are not in dams_info_gdf
tempsites = dams_shp_prj['NID_ID_Cod'].tolist()
x = dams_info_gdf.loc[dams_info_gdf['NIDID'].isin(tempsites),'NIDID'].tolist()
dams_shp_prj = dams_shp_prj.loc[dams_shp_prj['NID_ID_Cod'].isin(x)]

# Reading flow gages shapefile which is used for finding the outlet of a watershed
gages = gpd.read_file(path_dict['gages_path'])
gages_prj = gages.to_crs('epsg:4269')  # Do projection for the shapefile

##################### MAIN PART #########################
def reservoirs(wtshd):
    data = []  # to store all variables we need and append it to pandas dataframe file at the end
    watershed = gpd.read_file(wtshd)
    geom_buffer = watershed.buffer(0)    # it was 400 before, Farshid changed it to 0
    watershed1 = gpd.read_file(wtshd)
    watershed1['geometry'] = geom_buffer
    watershed_prj = watershed1.to_crs('epsg:4269')
    watershed_prj_path = os.path.join(path_dict['tempFolder_path'], 'watershed_prj.shp')
    watershed_prj.to_file(watershed_prj_path)
    watershed_prj_noBuffer = watershed.to_crs('epsg:4269')

    # to get basin's ID:
    # the column_name is different in shapefile directories:
    if "GAGE_ID" in watershed.columns:    # this is for Conus shapefiles,
        data.append(watershed_prj_noBuffer['GAGE_ID'][0])
    elif "HUC10" in watershed.columns:   # this is for running SRB-HUC10 shapefiles
        data.append(watershed_prj_noBuffer['HUC10'][0])

    # to get basin's area:
    # The column_name is different in different shapefiles
    if "AREA" in watershed.columns:
       data.append(watershed_prj_noBuffer['AREA'][0])
    elif "AreaSqKm" in watershed.columns:
        data.append(watershed_prj_noBuffer['AreaSqKm'][0] * 1e6)    # converting sqkm to sqm

    if (needDEM == True) & (data[1] < 24e9):  # 25e9 is the maximum area that we can musaic,
        # larger than this, we get memory issue. needs to get fixed in the future
        flowAccu_temp_path, AccuProcess = create_FlowAccu_tif_file(watershed_prj_path, path_dict)
        ### if AccuProcess = True --> it means there is at least a tif file that has overlap with the watershed
        ### if AccuProcess = False --> it means there is not any tif file that has overlap with the watershed
    else:
        AccuProcess = False

    if AccuProcess == True:
        watershed_outlet = find_watershed_outlet(watershed_prj, flowAccu_temp_path, gages_prj)
    else:
        watershed_outlet = ["_", "_", "_", "_"]

    data.extend(watershed_outlet)

    ## finding all major dams inside a watershed
    selected_maj_dams = gpd.clip(dams_shp_prj, watershed_prj_noBuffer)    # it was dams_shp_prj
    # selected_maj_dams = selected_dams.loc[(selected_dams['DAM_HEIGHT'] >= 50) |
    #                                       (selected_dams['NORMAL_STORAGE'] >= 5000)]
    MAJ_NDAMS = len(selected_maj_dams)  # number of dams inside a watershed
    data.append(MAJ_NDAMS)

    if (MAJ_NDAMS) > 0:  # at least one dam in the watershed

        # now calculate the Minimum and Mean Distance between the dams and watershed_outlet
        # and to get "General_Purpose" of watershed
        distance_outlet_dams = []
        dam_ID = []
        for i, point in enumerate(selected_maj_dams['geometry']):
            x = point.xy[0][0]
            y = point.xy[1][0]
            dam_ID.append(selected_maj_dams['NID_ID_Cod'].values[i])    #NID_ID_Cod

            # calculating distance between outlet and dams
            if (watershed_outlet[2] != "_") & (watershed_outlet[3] != "_"):
                dam_point = (x, y)
                outlet_point = (watershed_outlet[2], watershed_outlet[3])
                distance = hs.haversine(dam_point, outlet_point, unit=hs.Unit.METERS)
                distance_outlet_dams.append(distance)
            else:
                distance_outlet_dams.append(-999)   # it was np.nan before

        data.append(np.nanmin(distance_outlet_dams))  # distance of nearest dam from the outlet
        data.append(np.nanmean(distance_outlet_dams))  # average distance of dams from outlet

        # To calculate General Purpose of each watershed based on the major dams inside it
        dam = dams_info_gdf.loc[dams_info_gdf["NIDID"].isin(dam_ID)]
        if len(dam) > 0:  # sometimes some major dams in shape file are not in the excel file
            general_purpose, max_norm_stor, std_norm_stor = general_purpose_watershed(dam, dams_shp_prj)
        else:   # sometimes the dataset has some problem
            general_purpose = -1
            max_norm_stor = 0
            std_norm_stor = 0

    else:  # No dam in watershed
        data.extend([-999, -999])  # this is for dams distance and watershed outlet (means no dam in watershed)
        general_purpose = -1
        max_norm_stor = 0
        std_norm_stor = 0

    data.append(general_purpose)
    data.append(max_norm_stor)
    data.append(std_norm_stor)
    ### finding NDAMS_2009 and normal storage for all dams:
    NDAMS, STOR_NOR_2009 = finding_NDAMS(watershed_prj_noBuffer, dams_info_gdf, path_dict, data)

    data.append(NDAMS)
    data.append(STOR_NOR_2009)

    print("Watershed:       ", os.path.split(wtshd)[-1])
    return data

result = []
count = 1
shp_lst = glob.glob(os.path.join(path_dict['path_shp'], '*.shp'))
for wtshd in shp_lst:   # [1878:]
    data = reservoirs(wtshd)
    result.append(data)
    print(count)
    count = count + 1
    columns = [
        'GAGE_ID', 'AREA', "STAID", "flow_Accu", "LAT_GAGE", "LNG_GAGE", 'MAJ_NDAMS', "NEAREST_DIS", "AVE_DIS",
        'general_purpose', "max-normal_storage", 'std_norm_stor', 'NDAMS_2009', 'STOR_NOR_2009'
    ]
    Reservoirs = pd.DataFrame(result, columns=columns)
    Reservoirs.to_csv(os.path.join(path_dict['output_dir'], 'Reservoirs.csv'))
###########################################################
#### multiprocessing part is not working yet ##############

# num_cores = multiprocessing.cpu_count()
# num_cores = 6
# print('Number of cores:', num_cores, '\n')
# p = multiprocessing.Pool(num_cores)
# start = timeit.default_timer()
# result = p.map(reservoirs, shp_lst)
# p.close()
# p.join()
# columns = [
#         'GAGE_ID', 'AREA', "STAID", "flow_Accu", "LAT_GAGE", "LNG_GAGE", 'MAJ_NDAMS', "NEAREST_DIS", "AVE_DIS",
#         'general_purpose', "max-normal_storage", 'std_norm_stor', 'NDAMS_2009', 'STOR_NOR_2009'
#     ]
# Reservoirs = pd.DataFrame(result, columns=columns)
# Reservoirs.to_csv(os.path.join(path_dict['output_dir'], 'Reservoirs.csv'))

# appending data that is collected for this watershed to Reservoirs dataframe
###### difining a pandas file to store averything inside it.
###### This is my results

print('END')


# ---
# jupyter:
#   jupytext:
#     formats: ipynb,py:percent
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.14.0
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# %% [markdown]
# # Orientation and direction selectivity analysis

# %%
# general python modules
import sys, os, pprint
import numpy as np
import matplotlib.pylab as plt

# *_-= physion =-_*
physion_folder = os.path.join(os.path.expanduser('~'), 'work', 'physion') # UPDATE to your folder location
# -- physion core
sys.path.append(os.path.join(physion_folder, 'physion'))
from analysis.read_NWB import Data, scan_folder_for_NWBfiles
from dataviz.show_data import MultimodalData, EpisodeResponse
# -- physion data visualization
sys.path.append(os.path.join(physion_folder, 'dataviz', 'datavyz'))
from datavyz import ge

# %% [markdown]
# ## Some useful functions

# %%
def selectivity_index(angles, resp):
    """
    computes the selectivity index: (Pref-Orth)/(Pref+Orth)
    clipped in [0,1]
    """
    imax = np.argmax(resp)
    iop = np.argmin(((angles[imax]+90)%(180)-angles)**2)
    if (resp[imax]>0):
        return min([1,max([0,(resp[imax]-resp[iop])/(resp[imax]+resp[iop])])])
    else:
        return 0

def shift_orientation_according_to_pref(angle,
                                        pref_angle=0,
                                        start_angle=-45,
                                        angle_range=360):
    new_angle = (angle-pref_angle)%angle_range
    if new_angle>=angle_range+start_angle:
        return new_angle-angle_range
    else:
        return new_angle

# %% [markdown]
# ## Load and plot raw data

# %%
data_folder =  os.path.join(os.path.expanduser('~'), 'DATA', 'taddy_GluN3KO')
FILES, _, _ = scan_folder_for_NWBfiles(data_folder)

# %%
EPISODES = EpisodeResponse(FILES[0],
                           quantities=['dFoF'],
                           protocol_id=0,
                           verbose=True)

# %%
Nsamples = 10

fig, AX = ge.figure((len(EPISODES.varied_parameters['angle']), Nsamples),
                    figsize=(.8,.9), right=10)

stat_test_props=dict(interval_pre=[-1,0], interval_post=[1,2],
                     test='ttest', positive=True)
response_significance_threshold = 0.01

for i, r in enumerate(np.random.choice(np.arange(EPISODES.data.iscell.sum()),
                                       Nsamples, replace=False)):

    # SHOW trial-average
    EPISODES.plot_trial_average(column_key='angle',
                                #condition=EPISODES.find_episode_cond(key='contrast', value=1.),
                                #color_key='contrast',
                                quantity='dF/F',
                                ybar=1., ybarlabel='1dF/F',
                                xbar=1., xbarlabel='1s',
                                roiIndex=r,
                                AX=[AX[i]], no_set=False)
    ge.annotate(AX[i][0], 'roi #%i  ' % (r+1), (0,0), ha='right')

    # SHOW summary angle dependence
    inset = ge.inset(AX[i][-1], (2, 0.2, 1.2, 0.8))

    angles, y, sy, responsive_angles = [], [], [], []
    responsive = False

    for a, angle in enumerate(EPISODES.varied_parameters['angle']):

        stats = EPISODES.stat_test_for_evoked_responses(episode_cond=EPISODES.find_episode_cond('angle', a),
                                                        response_args=dict(quantity='dFoF', roiIndex=r),
                                                        **stat_test_props)

        angles.append(angle)
        y.append(np.mean(stats.y))    # means "post"
        sy.append(np.std(stats.y))    # std "post"

        if stats.significant(threshold=response_significance_threshold):
            responsive = True
            responsive_angles.append(angle)

    ge.plot(angles, np.array(y), sy=np.array(sy), ax=inset,
            axes_args=dict(ylabel='<post dF/F>         ', xlabel='angle ($^{o}$)',
                           xticks=angles, size='small'),
            m='o', ms=2, lw=1)

    SI = selectivity_index(angles, y)
    ge.annotate(inset, 'SI=%.2f ' % SI, (0, 1), ha='right', weight='bold', fontsize=8,
                color=('k' if responsive else 'lightgray'))
    ge.annotate(inset, ('responsive' if responsive else 'unresponsive'), (1, 1), ha='right',
                weight='bold', fontsize=6, color=(plt.cm.tab10(2) if responsive else plt.cm.tab10(3)))

# %% [markdown]
# ## Summary data

# %%
print(EPISODES.varied_parameters['angle'])
shifted_angle = EPISODES.varied_parameters['angle']-EPISODES.varied_parameters['angle'][1]
print(shifted_angle)

# %%
stat_test_props=dict(interval_pre=[-1,0], interval_post=[1,2],
                     test='ttest', positive=True)
response_significance_threshold = 0.01

RESPONSES = []

for roi in np.arange(EPISODES.data.iscell.sum()):

    cell_resp = EPISODES.compute_summary_data(response_significance_threshold=response_significance_threshold,
                                              response_args=dict(quantity='dFoF', roiIndex=roi),
                                              stat_test_props=stat_test_props)

    condition = np.ones(len(cell_resp['angle']), dtype=bool) # no condition
    # condition = (cell_resp['contrast']==1.) # if specific condition

    if np.sum(cell_resp['significant'][condition]):

        ipref = np.argmax(cell_resp['value'][condition])
        prefered_angle = cell_resp['angle'][condition][ipref]

        RESPONSES.append(np.zeros(len(shifted_angle)))

        for a, angle in enumerate(cell_resp['angle'][condition]):

            new_angle = shift_orientation_according_to_pref(angle,
                                                            pref_angle=prefered_angle,
                                                            start_angle=shifted_angle[0],
                                                            angle_range=180)
            iangle = np.argwhere(shifted_angle==new_angle)[0][0]
            RESPONSES[-1][iangle] = cell_resp['value'][a]

# %%
fig, AX = ge.figure(axes=(2,1), wspace=2)
# raw
ge.scatter(shifted_angle, np.mean(RESPONSES, axis=0),
           sy=np.std(RESPONSES, axis=0), ms=4, lw=1, ax=AX[0],
           xlabel='angle ($^o$)', ylabel='$\Delta$F/F', title='raw resp.')
# peak normalized
N_RESP = [resp/np.max(resp) for resp in RESPONSES]
ge.scatter(shifted_angle, np.mean(N_RESP, axis=0), sy=np.std(N_RESP, axis=0),
           ms=4, lw=1, ax=AX[1], axes_args={'yticks':[0,1]},
           xlabel='angle ($^o$)', ylabel='n. $\Delta$F/F', title='peak normalized')