# -*- coding: utf-8 -*-
"""
Unit tests for the flight_log_code.py.

@author Adrian Weishaeupl (aw6g15@soton.ac.uk)
"""

import unittest
import os
import json
import sys
from autoflpy.util import flight_log_code
from datetime import datetime
from autoflpy.util import nearest_ICAO_finder
from autoflpy.util.testing.check_str_in_content import *

def notebook_sample_code(flight_data_file_path, flight_data_file_name,
                         csv_data_file_path, csv_flight_data_name):
    """Sample flight log code for testing purposes"""
    # GRAPH_DATA_IMPORT

    # Creates a base_path to the test files.
    base_path = os.path.dirname(__file__) + os.sep

    # Creates a link to where the code is stored.
    sys.path.append(base_path[:-11])

    # file_path of the flight data.
    # flight_data_file_path = base_path
    # Excel File name
    # flight_data_file_name = "test_xlsx.xlsx"
    # CSV File name
    # csv_flight_data_name = "test_csv.CSV"
    # csv Data file path
    # csv_data_file_path = base_path
    # Excel Sheets
    frame_list = flight_log_code.flight_data(flight_data_file_path,
                                             flight_data_file_name)
    # A list containing the date first and then the flight number
    date_and_flight_number = flight_log_code.date_and_flight_number(frame_list)
    # Retrieves csv flight data
    csv_flight_data_frame = \
        flight_log_code.csv_frame(csv_data_file_path,
                                  csv_flight_data_name)
    # Appends csv frame to flight data from pixhawk
    frame_list.append(csv_flight_data_frame)
    # Sorts frames by time
    sorted_frames = flight_log_code.flight_data_time_sorter(frame_list)
    # Creates a list of all the values.
    values_list = flight_log_code.flight_data_and_axis(sorted_frames)
    return (frame_list, date_and_flight_number, csv_flight_data_frame,
            sorted_frames, values_list)

class TestFlightLogCode(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        # Runs once before any tests
        # Defines the base path
        base_path = os.path.join(os.path.dirname(__file__), "test_files"
                                 ) + os.sep
        # Tidies base path
        base_path = base_path.replace(os.sep, "/")
        # Opens the input file and reads content
        with open(base_path + 'test_Input_File.json') as file:
            data = json.load(file)
        # Defines variables
        flight_data_file_path = base_path
        flight_data_file_name = "test_xlsx.xlsx"
        csv_flight_data_file_path = base_path + data[
            "flight_log_generator_input"]["csv_flight_data_file_path"]
        csv_flight_data_name = data["flight_log_generator_input"
        ]["csv_flight_data_name"]
        # Creates a global variable to be used in the testing
        global notebook_results
        # Populates the global variable
        notebook_results = \
            notebook_sample_code(flight_data_file_path,
                                 flight_data_file_name,
                                 csv_flight_data_file_path,
                                 csv_flight_data_name)

    def setUp(self):
        # Sets up all of the test variables and locations to be used
        # throughout.
        self.base_path = os.path.join(os.path.dirname(__file__),
                                      "test_files") + os.sep
        self.base_path = self.base_path.replace(os.sep, "/")
        # Imports data from template.
        with open(self.base_path + 'test_Input_File.json') as file:
            self.data = json.load(file)
        self.flight_number = self.data["log_to_xlsx_input"]["flight_number"].replace(" ", "").split(",")
        self.flight_date = self.data["log_to_xlsx_input"]["date"].replace(" ", "").split(",")
        self.template_file_path = self.base_path
        self.template_file_name = self.data["flight_log_generator_input"][
            "template_file_name"]
        self.flight_log_file_path = self.base_path + self.data[
            "flight_log_generator_input"]["flight_log_destination"]
        self.flight_data_file_path = self.base_path  # Path to xlsx file
        self.flight_data_file_name = "test_xlsx.xlsx"
        self.flight_data_file_name_list = ["test_xlsx.xlsx"]
        self.csv_flight_data_file_path = self.base_path + self.data[
            "flight_log_generator_input"]["csv_flight_data_file_path"]
        self.csv_flight_data_name = self.data["flight_log_generator_input"
        ]["csv_flight_data_name"].replace(" ", "").split(",")
        self.flight_log_file_name_header = "test_generated_flight_log"
        self.start_time_hours = self.data["flight_log_generator_input"][
            "start_time_hours"].replace(" ", "").split(",")
        self.end_time_hours = self.data["flight_log_generator_input"][
            "end_time_hours"].replace(" ", "").split(",")
        self.metar_file_path = self.base_path + self.data[
            "flight_log_generator_input"]["metar_file_path"]
        self.comp_data_file_path = self.flight_data_file_path + "test_xlsx.pkl"
        self.weather_data = flight_log_code.multi_dictionary_data_formatter(self.data["weather_data"], self.flight_date
                                                                            , "weather_data")
        self.weather_data_dict = self.data["weather_data"]
        self.runway_data = flight_log_code.multi_dictionary_data_formatter(self.data["runway_data"], self.flight_date,
                                                                           "runway_data")
        self.runway_data_dict = self.data["runway_data"]

    def tearDown(self):
        # Removes generated flight log.
        base_path = os.path.join(os.path.dirname(__file__),
                                 "test_files") + os.sep
        generated_file_names = [base_path + "test_generated_flight_logtest_xlsx.ipynb", base_path +
                               "test_generated_flight_log20190123_2.ipynb"]
        for item in range(len(generated_file_names)):
            if os.path.exists(generated_file_names[item]):
                os.remove(generated_file_names[item])

        # Removes pickled data.
        pickle_file_names = [base_path + "test_xlsx.pkl"]
        for item in range(len(pickle_file_names)):
            if os.path.exists(pickle_file_names[item]):
                os.remove(pickle_file_names[item])

    def test_flight_log_maker(self):
        self.ICAO_airfield = nearest_ICAO_finder.icao_finder(
            self.flight_data_file_path,
            self.flight_data_file_name)
        flight_log_code.flight_log_maker(self.template_file_path,
                                         self.template_file_name,
                                         self.flight_log_file_path,
                                         self.flight_data_file_path,
                                         self.flight_data_file_name_list,
                                         self.csv_flight_data_file_path,
                                         self.csv_flight_data_name,
                                         self.flight_date,
                                         self.flight_number,
                                         self.flight_log_file_name_header,
                                         self.ICAO_airfield,
                                         self.start_time_hours,
                                         self.end_time_hours,
                                         self.metar_file_path,
                                         self.weather_data,
                                         self.runway_data,
                                         True)
        # This code tests the flight_log_maker function.
        # First, check that a file has been created.
        test_flight_log_file_path = self.base_path + 'test_generated_flight_logtest_xlsx.ipynb'
        if os.path.exists(test_flight_log_file_path) is True:
            file_exists = True
        else:
            file_exists = False
        self.assertTrue(file_exists)
        # Checks that the file was recently created.
        filetime = os.stat(test_flight_log_file_path)
        # Finds the time since the file has been created.
        time_diff = datetime.now() - datetime.fromtimestamp(filetime.st_mtime)
        # Checks that the time difference is less than 1 second.
        self.assertLess(time_diff.microseconds, 1e6)
        # Check that the files weren't created in the past.
        self.assertGreaterEqual(time_diff.microseconds, 0)

    def test_flight_data(self):
        # Tests the flight data code.
        frame_list = flight_log_code.flight_data(
            self.flight_data_file_path,
            self.flight_data_file_name)
        # Checks that the expected frame dimensions are the correct size.
        frame_dimensions = [17780, 95220, 22860, 25400, 57141, 44436, 57132,
                            19044, 6, 4, 8]
        if len(frame_list) == 11:
            for frame in range(len(frame_list)):
                self.assertEqual(frame_list[frame].size,
                                 frame_dimensions[frame])
        else:
            # Raises a fault if the length of the frame_list is not as
            # expected.
            raise ValueError('Frame list length is not as expected: {0} != 11'
                             .format(len(frame_list)))

    def test_flight_log_creator(self):
        # Generates content
        content = flight_log_code.contents_opener(self.template_file_path,
                                                  self.template_file_name)
        file_name_data = str(self.flight_date[0]) + "_" + str(self.flight_number[0])
        # Runs the flight log creator code
        flight_log_code.flight_log_creator(content, self.base_path,
                                           file_name_data,
                                           self.flight_log_file_name_header)
        # Checks the file was created correctly
        file_exists = os.path.exists(self.base_path
                                     + 'test_generated_flight_log20190123'
                                       '_2.ipynb')
        self.assertTrue(file_exists)

    def test_date_and_flight_number(self):
        # Gets run in the Jupyter Notebook.
        # Extracts the desired variables from the sample notebook
        date_and_flight_number = notebook_results[1]
        # Checks that the output is as expected
        expected_date_and_flight_number = ('20190123', '2')
        self.assertEqual(expected_date_and_flight_number,
                         date_and_flight_number)

    def test_csv_frame(self):
        # Gets run in the Jupyter Notebook.
        # Runs the sample notebook code to extract teh desired variable
        csv_frame = notebook_results[2]
        try:
            # Checks if the key is correct
            csv_frame['X']
            # Failed if no error is raised
            self.assertFalse(True)
        except KeyError:
            # Passes if an error is raised
            self.assertTrue(True)
        # Loads values
        values = csv_frame['Temp0_degC_ArduinoMicro_20190123_Flight2'
        ]
        # Compares expected values with values from the data frame
        expected_values = [24.38, 24.38, 24.38, 24.44, 25.31, 26.69, 27.69,
                           28.44, 29.00, 29.38, 29.69, 30.00, 30.06, 29.69,
                           29.38, 29.06, 28.81, 28.63, 28.38, 28.25, 28.06,
                           27.88, 27.75, 27.63, 27.50, 27.38, 27.25]
        for item in range(len(expected_values)):
            self.assertEqual(expected_values[item], values[item])

    def test_flight_data_time_sorter(self):
        # Gets run in the Jupyter Notebook.
        data_frames = notebook_results[3]
        # Checks that the frames were split into the correct number of
        # subframes
        self.assertEqual(len(data_frames), 12)
        # Assigns expected titles
        data_frame_section_titles = [
            'Status_unavailable_GPS_20190123_Flight2',
            'aileron_CH1_us_RCIN_20190123_Flight2',
            'Altitude_m_BARO_20190123_Flight2',
            'Airspeed_mpers_ARSP_20190123_Flight2',
            'Desired_Roll_degrees_ATT_20190123_Flight2',
            'VibeX_mperspers_VIBE_20190123_Flight2',
            'NavRoll_unavailable_CTUN_20190123_Flight2',
            'AOA_degrees_AOA_20190123_Flight2',
            'Action_time_hh:mm_WEATHER_20190123_Flight2',
            'take_off_direction_RUNWAY_20190123_Flight2',
            'm_empty_kg_AIRCRAFT_20190123_Flight2',
            'Temp0_degC_ArduinoMicro_20190123_Flight2']
        correct_titles = 0
        # Runs through titles. Checks that each title can be called and adds 1
        # to the counter (correct_titles). If a title is not correct/cannot be
        # called, raise a failure.
        for item in range(len(data_frame_section_titles)):
            try:
                data_frames[item][data_frame_section_titles[item]]
                correct_titles += 1
            except KeyError:
                self.assertEqual(1, 2)
        # Checks that all titles are correct
        self.assertEqual(correct_titles, 12)

    def test_flight_data_and_axis(self):
        # Gets run in the Jupyter Notebook.
        # Loads the data generated previously
        flight_data_and_axis = notebook_results[4]
        # Assigns expected titles
        expected_titles = ['GPS', 'RCIN', 'BARO', 'ARSP', 'ATT', 'VIBE',
                           'CTUN', 'AOA', 'WEATHER', 'RUNWAY', 'AIRCRAFT', 'ArduinoMicro']
        # Checks that the titles are present and in the correct place
        for item in range(len(expected_titles)):
            self.assertEqual(expected_titles[item], flight_data_and_axis[1][item][0])

    def test_file_type_finder(self):
        # Detects xlsx files from the test_files folder
        detected_files = flight_log_code.file_type_finder(self.base_path, ".xlsx")
        expected_file_names = ['test_xlsx.xlsx']
        # Checks that all the expected files are present
        for file_name in expected_file_names:
            if file_name in detected_files:
                file_detected = True
            else:
                file_detected = False
            self.assertTrue(file_detected)

    def test_compile_and_compress(self):
        # Runs the compiler and compressor.
        flight_log_code.compile_and_compress(self.flight_data_file_path, [self.flight_data_file_name],
                                             self.csv_flight_data_file_path, self.csv_flight_data_name,
                                             self.comp_data_file_path)
        pickle_file_name = self.base_path + "test_xlsx.pkl"  # Defines the file name
        # Checks that the file exists
        self.assertTrue(os.path.exists(pickle_file_name))

    def test_dictionary_reader(self):
        # Returns a string of formatted weather data
        read_data = flight_log_code.dictionary_reader(self.weather_data_dict, units_present=True, flight_number="1")
        # Adds a string of formatted runway data
        read_data += flight_log_code.dictionary_reader(self.runway_data_dict, units_present=False, flight_number="2")
        expected_results = [" | Flight | Action time  (hh:mm) | Pressure  (Pa) | Temperature  (C) | Wind direction"
                            "  (degrees) | Wind speed  (mps) |",
                            " | --- | --- | --- | --- | --- | --- |",
                            "| Flight 1| 12:34 | 1234567 | -12.3 | 123 | 12.3 |",
                            " | Flight | runway surface  | surface condition  | take off direction  |",
                            " | --- | --- | --- | --- |",
                            "| Flight 2| XYZ | ZYX | 321 |"]
        # Checks that the correct data is present in the read_data string
        for string in expected_results:
            self.assertTrue(string in read_data)

    def test_multi_string_data_formatter(self):
        """A method to test the multi_string_data_formatter()"""
        # Assigns testing strings
        input1 = "Fish,Ca t,Dog,35, Mouse,   89"
        flight_names_1 = [1, 2, 3, 4, 5, 6]
        debug_name_1 = "Input 1"
        input2 = ""
        flight_names_2 = [0, 1]
        debug_name_2 = "Input 2"
        input3 = "Fail1"
        flight_names_3 = [7, 8]
        debug_name_3 = "TEST"
        # Assigns testing outputs
        expected_output1 = ["Fish", "Cat", "Dog", "35", "Mouse", "89"]
        expected_output2 = [""]
        expected_output3 = False
        # Tests that the method works as expected
        self.assertEqual(flight_log_code.multi_string_data_formatter(input1, flight_names_1, debug_name_1),
                         expected_output1)
        self.assertEqual(flight_log_code.multi_string_data_formatter(input2, flight_names_2, debug_name_2),
                         expected_output2)
        try:
            flight_log_code.multi_string_data_formatter(input3, flight_names_3, debug_name_3)
            # Should raise an index error. If not, fail the test.
            self.assertTrue(expected_output3)
        except IndexError:
            # Planned error
            self.assertTrue(True)

    def test_remove_dictionary_key(self):
        """A method to test the remove_dictionary_key()"""
        # Create variables
        dictionary1 = {"DOG": "4F", "Mouse": "Cat", "Donkey": 7, 8906: "Fish", "PiG": "Farm"}
        keys_to_remove = ["Mouse", 8906, "DOG"]
        # Remove the keys and check if they have been removed
        for key_index in range(len(keys_to_remove)):
            output = flight_log_code.remove_dictionary_key(dictionary1, keys_to_remove[key_index])
            if keys_to_remove[key_index] in output.keys():
                # Fail if the key is still present
                self.assertTrue(False)
            else:
                # Key successfully removed, pass the test
                self.assertTrue(True)

    def test_multi_dictionary_data_formatter(self):
        """A method to test the multi_dictionary_data_formatter()"""
        # Create variables
        dictionary1 = {"DOG": "4F, Squirrel", "Mouse": "Cat, 78", "Donkey": "7, 7", 8906: "Fish , Bird",
                       "PiG": "Farm, Animal"}
        debug_name1 = "dict1"
        flight_dates1 = [0, 1]
        dictionary2 = {"Face": "Nose, Ear, Mouth"}
        flight_dates2 = [2, 3]
        debug_name2 = "TEST"

        # Assigns expected outputs:
        expected_output_dict1_1 = ["4F", "Cat", "7", "Fish", "Farm"]
        expected_output_dict1_2 = ["Squirrel", "78", "7", "Bird", "Animal"]
        key = ["DOG", "Mouse", "Donkey", 8906, "PiG"]
        expected_output_dict2 = False

        # Runs the method
        output1 = flight_log_code.multi_dictionary_data_formatter(dictionary1, flight_dates1, debug_name1)

        try:
            flight_log_code.multi_dictionary_data_formatter(dictionary2, flight_dates2, debug_name2)
            # Fails if this does not raise an error
            self.assertTrue(expected_output_dict2)
        except IndexError:
            # Passes if an error is raised
            self.assertFalse(expected_output_dict2)

        for key_index in range(len(dictionary1.keys())):
            self.assertEqual(output1[0][key[key_index]], expected_output_dict1_1[key_index])
            self.assertEqual(output1[1][key[key_index]], expected_output_dict1_2[key_index])

if __name__ == '__main__':
    unittest.main()


# -*- coding: utf-8 -*-
import os
import shutil
import time
from PIL import Image
from PIL.ExifTags import TAGS
# 照片自动按时间归类小程序，对于给定的目录，扫描其下的所有图片，取它的相机时间或修改时间，然后把它放在输出目录的图片时间戳YYYYMM所在的目录。
# az 2018-11-27
def ispic(f=None):
    pictype=['.jpg','.jpeg','.png','.bmp','.gif']
    if os.path.splitext(f)[-1].lower() in pictype:
        return True
    else:
        return False
def getImgTag(f=None,t='DateTime'):
    im = Image.open(f)
    info = im._getexif()
    dt = time.strftime('%Y:%m:%d %H:%M:%S',time.localtime(os.path.getmtime(f)))
    if info:
        for tag,value in info.items():
            key = TAGS.get(tag,tag)
            #print (key,value)
            if key == t:
                dt = value
                break
    return dt
def mkdirifnotexists(d=None):
    if not os.path.isdir(d):
        os.makedirs(d, exist_ok=True)
        return True
    else:
        return False
def getfiles(d=None):
    for i in os.walk(d):
        for j in i[2]:
            if ispic(os.path.join(i[0],j)):
                yield os.path.join(i[0],j)
def handler():
    rtdir="d:/1/"
    outdir="d:/1/"
    for f in getfiles(rtdir):
        subdir = getImgTag(f)[:7].replace(':','')
        mkdirifnotexists(outdir+subdir)
        try:
            shutil.move(f, outdir+subdir)
        except:
            pass
if __name__ == "__main__":
    #print ( getImgTag(f='D:/1/IMG_7901.JPG') )
    #print ( getImgTags(f='D:/1/IMG_7581.JPG') )
    handler()


from galvanalyser_test_case import GalvanalyserTestCase
from galvanalyser.database.experiment import (
    InputFile,
    TimeseriesDataRow,
    DatasetRow,
)
import datetime

class DummyInput(InputFile):

    data = [
        [1.0, 2.0, 3.0],
        [2.0, 3.0, 4.0],
        [3.0, 4.0, 5.0],
        [4.0, 5.0, 6.0],
        [5.0, 6.0, 7.0],
        [6.0, 7.0, 8.0],
    ]

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

    def get_file_column_to_standard_column_mapping(self):
        return {"amps": 2, "volts": 3, "test_time": 1}

    def load_data(self, file_path, columns, column_renames=None):
        columns_of_interest = []
        column_names = ["test_time", "amps", "volts"]
        for col_idx, column_name in enumerate(column_names):
            if column_name in columns:
                columns_of_interest.append(col_idx)
            if column_renames is not None and column_name in column_renames:
                column_names[col_idx] = column_renames[column_name]
        for row in self.data:
            yield {
                column_names[col_idx]: row[col_idx]
                for col_idx in columns_of_interest
            }

    def get_data_labels(self):
        for item in []:
            yield item

    def load_metadata(self):
        metadata = {}
        metadata["Machine Type"] = "Dummy"
        metadata["Dataset Name"] = "Dummy1"
        metadata["Date of Test"] = datetime.datetime.now()
        columns_with_data = {
            "amps": {
                "has_data": True, "is_numeric": True,
                "unit": "Amps",

            },
            "volts": {
                "has_data": True, "is_numeric": True,
                "unit": "V",

            },
            "test_time": {
                "has_data": True, "is_numeric": True,
                "unit": "s",

            },
        }
        metadata["num_rows"] = len(self.data)
        metadata["first_sample_no"] = 1
        metadata["last_sample_no"] = len(self.data)
        print(metadata)
        return metadata, columns_with_data

    def validate_file(self):
        return

class TestInputFile(GalvanalyserTestCase):
    def test_dummy_input(self):

        dataset = DatasetRow(
            name='dummy',
            date=datetime.datetime.now(),
            dataset_type='test',
        )
        dataset.insert(self.harvester_conn)

        input_file = DummyInput('nofile')
        TimeseriesDataRow.insert_input_file(
            input_file, dataset.id, self.harvester_conn
        )
        latest_row = TimeseriesDataRow\
            .select_latest_by_dataset_id(dataset.id, self.harvester_conn)
        self.assertCountEqual(
            [6.0, 7.0, 8.0],
            [x.value for x in latest_row]
        )


from PyQt5.QtWidgets import *
from include.marver_r.RMLOracle import RMLOracle
from include.marver_r.TLOracle import TLOracle
from src.marver_r.GuiLog import GuiLog
from include.marver_r.Monitor import Monitor
from include.marver_r.Property import Property
from bin.UI_MARVer import Ui_MainWindow
from include.marver_r.Verifier import Verifier
from include.marver_r.XmlParser import XmlParser
import json

class GuiVerifier:
    def __init__(self, ui: Ui_MainWindow = None, logger: GuiLog = None, monitor: [Monitor] = [],
                 rmlOracle: RMLOracle = None, tlOracle: TLOracle = None):
        self.__rmlOracle = rmlOracle
        self.__ui = ui
        self.__monitor = monitor
        self.__tlOracle = tlOracle
        self.__logger = logger

        self.initComponents()
        # self.test()

    def initComponents(self):
        self.__ui.btnPropertyDefineDelete.setEnabled(False)
        self.__ui.btnPropertyDefineUpdate.setEnabled(False)
        self.__ui.btnPropertyDefineCreate.setEnabled(True)

    def getProperties(self) -> [Property]:
        temp = []
        temp.extend(self.__rmlOracle.getProperties())
        temp.extend(self.__tlOracle.getProperties())
        return temp

    def addProperty(self):
        if not self.checkMonitorIsExist():
            self.__logger.printLog("Please create a monitor first ! ", "red")
            return

        if self.__ui.txtPropertyDefineName.text() == "" or \
                self.__ui.txtPropertyDefineDescription.toPlainText() == "" \
                or self.__ui.txtPropertyDefineFormula.text() == "" \
                or not self.__ui.lwPropertyDefineNodes.selectedItems():
            self.__logger.printLog(message="Please fill in all fields !!", color="red")
            return

        if self.__ui.cbxPropertyDefineType.currentText() == "TL Oracle":
            if self.__tlOracle.isExist(self.__ui.txtPropertyDefineName.text())[0] or \
                    self.__rmlOracle.isExist(self.__ui.txtPropertyDefineName.text())[0]:
                self.__logger.printLog(message="The name of the property is not unique. Change the name !!",
                                       color="red")
                return
            else:
                self.__tlOracle.setProperties(Property(name=self.__ui.txtPropertyDefineName.text(),
                                                       description=self.__ui.txtPropertyDefineDescription.toPlainText(),
                                                       formula=self.__ui.txtPropertyDefineFormula.text(),
                                                       nodeNames=[item.text() for item in
                                                                  self.__ui.lwPropertyDefineNodes.selectedItems()]))
                self.__logger.printLog(message="The property is added successfully", color="green")
        else:
            if self.__rmlOracle.isExist(self.__ui.txtPropertyDefineName.text())[0] or \
                    self.__tlOracle.isExist(self.__ui.txtPropertyDefineName.text())[0]:
                self.__logger.printLog(message="The name of the property is not unique. Change the name !!",
                                       color="red")
                return
            else:
                self.__rmlOracle.setProperties(Property(name=self.__ui.txtPropertyDefineName.text(),
                                                        description=self.__ui.txtPropertyDefineDescription.toPlainText(),
                                                        formula=self.__ui.txtPropertyDefineFormula.text(),
                                                        nodeNames=[item.text() for item in
                                                                   self.__ui.lwPropertyDefineNodes.selectedItems()]))
                self.__logger.printLog(message="The property is added successfully", color="green")
        self.__ui.txtPropertyDefineName.clear()
        self.__ui.txtPropertyDefineDescription.clear()
        self.__ui.txtPropertyDefineFormula.clear()
        self.__ui.lwPropertyDefineNodes.clearSelection()
        self.updateVerifierDefineCbx()
        self.updateVerifierSaveCbx()

    def editProperty(self):
        property = self.__tlOracle.getPropertyByName(self.__ui.cbxPropertyDefineVerifier.currentText())
        if property:
            self.__tlOracle.deletePropertyByName(property.getName())
        else:
            property = self.__rmlOracle.getPropertyByName(self.__ui.cbxPropertyDefineVerifier.currentText())
            if property:
                self.__rmlOracle.deletePropertyByName(property.getName())
        self.addProperty()

    def deleteProperty(self):
        propertyName = self.__ui.cbxPropertyDefineVerifier.currentText()
        if self.__tlOracle.isExist(propertyName)[0]:
            self.__tlOracle.getProperties().pop(self.__tlOracle.isExist(propertyName)[1])
        else:
            self.__rmlOracle.getProperties().pop(self.__rmlOracle.isExist(propertyName)[1])

        self.__ui.txtPropertyDefineName.clear()
        self.__ui.txtPropertyDefineDescription.clear()
        self.__ui.txtPropertyDefineFormula.clear()
        self.__ui.lwPropertyDefineNodes.clearSelection()
        self.updateVerifierDefineCbx()
        self.updateVerifierSaveCbx()

    def importProperty(self):
        filePath = self.openFileDialogWindow('json')
        if not filePath:
            return
        properties = self.getJSONFileContent(filePath=filePath)
        for property in properties["properties"]:
            if self.__ui.cbxPropertyImportType.currentText() == "TL Oracle":
                self.__tlOracle.setProperties(Property(name=property["name"],
                                                       description=property["description"],
                                                       formula=property["formula"],
                                                       nodeNames=property["nodeNames"]))
                self.__logger.printLog(message="TL properties imported successfully", color="green")
            else:
                self.__rmlOracle.setProperties(Property(name=property["name"],
                                                        description=property["description"],
                                                        formula=property["formula"],
                                                        nodeNames=property["nodeNames"]))
                self.__logger.printLog(message="RML properties imported successfully", color="green")
        self.updateVerifierDefineCbx()
        self.updateVerifierSaveCbx()

    def openFileDialogWindow(self, ext) -> str:
        try:
            filePath, check = QFileDialog.getOpenFileName(None, "Open File", "3rdparty/rosmonitoring/oracle",
                                                          "Text Files (*." + ext + ")")
            if filePath:
                self.__logger.printLog(message="File selection completed successfully", color="green")
                return filePath
            else:
                raise IOError(f"An error occurred while opening the file")
        except IOError:
            self.__logger.printLog(message=f"An error occurred while opening the file", color="red")
        except:
            self.__logger.printLog(message="ERROR in openFileDialogWindow()", color="red")

    def getJSONFileContent(self, filePath: str):
        try:
            with open(filePath) as file:
                if not file:
                    raise IOError(f"An error occurred while reading the file")
                self.__logger.printLog(message="File content read successfully", color="green")
                return json.load(file)
        except IOError:
            self.__logger.printLog(message=f"An error occurred while reading the file", color="red")
        except:
            self.__logger.printLog(message="ERROR in getJSONFileContent()", color="red")

    def saveProperty2File(self):
        fileName = self.__ui.txtPropertySaveFileName.text()
        if fileName:
            items = self.__ui.lwPropertySaveSelect.selectedItems()
            if items:
                try:
                    if self.__ui.cbxPropertySaveType.currentText() == "TL Oracle":
                        with open(f'3rdparty/rosmonitoring/oracle/TLOracle/properties/{fileName}.json', 'w') as f:
                            f.write(self.preparePropertiesJSON(
                                self.__tlOracle.getPropertiesByName(names=[item.text() for item in items])))
                    else:
                        with open(f'3rdparty/rosmonitoring/oracle/RMLOracle/rml/properties/{fileName}.json',
                                  'w') as f:
                            f.write(self.preparePropertiesJSON(
                                self.__rmlOracle.getPropertiesByName(names=[item.text() for item in items])))

                    self.__ui.txtPropertySaveFileName.clear()
                    self.__logger.printLog(message="File content saved successfully", color="green")
                except IOError:
                    self.__logger.printLog(message=f"An error occurred while writing to the file", color="red")
                except:
                    self.__logger.printLog(message="ERROR in saveProperty2File()", color="red")

            else:
                self.__logger.printLog(message="Please select the properties", color="red")
        else:
            del fileName
            self.__logger.printLog(message="Please fill the file name", color="red")

    @staticmethod
    def preparePropertiesJSON(properties: [Verifier]) -> str:
        content = {"properties": []}
        for property in properties:
            content["properties"].append(
                {"name": property.getName(), "description": property.getDescription(), "formula": property.getFormula(),
                 "nodeNames": property.getNodeNames()})

        return json.dumps(content, indent=4)

    def setVerifierComponentsStatus(self):
        if self.__ui.cbxPropertyDefineVerifier.currentText() == "New Property":
            self.__ui.btnPropertyDefineDelete.setEnabled(False)
            self.__ui.btnPropertyDefineUpdate.setEnabled(False)
            self.__ui.btnPropertyDefineCreate.setEnabled(True)
        else:
            verifier = self.__tlOracle.getPropertyByName(self.__ui.cbxPropertyDefineVerifier.currentText())
            if verifier:
                self.__ui.cbxPropertyDefineType.setCurrentIndex(0)
            else:
                self.__ui.cbxPropertyDefineType.setCurrentIndex(1)
                verifier = self.__rmlOracle.getPropertyByName(self.__ui.cbxPropertyDefineVerifier.currentText())

            self.__ui.txtPropertyDefineName.setText(verifier.getName())
            self.__ui.txtPropertyDefineDescription.setPlainText(verifier.getDescription())
            self.__ui.txtPropertyDefineFormula.setText(verifier.getFormula())
            self.__ui.lwPropertyDefineNodes.clear()

            for name in verifier.getNodeNames():
                self.__ui.lwPropertyDefineNodes.addItem(name)

            self.__ui.btnPropertyDefineDelete.setEnabled(True)
            self.__ui.btnPropertyDefineUpdate.setEnabled(True)
            self.__ui.btnPropertyDefineCreate.setEnabled(False)

    def updateVerifierDefineCbx(self):
        self.__ui.cbxPropertyDefineVerifier.clear()
        self.__ui.cbxPropertyDefineVerifier.addItem("New Property")
        for tlVerifier in self.__tlOracle.getProperties():
            self.__ui.cbxPropertyDefineVerifier.addItem(tlVerifier.getName())
        for rmlVerifier in self.__rmlOracle.getProperties():
            self.__ui.cbxPropertyDefineVerifier.addItem(rmlVerifier.getName())

        self.__ui.cbxPropertyDefineVerifier.setCurrentIndex(0)
        self.__ui.btnPropertyDefineDelete.setEnabled(False)
        self.__ui.btnPropertyDefineUpdate.setEnabled(False)
        self.__ui.btnPropertyDefineCreate.setEnabled(True)

        self.__ui.lwPropertyDefineNodes.clear()
        if self.checkMonitorIsExist():
            for node in self.__monitor[0].getNodes():
                self.__ui.lwPropertyDefineNodes.addItem(node.getName())

    def updateVerifierSaveCbx(self):
        self.__ui.lwPropertySaveSelect.clear()
        if self.__ui.cbxPropertySaveType.currentText() == "TL Oracle":
            for tlVerifier in self.__tlOracle.getProperties():
                self.__ui.lwPropertySaveSelect.addItem(tlVerifier.getName())
        else:
            for rmlVerifier in self.__rmlOracle.getProperties():
                self.__ui.lwPropertySaveSelect.addItem(rmlVerifier.getName())
        self.__ui.cbxPropertyDefineVerifier.setCurrentIndex(0)

    def checkMonitorIsExist(self) -> bool:
        if not self.__monitor:
            return False
        return True

    def clearVerifierComponents(self):
        self.__ui.cbxPropertyDefineVerifier.clear()
        self.__ui.cbxPropertyDefineVerifier.addItem("New Property")
        self.__ui.cbxPropertyDefineVerifier.setCurrentIndex(0)
        self.__ui.txtPropertyDefineName.clear()
        self.__ui.txtPropertyDefineDescription.clear()
        self.__ui.lwPropertyDefineNodes.clear()
        self.__ui.lwPropertySaveSelect.clear()
        self.__ui.txtPropertySaveFileName.clear()
        self.__ui.txtPropertyDefineFormula.clear()

        self.__ui.btnPropertyDefineDelete.setEnabled(False)
        self.__ui.btnPropertyDefineUpdate.setEnabled(False)
        self.__ui.btnPropertyDefineCreate.setEnabled(True)

    def importUppaalModel(self):
        f = self.openFileDialogWindow('xml')
        file = XmlParser(f)

        for formula in file.getQueries():
            formula=formula.strip()
            if self.tctl2Reelay(formula):
                self.__ui.cbxPropertyDefineFormula.addItem(self.tctl2Reelay(formula))

        self.__ui.cbxPropertyDefineFormula.setEnabled(True)
        print()

    def tctl2Reelay(self,formula: str) -> str:
        # check str is blank or not
        if not formula or "deadlock" in formula:
            return None

        temp = ""
        # Eliminate E<> or A[]
        for f in formula.split(" ")[1:]:
            if "&&" in f:
                temp += " and "
            elif "||" in f:
                temp += " or "
            elif "." in f:
                temp += str(f.split(".")[0]) + "_state: " + str(f.split(".")[1])
            else:
                temp += f

        return "{" + temp + "}"


# exercise 01

"""
Which day of the week?

Hurricane Andrew, which hit Florida on August 24, 1992, was one of the costliest and deadliest hurricanes in US history. Which day of the week did it make landfall?

Let's walk through all of the steps to figure this out.
"""

# steps

"""
Import date from datetime.
---
Create a date object for August 24, 1992.
---
Now ask Python what day of the week Hurricane Andrew hit (remember that Python counts days of the week starting from Monday as 0, Tuesday as 1, and so on).
"""

# solution

# Import date from datetime
from datetime import date

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

# Import date from datetime
from datetime import date

# Create a date object
hurricane_andrew = date(1992,8,24)

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

# Import date from datetime
from datetime import date

# Create a date object
hurricane_andrew = date(1992, 8, 24)

# Which day of the week is the date?
print(hurricane_andrew.weekday())

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

# exercise 02

"""
How many hurricanes come early?

In this chapter, you will work with a list of the hurricanes that made landfall in Florida from 1950 to 2017. There were 235 in total. Check out the variable florida_hurricane_dates, which has all of these dates.

Atlantic hurricane season officially begins on June 1. How many hurricanes since 1950 have made landfall in Florida before the official start of hurricane season?
"""

# steps

"""
Complete the for loop to iterate through florida_hurricane_dates.
Complete the if statement to increment the counter (early_hurricanes) if the hurricane made landfall before June.
"""

# solution

# Counter for how many before June 1
early_hurricanes = 0

# We loop over the dates
for hurricane in florida_hurricane_dates:
  # Check if the month is before June (month number 6)
  if hurricane.month < 6:
    early_hurricanes = early_hurricanes + 1

print(early_hurricanes)

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

# exercise 03

"""
Subtracting dates

Python date objects let us treat calendar dates as something similar to numbers: we can compare them, sort them, add, and even subtract them. This lets us do math with dates in a way that would be a pain to do by hand.

The 2007 Florida hurricane season was one of the busiest on record, with 8 hurricanes in one year. The first one hit on May 9th, 2007, and the last one hit on December 13th, 2007. How many days elapsed between the first and last hurricane in 2007?
"""

# steps

"""
Import date from datetime.
Create a date object for May 9th, 2007, and assign it to the start variable.
Create a date object for December 13th, 2007, and assign it to the end variable.
Subtract start from end, to print the number of days in the resulting timedelta object.
"""

# solution

# Import date
from datetime import date

# Create a date object for May 9th, 2007
start = date(2007, 5, 9)

# Create a date object for December 13th, 2007
end = date(2007, 12, 13)

# Subtract the two dates and print the number of days
print((end - start).days)

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

# exercise 04

"""
Counting events per calendar month

Hurricanes can make landfall in Florida throughout the year. As we've already discussed, some months are more hurricane-prone than others.

Using florida_hurricane_dates, let's see how hurricanes in Florida were distributed across months throughout the year.

We've created a dictionary called hurricanes_each_month to hold your counts and set the initial counts to zero. You will loop over the list of hurricanes, incrementing the correct month in hurricanes_each_month as you go, and then print the result.
"""

# steps

"""
Within the for loop:
Assign month to be the month of that hurricane.
Increment hurricanes_each_month for the relevant month by 1.
"""

# solution

# A dictionary to count hurricanes per calendar month
hurricanes_each_month = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6:0,
		  				 7: 0, 8:0, 9:0, 10:0, 11:0, 12:0}

# Loop over all hurricanes
for hurricane in florida_hurricane_dates:
  # Pull out the month
  month = hurricane.month
  # Increment the count in your dictionary by one
  hurricanes_each_month[month] += 1

print(hurricanes_each_month)

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

# exercise 05

"""
Putting a list of dates in order

Much like numbers and strings, date objects in Python can be put in order. Earlier dates come before later ones, and so we can sort a list of date objects from earliest to latest.

What if our Florida hurricane dates had been scrambled? We've gone ahead and shuffled them so they're in random order and saved the results as dates_scrambled. Your job is to put them back in chronological order, and then print the first and last dates from this sorted list.
"""

# steps

"""
Print the first and last dates in dates_scrambled.
---
Sort dates_scrambled using Python's built-in sorted() function, and save the results to dates_ordered.
Print the first and last dates in dates_ordered.
"""

# solution

# Print the first and last scrambled dates
print(dates_scrambled[0])
print(dates_scrambled[-1])

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

# Print the first and last scrambled dates
print(dates_scrambled[0])
print(dates_scrambled[-1])

# Put the dates in order
dates_ordered = sorted(dates_scrambled)

# Print the first and last ordered dates
print(dates_ordered[0])
print(dates_ordered[-1])

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

# exercise 06

"""
Printing dates in a friendly format

Because people may want to see dates in many different formats, Python comes with very flexible functions for turning date objects into strings.

Let's see what event was recorded first in the Florida hurricane data set. In this exercise, you will format the earliest date in the florida_hurricane_dates list in two ways so you can decide which one you want to use: either the ISO standard or the typical US style.
"""

# steps

"""
Assign the earliest date in florida_hurricane_dates to first_date.
Print first_date in the ISO standard. For example, December 1st, 2000 would be "2000-12-01".
Print first_date in the US style, using .strftime(). For example, December 1st, 2000 would be "12/1/2000".
"""

# solution

# Assign the earliest date to first_date
first_date = min(florida_hurricane_dates)

# Convert to ISO and US formats
iso = "Our earliest hurricane date: " + first_date.strftime('%Y-%m-%d')
us = "Our earliest hurricane date: " + first_date.strftime("%m/%d/%Y")

print("ISO: " + iso)
print("US: " + us)

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

# exercise 07

"""
Representing dates in different ways

date objects in Python have a great number of ways they can be printed out as strings. In some cases, you want to know the date in a clear, language-agnostic format. In other cases, you want something which can fit into a paragraph and flow naturally.

Let's try printing out the same date, August 26, 1992 (the day that Hurricane Andrew made landfall in Florida), in a number of different ways, to practice using the .strftime() method.

A date object called andrew has already been created.
"""

# steps

"""
Print andrew in the format 'YYYY-MM'.
---
Print andrew in the format 'MONTH (YYYY)', using %B for the month's full name, which in this case will be August.
---
Print andrew in the format 'YYYY-DDD' (where DDD is the day of the year) using %j.
"""

# solution

# Import date
from datetime import date

# Create a date object
andrew = date(1992, 8, 26)

# Print the date in the format 'YYYY-MM'
print(andrew.strftime('%Y-%m'))

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

# Import date
from datetime import date

# Create a date object
andrew = date(1992, 8, 26)

# Print the date in the format 'MONTH (YYYY)'
print(andrew.strftime('%B (%Y)'))

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

# Import date
from datetime import date

# Create a date object
andrew = date(1992, 8, 26)

# Print the date in the format 'YYYY-DDD'
print(andrew.strftime('%Y-%j'))

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


from django.shortcuts import render
from django.shortcuts import render_to_response, get_object_or_404
from .models import MainProduct,Category

#category list view

def home(request):
	post = MainProduct.objects.all()
	recent_post = MainProduct.objects.all()[:5]
	category_list = Category.objects.all()
	context_dict  = {'categories':category_list,'post':post,'recent_post':recent_post,}
	return render(request,'product/index.html',context_dict)

# Single Product detail view

def product_detail(request, slug):
    items = MainProduct.objects.filter(slug=slug)
    category_item = Category.objects.filter(slug=slug)
    return render_to_response('product/product-detail.html', {'items':items,'category-items':category_item,})

# Single Category with associated Product view

def category_list(request, category_name_slug):
    context_dict = {}
    try:
        category = Category.objects.get(slug=category_name_slug)
        context_dict['category_name'] = category.name

        posts = MainProduct.objects.filter(categories=category)
        context_dict['posts'] = posts
        context_dict['category'] = category

    except Category.DoesNotExist:
        pass
    return  render(request, 'product/category-list.html',context_dict)


"""Contains unittests for BOUT paths."""

from pathlib import Path
from typing import Callable

import pytest

from bout_runners.parameters.bout_paths import BoutPaths

def test_bout_path(
    yield_conduction_path: Path, copy_bout_inp: Callable[[Path, str], Path]
) -> None:
    """
    Test that BoutPath is copying files.

    Parameters
    ----------
    yield_conduction_path : Path
        Path to the BOUT++ conduction example
        See the yield_conduction_path for more details
    copy_bout_inp : function
        Function which copies BOUT.inp and returns the path to the temporary
        directory
        See the copy_bout_inp fixture for more details
    """
    project_path = yield_conduction_path
    tmp_path_name = "tmp_BoutPath_test"

    # NOTE: We use the fixture here to automatically remove the
    # directory after the test
    tmp_path_dir = copy_bout_inp(project_path, tmp_path_name)

    # We remove the BOUT.inp to verify that BoutPaths copied the file
    tmp_path_dir.joinpath("BOUT.inp").unlink()

    bout_paths = BoutPaths(project_path=project_path, bout_inp_dst_dir=tmp_path_name)

    assert project_path.joinpath(tmp_path_name, "BOUT.inp").is_file()

    # Test that an error is raised if the source dir is pointed to a directory
    # without BOUT.inp
    with pytest.raises(FileNotFoundError):
        # NOTE: type: ignore due to https://github.com/python/mypy/issues/3004
        bout_paths.bout_inp_src_dir = "dir_without_BOUT_inp"  # type: ignore


from models.mobilenetv2 import MobileNetV2, MobileNetV2VGGBlock
import os
import os.path as osp

import torch
import torch.nn as nn
import torch.optim as optim
import torch.utils.model_zoo as model_zoo
from torch.utils.data import DataLoader
from tqdm import tqdm
import pytorch_lightning as pl
from pytorch_lightning.callbacks import ModelCheckpoint, LearningRateMonitor
from pytorch_lightning.loggers import TensorBoardLogger
from pytorch_lightning.loggers.neptune import NeptuneLogger

from config import Config as cfg
from datasets import WiderFace
from models.mobilenetv2 import MobileNetTemperWrapper, configs

pl.seed_everything(42)

# Data Setup
traindataset = WiderFace(cfg.dataroot, cfg.annfile, cfg.sigma,
                         cfg.downscale, cfg.insize, cfg.train_transforms, 'train')
trainloader = DataLoader(traindataset, batch_size=cfg.batch_size,
                         pin_memory=cfg.pin_memory, num_workers=cfg.num_workers)
valdataset = WiderFace(cfg.dataroot, cfg.annfile, cfg.sigma,
                       cfg.downscale, cfg.insize, cfg.train_transforms, 'val')
valloader = DataLoader(valdataset, batch_size=cfg.batch_size,
                       pin_memory=cfg.pin_memory, num_workers=cfg.num_workers)

device = 'cpu'

if __name__ == '__main__':
    mode = 'temper'
    config = configs[0]
    if mode == 'temper':
        log_name = 'centerface_logs/{}'.format(mode)
        logger = TensorBoardLogger(
            save_dir=os.getcwd(),
            name=log_name,
            # log_graph=True,
            # version=0
        )
        loss_callback = ModelCheckpoint(
            monitor='val_loss',
            dirpath='',
            filename='checkpoint-{epoch:02d}-{val_loss:.4f}',
            save_top_k=-1,
            mode='min',
        )
        lr_monitor = LearningRateMonitor(logging_interval='epoch')
        callbacks = [loss_callback, lr_monitor]
        model = MobileNetTemperWrapper(
            config['orig'](), config['tempered'](), mode, config["orig_module_names"], config["tempered_module_names"], config["is_trains"])
        checkpoint_path = 'checkpoints/checkpoint-epoch=55-val_loss=0.0230.ckpt'
        if device == 'cpu' or device == 'tpu':
            checkpoint = torch.load(
                checkpoint_path, map_location=lambda storage, loc: storage)
        else:
            checkpoint = torch.load(checkpoint_path)
        state_dict = checkpoint['state_dict']
        model.orig.migrate(state_dict, force=True, verbose=0)
        if device == 'tpu':
            trainer = pl.Trainer(
                max_epochs=90,
                logger=logger,
                callbacks=callbacks,
                tpu_cores=8
            )
        elif device == 'gpu':
            trainer = pl.Trainer(
                max_epochs=90,
                logger=logger,
                callbacks=callbacks,
                gpus=1
            )
        else:
            trainer = pl.Trainer(
                max_epochs=90,
                logger=logger,
                callbacks=callbacks
            )
        trainer.fit(model, trainloader, valloader)


from typing import List, Optional, Tuple, Union

import jax
import jax.numpy as jnp
import pywt
from jax.config import config
from functools import partial

from .conv_fwt import _get_filter_arrays
from .utils import Wavelet

config.update("jax_enable_x64", False)

@partial(jax.jit, static_argnames=['level'])
def wavedec2(
    data: jnp.ndarray,
    wavelet: jnp.ndarray,
    level: Optional[int] = None
) -> List[Union[jnp.ndarray, Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]]]:
    """Compute the two dimensional wavelet analysis transform on the last two dimensions of the input data array.

    Args:
        data (jnp.array): Jax array containing the data to be transformed. Assumed shape:
                         [batch size, hight, width].
        wavelet (Wavelet): A namedtouple containing the filters for the transformation.
        level (int): The max level to be used, if not set as many levels as possible
                               will be used. Defaults to None.
        mode (str): The desired padding mode. Choose reflect, symmetric or zero.
            Defaults to reflect.

    Returns:
        list: The wavelet coefficients in a nested list.
            The coefficients are in pywt order. That is:
            [cAn, (cHn, cVn, cDn), … (cH1, cV1, cD1)].
            A denotes approximation, H horizontal, V vertical
            and D diagonal coefficients.

    Examples:
        >>> import pywt, scipy.misc
        >>> import jaxwt as jwt
        >>> import jax.numpy as jnp
        >>> face = jnp.transpose(scipy.misc.face(), [2, 0, 1]).astype(jnp.float64)
        >>> jwt.wavedec2(face, pywt.Wavelet("haar"), level=2, mode="reflect")
    """
    data = jnp.expand_dims(data, 1)
    mode = "reflect"

    if level is None:
        level = pywt.dwtn_max_level(
            [data.shape[-1], data.shape[-2]], pywt.Wavelet("MyWavelet", wavelet)
        )

    wavelen = wavelet.shape[-1]
    dec_lo, dec_hi, _, _ = _get_custom_filter_arrays(wavelet)
    dec_filt = construct_2d_filt(lo=dec_lo, hi=dec_hi)

    result_lst: List[
        Union[jnp.ndarray, Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]]
    ] = []
    res_ll = data
    for _ in range(level):
        res_ll = _fwt_pad2d(res_ll, wavelen, mode=mode)

        ## FFT Routine
        res_ll = jnp.squeeze(_fwt_pad2d(res_ll, len(wavelet), mode=mode))
        dec_filt = jnp.squeeze(dec_filt)

        fft_shape = (res_ll.shape[1], res_ll.shape[2])

        a = jnp.fft.rfftn(res_ll, s=fft_shape, axes=[1,2])
        b = jnp.fft.rfftn(dec_filt, s=fft_shape, axes=[1,2])

        a = jnp.expand_dims(a, axis=1)
        ret = jnp.fft.irfftn(a * b, fft_shape)

        # Strides
        res = ret[:,:,::2,::2]

        res_ll, res_lh, res_hl, res_hh = jnp.split(res, 4, 1)
        result_lst.append([res_lh.squeeze(1), res_hl.squeeze(1), res_hh.squeeze(1)])
    result_lst.append(res_ll.squeeze(1))
    result_lst.reverse()
    return result_lst

@jax.jit
def waverec2(
    coeffs: List[Union[jnp.ndarray, Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]]],
    wavelet: jnp.ndarray,
) -> jnp.ndarray:
    """Compute a two dimensional synthesis wavelet transfrom.

       Use it to reconstruct the original input image from the wavelet coefficients.

    Args:
        coeffs (list): The input coefficients, typically the output of wavedec2.
        wavelet (Wavelet): The named tuple contining the filters used to compute the analysis transform.

    Returns:
        jnp.array: Reconstruction of the original input data array of shape [batch, height, width].

    Raises:
        ValueError: If `coeffs` is not in the shape as it is returned from `wavedec2`.

    Example:
        >>> import pywt, scipy.misc
        >>> import jaxwt as jwt
        >>> import jax.numpy as jnp
        >>> face = jnp.transpose(scipy.misc.face(), [2, 0, 1]).astype(jnp.float64)
        >>> transformed = jwt.wavedec2(face, pywt.Wavelet("haar"), level=2, mode="reflect")
        >>> jwt.waverec2(transformed, pywt.Wavelet("haar"))

    """
    if not isinstance(coeffs[0], jnp.ndarray):
        raise ValueError(
            "First element of coeffs must be the approximation coefficient tensor."
        )

    _, _, rec_lo, rec_hi = _get_custom_filter_arrays(wavelet)
    filt_len = rec_lo.shape[-1]
    rec_filt = construct_2d_filt(lo=rec_lo, hi=rec_hi)
    rec_filt = jnp.transpose(rec_filt, [1, 0, 2, 3])
    res_ll = jnp.expand_dims(coeffs[0], 1)
    for c_pos, res_lh_hl_hh in enumerate(coeffs[1:]):

        res_ll = jnp.concatenate(
            [
                res_ll,
                jnp.expand_dims(res_lh_hl_hh[0], 1),
                jnp.expand_dims(res_lh_hl_hh[1], 1),
                jnp.expand_dims(res_lh_hl_hh[2], 1),
            ],
            1,
        )

        ## FFT ROUTINE
        res_ll = jnp.squeeze(res_ll)
        rec_filt = jnp.squeeze(rec_filt)

        res_ll_dialated = jnp.zeros((res_ll.shape[0], res_ll.shape[1], res_ll.shape[2]*2, res_ll.shape[3]*2))
        res_ll = res_ll_dialated.at[:,:,::2,::2].set(res_ll)

        fft_shape = (res_ll.shape[-2], res_ll.shape[-1])
        a = jnp.fft.rfftn(res_ll, axes=[-2,-1])
        b = jnp.fft.rfftn(rec_filt, s=fft_shape, axes=[-2,-1])

        ret = jnp.fft.irfftn(a * b, s=fft_shape, axes=[-2,-1])
        res_ll = jnp.sum(ret, axis=1)

        # remove the padding
        padl = (2 * filt_len - 3) // 2
        padr = (2 * filt_len - 3) // 2
        padt = (2 * filt_len - 3) // 2
        padb = (2 * filt_len - 3) // 2
        if c_pos < len(coeffs) - 2:
            pred_len = res_ll.shape[-1] - (padl + padr)
            next_len = coeffs[c_pos + 2][0].shape[-1]
            pred_len2 = res_ll.shape[-2] - (padt + padb)
            next_len2 = coeffs[c_pos + 2][0].shape[-2]
            if next_len != pred_len:
                padr += 1
                pred_len = res_ll.shape[-1] - (padl + padr)
                assert (
                    next_len == pred_len
                ), "padding error, please open an issue on github "
            if next_len2 != pred_len2:
                padb += 1
                pred_len2 = res_ll.shape[-2] - (padt + padb)
                assert (
                    next_len2 == pred_len2
                ), "padding error, please open an issue on github "
        # print('padding', padt, padb, padl, padr)
        if padt > 0:
            res_ll = res_ll[..., padt:, :]
        if padb > 0:
            res_ll = res_ll[..., :-padb, :]
        if padl > 0:
            res_ll = res_ll[..., padl:]
        if padr > 0:
            res_ll = res_ll[..., :-padr]
    return res_ll

@jax.jit
def CQF_expansion(dec_lo: jnp.ndarray) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray, jnp.ndarray]:
    N = dec_lo.shape[0]
    a = jnp.empty((N,),int)
    a = a.at[::2].set(1)
    a = a.at[1::2].set(-1)

    #a[::2] = 1
    #a[1::2] = -1

    dec_hi = -1 * a * jnp.flip(dec_lo)
    rec_lo = jnp.flip(dec_lo)
    rec_hi = a * dec_lo
    return dec_lo, dec_hi, rec_lo, rec_hi

@jax.jit
def _get_custom_filter_arrays(
    wavelet: jnp.ndarray, dtype: jnp.dtype = jnp.float32  # type: ignore
) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray, jnp.ndarray]:
    """Extract the filter coefficients from an input wavelet object.

    Args:
        wavelet (Wavelet): input wavelet dec_lo parameters
        flip (bool): If true flip the input coefficients.
        dtype: The desired precision. Defaults to jnp.float64 .

    Returns:
        tuple: The dec_lo, dec_hi, rec_lo and rec_hi
            filter coefficients as jax arrays.
    """

    def create_array(filter: Union[List[float], jnp.ndarray]) -> jnp.ndarray:

        return jnp.expand_dims(filter, 0)

    dec_lo, dec_hi, rec_lo, rec_hi = CQF_expansion(wavelet)
    return dec_lo, dec_hi, rec_lo, rec_hi

@jax.jit
def construct_2d_filt(lo: jnp.ndarray, hi: jnp.ndarray) -> jnp.ndarray:
    """Construct 2d filters from 1d inputs using outer products.

    Args:
        lo (jnp.array): 1d lowpass input filter of size [1, length].
        hi (jnp.array): 1d highpass input filter of size [1, length].

    Returns
        jnp.array: 2d filter arrays of shape [4, 1, length, length].
    """
    ll = jnp.outer(lo, lo)
    lh = jnp.outer(hi, lo)
    hl = jnp.outer(lo, hi)
    hh = jnp.outer(hi, hi)
    filt = jnp.stack([ll, lh, hl, hh], 0)
    filt = jnp.expand_dims(filt, 1)
    return filt

def _fwt_pad2d(data: jnp.ndarray, filt_len: int, mode: str = "reflect") -> jnp.ndarray:
    padr = 0
    padl = 0
    padt = 0
    padb = 0
    if filt_len > 2:
        # we pad half of the total requried padding on each side.
        padr += (2 * filt_len - 3) // 2
        padl += (2 * filt_len - 3) // 2
        padt += (2 * filt_len - 3) // 2
        padb += (2 * filt_len - 3) // 2

    # pad to even singal length.
    if data.shape[-1] % 2 != 0:
        padr += 1
    if data.shape[-2] % 2 != 0:
        padb += 1

    data = jnp.pad(data, ((0, 0), (0, 0), (padt, padb), (padl, padr)), mode)
    return data


from django.urls import path
from unicodedata import name
from .views import *

urlpatterns = [
    path('', index, name='index'),
    path('IndexLogin/', indexLogin, name='indexLogin'),
    path('Login/', login, name='login'),
    path('CategoriaPerro/', catPerro, name='perrito'),
    path('CategoriaPerroLogeado/', catPerroLogin, name='perritoLog'),
    path('CategoriaGato/', catGato, name='gatito'),
    path('CategoriaGatoLogeado/', catGatoLogin, name='gatitoLog'),
    path('CategoriaAccesorio/', catAcc, name='accesorio'),
    path('CategoriaAccesorioLogeado/', catAccLogin, name='accesorioLog'),
    path('Perfil/', perfil, name='perfil'),
    path('PerfilEdit/', perfilEdit, name='perfilEdit'),
    path('PerfilHisto/', perfilHistorial, name='perfilHistorial'),
    path('PerfilSegui/<orden>', perfilSegui, name='perfilSegui'),
    path('ListadoProductos/', listarProducto, name='listarProd'),
    path('ModificarProductos/<codigo>', modificarProducto, name='modProd'),
    path('EliminarProductos/<codigo>', eliminarProducto, name='eliminarProd'),
    path('AgregarProductos/', agregarProducto, name='agregarProd'),
    path('Registrar/', registro_usuario, name='registro'),
    path('ListaUsuarios/', listarUsuarios, name='listarUsuario'),
    path('EliminarUsuario/<run>', eliminarUsuario, name='eliminarUsuario'),
    path('ModificarUsuario/<run>', modUsuarios, name='modificarUsuario'),
    path('Pagar/', pagar, name='pagar'),
    path('PagoExitoso/', pago, name='pago'),
    path('Carrito/', carrito, name='carrito'),
    path('QuitarCarrito/<id>', quitar, name='quitarCarrito'),
    path('registro_usuario/', registro_usuario, name='registro_usuario'),
    path('prueba/', prueba, name='prueba'),
    path('ProductosApi/', productosApi, name='productosApi'),
    path('UniversoApi/', universoApi, name='universoApi'),
    path('Suscripcion/', suscripcion, name='suscripcion'),

]


import scrapy
from WetBulbBackEnd.items import WeatherItem

class WeatherSpider(scrapy.Spider):
    name = 'weather'
    start_urls = [
        'https://forecast.weather.gov/MapClick.php?lat=36.2424&lon=-115.042']

    def parse(self, response):
        item = WeatherItem()
        item['temperature'] = response.css(
            'p.myforecast-current-lrg::text').get()
        weatherRows = response.css('tr')
        for row in weatherRows:
            title = row.css('b::text').get()
            if title == 'Humidity':
                item['humidity'] = row.css('td::text').get()
            if title == 'Wind Speed':
                item['windspeed'] = row.css('td::text').get()
            if title == 'Barometer':
                item['barometer'] = row.css('td::text').get()
        yield item


#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
# Scipy imports
from scipy.special import kn
from scipy.special import i0, i1, ellipe, ellipk
from scipy.integrate import trapz, simps
from scipy.fftpack import dct
# Argparse
import argparse
from argparse import RawTextHelpFormatter

#===================================================================
#
#  Code: pseudo_spectral.py
#
#  Purpose: Test the pseudo spectral method for self-gravity in
#           cylindrical coordinates. The goal of this code is simply
#           to learn how the method works and apply it to test problems.
#
#  References: Jung, M., Illenseer, T.F., Duschl, W.J. A&A (2018)
#                "Multi-scale simulations of black hole accretion in
#                 barred galaxies: Numerical methods and tests."
#              Li, S., Buoni, M.J., Li, H., ApJ (2009)
#                "A fast potential and self-gravity solver for
#                 nonaxisymmetric disks"
#              Chan, C., Psaltis, D., Ozel, F. (2005)
#                "Spectral methods for time-dependent studies of
#                 accretion flow II..."
#
#  Keywords: python pseudo_spectral.py -h
#
#  Usage: python pseudo_spectral.py args
#
#  Author: John Dupuy & Boyu Xue
#          UNC Chapel Hill
#  Date:    06/15/18
#  Updated: 06/15/18
#====================================================================

#---------------- GLOBAL CONSTANTS ----------------------------------
G = 1.0 # gravitational constant

#---------------- FUNCTION DEFINITIONS ------------------------------
# \func get_Gfunc()
# Given, Z(r,z) this returns the corresponding Green's function
def get_Gfunc(zfunc):
    if   zfunc == 'delta':
        Gfunc = Grazor
    elif zfunc == 'gauss':
        Gfunc = Ggauss
    elif zfunc == 'cons':
        Gfunc = Glog
    else:
        print("[get_Gfunc]: zfunc not understood, got %s, exiting..." %(zfunc))
        quit()
    return Gfunc

# \func Grazor()
# Green's function for razor thin disk
#   Z(r,z) = \delta (z)
# pmpp = \phi - \phi'
def Grazor(r, rp, pmpp):
    eps = 0.0
    R2  = r**2. + rp**2. - 2.*r*rp*np.cos(pmpp) + eps**2.
    return -R2**(-0.5)

# \func Glog()
# Green's function for infinite disk
#   Z(r,z) = const
# pmpp = \phi - \phi'
def Glog(r, rp, pmpp):
    eps = 0.0
    R2  = r**2. + rp**2. - 2.*r*rp*np.cos(pmpp) + eps**2.
    return np.log(R2)

# \func Ggauss()
# Green's function for Gaussian vertical structure
#   Z(r,z) \propto exp(-z^2/( 2 H^2 (r) ))
def Ggauss(r, rp, pmpp):
    # here H is the scale height function
    eps = 0.0
    R2  = (r**2. + rp**2. - 2.*r*rp*np.cos(pmpp) + eps**2.)/(H(rp))**2.
    fac = -np.exp(R2/4.0)/(np.sqrt(2.*np.pi) * H(rp) )
    # here kn(0,x) is the modified bessel function of the 2nd kind
    fac *= kn(0,R2/4.)
    print(kn(0,1.))
    return fac

# \func H()
# Scale height function, currently just assumed to be constant
def H(r):
    return 100

# \func Ifunc()
def Ifunc(Gfunc, r, rp, pmpp):
    return 2.0*np.pi*G*rp*Gfunc(r,rp,pmpp)

# \func init_data()
# Initialize the density data based on test problem
def init_data(prob,R,p):
    nx1, nx2 = len(R), len(p)
    R, p = np.meshgrid(R,p, indexing='xy')
    if prob == 'constant':
        sig0 = 2.e-3
        data = sig0*np.ones( (nx2, nx1) )
    elif prob == 'exp':
        Rd   = 1.
        data = np.exp(-R/Rd)
    elif prob == 'mestel':
        v0   = 1.
        data = v0**2./(2.*np.pi*G*R)
    elif prob == 'kuzmin':
        a = 1.
        M = 1.
        data = a*M/(2.*np.pi*(R**2. + a**2.)**(1.5))
    elif prob == 'cylinders':
        sig = 0.1
        Rk   = lambda rk,pk: np.sqrt( R**2. + rk**2. - 2.*R*rk*np.cos(p-pk) )
        dens = lambda rk,pk: np.exp(-Rk(rk,pk)/sig)/(2.*np.pi*sig**2.)
        data = 2.*dens(1.,1e-3) + 0.5*dens(1.,np.pi+1e-3) + dens(0.9,0.75*np.pi)
    elif prob == 'rotcyl':
        sig = 0.1
        Rk   = lambda rk,pk: np.sqrt( R**2. + rk**2. - 2.*R*rk*np.cos(p-pk) )
        dens = lambda rk,pk: np.exp(-Rk(rk,pk)**2./(2.*sig**2.))/(2.*np.pi*sig**2.)
        data = dens(1., np.pi/4. + 1e-3) + dens(1.,5.*np.pi/4. + 1e-3) + 2e-2/(3.2*np.pi)
    else:
        print('[init_data]: Prob not understood, got %s, exiting...' %(prob))
        quit()
    return data

# \func init_grid()
# Initialize the 2D polar grid
# x1: R, x2: \phi
def init_grid(x1min,x1max,x2min,x2max,nx1,nx2,log=False):
    # First get the face-centered grid
    if log: # for a log grid
        lx1f = np.linspace(np.log(x1min),
                           np.log(x1max), nx1+1)
        x1f  = np.exp(lx1f)
    else:
        x1f  = np.linspace(x1min,
                           x1max, nx1+1)
    x2f = np.linspace(x2min,
                      x2max, nx2+1)

    # Now get the cell-centered grid, no ghost zones
    if log:
        dlx1 = lx1f[1]-lx1f[0]
        lx1c = np.arange(np.log(x1min)+0.5*dlx1,
                         np.log(x1max)         , dlx1)
        x1c  = np.exp(lx1c)
    else:
        dx1 = x1f[1] - x1f[0]
        x1c = np.arange(x1min+0.5*dx1,
                        x1max        , dx1)

    dx2 = x2f[1] - x2f[0]
    x2c = np.arange(x2min+0.5*dx2,x2max, dx2)

    return x1f, x1c, x2f, x2c

# \func fft()
# Given a 2D array (\phi, R), this computes
# and returns the Fourier transform of the data over \phi
# Note that the \phi axis is assumed to be the 0 axis
def fft(data):
    fdata = np.fft.fft(data, axis=0)
    return fdata

# \func ifft()
# Given a 2D array (\phi, R), this computes
# and returns the inverse Fourier transform of the data over \phi
# Note that the \phi axis is assumed to be the 0 axis
def ifft(data):
    fdata = np.fft.ifft(data, axis=0)
    return fdata

#\func initialize()
# Initializes everything to be passed to the solver
def initialize(prob, zfunc, log):
    # first setup grid data based on prob
    nx1 = 256
    nx2 = 256
    if prob == 'constant':
        x1min = 0.4
        x1max = 2.5
        x2min = 0.0
        x2max = 2.0*np.pi
    elif prob == 'exp':
        x1min = 1e-1
        x1max = 30.0
        x2min = 0.0
        x2max = 2.0*np.pi
    elif prob == 'mestel':
        x1min = 1e-1
        x1max = 20000.0
        x2min = 0.0
        x2max = 2.0*np.pi
    elif prob == 'kuzmin':
        x1min = 0.1
        x1max = 20.0
        x2min = 0.0
        x2max = 2.0*np.pi
    elif prob == 'cylinders': # cylinder prob of Chan et al. 2005
        x1min = 0.2
        x1max = 1.8
        x2min = 0.0
        x2max = 2.0*np.pi
    elif prob == 'rotcyl':
        x1min = 0.2
        x1max = 1.8
        x2min = 0.0
        x2max = 2.0*np.pi
    else:
        print('[initialize]: prob %s not understood, exiting...' %(prob))
        quit()

    # Initialize the grid
    x1f, x1c, x2f, x2c = init_grid(x1min,x1max,
                                   x2min,x2max, nx1, nx2, log=log)
    # Choose the Gfunc
    Gfunc = get_Gfunc(zfunc)

    # Initialize the grid data
    data  = init_data(prob,x1c,x2c)

    # Set \phi - \phi'
    pmpp = x2c

    # Initialize I(R,R',\phi-\phi')
    R, PMPP, RP = np.meshgrid(x1c, pmpp, x1c)
    Idata = Ifunc(Gfunc, R, RP, PMPP)
    '''
    Idata = np.zeros( (len(x2c), len(x1c), len(x1c)) )
    for j in range(nx2):
        for i in range(nx1):
            for ii in range(nx1):
                Idata[j,i,ii] = Ifunc(Gfunc, x1c[i], x1c[ii], x2c[j])
    '''

    # Package the grid data
    gdata = (x1f, x1c, x2f, x2c)

    return gdata, data, Idata

#\func solve()
# Given the returned stuff from initialize
# solves for the potential \PHI(R,\phi)
def solve(myinit):
    # Parse mystuff
    gdata, data, Idata = myinit

    # Parse gdata
    x1f, x1c, x2f, x2c = gdata

    nx2  = len(x2c)
    nx1  = len(x1c)

    # Take fourier transform of data and Idata
    fdata  = fft(data)
    fIdata = fft(Idata) # dct(Idata,type=2,axis=0)/(2.0*np.pi)

    # Now compute potential in Fourier space
    Phim  = np.zeros( (nx2, nx1), dtype='complex')
    for j in range(nx2):
        Phim[j]  = trapz(fdata[j,:] * fIdata[j,:,:] , axis=-1, x=x1c)
        '''
        for i in range(nx1):
            # here the last axis is the rp axis (the axis of integration)
            Phim[j,i] = trapz(fdata[j,:] * fIdata[j,i,:], x=x1c)
        '''

    # Now transfer the potential back to real space
    Phi = ifft(Phim)/float(nx2)
    Phi = np.real(Phi)

    print(np.mean(Phi))

    return Phi
#\func plot()
# Given Phi and myinit
# plots the potential
def plot(Phi, myinit, prob, ana):
    # Parse myinit
    gdata, data, Idata = myinit

    # Parse gdata
    x1f, x1c, x2f, x2c = gdata

    # Plot Phi
    fig = plt.figure(facecolor='white')
    ax1 = fig.add_subplot(111)

    X1F, X2F   = np.meshgrid(x1f, x2f, indexing='xy')
    x1cf, x2cf = X1F*np.cos(X2F), X1F*np.sin(X2F)

    if ana:
        X1C, X2C = np.meshgrid(x1c, x2c, indexing='xy')
        if prob == 'exp':
            sig  = 1.
            sig0 = 1.
            y   = X1C/(2.*sig)
            Phiana = -np.pi*G*sig0*X1C*(i0(y)*kn(1,y) - i1(y)*kn(0,y))
        elif prob == 'constant':
            sig0  = 2.e-3
            v_max = x1c/np.max(x1c)
            u_min = np.min(x1c)/x1c
            grana = 4*G*sig0*( (ellipe(v_max)-ellipk(v_max))/v_max + ellipk(u_min) - ellipe(u_min))

        elif prob == 'mestel':
            v0 = 1.
            eps    = np.max(x1f)
            Phiana = v0**2.*(np.log(X1C/eps) + np.log(0.5) )

        elif prob == 'kuzmin':
            a = 1.
            M = 1.
            Phiana = -G*M/np.sqrt( X1C**2. + a**2. )

        elif prob == 'cylinders':
            sig  = 0.1
            Rk   = lambda rk,pk: np.sqrt( X1C**2. + rk**2. - 2.*X1C*rk*np.cos(X2C-pk) )
            yk   = lambda rk,pk: Rk(rk,pk)/(2.*sig)
            Phik = lambda rk,pk: -0.5*(G/sig**2.)*Rk(rk,pk)*( i0(yk(rk,pk))*kn(1,yk(rk,pk)) - i1(yk(rk,pk))*kn(0,yk(rk,pk)) )

            Phiana = 2.*Phik(1.,1e-3) + 0.5*Phik(1.,np.pi+1e-3) + Phik(0.9,0.75*np.pi)

        else:
            print('[plot]: Analytic solution not provided for prob: %s, exiting...' %(prob))
            quit()

        print('[plot]: Plotting analytical solution for %s prob' %(prob))
        # Compute RMS error
        rms = np.sqrt( np.sum((Phiana - Phi)**2.)/(float(Phiana.size)) )

        # Plot analytic result
        plt.figure(facecolor='white')
        plt.plot(x1c, Phi[0],'b-',label='$\Phi_G$ (numerical)')
        plt.plot(x1c, Phiana[0],'r--',label='$\Phi_G$ (analytical)')
        plt.xlabel('R [code units]')
        plt.ylabel('$\Phi_G$')
        plt.title('RMS error: %3.2e' %(rms) )
        plt.legend(loc=4)

    im  = ax1.pcolorfast(x1cf, x2cf, Phi,cmap='magma')
    ax1.set_aspect('equal')
    ax1.set_xlabel('x [code units]')
    ax1.set_ylabel('y [code units]')
    cbar = fig.colorbar(im,label='$\Phi_G$')

    plt.show()
    return

def main():
    # Step 0) Read in system arguments
    parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)

    parser.add_argument('--prob',type=str,default='exp',
                        help="Test problem to solve, options are:\n"
                             "      (a) constant (b) exp",required=False)
    parser.add_argument('--zfunc',type=str,default='delta',
                        help="Z(r,z) profile to use, options are:\n"
                             "      (a) delta (b) gauss",required=False)
    parser.add_argument('--log',action='store_true',required=False,
                        help="Switch for log-grid")
    parser.add_argument('--ana', action='store_true',required=False,
                        help="Switch to compare numerical to analytical solution")
    parser.add_argument('--gana', action='store_true', required=False,
                        help="Switch to compare numerical grav acc to analytic grav acc")
    args  = parser.parse_args()
    prob  = args.prob
    zfunc = args.zfunc
    log   = args.log
    ana   = args.ana
    gana  = args.gana

    # Step 1) Initialize the system
    pass_to_solve = initialize(prob,zfunc,log)
    # Step 2) Solve the system
    Phi           = solve(pass_to_solve)
    # Step 3) Plot the system
    plot(Phi, pass_to_solve, prob, ana)

    return
main()


"""
Get stock analysis, stats and updates
"""
import os
import sys
import random
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import requests
import time

# from . import config

chrome_options = Options()
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.headless = True

URL = 'https://www.tradingview.com/markets/stocks-usa/market-movers-large-cap/'
DELAY = random.randint(1, 5)
driver = webdriver.Chrome(os.path.join(os.getcwd(), 'chromedriver'))
driver.get(URL)

class StockScrapper:

    def __init__(self, URL):
        self.URL = URL

    def get_urls(self):
        """
        get SUBJECT from cl_reader as variable to look for on google
        :return: review sites
        """

        # getting stock urls and sectors urls
        stocks = driver.find_elements_by_class_name('tv-data-table__tbody')
        links = [stock.find_elements_by_tag_name('a') for stock in stocks]
        urls = [link.get_attribute("href") for link in links[1]]

        sectors_urls = []
        stocks_urls = []
        for i in range(len(urls)):
            if i % 2 == 0:
                sectors_urls.append(urls[i])
            else:
                stocks_urls.append(urls[i])

        return stocks_urls, sectors_urls

    def stock_scrapper(self):
        """
        this func will look for kw for each site main_scraper returns
        :return: purchase links, prices, top choices.
        """
        stocks_urls, sectors_urls = self.get_urls()

        # getting top stocks concise info
        stock = []
        name = []
        info = []
        stocks = driver.find_elements_by_class_name('tv-data-table__tbody')
        data_list = [i.text for i in stocks[1:]][0].split('\n')
        data_len = len(data_list)
        for i in range(0, data_len - 2, 3):
            stock.append(data_list[i])
            name.append('_'.join(data_list[i + 1].split()))
            info.append(data_list[i + 2].replace('Strong Buy', 'Strong_Buy'))
        info = [i.split()[0:8] for i in info]

        # getting top stocks in depth info
        ########

        return stock, name, info

    def summarizer(self):
        """
        sum info in data frame
        """
        # get table inputs
        urls = self.get_urls()[0]
        stock, name, info = self.stock_scrapper()

        # get main page headers
        header = driver.find_element_by_xpath('//*[@id="js-screener-container"]/div[3]/table/thead')
        header = header.text.replace('TICKER\n\n100 matches', 'STOCK').split('\n')

        # creating data frame
        df = pd.DataFrame(index=stock, data=info, columns=header[1:])

        return df

    def to_csv(self):
        """
        get df and output to csv file
        """
        df = self.summarizer()

        # create CSV file
        if not df.empty:
            # if file does not exist write header
            file_name = 'Stock Info.csv'
            if not os.path.isfile(file_name):
                df.to_csv(file_name, encoding='utf-8')
            else:  # else it exists so append without writing the header
                df.to_csv(file_name, encoding='utf-8', mode='a', header=False)

    def cl_reader(self):
        """
        function takes subject and kw from CL and  will parse them
        :return: the parses to function
        """
        pass

def main():
    scrap = StockScrapper(URL)
    sites, sectors = scrap.get_urls()
    print(sites)
    top_stocks = scrap.summarizer()
    print(top_stocks)
    scrap.to_csv()
    driver.close()

if __name__ == '__main__':
    main()