import pkg_resources
from prettytable import PrettyTable
from wishbone.error import InvalidComponent, NoSuchComponent
from wishbone.actor import Actor
from wishbone.function.template import TemplateFunction
from wishbone.function.module import ModuleFunction
from wishbone.protocol import Encode
from wishbone.protocol import Decode

class ComponentManager():

    '''
    Loads Wishbone components and information.

    When initiated it indexes all the Wishbone components found in the
    available ``<namespace>.<component_type>.<category>`` combinations.

    A complete component reference would then be something like:
    ``wishbone.module.process.modify``

    - ``wishbone`` is the namespace
    - ``module`` is the component type
    - ``process`` is the component category
    - ``modify`` is the name of the component

    Note:

        The default Wishbone namespaces are ``wishbone``, for the builtin
        modules and ``wishbone_contrib`` or ``wishbone_external`` for
        externally developed components.

        There exist 3 component types:
            - module
            - function
            - protocol

    Args:

        namespace (list): The list of namespaces to search for <categories>
        module_categories (list): The list of module categories to search
        function_categories (list): The list of function categories to search
    '''

    COMPONENT_TYPES = [
        "protocol",
        "module",
        "function",
    ]

    def __init__(self,
                 namespace=["wishbone", "wishbone_contrib", "wishbone_external"],
                 protocol_categories=["encode", "decode"],
                 module_categories=["flow", "input", "output", "process"],
                 function_categories=["template", "module"],
                 ):
        self.namespace = namespace
        self.component_types = ["protocol", "module", "function"]
        self.protocol_categories = protocol_categories
        self.module_categories = module_categories
        self.function_categories = function_categories

    def exists(self, name):

        '''
        Validates whether the component with <name> exists.

        Args:
            name (str): The complete name of the component.

        Returns:
            bool: True if component exists. False otherwise.
        '''

        if self.getComponentByName(name) is None:
            return False
        else:
            return True

    def getComponent(self, namespace, component_type, category, name):
        '''
        Returns the module with name <namespace>.<component_type>.<category>.<name>

        <namespace>.<component_type>.<category>.<name> must be an entrypoint.

        Args:
            namespace (str): The component namespace
            component_type (str): The component type.
            category (str): The component category.
            name (str): The component name.

        Returns:
            class: A ``wishbone.Actor``, ``wishbone.Function`` based class

        Raises:
            NoSuchComponent: The module does not exist.
            InvalidComponent: There was module found but it was not deemed valid.
        '''

        m = None
        for module in pkg_resources.iter_entry_points("%s.%s.%s" % (namespace, component_type, category)):
            if module.name == name:
                m = module.load()
                break

        if m is None:
            raise NoSuchComponent("Component %s.%s.%s.%s cannot be found." % (namespace, component_type, category, name))
        else:
            if issubclass(m, Actor) or issubclass(m, TemplateFunction) or issubclass(m, ModuleFunction) or issubclass(m, Decode) or issubclass(m, Encode):
                return m
            else:
                raise InvalidComponent("'%s.%s.%s.%s' is not a valid wishbone component." % (namespace, component_type, category, name))

    def getComponentByName(self, name):
        '''
        Returns the module with name ``name``.
        ``name`` should be a valid entrypoint.

        Args:
            name (str): The complete module name.

        Returns:
            class: A `wishbone.Actor` or `wishbone.Function` based class

        Raises:
            NoSuchComponent: The module does not exist.
            InvalidComponent: There was module found but it was not deemed valid.
        '''
        self.validateComponentName(name)
        (namespace, component_type, category, name) = name.split('.')
        return self.getComponent(namespace, component_type, category, name)

    def getComponentList(self):
        '''
        Finds and lists all the components found at the defined
        <namespace>.<module_categories>. combinations.

        Yields:
            tuple: A 4 element tuple: (`namespace`, `component_type`, `category`, `name`)
        '''

        for namespace in self.namespace:
            for category in sorted(self.protocol_categories):
                prefix = "%s.protocol.%s" % (namespace, category)
                for item in [m.name for m in pkg_resources.iter_entry_points(group=prefix)]:
                    yield (namespace, "protocol", category, item)
            for category in sorted(self.function_categories):
                prefix = "%s.function.%s" % (namespace, category)
                for item in [m.name for m in pkg_resources.iter_entry_points(group=prefix)]:
                    yield (namespace, "function", category, item)
            for category in sorted(self.module_categories):
                prefix = "%s.module.%s" % (namespace, category)
                for item in [m.name for m in pkg_resources.iter_entry_points(group=prefix)]:
                    yield (namespace, "module", category, item)

    def getComponentDoc(self, namespace, component_type, category, name):
        '''
        Returns the docstring of module `namespace`.`category`.`group`.`name`

        Args:
            namespace (str): The namespace value.
            component_type (str): The component type.
            category (str): The component type category.
            name (str): The component name name.

        Returns:
            str: The docstring of the module.

        Raises:
            InvalidModule: The docstring does not have the correct format.
        '''

        doc = self.getComponent(namespace, component_type, category, name).__doc__

        if doc is None:
            raise InvalidComponent("Component '%s' does not seem to have the expected docstring format." % ())
        else:
            return doc

    def getComponentTitle(self, namespace, component_type, category, name):
        '''
        Returns the title of the module `category`.`group`.`name` docstring.

        Args:
            namespace (str): The namespace value.
            component_type (str): The component type.
            category (str): The component type category.
            name (str): The component name name.

        Returns:
            str: The docstring/module title

        Raises:
            InvalidModule: The docstring does not have the correct format.
        '''

        doc = self.getComponent(namespace, component_type, category, name).__doc__
        if doc is None:
            return "The component doesn't have a docstring."

        title = doc.strip().split('\n')[0].strip('*')
        if title is None:
            raise InvalidComponent("Component '%s' does not seem to have the expected docstring format." % (name))
        else:
            return title

    def getComponentTable(self):
        '''
        Returns an ascii table of all found Wishbone components.

        Returns:
            str: The ascii table containing all modules.
        '''

        table = self.__getComponentTable()
        namespace_header = None
        component_type_header = None
        category_header = None

        for (namespace, component_type, category, name) in self.getComponentList():

            title = self.getComponentTitle(namespace, component_type, category, name)
            version = self.getComponentVersion(namespace, component_type, category, name)

            if namespace_header == namespace:
                namespace = ""
            else:
                namespace_header = namespace
                component_type_header = None
                category_header = None

            if component_type_header == component_type:
                component_type = ""
            else:
                component_type_header = component_type

            if category_header == category:
                category = ""
            else:
                category_header = category
                table.add_row(["", "", "", "", "", ""])

            table.add_row([namespace, component_type, category, name, version, title])

        table.add_row(["", "", "", "", "", ""])
        return table.get_string()

    def getComponentVersion(self, namespace, component_type, category, name):
        '''
        Returns the version of the module.

        Args:
            namespace (str): The namespace value.
            component_type (str): The component type.
            category (str): The component type category.
            name (str): The component name name.

        Returns:
            str: The version of the module.
        '''

        try:
            for module in pkg_resources.iter_entry_points("%s.%s.%s" % (namespace, component_type, category)):
                if module.name == name:
                    return module.dist.version
        except:
            return "?"

    def validateComponentName(self, name):
        '''
        Validates a component reference name for the proper format.

        Args:
            name (str): The name to validate.

        Returns:
            bool: True when valid.  False when invalid.
        '''

        if len(name.split('.')) != 4:
            raise InvalidComponent("Component name '%s' should consist out of 4 parts." % (name))
        if name.split('.')[1] not in self.COMPONENT_TYPES:
            raise InvalidComponent("Component name '%s' has an invalid component type name." % (name))

        return True

    def __getComponentTable(self):
        '''
        Returns a skeleton ascii module table object
        '''

        t = PrettyTable(["Namespace", "Component type", "Category", "Name", "Version", "Description"])
        t.align["Namespace"] = "l"
        t.align["Component type"] = "l"
        t.align["Category"] = "l"
        t.align["Name"] = "l"
        t.align["Version"] = "r"
        t.align["Description"] = "l"
        return t


import os
from flask import render_template, request, redirect, url_for,send_file,current_app
from  werkzeug.utils import secure_filename
import random
import string
from datetime import timedelta
import pyqrcode
import png
from pyqrcode import QRCode
#Vianney

def get_random_alphanumeric_string(letters_count, digits_count):
    sample_str = ''.join((random.choice(string.ascii_uppercase) for i in range(letters_count)))
    sample_str += ''.join((random.choice(string.digits) for i in range(digits_count)))

    # Convert string to list and shuffle it to mix letters and digits
    sample_list = list(sample_str)
    random.shuffle(sample_list)
    final_string = ''.join(sample_list)
    return final_string

#Create a directory in a known location to save files to .
def get_path(app,uploads_dir):
    with app.get_context():
        os.makedirs(uploads_dir, exist_ok=True)
        return uploads_dir

def file_upload(method,files_data,name,nb='one'):
    app = current_app._get_current_object()

    uploads_dir = app.config['UPLOADS_DIR']

    if method == 'POST' and files_data.files:
        # save the single "profile" file
        print(files_data.files[name].filename)
        if (nb=='one') and files_data.files[name].filename:
            print(files_data.files)
            data = files_data.files[name]
            filename = secure_filename(data.filename)
            data.save(os.path.join(uploads_dir, filename))
            return filename

        if (nb=='*') and files_data.files[name].filename:
            filename = []
            for file in files_data.files.getlist(name):
                filename.append(secure_filename(data.filename))
                file.save(os.path.join(uploads_dir, filename[-1]))
            return filename

    return False

def dowloadFile():
    app = current_app._get_current_object()

    uploads_dir = app.config['UPLOADS_DIR']

    path = os.path.join(uploads_dir,"carte_grise.jpg")
    return send_file(path, as_attachment=True)

def time_Average(tickets):
    e = 0
    allTickets = tickets.all()
    time = timedelta(days=365) - timedelta(days=365)
    for ticket in allTickets:

        if e > 0 and ticket.date_modify:
            time += ticket.date_modify-allTickets[e-1].date_modify
        e+=1

    if tickets.count()>0:
        # print(time,tickets.count())

        time = time/tickets.count()
        return round(time.total_seconds()/60)
    else :
        return 0

def wait_time_Average(tickets,tickets_new):
    print(time_Average(tickets),tickets_new.count())
    return time_Average(tickets)*tickets_new.count()

def generate_qr(datas):
    qr_path = os.path.abspath(os.path.dirname(__file__)+ '/static/qrcodes')
    url = pyqrcode.create(datas)
    url.png('{}/{}.png'.format(qr_path,datas), scale = 6)
    qr_code = f"{datas}.png"
    return qr_code


# -*- coding: utf-8 -*-

from gevent import monkey
from flask import Flask, Response, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.mixins import BroadcastMixin
from datetime import datetime, time
import pytz
import random
import threading

monkey.patch_all()

app = Flask(__name__)
app.debug = True
app.config['DEBUG'] = True
app.config['PORT'] = 5000

peoples = ['Andy', 'Gordon', 'Heiley', 'Henry', 'Ho', 'Jan', 'Kelvin', 'Kristin', 'Matt', 'Thomas']
shops = ['Garden', 'Coffee', '新境界', 'Factory 99', '開餐達人', 'Food Court', '爭鮮', 'Drink Tea', 'Shabu SEN']
status = {
    'shop': [{
        'name': shop
    } for shop in shops],
    'people': [{
        'name': people,
        'joined': False,
        'shop': None
    } for people in peoples]
}

game_started = False
tz = pytz.timezone('Asia/Hong_Kong')
timer = None
timer_count = 0
lunch_hour = 13
lunch_min = 45
game_length = 45

def set_interval(interval):
    def decorator(function):
        def wrapper(*args, **kwargs):
            stopped = threading.Event()

            def loop(): # executed in another thread
                while not stopped.wait(interval): # until stopped
                    function(*args, **kwargs)

            t = threading.Thread(target=loop)
            t.daemon = True # stop if the program exits
            t.start()
            return stopped
        return wrapper
    return decorator

@set_interval(1)
def check_time():
    global game_started
    global timer_count

    now = datetime.now(tz)
    lunch_time = tz.localize(datetime(now.year, now.month, now.day, lunch_hour, lunch_min, 0), is_dst=None)
    time_left = int((lunch_time - now).total_seconds())

    if time_left > 0 and time_left < (game_length * 60):
        if game_started != True:
            print("GAME JUST STARTED");
            game_started = True
    else:
        if game_started != False:
            print("GAME JUST STOPED");
            game_started = False

    if timer_count % 30 == 0:
        print(time_left, game_started);
    timer_count = timer_count + 1

timer = check_time()

class HungryNamespace(BaseNamespace, BroadcastMixin):

    def log(self, message):
        now = datetime.now(tz)
        self.logger.info("[%s/%s/%s %s:%s:%s] %s" % (now.year, now.month, now.day, now.hour, now.minute, now.second, message))
        # print "[%s:%s:%s] %s" % (now.hour, now.minute, now.second, message);

    def initialize(self):
        self.logger = app.logger
        self.log("Socketio session started")

    def recv_connect(self):
        self.log("New connection")
        self.session['id'] = None
        self.session['name'] = None
        self.session['shop_id'] = None
        self.session['shop_name'] = None
        self.emit("init", (peoples, shops, [lunch_hour, lunch_min, game_length]))
        self.broadcast_event("status", status)

    def recv_disconnect(self):
        if self.session["id"] != None:
            people_id = self.session['id']
            people_name = peoples[people_id]
            status['people'][people_id]['joined'] = False
            # status['people'][people_id]['shop'] = None
            self.log("%s disconnected" % people_name)
            self.broadcast_event_not_me("status", status)
            self.broadcast_event_not_me("system", "%s disconnected" % people_name)
        else:
            self.log("Client disconnected")

    def broadcast_status(self):
        self.emit("status", status)

    def on_join(self, people_id):
        if game_started == False:
            return "not_started", "The game is not started yet"

        self.log(people_id)
        people_name = peoples[people_id]

        # if select his name, means sign out
        if self.session['id'] == people_id:
            status['people'][people_id]['joined'] = False
            status['people'][people_id]['shop'] = None
            self.broadcast_event("status", status)
            self.session['id'] = None
            self.session['name'] = None
            msg = "%s unjoined" % people_name
            self.log(msg)
            return "unjoin", msg
        elif status['people'][people_id]['joined'] == True:
            # can't select someone that already joined
            # this shouldn't happen becoz checked in frontend
            return "cant_join", "You can't select other user"
        else:
            # change to other user
            if self.session['id'] != None:
                original_name = self.session['name']
                status['people'][self.session['id']]['joined'] = False
                status['people'][self.session['id']]['shop'] = None
                status['people'][people_id]['joined'] = True
                self.broadcast_event("status", status)
                self.session['id'] = people_id
                self.session['name'] = people_name
                self.session['shop_id'] = None
                self.session['shop_name'] = None
                msg = "%s changed to %s" % (original_name, people_name)
                self.log(msg)
                return "change_user", msg
            # new join
            else:
                status['people'][people_id]['joined'] = True
                self.broadcast_event("status", status)
                self.session['id'] = people_id
                self.session['name'] = people_name
                self.session['shop_id'] = None
                self.session['shop_name'] = None
                msg = "%s joined" % people_name
                self.log(msg)
                return "join", msg

    def on_vote(self, shop_id):
        if game_started == False:
            return "not_started", "The game is not started yet"

        # must have joined, then can vote
        if self.session['id'] != None:
            shop_name = shops[shop_id]
            people_id = self.session['id']
            people_name = self.session['name']

            # if vote a shop that already voted, means cancel voting this shop
            if self.session['shop_id'] == shop_id:
                status['people'][people_id]['shop'] = None
                self.broadcast_event("status", status)
                self.session['shop_id'] = None
                self.session['shop_name'] = None
                msg = "%s unvoted %s" % (people_name, shop_name)
                self.log(msg)
                return "unvote", msg
            else:
                status['people'][people_id]['shop'] = shop_id
                self.broadcast_event("status", status)
                self.session['shop_id'] = shop_id
                self.session['shop_name'] = shop_name
                msg = "%s voted %s" % (people_name, shop_name)
                self.log(msg)
                return "vote", msg
        else:
            return "cant_vote", "You must join first"

    def on_addshop(self, shop_name):
        if game_started == False:
            return "not_started", "The game is not started yet"

        people_name = self.session['name']

        # check if new suggested shop is in shops list
        if not shop_name.lower() in (shop.lower() for shop in shops):
            shops.append(shop_name)
            status['shop'].append({'name': shop_name})
            self.broadcast_event("status", status)
            msg = "%s added %s" % (people_name, shop_name)
            self.broadcast_event_not_me("system", msg)
            self.log(msg)
            return "addshop", msg
        else:
            return "cant_addshop", "%s is already in the shops list" % shop_name

@app.route('/dev', methods=['GET'])
def index_dev():
    return render_template('index.html', mode='dev', peoples=peoples, shops=shops)

@app.route('/', methods=['GET'])
def index_prod():
    return render_template('index.html', mode='prod', peoples=peoples, shops=shops)

@app.route('/socket.io/<path:remaining>')
def socketio(remaining):
    try:
        socketio_manage(request.environ, {'/hungry': HungryNamespace}, request)
    except:
        app.logger.error("Exception while handling socketio connection",
                         exc_info=True)
    return Response()


import game, collections, os, pygame_gui, time, pprint

# starts the game
def start():
    game.pygame.init()

    # centers the window to be opened
    os.environ["SDL_VIDEO_CENTERED"] = "1"

    date = time.strftime("%Y-%m-%d")

    game.pygame.display.set_caption("j-masci/game-1 | " + game.config.version + " | " + date)

    game.window_surface = game.pygame.display.set_mode((game.window_props.width, game.window_props.height))

    game.gui_manager = pygame_gui.UIManager((game.window_props.width, game.window_props.height))

    # add entities
    game.populator.populate()

    # invisible mouse
    # game.pygame.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
    game.pygame.mouse.set_visible(False)

    # font init
    game.pygame.font.init()

    while True:

        game.loop.count += 1

        timer = game.classes.Timer()
        times = collections.OrderedDict()

        # gets and clears queued events
        game.loop.events = game.pygame.event.get()
        game.loop.keys_pressed = game.pygame.key.get_pressed()

        # fill the background
        game.window_surface.fill(game.config.background_color)

        # update/draw
        game.world.process()

        game.on_screen_debugger.draw(game.window_surface)

        times["process_1"] = timer.time_since_start()

        # updates the window with the new stuff
        game.pygame.display.flip()

        end_time = timer.time_since_start()
        times["end_time"] = end_time

        # throttle updates per second
        game.loop.dt = game.clock.tick(game.config.updates_per_second)
        time_delta = game.loop.dt / 1000
        game.gui_manager.update(time_delta)
        game.gui_manager.draw_ui(game.window_surface)

        game.on_screen_debugger.state["frame_time"] = str(round(end_time * 100, 1)) + "ms"

        if game.config.track_times_in_loop:
            game.utils.debug_append("Loop # " + str(game.loop.count), times)

def quit(restart=False):
    if game.config.debug_print_on_exit:
        game.debugger.print()

    if game.config.debug_log_on_exit:
        game.debugger.log()

    raise game.exceptions.QuitGameException("restart" if restart else "quit")


import dlib
import cv2
import matplotlib.pyplot as plt
import math
import numpy as np
from imutils import face_utils
import os
import sys

dirname = os.path.dirname(__file__)
head, tail = os.path.split(dirname)
mediapipe_dir = os.path.join(head, 'Utils/Mediapipe')
utils_dir = os.path.join(head, 'Utils')
my_dlib_dir = os.path.join(head, 'face_detection/model1_Dlib')

sys.path.insert(1, mediapipe_dir)
sys.path.insert(1, utils_dir)
sys.path.insert(1, my_dlib_dir)

from fd_dlib import *

from utils_video import *
from mediapipe_face_landmarks import *

ROLL_LOC = 0
PITCH_LOC = 1
YAW_LOC = 2

detector = FaceMeshDetector(maxFaces=1, staticMode=True)

def plot_mesh_mp(img, show=False, text=True):
    img_copy = img.copy()
    img_copy, faces = detector.findFaceMesh(img_copy)
    return img_copy

def head_pose_mediapipe(img, show=False, text=True):
    ''' from img return (roll, pitch, yaw) of face detected with MEDIAPIPE'''

    if img is not None:
        landmarks_res = detector.getLandmarks(img) # mediapipe landmarks
        if landmarks_res is not None:
            landmarks = landmarks_res[0]
            nose, chin, left_eye, right_eye, left_mouth, right_mouth = getLandmarksHeadPose(img, landmarks)

            landmarks_head_pose = np.array([
                            nose,     # Nose tip
                            chin,   # Chin
                            left_eye,     # Left eye left corner
                            right_eye,     # Right eye right corne
                            left_mouth,     # Left Mouth corner
                            right_mouth    # Right mouth corner
                        ], dtype="double")

            imgpts, modelpts, rotate_degree = face_orientation(img, landmarks_head_pose)

            if(show):
                int_landmarks_head_pose = landmarks_head_pose. astype(int)
                nose = tuple(int_landmarks_head_pose[0])

                show_head_pose(img, nose, imgpts, int_landmarks_head_pose, rotate_degree)

            if(text):
                return rotate_degree
            else:
                rotate_degree_float = [float(el) for el in rotate_degree]
                return rotate_degree_float
        else:
            print("Mediapipe's Landmarks = None")
            return None
    else:
        print("Input img is None")
        return None

def head_pose(img, rect, show=False, text=True):
    ''' from img dlib and rect of face return (roll, pitch, yaw) of face'''

    if img is not None:
        landmarks_dlib = dlib_facial_landmarks(img, rect)

        landmarks_head_pose = landmarks_productions(landmarks_dlib)

        imgpts, modelpts, rotate_degree = face_orientation(img, landmarks_head_pose)

        if(show):
            int_landmarks_head_pose = landmarks_head_pose. astype(int)
            nose = tuple(int_landmarks_head_pose[0])

            show_head_pose(img, nose, imgpts, int_landmarks_head_pose, rotate_degree)

        if(text):
            return rotate_degree
        else:
            rotate_degree_float = [float(el) for el in rotate_degree]
            return rotate_degree_float
    else:
        print("Input img is None")
        return None

def dlib_facial_landmarks(img, rect):
    ''' return facial landmarks of dlib's predictor '''
    predictor = dlib.shape_predictor("path of shape_predictor_68_face_landmarks.dat")
    landmarks = predictor(img, rect)

    return landmarks

def landmarks_productions(landmarks_dlib):
    ''' return the landmarks of interest for the estimation of head pose '''
    landmarks_head_pose = np.array([
                            (landmarks_dlib.part(30).x, landmarks_dlib.part(30).y),     # Nose tip
                            (landmarks_dlib.part(8).x, landmarks_dlib.part(8).y),   # Chin
                            (landmarks_dlib.part(36).x, landmarks_dlib.part(36).y),     # Left eye left corner
                            (landmarks_dlib.part(45).x, landmarks_dlib.part(45).y),     # Right eye right corne
                            (landmarks_dlib.part(48).x, landmarks_dlib.part(48).y),     # Left Mouth corner
                            (landmarks_dlib.part(54).x, landmarks_dlib.part(54).y)    # Right mouth corner
                        ], dtype="double")
    return landmarks_head_pose

def face_orientation(frame, image_points):
    size = frame.shape #(height, width, color_channel)

    model_points = np.array([
                            (0.0, 0.0, 0.0),             # Nose tip
                            (0.0, -330.0, -65.0),        # Chin
                            (-165.0, 170.0, -135.0),     # Left eye left corner
                            (165.0, 170.0, -135.0),      # Right eye right corner
                            (-150.0, -150.0, -125.0),    # Left Mouth corner
                            (150.0, -150.0, -125.0)      # Right mouth corner
                        ])

    # Camera internals

    center = (size[1]/2, size[0]/2)
    focal_length = center[0] / np.tan(60/2 * np.pi / 180)
    camera_matrix = np.array(
                         [[focal_length, 0, center[0]],
                         [0, focal_length, center[1]],
                         [0, 0, 1]], dtype = "double"
                         )

    dist_coeffs = np.zeros((4,1)) # Assuming no lens distortion
    (success, rotation_vector, translation_vector) = cv2.solvePnP(model_points, image_points, camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE)

    axis = np.float32([[500,0,0],
                          [0,500,0],
                          [0,0,500]])

    imgpts, jac = cv2.projectPoints(axis, rotation_vector, translation_vector, camera_matrix, dist_coeffs)
    modelpts, jac2 = cv2.projectPoints(model_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs)
    rvec_matrix = cv2.Rodrigues(rotation_vector)[0]

    proj_matrix = np.hstack((rvec_matrix, translation_vector))
    eulerAngles = cv2.decomposeProjectionMatrix(proj_matrix)[6]

    pitch, yaw, roll = [math.radians(_) for _ in eulerAngles]

    pitch = math.degrees(math.asin(math.sin(pitch)))
    roll = -math.degrees(math.asin(math.sin(roll)))
    yaw = math.degrees(math.asin(math.sin(yaw)))

    return imgpts, modelpts, (str(int(roll)), str(int(pitch)), str(int(yaw)))

def show_head_pose(frame, nose, imgpts, landmarks, rotate_degree):
    cv2.line(frame, nose, tuple(imgpts[1].ravel()), (0,255,0), 3) #GREEN
    cv2.line(frame, nose, tuple(imgpts[0].ravel()), (255,0,), 3) #BLUE
    cv2.line(frame, nose, tuple(imgpts[2].ravel()), (0,0,255), 3) #RED

    '''
    remapping = [2,3,0,4,5,1]
    for index in range(len(landmarks)//2):
        random_color = tuple(np.random.random_integers(0,255,size=3))
        print((landmarks[index*2], landmarks[index*2+1]))
        cv2.circle(frame, (landmarks[index*2], landmarks[index*2+1]), 5, random_color, -1)
        cv2.circle(frame,  tuple(modelpts[remapping[index]].ravel().astype(int)), 2, random_color, -1)
    '''
    for j in range(len(rotate_degree)):
        cv2.putText(frame, ('{:05.2f}').format(float(rotate_degree[j])), (10, 30 + (50 * j)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), thickness=2, lineType=2)

    plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    plt.show()

def save_head_pose_img(img, rect, destination, name):
    landmarks_dlib = dlib_facial_landmarks(img, rect)
    for (x, y) in face_utils.shape_to_np(landmarks_dlib):
        cv2.circle(img, (x, y), 1, (0, 0, 255), 2)

    landmarks_head_pose = landmarks_productions(landmarks_dlib)

    imgpts, modelpts, rotate_degree = face_orientation(img, landmarks_head_pose)

    int_landmarks_head_pose = landmarks_head_pose. astype(int)
    nose = tuple(int_landmarks_head_pose[0])

    cv2.putText(img, ('ROLL = {:05.2f}').format(float(rotate_degree[ROLL_LOC])), (10, 460 - (30 * 2)), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), thickness=2, lineType=1)
    cv2.putText(img, ('PITCH = {:05.2f}').format(float(rotate_degree[PITCH_LOC])), (10, 460 - (30 * 1)), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), thickness=2, lineType=1)
    cv2.putText(img, ('YAW = {:05.2f}').format(float(rotate_degree[YAW_LOC])), (10, 460 - (25 * 0)), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), thickness=2, lineType=1)

    cv2.imwrite(destination+'/'+name+'.jpg', img)


# -*- coding: utf-8 -*-
# @Author: lnorb.com
# @Date:   2022-01-17 07:23:11
# @Last Modified by:   lnorb.com
# @Last Modified time: 2022-01-27 09:55:38

from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.togglebutton import ToggleButton
from kivy.lang import Builder
import kivy.properties as kp
import os

from .context_menu import (
    AbstractMenu,
    AbstractMenuItem,
    AbstractMenuItemHoverable,
    HIGHLIGHT_COLOR,
)

class AppMenu(StackLayout, AbstractMenu):
    bounding_box = kp.ObjectProperty(None)

    def __init__(self, *args, **kwargs):
        super(AppMenu, self).__init__(*args, **kwargs)
        self.hovered_menu_item = None

    def update_height(self):
        max_height = 0
        for widget in self.menu_item_widgets:
            if widget.height > max_height:
                max_height = widget.height
        return max_height

    def on_children(self, obj, new_children):
        for w in new_children:
            # bind events that update app menu height when any of its children resize
            w.bind(on_size=self.update_height)
            w.bind(on_height=self.update_height)

    def get_context_menu_root_parent(self):
        return self

    def self_or_submenu_collide_with_point(self, x, y):
        collide_widget = None

        # Iterate all siblings and all children
        for widget in self.menu_item_widgets:
            widget_pos = widget.to_window(0, 0)
            if (
                widget.collide_point(x - widget_pos[0], y - widget_pos[1])
                and not widget.disabled
            ):
                if self.hovered_menu_item is None:
                    self.hovered_menu_item = widget

                if self.hovered_menu_item != widget:
                    self.hovered_menu_item = widget
                    for sibling in widget.siblings:
                        sibling.state = "normal"

                    if widget.state == "normal":
                        widget.state = "down"
                        widget.on_release()

                    for sib in widget.siblings:
                        sib.hovered = False
            elif widget.get_submenu() is not None and not widget.get_submenu().visible:
                widget.state = "normal"

        return collide_widget

    def close_all(self):
        for submenu in [
            w.get_submenu()
            for w in self.menu_item_widgets
            if w.get_submenu() is not None
        ]:
            submenu.hide()
        for w in self.menu_item_widgets:
            w.state = "normal"

    def hide_app_menus(self, obj, pos):
        if not self.collide_point(pos.x, pos.y):
            for w in [
                w
                for w in self.menu_item_widgets
                if not w.disabled and w.get_submenu().visible
            ]:
                submenu = w.get_submenu()
                if submenu.self_or_submenu_collide_with_point(pos.x, pos.y) is None:
                    self.close_all()
                    self._cancel_hover_timer()

class AppMenuTextItem(ToggleButton, AbstractMenuItem):
    label = kp.ObjectProperty(None)
    text = kp.StringProperty("")
    font_size = kp.NumericProperty(14)
    color = kp.ListProperty([1, 1, 1, 1])
    hl_color = kp.ListProperty(HIGHLIGHT_COLOR)

    def on_release(self):
        submenu = self.get_submenu()

        if self.state == "down":
            root = self._root_parent
            submenu.bounding_box_widget = (
                root.bounding_box if root.bounding_box else root.parent
            )

            submenu.bind(visible=self.on_visible)
            submenu.show(self.x, self.y - 1)

            for sibling in self.siblings:
                if sibling.get_submenu() is not None:
                    sibling.state = "normal"
                    sibling.get_submenu().hide()

            self.parent._setup_hover_timer()
        else:
            self.parent._cancel_hover_timer()
            submenu.hide()

    def on_visible(self, *args):
        submenu = self.get_submenu()
        if self.width > submenu.get_max_width():
            submenu.width = self.width

    def _check_submenu(self):
        super(AppMenuTextItem, self)._check_submenu()
        self.disabled = self.get_submenu() is None

    # def on_mouse_down(self):
    #     print('on_mouse_down')
    #     return True

_path = os.path.dirname(os.path.realpath(__file__))
# Builder.load_file(os.path.join(_path, "app_menu.kv"))


from pymongo import MongoClient
import pandas as pd
from clean_data import clean_Data

filename = "C:\\deps\\Hotel_Reviews.csv"
client = MongoClient("localhost:27017")

# Dataframe for the reviews only
df = clean_Data(filename)

# The dataframe responsible for hotels. Not reviews
all_dat = pd.read_csv(filename, sep=",")

# Drop empty values
all_dat.dropna(inplace=True)

# Remove all duplicate hotel names to make sure that only unique hotel names are left over
all_dat = all_dat.drop_duplicates(subset="Hotel_Name")

# Create a new column "country". Will contain the name of the country where a hotel is located
all_dat["country"] = all_dat.Hotel_Address.apply(lambda x: "The Netherlands" if("Netherlands" in x) else "United Kingdom" if("United Kingdom" in x) else "France" if("France" in x) else "Spain" if("Spain" in x) else "Austria" if("Austria" in x) else "Italy" if("Italy" in x) else "Somewhere Else!?")

# Streamlit understands lon but not lng. Replace to work
all_dat = all_dat.rename(columns={"lng": "lon"})

# This new dataframe will be used to supply the new hotel dataframe with relavant info such as the number of reviews for each hotel and pos and neg reviews for later
df2 = pd.read_csv(filename, sep=",")

# Create a new column which will insert the number of reviews a hotel has
z = df2['Hotel_Name'].value_counts()
z1 = z.to_dict()
all_dat['nr_revs'] = all_dat['Hotel_Name'].map(z1)

# Connect to the respective database. It'll create one if one does not already exist
db = client.for_reviews
db2 = client.for_hotels

# Sends the cleaned datasets both for the reviews (used in Models) and hotels (used in the dashboard)
db.reviews.insert_many(df.to_dict('records'))
db2.hotels.insert_many(all_dat.to_dict('records'))


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

#Reading Dataset
df = pd.read_csv('Data_Negara_HELP.csv')
df = df.rename(columns={'Kematian_anak':'Kematian Anak', 'Harapan_hidup':'Harapan Hidup','Jumlah_fertiliti':'Jumlah Fertilitas','GDPperkapita':'GDP per Kapita'})
display(df)
df.info()
df.describe()

#Univariate Analysis
plt.figure(figsize=(20,16))

for i in enumerate(df.describe().columns):
  plt.subplot(3,3, i[0]+1)
  sns.distplot(df[i[1]])
plt.show()

#Bivariate Analysis
anak_mati = df['Kematian Anak'] > 100
gdp_rendah = df['GDP per Kapita'] < 500

kematian_gdp = df[(anak_mati) & (gdp_rendah)].sort_values('Kematian Anak', ascending=False)
display(kematian_gdp)

#Heatmap
sns.heatmap(df.corr(), annot =True, fmt='.2g');

#Outliers
sns.boxplot('Kematian Anak', data=df)
sns.boxplot('GDP per Kapita', data=df)
sns.boxplot('Harapan Hidup', data=df)
sns.boxplot('Pendapatan', data=df)

def finding_outlier(df):
  Q1 = df.quantile(0.25)
  Q3 = df.quantile(0.75)
  IQR = Q3-Q1
  df_final = df[(df<(Q1-(1.5*IQR))) | (df>(Q3+(1.5*IQR)))]
  return df_final

print(finding_outlier(df['Kematian Anak']))
print(finding_outlier(df['Pendapatan']))
print(finding_outlier(df['Harapan Hidup']))
print(finding_outlier(df['Jumlah Fertilitas']))
print(finding_outlier(df['GDP per Kapita']))

def remove_outlier(df):
  Q1 = df.quantile(0.25)
  Q3 = df.quantile(0.75)
  IQR = Q3-Q1
  df_final = df[~((df<(Q1-(1.5*IQR))) | (df>(Q3+(1.5*IQR))))]
  return df_final

df2 = remove_outlier(df[['Kematian Anak','Pendapatan','Harapan Hidup','Jumlah Fertilitas','GDP per Kapita']])
df2.fillna(df2[['Kematian Anak','Pendapatan','Jumlah Fertilitas','GDP per Kapita']].max(),axis=0, inplace=True)
df2.fillna(df2[['Harapan Hidup']].min(),axis=0, inplace=True)

print(finding_outlier(df2['Kematian Anak']))
print(finding_outlier(df2['Pendapatan']))
print(finding_outlier(df2['Harapan Hidup']))
print(finding_outlier(df2['Jumlah Fertilitas']))
print(finding_outlier(df2['GDP per Kapita']))

#Clustering

#Elbow Method
wcss = []
for i in range(1,11):
  kmeans = KMeans(n_clusters=i, init='k-means++', random_state=42)
  kmeans.fit(df2)
  wcss.append(kmeans.inertia_)

plt.plot(range(1,11), wcss)
plt.title('Elbow method')
plt.xlabel('n_clusters')
plt.ylabel('wcss')

plt.show()

#Cluster 1 dan 2 merupakan clustering negara dari aspek kesehatan

#Cluster 1
data_cluster = df2[['Kematian Anak','Harapan Hidup']]
new_df = pd.DataFrame(data=data_cluster)
new_df['label1_kmeans'] = labels1

plt.figure(figsize=(12,8))

plt.scatter(new_df['Kematian Anak'][new_df.label1_kmeans == 0], new_df['Harapan Hidup'][new_df.label1_kmeans == 0], c='red', s=100, edgecolor='black')
plt.scatter(new_df['Kematian Anak'][new_df.label1_kmeans == 1], new_df['Harapan Hidup'][new_df.label1_kmeans == 1], c='yellow', s=100, edgecolor='black')

plt.scatter(kmeans1.cluster_centers_[:, 0],kmeans1.cluster_centers_[:, 1], c='k', s = 300)
plt.title('Country Cluster')
plt.xlabel('Kematian Anak')
plt.ylabel('Harapan Hidup')
plt.show()

#Cluster 2
kmeans2 = KMeans(n_clusters = 3, init='k-means++', random_state=42).fit(data_cluster)
labels2 = kmeans2.labels_

new_df['label2_kmeans'] = labels2
new_df

plt.figure(figsize=(12,8))

plt.scatter(new_df['Kematian Anak'][new_df.label2_kmeans == 0], new_df['Harapan Hidup'][new_df.label2_kmeans == 0], c='red', s=100, edgecolor='black')
plt.scatter(new_df['Kematian Anak'][new_df.label2_kmeans == 1], new_df['Harapan Hidup'][new_df.label2_kmeans == 1], c='yellow', s=100, edgecolor='black')
plt.scatter(new_df['Kematian Anak'][new_df.label2_kmeans == 2], new_df['Harapan Hidup'][new_df.label2_kmeans == 2], c='orange', s=100, edgecolor='black')

plt.scatter(kmeans2.cluster_centers_[:, 0],kmeans2.cluster_centers_[:, 1], c='k', s=300 )
plt.title('Country Cluster')
plt.xlabel('Kematian Anak')
plt.ylabel('Harapan Hidup')
plt.show()

#Silhoutter Score Cluster 1 & 2
from sklearn.metrics import silhouette_score
print(silhouette_score(data_cluster, labels=labels1))
print(silhouette_score(data_cluster, labels=labels2))

#List Negara Kesehatan
new_df['Negara'] = df['Negara']
cluster1 = new_df.label1_kmeans == 1
cluster2 = new_df.label2_kmeans == 1
new_df[(cluster1) & (cluster2)].sort_values('Kematian Anak', ascending=False).head()

#Cluster 3 dan 4 merupakan clustering negara dari aspek kesehatan

#Cluster 3
data_cluster3 = df2[['GDP per Kapita','Pendapatan']]

kmeans4 = KMeans(n_clusters = 2, random_state=42).fit(data_cluster3)
labels4 = kmeans4.labels_

new_df_eko = pd.DataFrame(data=data_cluster3)
new_df_eko['label4_kmeans'] = labels4

plt.figure(figsize=(12,8))

plt.scatter(new_df_eko['GDP per Kapita'][new_df_eko.label4_kmeans == 0], new_df_eko['Pendapatan'][new_df_eko.label4_kmeans == 0], c='red', s=100, edgecolor='black')
plt.scatter(new_df_eko['GDP per Kapita'][new_df_eko.label4_kmeans == 1], new_df_eko['Pendapatan'][new_df_eko.label4_kmeans == 1], c='yellow', s=100, edgecolor='black')

plt.scatter(kmeans4.cluster_centers_[:, 0],kmeans4.cluster_centers_[:, 1], c='k', s = 300)
plt.title('Country Cluster')
plt.xlabel('GDP per Kapita')
plt.ylabel('Pendapatan')
plt.show()

#Cluster 4
kmeans5 = KMeans(n_clusters = 3, init='k-means++', random_state=42).fit(data_cluster3)
labels5 = kmeans5.labels_

new_df_eko['label5_kmeans'] = labels5

plt.figure(figsize=(12,8))

plt.scatter(new_df_eko['GDP per Kapita'][new_df_eko.label5_kmeans == 0], new_df_eko['Pendapatan'][new_df_eko.label5_kmeans == 0], c='red', s=100, edgecolor='black')
plt.scatter(new_df_eko['GDP per Kapita'][new_df_eko.label5_kmeans == 1], new_df_eko['Pendapatan'][new_df_eko.label5_kmeans == 1], c='yellow', s=100, edgecolor='black')
plt.scatter(new_df_eko['GDP per Kapita'][new_df_eko.label5_kmeans == 2], new_df_eko['Pendapatan'][new_df_eko.label5_kmeans == 2], c='orange', s=100, edgecolor='black')

plt.scatter(kmeans5.cluster_centers_[:, 0],kmeans5.cluster_centers_[:, 1], c='k', s=300 )
plt.title('Country Cluster')
plt.xlabel('GDP per Kapita')
plt.ylabel('Pendapatan')
plt.show()

#Silhoutte Score Cluster 3 & 4
from sklearn.metrics import silhouette_score
print(silhouette_score(data_cluster3, labels=labels4))
print(silhouette_score(data_cluster3, labels=labels5))

#List Negara Ekonomi
new_df_eko['Negara'] = df['Negara']
cluster4 = new_df_eko.label4_kmeans == 0
cluster5 = new_df_eko.label5_kmeans == 0
new_df_eko[(cluster4) & (cluster5)].sort_values('GDP per Kapita', ascending=True).head()

#KESIMPULAN (gabungan list negara dari bagian kesehatan dan bagian ekonomi)
kes = new_df[(cluster1) & (cluster2)].sort_values('Kematian Anak', ascending=False).head(10)
eko = new_df_eko[(cluster4) & (cluster5)].sort_values('GDP per Kapita', ascending=True).head(10)

overall = pd.merge(kes, eko, on='Negara', how='inner')
overall = overall[['Negara','Kematian Anak','Harapan Hidup','GDP per Kapita','Pendapatan']]
display(overall)


import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence
from sklearn.metrics import classification_report

class BiLSTM_CRF(nn.Module):
    def __init__(self, vocab_size, tag_to_ix, embedding_dim, hidden_dim):
        super(BiLSTM_CRF, self).__init__()
        self.vocab_size = vocab_size
        self.tag_to_ix = tag_to_ix
        self.embedding_dim = embedding_dim
        self.hidden_dim = hidden_dim

        self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, num_layers=1, bidirectional=True)
        self.hidden2tag = nn.Linear(hidden_dim, len(tag_to_ix))
        self.transitions = nn.Parameter(torch.randn(len(tag_to_ix), len(tag_to_ix)))
        self.transitions.data[tag_to_ix['<START>'], :] = -10000
        self.transitions.data[:, tag_to_ix['<STOP>']] = -10000

    def _forward_alg(self, feats):
        init_alphas = torch.full((1, self.tagset_size), -10000.)
        init_alphas[0][self.tag_to_ix['<START>']] = 0.
        forward_var = init_alphas

        for feat in feats:
            alphas_t = []
            for next_tag in range(self.tagset_size):
                emit_score = feat[next_tag].view(1, -1).expand(1, self.tagset_size)
                trans_score = self.transitions[next_tag].view(1, -1)
                next_tag_var = forward_var + trans_score + emit_score
                alphas_t.append(torch.logsumexp(next_tag_var, dim=1).view(1))
            forward_var = torch.cat(alphas_t).view(1, -1)
        terminal_var = forward_var + self.transitions[self.tag_to_ix['<STOP>']]
        alpha = torch.logsumexp(terminal_var, dim=1)[0]
        return alpha

    def _score_sentence(self, feats, tags):
        score = torch.zeros(1)
        tags = torch.cat([torch.tensor([self.tag_to_ix['<START>']], dtype=torch.long), tags])
        for i, feat in enumerate(feats):
            score = score + self.transitions[tags[i + 1], tags[i]] + feat[tags[i + 1]]
        score = score + self.transitions[self.tag_to_ix['<STOP>'], tags[-1]]
        return score

    def _viterbi_decode(self, feats):
        backpointers = []
        init_vvars = torch.full((1, self.tagset_size), -10000.)
        init_vvars[0][self.tag_to_ix['<START>']] = 0
        forward_var = init_vvars
        for feat in feats:
            bptrs_t = []
            viterbivars_t = []
            for next_tag in range(self.tagset_size):
                next_tag_var = forward_var + self.transitions[next_tag]
                best_tag_id = torch.argmax(next_tag_var)
                bptrs_t.append(best_tag_id)
                viterbivars_t.append(next_tag_var[0][best_tag_id].view(1))
            forward_var = (torch.cat(viterbivars_t) + feat).view(1, -1)
            backpointers.append(bptrs_t)
            terminal_var = forward_var + self.transitions[self.tag_to_ix['<STOP>']]
            best_tag_id = torch.argmax(terminal_var).tolist()
            path_score = terminal_var[0][best_tag_id]
            best_path = [best_tag_id]
            for bptrs_t in reversed(backpointers):
                best_tag_id = bptrs_t[best_tag_id]
                best_path.append(best_tag_id)
            start = best_path.pop()
            assert start == self.tag_to_ix['<START>']
            best_path.reverse()
            return path_score, best_path

        def _get_lstm_features(self, sentence):
            embeds = self.word_embeddings(sentence)
            lstm_out, _ = self.lstm(embeds.view(len(sentence), 1, -1))
            lstm_out = lstm_out.view(len(sentence), self.hidden_dim)
            lstm_feats = self.hidden2tag(lstm_out)
            return lstm_feats

        def neg_log_likelihood(self, sentence, tags):
            self.tagset_size = len(self.tag_to_ix)
            lstm_feats = self._get_lstm_features(sentence)
            forward_score = self._forward_alg(lstm_feats)
            gold_score = self._score_sentence(lstm_feats, tags)
            return forward_score - gold_score

        def forward(self, sentence):
            self.tagset_size = len(self.tag_to_ix)
            lstm_feats = self._get_lstm_features(sentence)
            score, tag_seq = self._viterbi_decode(lstm_feats)
            return score, tag_seq

class TextDataset(Dataset):
    def init(self, X, y):
        self.X = X
        self.y = y

    def __len__(self):
        return len(self.X)

    def __getitem__(self, index):
        return self.X[index], self.y[index]

def pad_collate(batch):
    X_batch, y_batch = zip(*batch)
    X_batch = pad_sequence(X_batch, batch_first=True)
    y_batch = pad_sequence(y_batch, batch_first=True)
    return X_batch, y_batch

def train(model, optimizer, train_loader, device):
    model.train()
    for i, (X_batch, y_batch) in enumerate(train_loader):
        optimizer.zero_grad()
        X_batch, y_batch = X_batch.to(device), y_batch.to(device)
        loss = model.neg_log_likelihood(X_batch, y_batch)
        loss.backward()
        optimizer.step()
        if i % 50 == 0:
            print(f"Step {i} Loss: {loss.item()}")

def evaluate(model, test_loader, device):
    model.eval()
    y_true = []
    y_pred = []
    with torch.no_grad():
        for X_batch, y_batch in test_loader:
            X_batch, y_batch = X_batch.to(device), y_batch.to(device)
            _, y_pred_batch = model(X_batch)
            y_true.extend(y_batch.view(-1).tolist())
            y_pred.extend(y_pred_batch)
            print(classification_report(y_true, y_pred))

def main():
    tag_to_ix = {'O': 0, 'B': 1, 'I': 2, '<START>': 3, '<STOP>': 4}
    vocab_size = 10000
    embedding_dim = 100
    hidden_dim = 128
    model = BiLSTM_CRF(vocab_size, tag_to_ix, embedding_dim, hidden_dim)
    optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4)

    train_dataset = TextDataset(X_train, y_train)
    train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, collate_fn=pad_collate)

    test_dataset = TextDataset(X_test, y_test)
    test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, collate_fn=pad_collate)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)

    for epoch in range(5):
        print(f"Epoch {epoch + 1}")
        train(model, optimizer, train_loader, device)
        evaluate(model, test_loader, device)


import torch
from utils.buffer import Buffer
from utils.args import *
from models.utils.continual_model import ContinualModel
from copy import deepcopy
from torch import nn

def get_parser() -> ArgumentParser:
    parser = ArgumentParser(description='Complementary Learning Systems Based Experience Replay')
    add_management_args(parser)
    add_experiment_args(parser)
    add_rehearsal_args(parser)

    # Consistency Regularization Weight
    parser.add_argument('--reg_weight', type=float, default=0.1)
    parser.add_argument('--ema_update_freq', type=float, default=0.90)
    parser.add_argument('--ema_alpha', type=float, default=0.999)

    return parser

# =============================================================================
# Mean-ER
# =============================================================================
class EMAER(ContinualModel):
    NAME = 'emaer'
    COMPATIBILITY = ['class-il', 'domain-il', 'task-il', 'general-continual']

    def __init__(self, backbone, loss, args, transform):
        super(EMAER, self).__init__(backbone, loss, args, transform)
        self.buffer = Buffer(self.args.buffer_size, self.device)
        self.ema_model = deepcopy(self.net).to(self.device)
        # set regularization weight
        self.reg_weight = args.reg_weight
        # set parameters for ema model
        self.ema_update_freq = args.ema_update_freq
        self.ema_alpha = args.ema_alpha
        self.consistency_loss = nn.MSELoss(reduction='none')
        self.current_task = 0
        self.global_step = 0

    def observe(self, inputs, labels, not_aug_inputs):

        real_batch_size = inputs.shape[0]

        self.opt.zero_grad()
        loss = 0

        if not self.buffer.is_empty():

            buf_inputs, buf_labels = self.buffer.get_data(
                self.args.minibatch_size, transform=self.transform)

            ema_logits = self.ema_model(buf_inputs)
            l_cons = torch.mean(self.consistency_loss(self.net(buf_inputs), ema_logits.detach()))
            l_reg = self.args.reg_weight * l_cons
            loss += l_reg

            if hasattr(self, 'writer'):
                self.writer.add_scalar(f'Task {self.current_task}/l_cons', l_cons.item(), self.iteration)
                self.writer.add_scalar(f'Task {self.current_task}/l_reg', l_reg.item(), self.iteration)

            inputs = torch.cat((inputs, buf_inputs))
            labels = torch.cat((labels, buf_labels))

            # Log values
            if hasattr(self, 'writer'):
                self.writer.add_scalar(f'Task {self.current_task}/l_reg', l_reg.item(), self.iteration)

        outputs = self.net(inputs)
        ce_loss = self.loss(outputs, labels)
        loss += ce_loss

        # Log values
        if hasattr(self, 'writer'):
            self.writer.add_scalar(f'Task {self.current_task}/ce_loss', ce_loss.item(), self.iteration)
            self.writer.add_scalar(f'Task {self.current_task}/loss', loss.item(), self.iteration)

        loss.backward()
        self.opt.step()

        self.buffer.add_data(
            examples=not_aug_inputs,
            labels=labels[:real_batch_size],
        )

        # Update the ema model
        self.global_step += 1
        if torch.rand(1) < self.ema_update_freq:
            self.update_ema_model_variables()

        return loss.item()

    def update_ema_model_variables(self):
        alpha = min(1 - 1 / (self.global_step + 1), self.ema_alpha)
        for ema_param, param in zip(self.ema_model.parameters(), self.net.parameters()):
            ema_param.data.mul_(alpha).add_(1 - alpha, param.data)


from discord.ext import commands
import discord, random, sys, requests, urllib, json, re, extensions, hashlib

tokenFile = json.load(open("auth.json", "r"))
token = tokenFile["bot"]

questionList = []
insultList = []
characterLimit = 1800
inspireToken = requests.get("https://inspirobot.me/api?getSessionID=1").text
database = extensions.serverInfo()

with open("questions.txt", "r") as qFile :
    questionList = qFile.readlines()

with open("insults.txt", "r") as iFile :
    insultList = iFile.readlines()

# Load all the data we need

bot = commands.Bot(command_prefix='/')

bot.remove_command("help") # We're over riding this with our own

# - - - All event handlers that are not on message go here - - -

@bot.event
async def on_ready():
    print('Logged on as', bot.user, flush=True)

# - - - All extra functions go here - - -

@bot.check
async def globally_block_dms(ctx): # This blocks all commmands that are not a DM
    return ctx.guild is not None

def permitted(ctx) : # This is to check if we can run the API functions in the server
    return database.checkServer(ctx.message)

# - - - Commands go here - - -
@bot.command()
async def ask(ctx) :
    gameActive = database.gameInProgress(ctx.message)
    if gameActive :
        await ctx.channel.send("Oops! Looks like you already have a question active in this channel which is : *" +
        gameActive +
        "*\n\nYou will have to say `Goodbye` and then ask again if you would like another question!")

    else :
        question = random.choice(questionList).rstrip()
        database.startGame(question, ctx.message)
        await ctx.channel.send('Alright! Your statement is\n\n *' + question + '*\n\nRemember to say "Goodbye" once you are done!')

@bot.command()
@commands.has_permissions(manage_guild=True)
async def mode(ctx) :
    if permitted(ctx) : # If the server allows these, add it to the restricted list
        database.addServer(ctx.message)
        await ctx.channel.send("**API functions now disabled in this server**")

    else :
        database.removeServer(ctx.message) # If not, remove it
        await ctx.channel.send("**API functions now enabled in this server**")

@bot.command()
@commands.check(permitted)
async def inspire(ctx) :
    quoteJson = requests.get("https://inspirobot.me/api?generateFlow=1&sessionID=" + urllib.parse.quote(inspireToken, safe='')).text
    data = json.loads(quoteJson)
    quotes = "*" #Star for markdown

    for quote in data["data"] :
        try :
            quotes += re.sub("\[pause \d\][ \.]{0,}",".\n  ",quote["text"])
            quotes += "\n\n"
        except KeyError :
            pass

    quotes = quotes.rstrip() + "*" #Remove newlines so markdown still works
    await ctx.channel.send("AI, inspire us!\n\n" + quotes)
    print(hashlib.md5(str(ctx.guild.id).encode('utf-8')).hexdigest() + " recieved some inspiration", flush=True)

@bot.command()
@commands.check(permitted)
async def insult(ctx) :
    template = random.choice(insultList)
    insultString = requests.get("https://insult.mattbas.org/api/insult?template=" + urllib.parse.quote(template, safe ='')).text
    await ctx.channel.send('Oh, Elders of the AI, what slander do you grant for our foes?'+
        '\n\n *' + insultString.strip() + '*')
    print(hashlib.md5(str(ctx.guild.id).encode('utf-8')).hexdigest() + " was hit with a devestating insult ", flush=True)

@bot.command()
async def help(ctx): # Ripped from QuoteBot
    response = discord.Embed()
    response.set_author(name = "More detailed docs here", url = "https://comhad.github.io/oujiabot/")

    response.description = (
                "Hello there! I am **OujiaBot**! I'm a bot made by [Comhad](https://twitter.com/AonanComhad)! " +
                "My purpose of that is similar to **r/AskOujia** of reddit, when you issue `/ask`, " +
                "I will provide you with a sentence which contains a blank word, it is your job to put " +
                "one letter at a time in seperate messages and fill in the blanks and fill in the blank word with friends!* Like this :\n\n" +
                "```\nT\nH\nI\nS```\n\n" +
                "Once you are done, just say goodbye, and i will put it all into a string!\n\n" +
                "I have some additional commands! They are : \n\n" +
                "`/insult` - Use this command to get an AI generated insult!**\n\n" +
                "`/inspire` - Use this command to get 'inspiration' from inspirobot!**\n\n" +
                "`/mode` - Disable the above commands!\n\n"+
                "*The code prevents strings over " + str(characterLimit) + " characters so it doesn't have trouble sending them\n\n"
                "**These commands use APIs not moderated by me, and could output something potentially triggering, " +
                " if you think this is not right for your server, please use `/mode` to disable them. (requies manage server permissions)")

    response.set_footer(text = "All the APIs used can be looked at in more detail at the link above", icon_url = bot.user.avatar_url)
    response.color = discord.Color.from_rgb(235, 52, 235) # A nice purple
    await ctx.send(embed = response)

# - - - Error handling here - - -

@bot.event
async def on_command_error(ctx, error) :
    if isinstance(error, discord.ext.commands.CommandNotFound) : # If a user runs a command we don't have
        return
        # It's really not important to do anything here

    elif isinstance(ctx.channel, discord.DMChannel) : # If the user asks a command in DM's
        await ctx.channel.send("Sorry, commands do not work in DM's!")
        return

    elif isinstance(error, discord.ext.commands.errors.MissingPermissions) :
        await ctx.channel.send("Sorry, it doesn't look like you have permissions for that command! Make sure to read the docs on how commands work!")
        return

    elif isinstance(error, discord.ext.commands.errors.CheckFailure) :
        await ctx.channel.send("The command checks failed for this command! It may have been disabled in this server! You can undo this by typing `/mode`! (As long you have manager server permissions!)")
        return

    else :
        await ctx.channel.send("Oh! Something went wrong sorry :(")
        print("An unhandled error occured " + str(error)) # If it's an error we don't expect, do NOT send back the error

@bot.event
async def on_message(message):
    if message.author == bot.user: # Don't respond to ourselves
        return

    if message.author.bot : # If this is a bot sending the message
        return

    if len(message.content) == 1 and message.guild != None : # Don't allow DM's
        if database.gameInProgress(message) :
            # If a game is in progess append the answer
            answer = database.appendToAnswer(message)
            if len(answer) > characterLimit :
                fullStatement = database.completeAnswer(message)
                await message.channel.send("Sorry, you've gone over the limit for characters in a answer!\nThe final statement was \n\n*" + fullStatement + "*")

    if message.content.lower() == "goodbye" and message.guild != None :
        if database.gameInProgress(message) :
            fullStatement = database.completeAnswer(message)
            await message.channel.send("Ask complete! The final statement was\n\n *" + fullStatement + "*")
    await bot.process_commands(message)

bot.intents.messages = True
# Allow ourselves to get notified when we join a new server etc.

bot.run(token)


# -*- coding: utf-8 -*-
import csv
import json
import scrapy
from scrapy.http import FormRequest

from stores.items import StoresItem

class PingSpider(scrapy.Spider):
    name = 'ping'
    allowed_domains = ['ping.com']
    start_urls = ["ping.com"]

    def start_requests(self):
        urls = []
        with open('./USPostalCode.txt', 'r') as f:
            reader = csv.reader(f, delimiter='\t')
            for row in reader:
                latitude = row[9]
                longitude = row[10]
                url = "https://ping.com/FindRetailerAPI?Lat=%s&Long=%s&Radius=100" % (latitude, longitude)
                yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        items = []
        try:
            jsonresponse = json.loads(response.text)
            if "Retailers" in jsonresponse:
                i = 0
                for row in jsonresponse['Retailers']:
                    item = StoresItem()
                    item['name'] = row['Name']
                    item['address'] = "%s %s" % (row['Address1'], row['Address2'])
                    item['phone'] = row['Phone']
                    item['website'] = row['Website']
                    item['email'] = ''
                    item['hours_operation'] = ''

                    items.append(item)
        except:
            pass
        return items


from jumpscale import j
import sys

JSBASE = j.application.jsbase_get_class()

class DocFixer(JSBASE):
    """
    """

    def __init__(self):
        self.__jslocation__ = "j.tools.docfixer"
        JSBASE.__init__(self)

    def load(self,path=""):
        """
        js9 'j.tools.docfixer.load()'
        """
        j.clients.redis.core_check()

        if not path:
            path = j.sal.fs.getcwd()

        if not j.sal.fs.exists(path=path):
            raise j.exceptions.NotFound("Cannot find source path in load:'%s'" % path)

        j.sal.fs.remove(path + "errors.md")

        def clean(name):
            return j.data.text.strip_to_ascii_dense(name)

        def callbackForMatchDir(path, arg):
            base = j.sal.fs.getBaseName(path).lower()

            if base.startswith("_"):
                return False

            return True

        def callbackForMatchFile(path, arg):

            base = j.sal.fs.getBaseName(path).lower()

            if base == "_sidebar.md":
                return True

            if base.startswith("_"):
                return False

            ext = j.sal.fs.getFileExtension(path)

            if not ext in ["md", "yaml", "toml"]:
                return False

            if ext == "md" and base[:-3] in ["summary", "default"]:
                return False

            return True

        def callbackFunctionDir(path, arg):
            # will see if ther eis data.toml or data.yaml & load in data structure in this obj
            self._processData(path + "/data")
            dpath = path + "/default.md"
            if j.sal.fs.exists(dpath, followlinks=True):
                C = j.sal.fs.fileGetContents(dpath)
                rdirpath = j.sal.fs.pathRemoveDirPart(path, self.path)
                rdirpath = rdirpath.strip("/").strip().strip("/")
                self.content_default[rdirpath] = C
            return True

        def callbackFunctionFile(path, arg):
            self.logger.debug("file:%s"%path)
            ext = j.sal.fs.getFileExtension(path).lower()
            base = j.sal.fs.getBaseName(path)
            if ext == "md":
                self.logger.debug("found md:%s"%path)
                base = base[:-3]  # remove extension
                doc = Doc(path, base, docsite=self)
                # if base not in self.docs:
                #     self.docs[base.lower()] = doc
                self.docs[doc.name_dot_lower] = doc
            elif ext in ["html","htm"]:
                self.logger.debug("found html:%s"%path)
                l = len(ext)+1
                base = base[:-l]  # remove extension
                doc = HtmlPage(path, base, docsite=self)
                # if base not in self.htmlpages:
                #     self.htmlpages[base.lower()] = doc
                self.htmlpages[doc.name_dot_lower] = doc
            else:

                if ext in ["png", "jpg", "jpeg", "pdf", "docx", "doc", "xlsx", "xls", \
                            "ppt", "pptx", "mp4","css","js"]:
                    self.logger.debug("found file:%s"%path)
                    if base in self.files:
                        raise j.exceptions.Input(message="duplication file in %s,%s" %
                                                 (self, path), level=1, source="", tags="", msgpub="")
                    self.files[base.lower()] = path
                # else:
                #     self.logger.debug("found other:%s"%path)
                #     l = len(ext)+1
                #     base = base[:-l]  # remove extension
                #     doc = DocBase(path, base, docsite=self)
                #     if base not in self.others:
                #         self.others[base.lower()] = doc
                #     self.others[doc.name_dot_lower] = doc

        callbackFunctionDir(self.path, "")  # to make sure we use first data.yaml in root

        j.sal.fswalker.walkFunctional(
            self.path,
            callbackFunctionFile=callbackFunctionFile,
            callbackFunctionDir=callbackFunctionDir,
            arg="",
            callbackForMatchDir=callbackForMatchDir,
            callbackForMatchFile=callbackForMatchFile)

    def error_raise(self, errormsg, doc=None):
        if doc is not None:
            errormsg2 = "## ERROR: %s\n\n- in doc: %s\n\n%s\n" % (j.data.time.getLocalTimeHR(), doc, errormsg)
            j.sal.fs.writeFile(filename=self.path + "errors.md", contents=errormsg2, append=True)
            self.logger.error(errormsg2)
            doc.errors.append(errormsg)
        else:
            from IPython import embed
            self.logger.error("DEBUG NOW raise error")
            embed()
            raise RuntimeError("stop debug here")


import chess;
import chess.svg;
import cairosvg;
from cairosvg import svg2png;
from IPython.display import clear_output;
import cv2;
from google.colab.patches import cv2_imshow;
from cairosvg import svg2png;

class Partida:
    IAModelo=ChessModel;
    DefaultPath='partida';

    def __init__(self,aiWhite=False,newBoard=True,modelo=None):

      if newBoard:
          self.Board=chess.Board();
      else:
          self.Board=None;

      self.IsHumanTurn=not aiWhite;
      self.IsAIWhite=aiWhite;
      self.PlayerMove=Partida._PlayerMove;
      self.Tablas=Partida._Tablas;
      self.Ganas=Partida._Ganas;
      self.Pierdes=Partida._Pierdes;
      self.Pensando=Partida._Pensando;
      if modelo is None:
          modelo=Partida.IAModelo();
      self.Modelo=modelo;

    def Load(self,path=None):
      if path is None:
          path=Partida.DefaultPath;
      else:
          path=str(path);
      #cargo el archivo y leo las variables (Board,IsHumanTurn,IsAIWhite)
    def Save(self,path=None):
      if path is None:
          path=Partida.DefaultPath;
      else:
          path=str(path);
      #guardo en un archivo las variables (Board,IsHumanTurn,IsAIWhite)
    def Start(self):
      self.play_game();

    def play_game(self):
      """Play through the whole game

      Keyword arguments:
      turn -- True for A.I plays first
      current_board -- chess.Board()
      """
      if(self.Board.is_stalemate()):
        self.Tablas();

      else:
        if(self.IsHumanTurn):
          if(not self.Board.is_checkmate()):
            self.PlayerMove(self.Board,self.IsAIWhite);
            self.IsHumanTurn=False;
            self.play_game();
          else:
            self.Pierdes(self.Board,self.IsAIWhite);

        else:
          if(not self.Board.is_checkmate()):
            self.Pensando(self.Board,self.IsAIWhite);
            self.Modelo.Move(self.Board,self.IsAIWhite);
            self.IsHumanTurn=True;
            self.play_game()
          else:
            self.Ganas(self.Board,self.IsAIWhite);

    @staticmethod
    def _PlayerMove(current_board,ai_white):
      """Handle the human's turn

      Keyword arguments:
      current_board = chess.Board()
      """
      clear_output()
      Partida._draw_board(current_board,ai_white)
      print('\n')
      print('\n')
      print('ejemplo de uso (a2 a4 ->mueve el primer peon blanco dos celdas adelante) ');
      move_uci =''.join(input('Entre el movimiento: ').split(' ')).lower();

      try:
        move = chess.Move.from_uci(move_uci)
      except:
        return Partida._PlayerMove(current_board)
      if(move not in current_board.legal_moves):
        return Partida._PlayerMove(current_board)
      current_board.push(move)

    @staticmethod
    def _Tablas():
      clear_output()
      print('Tablas: A.I y tu GANAIS!')
    @staticmethod
    def _Ganas(current_board,ai_white):
      clear_output()
      Partida._draw_board(current_board,ai_white)
      print('Has GANADO!!')
    @staticmethod
    def _Pierdes(current_board,ai_white):
      clear_output()
      Partida._draw_board(current_board,ai_white)
      print('A.I Gana')
    @staticmethod
    def _Pensando(current_board,ai_white):
      clear_output()
      Partida._draw_board(current_board,ai_white)
      print('\n')
      print(r"""
                          ._ o o
                          \_`-)|_       Espera,
                      ,""       \        Estoy pensando...
                      ,"  ## |   ಠ ಠ.
                  ," ##   ,-\__    `.
                  ,"       /     `--._;)
              ,"     ## /
              ,"   ##    /
              """);
    @staticmethod
    def _draw_board(current_board,ai_white):
      """Draw board

      Keyword arguments:
      current_board -- chess.Board()
      """
      board_img = chess.svg.board(current_board, flipped = ai_white)
      svg2png(bytestring=board_img,write_to='/content/board.png')
      img = cv2.imread('/content/board.png', 1)
      cv2_imshow(img)


import smtplib
from email.message import EmailMessage
import datetime
import time
import json
from pathlib import Path
import requests
from urllib.parse import urljoin
import logging
import traceback
import sys

class Author:
    def __init__(self, data):
        self.firstName = data["firstName"]
        self.lastName = data["lastName"]
        self.affiliation = data.get("affiliation", "")
        self.ror = data.get("affiliationROR", "")

class Funder:
    def __init__(self, data):
        self.organisation = data.get("organization", "")
        self.identifier = data["identifier"]
        self.awardNumber = data.get("awardNumber", "")
        self.identifierType = data.get("identifierType", "")

class DataEntry:
    def __init__(self, data):
        self.links = data["_links"]
        self.href = self.links["self"]["href"]
        self.url = "https://datadryad.org/stash/dataset/" + self.href.split('/')[-1]
        self.identifier = data["identifier"]
        self.id = data["id"]
        self.storageSize = data["storageSize"]
        self.issn = data.get("relatedPublicationISSN", "")
        self.title = data["title"]
        self.abstract = data["abstract"]
        self.keywords = data.get("keywords", [])
        self.locations = [location["place"] for location in data.get("locations", [])]
        self.version = data["versionNumber"]
        self.versionStatus = data["versionStatus"]
        self.curationStatus = data["curationStatus"]
        self.versionChanges = data["versionChanges"]
        self.publicationDate = data["publicationDate"]
        self.lastModificationDate = data["lastModificationDate"]
        self.visibility = data["visibility"]
        self.sharingLink = data["sharingLink"]
        self.userID = data["userId"]
        self.license = data["license"]

        self.authors = [Author(author) for author in data["authors"]]
        self.funders = [Funder(funder) for funder in data.get("funders", [])]

    def __repr__(self):
        return str(self)

    def __str__(self):
        return str(self.data)

    def summarise(self):
        keywordList = '\t\t- ' + '\n\t\t- '.join(word for word in self.keywords)
        return f"{self.title}\n\t- {self.sharingLink}\n\t- Published {self.publicationDate}\n\t- Keywords:\n{keywordList}"

class DryadRequest:
    serverCall = "https://datadryad.org/api/v2/datasets"

    def __init__(self, pageNumber=1):
        call = urljoin(self.serverCall, f"?page={pageNumber}")
        response = requests.get(call)
        raw = response.json()

        self.page = pageNumber
        self.embedded = raw["_embedded"]
        self.links = raw["_links"]
        self.lastPage = int(self.links["last"]["href"].split('=')[-1])
        self.count = int(raw["count"])
        self.total = int(raw["total"])
        self.entries = [DataEntry(entry) for entry in self.embedded["stash:datasets"]]
        self.entryCount = len(self.entries)

    def __repr__(self):
        return str(self)

    def __str__(self):
        return f"{40*'-'}\nLinks: {self.links}\nCount: {self.count}\nTotal: {self.total}\n{40*'-'}"

class LastUpdate:
    def __init__(self, filename):
        self.filename = filename
        self.path = Path(filename)

        self.savedAttrs = ["page", "entryCount", "year", "month", "day"]

    def __repr__(self):
        return str(self.dataDict())

    def fileExists(self):
        return self.path.exists()

    def dataDict(self):
        return {attr: getattr(self, attr) for attr in self.savedAttrs}

    def setParameters(self, page, entryCount, year, month, day):
        self.page = page
        self.entryCount = entryCount
        self.year = year
        self.month = month
        self.day = day

        with open(self.path, 'w') as fp:
            json.dump(self.dataDict(), fp, indent=4)

    def loadFromFile(self):
        with open(self.path) as fp:
            data = json.load(fp)

        for attr, value in data.items():
            setattr(self, attr, int(value))

    def checkSentToday(self, datetime):
        if self.year == datetime.year and self.month == datetime.month and self.day == datetime.day:
            return True
        return False

    def getDate(self):
        return f"{self.day}/{self.month}/{self.year}"

if __name__ == '__main__':
    # Setup logging
    logging.basicConfig(
        filename="updater.LOG",
        format="[%(asctime)s] %(levelname)s - %(message)s",
        datefmt="%d/%m/%Y %I:%M:%S%p",
        encoding="utf-8",
        level=logging.DEBUG
    )

    # logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

    # Email messaging setup
    with open("sendFrom.txt") as fp:
        data = fp.read().splitlines()

    # Email address on first line, password key on second line
    sendingFrom = data[0]
    sendingPw = data[1]

    # Put addresses to email to one per line
    with open("sendTo.txt") as fp:
        sendingTo = fp.read().splitlines()

    sendTime = datetime.time(10, 0, 0) # 10 AM
    sendDay = 0 # Monday

    # Dryad API setup
    lastEntryDataFile = "lastUpdate.json"
    entriesPerPage = 20
    lastUpdate = LastUpdate(lastEntryDataFile)

    if lastUpdate.fileExists():
        lastUpdate.loadFromFile()
        logging.info("Loading previous update information from file")
    else:
        logging.info("No update file found")
        logging.info("Requesting first page")
        firstPageRequest = DryadRequest()
        logging.info("Requesting last page")
        finalPageRequest = DryadRequest(firstPageRequest.lastPage)

        now = datetime.datetime.now()
        lastUpdate.setParameters(finalPageRequest.page, finalPageRequest.entryCount, now.year, now.month, now.day)

        logging.info(f"Creating new update file with page {lastUpdate.page} and entry {lastUpdate.entryCount}")

    try:
        while True:
            now = datetime.datetime.now()
            if sendDay == now.weekday() and now.time() > sendTime and not lastUpdate.checkSentToday(now):
                logging.info("No update sent today as required, sending email now")
            else:
                dayDifference = (6 - now.weekday()) + sendDay
                hourDifference = (23 - now.hour) + sendTime.hour
                minuteDifference = (59 - now.minute) + sendTime.minute
                secondDifference = (59 - now.second) + sendTime.second

                diff = datetime.timedelta(days=dayDifference, hours=hourDifference, minutes=minuteDifference, seconds=secondDifference)

                if diff.days >= 7:
                    diff = diff - datetime.timedelta(7)

                logging.info(f"Sleeping {diff.total_seconds()} seconds until next update in {diff}")
                time.sleep(diff.total_seconds()) # Sleep until signal time

            previousLastPage = DryadRequest(lastUpdate.page)
            currentLastPage = DryadRequest(previousLastPage.lastPage)

            entriesToEmail = []

            # Entries that are new on the last page from the previous update
            previousLastPageNewEntries = previousLastPage.entryCount - lastUpdate.entryCount
            logging.info(f"Summarising info from last {previousLastPageNewEntries} entries on previous last page")
            for entry in previousLastPage.entries[entriesPerPage - previousLastPageNewEntries:]:
                entriesToEmail.append(entry.summarise())

            # Entries that are on new pages
            newPagesFullOfEntries = currentLastPage.page - previousLastPage.page
            for page in range(lastUpdate.page, newPagesFullOfEntries):
                logging.info(f"Summarising info on page {page}")
                pageData = DryadRequest(page)
                for entry in pageData.entries:
                    entriesToEmail.append(entry.summarise())

            smptServer = smtplib.SMTP_SSL('smtp.gmail.com', 465)
            smptServer.ehlo()
            smptServer.login(sendingFrom, sendingPw)

            msg = EmailMessage()
            msg['From'] = sendingFrom
            msg['To'] = sendingTo

            msg['Subject'] = f"Dryad update: {lastUpdate.getDate()} - {now.strftime('%d/%m/%y')}"

            if len(entriesToEmail):
                content = '\n\n'.join(entry for entry in entriesToEmail)
            else:
                content = "There are no new projects this week unfortunately."

            msg.set_content(f"Here are your new dryad projects:\n\n{content}\n\nRegards,\nARGA BOT")

            smptServer.sendmail(sendingFrom, sendingTo, msg.as_string())
            logging.info("Sent email with info summary")

            lastUpdate.setParameters(currentLastPage.page, currentLastPage.entryCount, now.year, now.month, now.day)
            logging.debug(f"Updating update file with page {currentLastPage.page} and entry {currentLastPage.entryCount}")

            smptServer.quit()

            print("UPDATE FILE:", lastUpdate)

    except KeyboardInterrupt:
        logging.info("Caught CTRL + C, exiting...")

    except Exception as e:
        logging.error(traceback.format_exc())

    finally:
        logging.shutdown()


from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome(executable_path="C:\\Users\iabba\AppData\Local\Programs\chromedriver_win32(1)\chromedriver.exe")
driver.get("https://developer.salesforce.com/signup")

obj=driver.find_elements(By.NAME,"job_role")
p1=obj.find_elements(By.TAG_NAME,"option")

print(len(p1))

for element in range(len(p1)):
    if p1[element].is_displayed():
        print(p1[element].text)


import typer
import sys
import subprocess

def main(day: int = typer.Argument(..., help="Day of the month (between 1 and 25)")):
    """
    Launches an advent of code for 2021 with a specified DAY ðŸŽ„
    """
    if not (1 <= day <= 25):
        typer.echo("Error. Day must be between 1 and 25.")
        raise typer.Abort()
    typer.echo(f"Launching solution for day {day}")
    result = subprocess.run([sys.executable, f"src/day{day}.py"], capture_output=True, text=True)
    typer.echo(result.stdout)
    typer.echo(result.stderr, err=True)

if __name__ == "__main__":
    typer.run(main)


import numpy as np
from sklearn.linear_model import Ridge

class Divination:
    def __init__(self, regression=None, periods=4):
        self.regression = Ridge() if regression is None else regression
        if isinstance(periods, int):
            periods = [2 ** idx for idx in range(periods)]
        self.periods = periods

    def fit(self, data):
        num_last = len(data) - max(self.periods) - 1
        self.regression.fit(
            X=self.factors(data.iloc[:-1], num_last),
            y=data.values[-num_last:, :]
        )
        return self

    def predict(self, data, num_steps):
        result = data.copy()
        for step in range(num_steps):
            new_index = result.index[-1] + (result.index[-1] - result.index[-2])
            new_row = self.regression.predict(self.factors(result, num_last=1))[0]
            result.loc[new_index] = new_row
        return result

    def factors(self, data, num_last):
        return np.array([
            data[var][-period - num_last:].rolling(window=period).mean()[-num_last:] for var in data.columns for period in self.periods
        ]).transpose()