#!../venv/bin/python3
import configparser
import sys

import pymongo
from dateutil import parser
from pymongo import MongoClient

def add_actuators(db: MongoClient, config: configparser.ConfigParser) -> None:
    """
    Adds actuators specified in configuration file

    :param db: the MongoClient
    :param config: the configuration parser
    :return: None
    """

    actuators = list()

    # fans
    num_fans = int(config["fans"]["number_small_fans"])

    actuators.extend([{
        "name": "fan" + str(i + 1).zfill(2),
        "type": "fan",
        "state": False
    } for i in range(num_fans)])

    additional_fans = config["fans"]["additional_fans"].split(",")

    actuators.extend([{
        "name": fan,
        "type": "fan",
        "state": False
    } for fan in additional_fans])

    # heater
    num_heaters = int(config["heaters"]["number_heaters"])

    actuators.extend([{
        "name": "heater" + str(i + 1).zfill(2),
        "type": "heater",
        "state": False
    } for i in range(num_heaters)])

    # water
    num_solenoids = int(config["solenoids"]["number_solenoids"])

    actuators.extend([{
        "name": "solenoid" + str(i + 1).zfill(2),
        "type": "water",
        "state": False
    } for i in range(num_solenoids)])

    # lights
    num_lights = int(config["lights"]["number_lights"])

    actuators.extend([{
        "name": "lights" + str(i + 1).zfill(2),
        "type": "lights",
        "state": False
    } for i in range(num_lights)])

    db.actuators.insert_many(actuators)

def add_default_settings(db: MongoClient, config: configparser.ConfigParser) -> None:
    """
    Adds the settings from the configuration file
    :param db: the MongoClient
    :param config: the configuration parser
    :return: None
    """
    lights_start_time = parser.parse(config["settings"]["lights_start_time"])
    lights_end_time = parser.parse(config["settings"]["lights_end_time"])

    default_settings = {
        "is_manual_mode": bool(config["settings"]["manual_mode"]),
        "temperature": {
            "min": int(config["settings"]["min_temperature"]),
            "max": int(config["settings"]["max_temperature"])
        },
        "soil_moisture": {
            "min": int(config["settings"]["min_soil_moisture"]),
            "max": int(config["settings"]["max_soil_moisture"])
        },
        "lights": {
            "start_time": lights_start_time.hour * 3600 + lights_start_time.minute * 60,  # seconds since midnight
            "end_time": lights_end_time.hour * 3600 + lights_end_time.minute * 60
        },
        "watering_times": [],
        "error_flush_times": [],
        "email_addresses": []
    }

    db.settings.insert_one(default_settings)

def create_indexes(db: MongoClient) -> None:
    """
    Creates indexes on the database
    :param db: the MongoClient
    :return: None
    """
    # need this index to make sure all the names are unique
    db.actuators.create_index([("name", pymongo.TEXT)], name="name_index", default_language="english", unique=True)

    # need these indexes because these collections could be large and we always query on name/sensor.type and timestamp
    db.actuators_state_log.create_index([("name", pymongo.ASCENDING), ("timestamp", pymongo.DESCENDING)],
                                        name="name_timestamp_index",
                                        default_language="english")
    db.data_readings.create_index([("sensor.type", pymongo.ASCENDING), ("timestamp", pymongo.DESCENDING)],
                                  name="type_timestamp_index",
                                  default_language="english")
    db.greenhouse_server_state.create_index([("timestamp", pymongo.DESCENDING)], name="timestamp_index")

def reinitialize_db(config: configparser.ConfigParser, tables: list = None) -> None:
    """
    Initializes the greenhouse database with data or reinitializes it
    :param config: the configuration parser
    :param tables: a list of tables to fill (actuators, settings, etc)
    :return: None
    """

    mongo_uri = config["mongo"]["mongo_uri"]

    if mongo_uri:
        client = MongoClient(mongo_uri)
    else:
        client = MongoClient()

    db = client.greenhouse

    if "actuators" in tables:
        db.actuators.drop()
        add_actuators(db, config)

    if "actuators_state_log" in tables:
        db.actuators_state_log.drop()
        state_log_size = 100 * 1000000
        db.create_collection("actuators_state_log", capped=True, size=state_log_size)

    if "settings" in tables:
        db.settings.drop()
        add_default_settings(db, config)

    if "data_readings" in tables:
        db.data_readings.drop()
        data_readings_size = 350 * 1000000
        db.create_collection("data_readings", capped=True, size=data_readings_size)

    if "greenhouse_server_state" in tables:
        db.greenhouse_server_state.drop()
        pings_size = 5 * 1000000
        db.create_collection("greenhouse_server_state", capped=True, size=pings_size)

    create_indexes(db)

if __name__ == "__main__":

    if len(sys.argv) != 3:
        print("Usage: " + sys.argv[0] + " [configfile] [tables (comma separated)]")
        exit(1)

    config = configparser.ConfigParser()
    config.read(sys.argv[1])
    tables_str = sys.argv[2]

    tables = tables_str.split(",")

    reinitialize_db(config, tables)


import copy
import itertools

from enum import Enum
import tensorflow as tf
import numpy as np

class HandGesture(Enum):
    NONE = "NONE"     # ❌

    FORWARD = "FORWARD"  # ✋
    STOP = "STOP"     # ✊
    UP = "UP"       # 👆
    LAND = "LAND"     # 👌
    DOWN = "DOWN"     # 👇
    BACK = "BACK"     # 👊
    LEFT = "LEFT"     # 👈 thumb
    RIGHT = "RIGHT"    # 👉 thumb

    def enum_all():
        return list(HandGesture)

class KeyPointClassifier(object):
    def __init__(
        self,
        model_path='modules/command_recognition/model/keypoint_classifier.tflite',
    ):
        self.interpreter = tf.lite.Interpreter(
            model_path=model_path,
            num_threads=1
        )
        self.interpreter.allocate_tensors()

        self.input = self.interpreter.get_input_details()
        self.output = self.interpreter.get_output_details()

    def classify(self, landmarks):
        landmarks = np.array([self._pre_process_landmark(landmarks)], dtype=np.float32)

        input_details_tensor_index = self.input[0]['index']
        self.interpreter.set_tensor(
            input_details_tensor_index,
            landmarks,
        )
        self.interpreter.invoke()

        output_index = self.output[0]['index']

        results = np.squeeze(self.interpreter.get_tensor(output_index))
        best_res = np.argmax(results)

        if results[best_res] <= 0.7 or \
            ((results[best_res] - sorted(results)[:-1]) < 0.5).any():
            return HandGesture.NONE
        else:
            return HandGesture.enum_all()[best_res + 1]

    def _pre_process_landmark(self, landmarks):
        temp_landmarks = copy.deepcopy(landmarks)

        # Convert to relative coordinates
        base_x, base_y = 0, 0
        for index, landmark_point in enumerate(temp_landmarks):
            if index == 0:
                base_x, base_y = landmark_point[0], landmark_point[1]

            temp_landmarks[index][0] = temp_landmarks[index][0] - base_x
            temp_landmarks[index][1] = temp_landmarks[index][1] - base_y
            temp_landmarks[index].pop()

        # Convert to a one-dimensional list
        temp_landmarks = list(itertools.chain.from_iterable(temp_landmarks))

        # Normalization
        max_value = max(list(map(abs, temp_landmarks)))

        temp_landmarks = list(map(lambda n: n / max_value, temp_landmarks))

        return temp_landmarks


import logging

from klio.metrics import dispatcher

class MetricsRegistry(object):
    """Main client to create and emit metrics.

    Args:
        relay_clients (list(klio.metrics.base.AbstractRelayClient)):
            configured relay clients.
    """

    def __init__(self, relay_clients, transform_name):
        self._relays = relay_clients
        self._transform_name = transform_name
        self._registry = {}

    def counter(self, name, value=0, **kwargs):
        """Get or create a counter.

        Creates a new counter if one is not found with a key of
        ``"counter_{name}_{transform}"`` of the given  ``transform``.

        New counters will be stored in memory for simple caching.

        Args:
            name (str): name of counter
            value (int): starting value of counter; defaults to 0
            kwargs (dict): keyword arguments passed to each configured
                relay clients' counter object.
        Returns:
            dispatcher.CounterDispatcher: instance of a counter dispatcher
        """
        transform_name = kwargs.pop("transform", self._transform_name)
        key = "counter_{}_{}".format(name, transform_name)
        if key in self._registry:
            return self._registry[key]

        counter = dispatcher.CounterDispatcher(
            relay_clients=self._relays,
            name=name,
            value=value,
            transform=transform_name,
            **kwargs
        )
        self._registry[key] = counter
        return counter

    def gauge(self, name, value=0, **kwargs):
        """Get or create a gauge.

        Creates a new gauge if one is not found with a key of
        "gauge_{name}_{transform}".

        New gauges will be stored in memory for simple caching.

        Args:
            name (str): name of gauge
            value (int): starting value of gauge; defaults to 0
            kwargs (dict): keyword arguments passed to each configured
                relay clients' gauge object.
        Returns:
            dispatcher.GaugeDispatcher: instance of a gauge dispatcher
        """
        transform_name = kwargs.pop("transform", self._transform_name)
        key = "gauge_{}_{}".format(name, transform_name)
        if key in self._registry:
            return self._registry[key]

        gauge = dispatcher.GaugeDispatcher(
            relay_clients=self._relays,
            name=name,
            value=value,
            transform=transform_name,
            **kwargs
        )
        self._registry[key] = gauge
        return gauge

    def timer(self, name, value=0, timer_unit="ns", **kwargs):
        """Get or create a timer.

        Creates a new timer if one is not found with a key of
        "timer_{name}_{transform}".

        New timers will be stored in memory for simple caching.

        Args:
            name (str): name of timer
            value (int): starting value of timer; defaults to 0
            timer_unit (str): desired unit of time; defaults to ns
            kwargs (dict): keyword arguments passed to each configured
                relay clients' timer object.
        Returns:
            dispatcher.TimerDispatcher: instance of a timer dispatcher
        """
        transform_name = kwargs.pop("transform", self._transform_name)
        key = "timer_{}_{}".format(name, transform_name)
        if key in self._registry:
            return self._registry[key]

        timer = dispatcher.TimerDispatcher(
            relay_clients=self._relays,
            name=name,
            value=value,
            transform=transform_name,
            timer_unit=timer_unit,
            **kwargs
        )
        self._registry[key] = timer
        return timer

    def marshal(self, metric):
        """Create a dictionary-representation of a given metric.

        Used when metric objects need to be pickled.

        Args:
            metric (dispatcher.BaseMetricDispatcher): metric instance to
                marshal.

        Returns:
            dict: the metric's data in dictionary form
        """
        data = {
            "type": metric.METRIC_TYPE,
            "name": metric.name,
            "value": metric.value,
        }
        data.update(metric.kwargs)
        return data

    def unmarshal(self, metric_data):
        """Create a metric instance based off of a dictionary.

        If "type" is not specified or is not one of "counter", "gauge",
        or "timer", it defaults to a gauge-type metric.

        Used when metrics objects need to be unpickled.

        Args:
            metric_data (dict): dictionary-representation of a given
                metric.

        Returns:
            dispatcher.BaseMetricDispatcher: a dispatcher relevant to
            metric type.
        """
        metric_type = metric_data.pop("type", None)
        name = metric_data.pop("name", None)
        value = metric_data.pop("value", None)
        kwargs = metric_data

        metric_method = {
            "counter": self.counter,
            "gauge": self.gauge,
            "timer": self.timer,
        }.get(metric_type)

        if not metric_method:
            msg = "Metric type '{}' not supported. Defaulting to gauge.".format(
                metric_type
            )
            logging.getLogger("klio.metrics").warning(msg)
            metric_method = self.gauge

        return metric_method(name=name, value=value, **kwargs)


import torch
from torch import autograd, nn
import torch.nn.functional as F

batch_size = 10
input_size = 3
hidden_size = 5
num_classes = 3

torch.manual_seed(1234)
input = autograd.Variable(torch.rand(batch_size, input_size))

# This will not work for the forward pass and will print:
#     RuntimeError: save_for_backward can only save input or output tensors,
#                   but argument 0 doesn't satisfy this condition
# input = torch.rand(batch_size, input_size)
print("\nInput: ", input)

class Model(nn.Module):

    def __init__(self, input_size, hidden_size, num_classes):
        super().__init__()
        self.h1 = nn.Linear(input_size, hidden_size)
        #self.h2 = nn.Softmax(hidden_size, num_classes)
        self.h2 = nn.Linear(hidden_size, num_classes)
        #### self.h3 = nn.Softmax()

    def forward(self, x):
        x = self.h1(x)
        x = F.tanh(x)
        x = self.h2(x)
        ### x = self.h3(x)
        x = F.softmax(x)
        return x

model = Model(input_size=input_size,
              hidden_size=hidden_size,
              num_classes=num_classes)

out = model(input)
print('out', out)


from django.test import TestCase
#from .models import Item
from .forms import ItemForm
# Create your tests here.
class TestToDoItemForm(TestCase):

    #methods
    #make a long descriptive name
    def test_can_create_an_item_with_just_a_name(self): #methods have to precede with test_ or django won't find them
        #self.assertEqual(1,1)
        form = ItemForm({"name":"Create tests"}) #instantiate it from the ItemForm
        self.assertTrue(form.is_valid())
        #self.assertFalse(form.is_valid())

    def test_correct_message_for_missing_name(self):
        form = ItemForm({"name":""}) #instantiate it from the ItemForm
        self.assertFalse(form.is_valid())
        self.assertEqual(form.errors["name"], [u'This field is required.'])


import os
import time
import collections
import codecs

class GCodeParser:
    @staticmethod
    def remove_comment(gcode):
        gcode = gcode.split(';')
        if gcode[0] == '':
            try:
                cura_layer = gcode[1].find('LAYER:')
                if cura_layer == 0:
                    layer = int(gcode[1].split('LAYER:')[1])
                    return 'L%d' % layer

                simplify_layer = gcode[1].find(' layer ')
                if simplify_layer == 0:
                    layer = gcode[1].split(' layer ')[1]
                    if layer != 'end':
                        layer = int(layer[:layer.find(',')])
                    # layer = gcode[1][8:gcode[1].find(',')]
                    return 'L%d' % layer
            except Exception as e:
                print(" in gcodeparser remove comment %s" % e,gcode)
            return None
        else:
            return gcode[0]

    @staticmethod
    def remove_chomp(gcode):
        return gcode.strip()

    @staticmethod
    def parse(gcode):
        gcode = gcode.split(' ')
        dic = collections.OrderedDict()
        for item in gcode:
            if item != '':
                dic['%s' % item[0:1]] = str(item[1:])
        return dic

    @staticmethod
    def rebuild_gcode(dic):
        gcode = ''
        for item in dic:
            gcode += (item + dic[item]) + ' '
        return gcode


# -*- coding: utf-8 -*-
from lib import db
from .userdata import userdata
import os
import copy
import fnmatch

class Settings:

        """Indicator Characters."""
        idents = '.=~'
        """Module search glob, {m} is replaced with the module name."""
        mglob = ['modules.{m}.*']

        def __init__(self, server):
            os.makedirs("%s/servers/%s" % (userdata, server.name),
                        exist_ok=True)
            os.makedirs("%s/shared/%s" % (userdata, server.shared),
                        exist_ok=True)
            self.db = db.DB("servers/%s/settings.json" % server.name)
            self.d = self.db.d
            self.defaults = {}
            self.user = []
            self.tree = {}

        def isdefault(self, n, v):
            """Determine if a setting <n> with value <v>
            is set to it's default."""
            pass

        def purge(self, m):
            """Purge module <m> from the settings DB."""
            c = 0
            tod = []
            for v in self.d:
                for g in self.mglob:
                    if fnmatch.fnmatch(v, g.format(m=m)):
                        c += 1
                        tod.append(v)
                        break
            for d in tod:
                self.d.pop(d)
            self.save()
            return c

        def pop(self, n):
            """Pop <n> from the DB."""
            r = self.d.pop(n)
            self.save()
            return r

        def get(self, n, pop=False):
            """Return the setting <n> or it's default."""
            if n not in self.d:
                self.d[n] = copy.deepcopy(self.defaults[n])
                ret = self.d[n]
                if type(ret) not in [list, dict] or pop:
                    self.d.pop(n)
                return ret
            return self.d[n]

        def branchdict(self, d, ss):
            s = None
            while ss:
                s = ss.pop(0)
                if s not in d:
                    d[s] = {}
                d = d[s]
            return d

        def addbranch(self, ss, n, rm=False):
            """Add <ss> to the tree with name <n>."""
            ss = copy.deepcopy(ss)
            d = self.tree
            d = self.branchdict(d, ss)
            if n.strip(self.idents) in d and rm:
                d.pop(n.strip(self.idents))
            d[n] = True

        def set(self, n, v):
            """Set <n> to <v>, adding it to the tree if neccessary."""
            split = n.split('.')
            sections = split[:-1]
            basen = split[-1]
            truename = '.'.join(sections + [basen.strip(self.idents)])
            self.d[truename] = v
            self.addbranch(sections, basen)
            self.save()

        def save(self):
            """Save the database, removing values set to their defaults."""
            tod = []
            for k in self.d:
                if k in self.defaults:
                    if self.d[k] == self.defaults[k]:
                        tod.append(k)
                else:
                    if self.isdefault(k, self.d[k]):
                        tod.append(k)
            for t in tod:
                self.d.pop(t)
            self.db.save()

        def add(self, n, v):
            """Add a default setting <n> with a value of <v>."""
            split = n.split('.')
            sections = split[:-1]
            basen = split[-1]
            truename = '.'.join(sections + [basen.strip(self.idents)])
            self.defaults[truename] = copy.deepcopy(v)
            if '=' not in basen and '~' not in basen:
                self.user.append(truename)
            self.addbranch(sections, basen)
            self.save()


from django.conf.urls import patterns, include, url
from django.contrib import admin

from django.contrib.staticfiles.urls import staticfiles_urlpatterns

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
admin.autodiscover()

from dajaxice.core import dajaxice_autodiscover, dajaxice_config
dajaxice_autodiscover()

urlpatterns = patterns('',
    url(r'^$', 'lifetracker.views.index', name='index'),
    url(r'^about/$', 'lifetracker.views.about', name='about'),
    url(r'^contact/$', 'lifetracker.views.contact', name='contact'),
    url(r'^login/$', 'lifetracker.views.login', name="login"),
    url(r'^register/$', 'lifetracker.views.register', name='register'),
    url(r'^addEvent/$', 'lifetracker.views.addEvent', name='addEvent'),
    url(r'^home/$', 'lifetracker.views.home', name='home'),
    url(r'^logout/$', 'lifetracker.views.logout', name='logout'),
    url(r'^analysis/$', 'lifetracker.views.analysis', name='analysis'),
    url(r'^forgotAccount/$', 'lifetracker.views.forgotAccount', name="forgotAccount"),
    url(r'^deleteEvent/(\d+)', 'lifetracker.views.deleteEvent', name='deleteEvent'),
    url(r'^editEvent/(\d+)', 'lifetracker.views.editEvent', name='editEvent'),
    url(r'^checkUsername/', 'lifetracker.views.isUsernameAvailable', name='isUsernameAvailable'),

    # url(r'^lifeTrackerSite/', include('lifeTrackerSite.foo.urls')),

    # Uncomment the admin/doc line below to enable admin documentation:
    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
    url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
)

urlpatterns += patterns('',
    url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
)

urlpatterns += staticfiles_urlpatterns()


from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from pathlib import Path
# we need to import the module Template
from string import Template
import smtplib
# Using the module Template we are going to read the HTML file
template = Template(Path("template.html").read_text())
message = MIMEMultipart()
message["from"] = "Yuri Avello"
message["to"] = "RECEIVER@gmail.com"
message["subject"] = "This is a test"
# With substitute we can indicate what to put instead of $name
body = template.substitute({"name": "john"})
message.attach(MIMEText(body, "html"))
message.attach(MIMEImage(Path("game2.jpg").read_bytes()))

with smtplib.SMTP(host="smtp.gmail.com", port=587) as smtp:
    smtp.ehlo()
    smtp.starttls()
    smtp.login("SENDER@gmail.com", "PASSWORD")
    smtp.send_message(message)
print("sent...")


"""
165 Compare Version Numbers

Compare two version numbers version1 and version2.
If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0.

You may assume that the version strings are non-empty and contain only digits and the . character.
The . character does not represent a decimal point and is used to separate number sequences.
For instance, 2.5 is not "two and a half" or "half way to version three", it is the fifth second-level revision of the second first-level revision.

Here is an example of version numbers ordering:

0.1 < 1.1 < 1.2 < 13.37
"""
from itertools import zip_longest
# What about '1.', '..'?
# pitfalls: '1' and '1.0', '01' and '1', '1.0.0.0' and '1.000.00'
class Solution:
    #
    def compareVersion(self, version1, version2):
        """
        :type version1: str
        :type version2: str
        :rtype: int
        """
        nums1 = list(map(int, version1.split('.')))
        nums2 = list(map(int, version2.split('.')))

        while nums1 and nums1[-1] == 0:
            nums1.pop()
        nums1.append(0)

        while nums2 and nums2[-1] == 0:
            nums2.pop()
        nums2.append(0)

        n1, n2 = len(nums1), len(nums2)
        for i in range(min(n1, n2)):
            if nums1[i] > nums2[i]:
                return 1
            elif nums1[i] < nums2[i]:
                return -1

        if n1 > n2:
            return 1
        elif n1 < n2:
            return -1
        else:
            return 0

    # 2nd visit on 7/16/2020
    def compareVersion2(self, version1, version2):
        nums1 = list(map(int, version1.split('.')))
        nums2 = list(map(int, version2.split('.')))
        for a, b in zip_longest(nums1, nums2, fillvalue=0):
            if a < b: return -1
            elif a > b: return 1

        return 0

test_cases = [('0','1'), ('1.0', '1'), ('0.0', '1.0'), ('0.1','0.1'), ('0.0.9', '0.1'), ('1.5.91', '1.4.100')]
obj = Solution()
for case in test_cases:
    print(obj.compareVersion2(case[0], case[1]))


# -*- coding: utf-8 -*-
import re
import pandas as pd
from urllib import request
from datetime import date

URL = 'https://www.sozialministerium.at/Informationen-zum-Coronavirus/Neuartiges-Coronavirus-(2019-nCov).html'
STATES = {
    'Burgenland': 'B',
    'Kärnten': 'K',
    'Niederösterreich': 'NÖ',
    'Oberösterreich': 'OÖ',
    'Salzburg': 'S',
    'Steiermark': 'ST',
    'Tirol': 'T',
    'Vorarlberg': 'V',
    'Wien': 'W'
}
FILE_FATALITIES = 'data_AT/covid19_fatalities_austria.csv'
FILE_CASES = 'data_AT/covid19_cases_austria.csv'

def retrieve():
    r = request.urlopen(URL).read().decode().replace('\n', '').replace('\t', '').replace(' ', '')
    regex_cases = re.compile('
Best(.*)Todes')
    regex_fatalities = re.compile('
Todes(.*)
')
    regex = re.compile('([\w&;]+) \(([\d.]+)\)')

    cases = regex.findall(regex_cases.search(r).group(1))
    fatalities = regex.findall(regex_fatalities.search(r).group(1))

    return cases, fatalities

def append_csv(filename, data):
    df = pd.read_csv(filename)

    values = {k[1]:0 for k in STATES.items()}
    for d in data:
        values[STATES[d[0]]] = int(d[1].replace('.', ''))

    values['AT'] = sum([v[1] for v in values.items()])
    values['Date'] = date.today().strftime('%Y-%m-%d')

    df = df.append(values, ignore_index=True)
    df['Date'] = pd.to_datetime(df['Date'])
    df = df.set_index('Date')
    df.to_csv(filename)

def update_data():
    cases, fatalities = retrieve()
    append_csv(FILE_CASES, cases)
    append_csv(FILE_FATALITIES, fatalities)

if __name__ == "__main__":
    update_data()


# программа расчета нейросети, реализующей функцию XOR
import numpy as np

# Обучающая выборка
X = np.array([ [1,0,0],[1,0,1],[1,1,0],[1,1,1] ])
y = np.array([[0,1,1,0]]).T

print(X)
print(y)

# -------------- Моделирование классификатора XOR -------------
# логистическая функция активации
def sigmoidfun(z):
    return 1 / (1 + np.exp(-z))

def sigmderiv(z):
    return sigmoidfun(z) * (1 - sigmoidfun(z))

def sigmoideriv(y):
    return y * (1 - y)

np.random.seed(1)
s0 = 2 * np.random.random((2, 3)) - 1
s1 = 2 * np.random.random((1, 3)) - 1

def xor_nnet(x, syn0=s0, syn1=s1):
    a1 = x.T
    a2 = sigmoidfun(np.dot(syn0, a1))
    if len(a2.shape) > 1:
        ones = np.ones(shape=(1, a2.shape[1]))
        a20 = np.vstack((ones, a2))
    else:
        a20 = np.array([1, a2[0], a2[1]])
    a3 = sigmoidfun(np.dot(syn1, a20))
    return a1, a20, a3

# -------------- Моделирование классификатора XOR -------------
# Задаем веса для классификатора, реализующего функцию XOR
syn0 = np.array([ [-5,-10,10],[-5,10,-10] ])
syn1 = np.array([ [-5,10,10] ])

ynet = np.array([[0.0, 0.0, 0.0, 0.0]]).T
err = ynet
for i in range(len(X)):
    x = X[i]
    a1,a2,ynet[i] = xor_nnet(x, syn0, syn1)
    err[i] = y[i] - ynet[i]
    print("Ответ классификатора для", x, "=", ynet[i])

# --------------- анализ ошибок классификации -----------------
# СРЕДНЕКВАДРАТИЧЕСКАЯ ОШИБКА (MSE)
print('MSE=', sum(err*err)/len(err))
# print('MSE=', np.mean(err*err))

# кроссэнтропия распределений (log-loss)
def plogp(p):
    if abs(p) < 1e-12:
        return 0
    return p*np.log2(p)

# ------- попробуем обучить нейросеть простым град-м спуском ---------
# Попробуем обучить нейросеть XOR с помощью оптимизации
# случайно инициализируем веса, в среднем - 0
np.random.seed(3)
syn0 = 2*np.random.random((2,3)) - 1
syn1 = 2*np.random.random((1,3)) - 1

a1,a2,ynet = xor_nnet(X, syn0, syn1)
print(ynet)
# learning coef
alpha = 0.01

# Попробуем обучить нейросеть XOR
for j in range(600000):
    # проходим вперёд по слоям 0, 1 и 2
    a10,a20,a3 = xor_nnet(X, syn0, syn1)

    # как сильно мы ошиблись относительно нужной величины?
    a3_error = y - a3.T

    # рапространим ошибку на выходе 3-го слоя на вход слоя
    z3_delta = a3_error * sigmoideriv(a3.T)

    # распространим ошибки z3 на ошибки выходов a2 (входы для a3)
    a20_error = np.dot(syn1.T, z3_delta.T)
    # уберем 0-ю строку, содержащую добавленный +1
    a2 = a20[1:]
    a2_error = a20_error[1:]

    # рапространим ошибку на выходе 2-го слоя на вход слоя
    z2_delta = a2_error * sigmoideriv(a2)

    # рассчитаем дельта весов каждого слоя,
    # используя ошибки на входе (l+1) слоя и выходы l-го слоя
    syn1_delta = np.dot(z3_delta.T, a20.T)
    syn0_delta = np.dot(z2_delta, a10.T)

    syn1 += alpha* syn1_delta
    syn0 += alpha* syn0_delta

    if (j % 10000) == 0:
        print("Error:" + str(np.mean(np.abs(a3_error))))

print(syn1)
print(syn0)
a1,a2,ynet = xor_nnet(X, syn0, syn1)
print(ynet)
"""
Error:0.501780663237
Error:0.499985281905
Error:0.499510817604
Error:0.495886793439
Error:0.462283439271
Error:0.388309485676
Error:0.344607287938
Error:0.21591328267
Error:0.124516190796
Error:0.0912743545313
Error:0.0743180365023
...
Error:0.0161680950144
Error:0.0160026083371
syn0=
[[-7.00981282  4.58522782  4.55025869]
 [-2.98183124  6.76632391  6.60203154]]
syn1=
[[ -4.48638541 -10.51846703   9.74164621]]

"""

# ------- попробуем обучить нейросеть стохастическим градиентом ---------
# случайно инициализируем веса, в среднем - 0
np.random.seed(3)
syn0 = 2*np.random.random((2,3)) - 1
syn1 = 2*np.random.random((1,3)) - 1

# learning coef
alpha = 0.01
# pack size
pack_size = 3

# Попробуем обучить нейросеть XOR
for j in range(600000):
    # проходим вперёд по слоям 0, 1 и 2
    a10,a20,a3 = xor_nnet(X, syn0, syn1)
    # выбираем случайно pack_size объектов из обучающей выборки
    nobjs = np.random.choice(list(range(len(X))), pack_size, replace=False)
    # фильтруем нужные объекты и ответы
    a10 = a10[:, nobjs]
    a20 = a20[:, nobjs]
    a3 = a3[:, nobjs]
    yp = y[nobjs]

    # как сильно мы ошиблись относительно нужной величины?
    a3_error = yp - a3.T

    # рапространим ошибку на выходе 3-го слоя на вход слоя
    z3_delta = a3_error * sigmoideriv(a3.T)

    # распространим ошибки z3 на ошибки выходов a2 (входы для a3)
    a20_error = np.dot(syn1.T, z3_delta.T)
    # уберем 0-ю строку, содержащую добавленный +1
    a2 = a20[1:]
    a2_error = a20_error[1:]

    # рапространим ошибку на выходе 2-го слоя на вход слоя
    z2_delta = a2_error * sigmoideriv(a2)

    # рассчитаем дельта весов каждого слоя,
    # используя ошибки на входе (l+1) слоя и выходы l-го слоя
    syn1_delta = np.dot(z3_delta.T, a20.T)
    syn0_delta = np.dot(z2_delta, a10.T)

    syn1 += alpha* syn1_delta
    syn0 += alpha* syn0_delta

    if (j % 50000) == 0:
        print("Error:" + str(np.mean(np.abs(a3_error))))

print(syn1)
print(syn0)
a1,a2,ynet = xor_nnet(X, syn0, syn1)
print(a1)
print(a2)
print(ynet)

"""
Результат получился только после снижения альфа
alpha = 0.01
pack_size = 3

Error:0.608719767912
Error:0.481788312008
Error:0.122990617585
Error:0.0526038141682
Error:0.0384722962964
Error:0.0308505911275
Error:0.0278507120516
Error:0.0244147135194
Error:0.0225262690888
Error:0.0205620923888
Error:0.0194889004117
syn0=
[[-6.81597651  4.46157227  4.42166219]
 [-2.92654715  6.69726717  6.5037521 ]]
syn1=
[[ -4.31594635 -10.19845644   9.41363496]]

"""

# ---------- реализация нейросети XOR с помощью KERAS
# import the necessary packages
from keras.optimizers import SGD, Adam, Nadam
from keras.utils import np_utils

# imports used to build the deep learning model
from keras.models import Sequential
from keras.layers.core import Activation
from keras.layers import Input, Dense
from keras.layers import BatchNormalization, Dropout, Lambda
from keras.models import Model

# from keras. import binary_crossentropy
from keras import backend as K

import matplotlib.pyplot as plt

def graph_training_history(history):
    plt.figure(1)

    # summarize history for accuracy

    plt.subplot(211)
    plt.plot(history.history['acc'])
    plt.plot(history.history['val_acc'])
    plt.title('model accuracy')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')

    # summarize history for loss

    plt.subplot(212)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')

    plt.show()

def build_XORnnet(nx = 2):
    # Input
    a1 = Input(shape=(nx,))
    a2 = Dense(2, activation='sigmoid')(a1)
    a3 = Dense(1, activation='sigmoid')(a2)

    classificator = Model(a1, a3, name="XORnnet")
    # Return the constructed network architecture
    return classificator

model = build_XORnnet(nx = 2)
# Посмотрим на число параметров
model.summary()

# Зададим основные параметры для обучения
batch_size = 4
Xb = X[:,1:]
yb = y

# Build and Compile the model
print("[INFO] Building and compiling the DNet model...")
#opt = SGD(lr=0.0002, momentum=0.0001, decay=0.00001, nesterov=True)
opt = Adam(lr=0.01)
#opt = Nadam(lr=0.0001)
model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"])

# Check the argument whether to train the model
print("[INFO] Training the model...")
history = model.fit(Xb, yb,
                    batch_size= batch_size,
                    epochs=50,
                    shuffle= True,
                    #validation_split= 0.3,
                    validation_data=(Xb, yb),
                    verbose=1)

# Visualize the training history
graph_training_history(history)

# Training of the model is now complete
#model.summary()

# Use the test data to evaluate the model
# check it on ctrldata - проверяем точность модели на контрольной выборке
#ctrlLabels_softmax = np_utils.to_categorical(ctrlLabels, n_classes)
print("[INFO] Evaluating the model...")
(loss, accuracy) = model.evaluate(ctrlData, ctrlLabels_softmax, verbose=1, batch_size= batch_size)
print("[INFO] CONTROL accuracy: {:.2f}%".format(accuracy * 100))


#!/usr/bin/env python3

#Get rid of those annoying numpy/tensorflow warnings
import warnings
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")

import keras
from PIL import Image
from keras.models import model_from_json
import numpy as np
from os import getcwd
from tqdm import trange, tqdm
import matplotlib.pylab as plt

# in_data = np.load("indata_for_prediction.npy")
# img_x, img_y = in_data[0].shape
# in_data = in_data.reshape(in_data.shape[0], img_x, img_y, 1)
#
# input_points = np.load("input_prediction_points.npy")

def get_input_data(p, i, j, kernel_size, h, w):
    min_i = int(i-(kernel_size-1)/2)
    max_i = int(i+(kernel_size-1)/2)
    min_j = int(j-(kernel_size-1)/2)
    max_j = int(j+(kernel_size-1)/2)

    data = []
    for k in range(min_i, max_i):
        row = [1 if k<0 or l<0 or k>=h or l>=w or p[k][l] <254 else 0 for l in range(min_j, max_j)]
        data.append(row)
        # for l in range(min_j, max_j):
        #     # if k != i and l != j:
        #     if k<0 or l<0 or k>=h or l>=w:
        #         data[k-min_i].append(1)
        #     elif p[k][l] >= 254:
        #         data[k-min_i].append(0)
        #     else:
        #         data[k-min_i].append(1)

    return np.array(data)

# load json and create model
json_file = open('convnet1.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
# load weights into new model
loaded_model.load_weights("convnet1.h5")

loaded_model.summary()

path = 'input_maps/maze.pgm'
# data_path = path + 'etu_1_condensed/'

I = Image.open(path)
p = np.asarray(I).astype('int')
w, h = I.size
im = np.zeros((w, h), dtype=int)
im_log = np.zeros((w, h), dtype=int)

kernel_size = 61

for i in trange(h, position=1):
    for j in range(w):
        if p[i][j] >= 254:
            in_data = get_input_data(p, i, j, kernel_size, h, w)
            img_x, img_y = in_data.shape
            in_data = in_data.reshape(1, img_x, img_y, 1)
            val = loaded_model.predict(in_data, verbose=0)

            # if len(val) != 1:
            # im[i][j] = val
            if val[0][0] >= 0:
            # tqdm.write("log val: " + str(val[0][0]))
                im_log[i][j] = np.log(val[0][0])-1

plt.imshow(im_log, cmap=plt.cm.Greys, interpolation='nearest')
plt.show()

# np.save("predicted_sim_data", im)


"""
This patch just drops some known indexes which aren't being used anymore or never were used.
"""

import click

import frappe

UNUSED_INDEXES = [
	("Comment", ["link_doctype", "link_name"]),
	("Activity Log", ["link_doctype", "link_name"]),
]

def execute():
	if frappe.db.db_type == "postgres":
		return

	db_tables = frappe.db.get_tables(cached=False)

	# All parent indexes
	parent_doctypes = frappe.get_all(
		"DocType",
		{"istable": 0, "is_virtual": 0, "issingle": 0},
		pluck="name",
	)
	db_tables = frappe.db.get_tables(cached=False)

	for doctype in parent_doctypes:
		table = f"tab{doctype}"
		if table not in db_tables:
			continue
		drop_index_if_exists(table, "parent")

	# Unused composite indexes
	for doctype, index_fields in UNUSED_INDEXES:
		table = f"tab{doctype}"
		index_name = frappe.db.get_index_name(index_fields)
		if table not in db_tables:
			continue
		drop_index_if_exists(table, index_name)

def drop_index_if_exists(table: str, index: str):
	if not frappe.db.has_index(table, index):
		click.echo(f"- Skipped {index} index for {table} because it doesn't exist")
		return

	try:
		frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`")
	except Exception as e:
		frappe.log_error("Failed to drop index")
		click.secho(f"x Failed to drop index {index} from {table}\n {str(e)}", fg="red")
		return

	click.echo(f"✓ dropped {index} index from {table}")


from typing import Dict, Optional

import httpx
from loguru import logger
from tenacity import retry, stop_after_attempt

from telegram_bot.intergration.http.base_http_client import HttpClient

class HttpxClient(HttpClient):
    def __init__(self):
        transport = httpx.AsyncHTTPTransport(retries=3)
        timeout = httpx.Timeout(15.0, connect=60.0)
        self.client = httpx.AsyncClient(transport=transport, timeout=timeout)

    @retry(stop=stop_after_attempt(3))
    async def get(self, url: str, params: Optional[Dict] = None) -> Dict:
        logger.info(f"[http][get][request]{url}")
        r = await self.client.get(url, params=params)
        logger.info(f"[http][get][response]{url},{r.status_code},{r.json()}")

        r.raise_for_status()
        return r.json()

    @retry(stop=stop_after_attempt(3))
    async def post(self, url: str, params: Optional[Dict] = None) -> Dict:
        logger.info(f"[http][post][request]{url}")
        headers = {"Content-Type": "application/json"}
        r = await self.client.post(url, json=params, headers=headers)
        logger.info(f"[http][post][response]{url},{r.status_code},{r.json()}")

        r.raise_for_status()
        return r.json()


import paddle
from paddle.amp import GradScaler

__Author__ = 'Quanhao Guo'
__Date__ = '2021.04.24.16.21'

class MaxClipGradScaler(GradScaler):
    def __init__(self, init_loss_scaling, max_scale: float, incr_every_n_steps=100):
        GradScaler.__init__(self, init_loss_scaling=init_loss_scaling, incr_every_n_steps=incr_every_n_steps)
        self.get_scale = 1.0
        self.max_scale = max_scale

    def scale_clip(self):
        if self.get_scale == self.max_scale:
            self._incr_ratio = 1.0
        elif self.get_scale < self.max_scale:
            self._incr_ratio = 2.0

    def scale(self, outputs):
        if not self._enable:
            return outputs
        self.scale_clip()
        # Short-circuit for the common case.
        if isinstance(outputs, paddle.Tensor):
            assert self._init_loss_scaling is not None
            return outputs * self._init_loss_scaling


import socket
import asyncio

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from starlette.requests import Request
from starlette.websockets import WebSocket, WebSocketDisconnect
from starlette.templating import Jinja2Templates

from blokus.blokus import Blokus
from blokus.players import WebSocketPlayer, AIPlayer, TestPlayer

app = FastAPI()

templates = Jinja2Templates(directory="templates")
jinja_env = templates.env

app.mount(
    "/static",
    StaticFiles(directory="static"),
    name="static"
)

que = list()

async def add_to_que(sock):
    que.append(sock)
    if len(que) >= 2:
        p1 = que[0]
        p2 = que[1]
        que.remove(p1)
        que.remove(p2)
        await start_game([p1, p2])

async def start_game(socks):
    sock1 = socks[0]
    sock2 = socks[1]
    sock1_cnn_status = await connection_succeeded(sock1)
    sock2_cnn_status = await connection_succeeded(sock2)
    if sock1_cnn_status and sock2_cnn_status:
        await start_game_helper(sock1, sock2)
    elif sock1_cnn_status:
        await send_connection_disconnected_message(sock1)
    elif sock2_cnn_status:
        await send_connection_disconnected_message(sock2)
    sock1.finish = True
    sock2.finish = True

async def start_game_helper(sock1, sock2):
    try:
        player1 = WebSocketPlayer(0, sock1)
        player2 = WebSocketPlayer(1, sock2)
        blokus = Blokus([player1, player2])
        await blokus.start()
    except:
        sock1_cnn_status = await connection_succeeded(sock1)
        sock2_cnn_status = await connection_succeeded(sock2)
        if sock1_cnn_status:
            await send_connection_disconnected_message(sock1)
            await sock1.close()
        if sock2_cnn_status:
            await send_connection_disconnected_message(sock2)
            await sock2.close()
    else:
        await sock1.close()
        await sock2.close()

async def connection_succeeded(sock):
    retval = True
    try:
        await send_test(sock)
    except:
        retval = False
    return retval

async def send_test(sock):
    await sock.send_json({
        "method": "test",
        "message": "Connection test...",
    })

async def send_connection_disconnected_message(sock):
    await sock.send_json({
        "method": "exception_occurred",
        "message": "The connection has been disconnected. Please reload.",
    })

def get_ip():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.connect(("8.8.8.8", 80))
    return sock.getsockname()[0]

# Return static page
@app.get('/')
async def index(request: Request):
    return templates.TemplateResponse("index.html", {
        "request": request,
        "ip": get_ip(),
        "port": 8000,
    })

@app.websocket("/ws")
async def websocket_endpoint(sock: WebSocket):
    await sock.accept()
    sock.finish = False
    await add_to_que(sock)
    while not sock.finish:
        await asyncio.sleep(3)


#!/usr/bin/env python3
'''
    sis_unpacker.py v0.3 by Yoti
'''

import os
import sys
import zlib
import struct
from datetime import datetime

def hexalign(value, align = 0):
    return '0x' + hex(value)[2:].zfill(align)

def Int64toTime(time):  # thx to Fire_Head
    return int((time / 1000000 - 3600) - 730497*24*3600 + (30*365*24*60*60+7*24*60*60))

def main():
    if len(sys.argv) < 2:
        print('usage: ' + sys.argv[0] + ' <in_file> [out_dir]')
        quit()

    if len(sys.argv) == 2:
        out_dir = os.path.splitext(sys.argv[1])[0]
    else:
        out_dir = sys.argv[2]

    fin_name = sys.argv[1]
    with open(fin_name, 'rb') as fin:
        fin_size = len(fin.read()); fin.seek(0)
        print('in: ' + fin_name + ' (' + str(fin_size) + ')', end='')

        uids = struct.unpack('<IIII', fin.read(16))
        if uids[1] != 0x10003a12 or uids[2] != 0x10000419:
            print()
            print('Unsupported file type!')
            quit()

        fin.seek(4, os.SEEK_CUR)
        files_count = struct.unpack('<H', fin.read(2))[0]
        print(' [' + str(files_count) + ']')

        fin.seek(30, os.SEEK_CUR)
        files_offset = struct.unpack('<I', fin.read(4))[0]
        fin.seek(files_offset, os.SEEK_SET)

        for i in range(0, files_count):
            file_record = struct.unpack('<IIIIIIIII', fin.read(36))
            fin.seek(12, os.SEEK_CUR)

            if file_record[0] == 0 or file_record[0] == 1:
                tmp = fin.tell()
                fin.seek(file_record[6], os.SEEK_SET)
                fout_name = out_dir + os.sep + fin.read(file_record[5]).decode('utf-16')[3:]
                print('out: ' + fout_name + ' (' + str(file_record[7]) + ' -> ', end='')

                fin.seek(file_record[8], os.SEEK_SET)
                fout_data_cmp = fin.read(file_record[7])

                try:
                    fout_data = zlib.decompress(fout_data_cmp)

                    print(str(len(fout_data)) + ')')
                    if not os.path.exists(os.path.dirname(fout_name)):
                        os.makedirs(os.path.dirname(fout_name))
                    with open(fout_name, 'wb') as fout:
                        fout.write(fout_data)

                    if fout_name.endswith('.app'):
                        time = Int64toTime(struct.unpack('<Q', fout_data[36:36+8])[0])
                        print('info: Application build timestamp is ', end='')
                        print(datetime.fromtimestamp(time).strftime("%Y/%m/%d %H:%M:%S"))
                except:
                    print(str(len(fout_data_cmp)) + ')')
                    if not os.path.exists(os.path.dirname(fout_name)):
                        os.makedirs(os.path.dirname(fout_name))
                    with open(fout_name, 'wb') as fout:
                        fout.write(fout_data_cmp)

                fin.seek(tmp, os.SEEK_SET)

if __name__ == "__main__":
    main()


from functools import total_ordering
import json
import os
from typing import final
from googlesearch import search
import cv2
import sys
from random import shuffle
import time
import msvcrt
from time import strptime
from numbers import Number
import cmd
from prettytable import PrettyTable
from datetime import datetime, timedelta
import string
import random
# pip install prettytable
clear = lambda: os.system('cls')
START_STUDYING_TIME: datetime.now() = datetime.now()
'''
This variable (WORK_FROM_DEFINATION) has a questionable name, took me to long do even decide with this one
so I decided ill just name it this and suffer writing an explanation what its responsible for.

If this variable is set to True.
The program will start with the defination of the topic (if available) and show you the notes you
have for it. and from those notes, you type into the console what that topic is. (your notes shouldn't contain
the answer, that defeats the purpose of flashcards, although small hints are okay) If you get it right, you go to the next question.

If this variable is set to False.
The program will show you the topic, and you have to explain to yourself or others what it means, theres no
legit, to type in the answer and check if its right, but youll have to be honest with your self. You can view a
defination (if available) or notes by using the provided commands. This doesn't track points, as theres no way
to do that.
'''
WORK_FROM_DEFINATION: bool = True

PATH_TO_THIS_FOLDER: str = 'C:/Users/jared/Documents/Code/flashcard_generator'

# Delay between getting the answer correc/incorrect and the next flashcard
class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
number_and_ordered_topics = []
completed_topics = []
sum_of_time: int = 0
def print_commands_list(description, topics, show_topics: bool = False, progress = "", show_words_list: bool = True, show_times: bool = False):
    global completed_topics, number_and_ordered_topics, sum_of_time
    number_and_ordered_topics.clear()
    if description:
        print(
            f'type "next" for next topic\t"d" - description\t"s" - view sources (wiki)\t"notes" - view notes\t"exit" - exit {progress if progress != "" else ""}\n'
        )

    else:
        print(
            f'type "next" for next topic\t"s" - view sources (wiki)\t"notes" - view notes\t"exit" - exit {progress if progress != "" else ""}\n'
        )
    if show_topics:
        topics = sorted(topics)
        completed_topics = sorted(completed_topics)
        # for i, topic in enumerate(topics):
        #     for ctopic in completed_topics:
        #         if topic in ctopic:
        #             topics.pop(i)
        # topics.extend(completed_topics)
        alphabets = list(string.ascii_lowercase)
        alphabet_indexes: dict = {} # this variable name doesnt make sense
        alphabet_indexes.update({'other':[{'index': 0},{'topics':[]}]})
        for alphabet in alphabets:
            alphabet_indexes.update({alphabet:[{'index': 0},{'topics':[]}]})

        for i, topic in enumerate(topics, start=1):
            try:
                first_letter = topic[0].lower()
            except IndexError:
                alphabet_indexes['other'][0]['index'] += 1
                alphabet_indexes['other'][1]['topics'].append(f'{topic}')
                continue
            try:
                alphabet_indexes[first_letter][0]['index'] += 1
                alphabet_indexes[first_letter][1]['topics'].append(f'{topic}')
            except KeyError:
                alphabet_indexes['other'][0]['index'] += 1
                alphabet_indexes['other'][1]['topics'].append(f'{topic}')
        # for topic in topics:
        alphabet_keys = list(alphabet_indexes.keys())
        new_list = []
        overall_count: int = 1
        for key in alphabet_keys:
            # print(t for t in )
            index = alphabet_indexes[key][0]['index']
            if index != 0:
                new_list.append(f'{key.upper()} - {index}')
                for t in alphabet_indexes[key][1]['topics']:
                    new_list.append(f'  {overall_count}) {t}')
                    number_and_ordered_topics.append(t)
                    overall_count += 1
            # new_list.append(t for t in alphabet_indexes[key][1]['topics'])
        # topics = [f'{i}) {topic}' for i, topic in enumerate(topics, start=1)]
        new_list.extend(completed_topics)
        cli = cmd.Cmd()
        cli.columnize(new_list, displaywidth=200)
        print('\n')
'''
    No idea how this function works, I just snatched it from SO, and it worked.
    https://stackoverflow.com/questions/11563615/matching-incorrectly-spelt-words-with-correct-ones-in-python
'''
def compare_words(u1: str, u2: str):
    '''
    Checks to see how similar the words are.
    It returns how many mistakes have been made in spelling.
    I have set the mininum number of mistakes to be 5.

    If the answer is "Theory", and you guessed: "thoery"
    This would still be correct as there is only one mistake.

    If the answer is "Population" and you guessed: "pupolation"
    This would still be correct as there are only 2 mistakes.

    So a maxinum of 6 mistakes, is pretty close to the real deal...
    '''
    try:
        s1 = unicode(u1)
        s2 = unicode(u2)
    except:
        s1 = u1
        s2 = u2
    if len(s1) < len(s2):
        return compare_words(u2, u1)
    if not s1:
        return len(s2)

    previous_row = range(len(s2) + 1)
    for i, c1 in enumerate(s1):
        current_row = [i + 1]
        for j, c2 in enumerate(s2):
            insertions = previous_row[j + 1] + 1 # j+1 instead of j since previous_row and current_row are one character longer
            deletions = current_row[j] + 1       # than s2
            substitutions = previous_row[j] + (c1 != c2)
            current_row.append(min(insertions, deletions, substitutions))
        previous_row = current_row

    return previous_row[-1]

def differnce_in_time(date: str) -> datetime:
    now = datetime.now()
    date = date.replace('  ', ' ')
    split_date = date.split(' ')
    year = int(split_date[-1])
    month_number = strptime(split_date[1], '%b').tm_mon
    day = int(split_date[2])
    hour = int(split_date[3].split(':')[0])
    minute = int(split_date[3].split(':')[1])
    second = int(split_date[3].split(':')[-1])
    new_date = datetime(year, month_number, day, hour, minute, second) #yr, mo, day, hr, min, sec
    return now-new_date

def show_notes(paths_to_notes, topic):
    startCol = int(0)
    for index, path in enumerate(paths_to_notes):
        try:
            img = cv2.imread(path)
        except: continue
        height, width = img.shape[0:2]
        startRow = int(350 if index == 0 else 0)
        endRow = int(height)
        endCol = int(width) # We go to the width of the document because titles could be long.
        croppedImage = img[startRow:endRow, startCol:endCol] # We crop the image accordingly to the values above.

        # Rescale the image so it fits to the screen.
        screen_res = 1380, 920
        scale_width = screen_res[0] / croppedImage.shape[1]
        scale_height = screen_res[1] / croppedImage.shape[0]
        scale = min(scale_width, scale_height)
        window_width = int(croppedImage.shape[1] * scale)
        window_height = int(croppedImage.shape[0] * scale)

        cv2.namedWindow("_" * len(topic), cv2.WINDOW_NORMAL)
        cv2.resizeWindow("_" * len(topic), window_width, window_height)

        cv2.imshow("_" * len(topic), croppedImage)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

def main(json_file, isTimed: bool = False, show_list_of_topics:bool=True, multiple_choices:bool=False):
    global completed_topics, START_STUDYING_TIME, number_and_ordered_topics, sum_of_time
    # with open(json_file) as f:
    #     d = json.load(f)
    d = json_file
    CONSTANT_UNCHANGED_TOPICS = list(d.keys())
    topics = list(d.keys())
    for num, t in enumerate(topics):
        if len(t) > 40 or len(t) <= 3:
            topics.pop(num)
    i: int = 0
    showed_notes = False
    number_of_correct: int = 0
    number_of_completed: int = -1 # Because im to lazy to actually fix this.
    seconds_in_day = 24 * 60 * 60

    # Untill we have went through all topics.
    while True:
        number_of_completed += 1
        START_TOPIC_TIME = datetime.now()
        try:
            topic = topics[i]
            answer = topic.replace(' ', '')
            paths_to_notes = d[topic][1]['paths']
            description = d[topic][0]['dictionary']

            '''
            Instead of choosing random WRONG topics, we can look for topics that are similar to the
            originally selected topic, this makes it harder to guess some topics and adds a level
            of difficultiy.
            '''
            similarity_score = {}
            for t in CONSTANT_UNCHANGED_TOPICS:
                if t != topic:
                    similarity_score.update({t:int(compare_words(topic, t))})

            similarity_score = dict(sorted(similarity_score.items(), key=lambda item: item[1]))
            words_sorted_by_similarity = list(similarity_score.keys())
            options = words_sorted_by_similarity[:5] # Just grabbing the first 5.
            answer_and_possible_answers = []
            for index_of_option, same_index in enumerate(options):
                answer_and_possible_answers.append(same_index)
            answer_and_possible_answers.append(topic)
            shuffle(answer_and_possible_answers)
        except Exception as e:
            print(e)
            clear()
            print_commands_list(
                description=description,
                topics=topics,
                show_topics=True,
                progress=f'\tCorrect: {number_of_correct}/{number_of_completed}\tTopics left: {len(topics)}\tPercentage: {round((number_of_correct/number_of_completed)*100, 1)}%',
            )

            difference = datetime.now()-START_STUDYING_TIME
            minutes, seconds = divmod(difference.days * seconds_in_day + difference.seconds, 60)

            print(f"It took: {minutes} minutes and {seconds} seconds to finish {(len(completed_topics))} topics, with an agerage of {round(sum_of_time/len(completed_topics),2)} seconds per topic! You can review your finals results.")
            break
        # We skip these
        if len(d[topic][1]['paths']) == 0 or topic == 'Useful notes' or topic == "" or topic == " ":
            topics.pop(i)
            continue
        clear()
        if WORK_FROM_DEFINATION:
            try:
                print_commands_list(
                    description=description,
                    topics=topics,
                    show_topics=show_list_of_topics,
                    progress=f'\tCorrect: {number_of_correct}/{number_of_completed}\tTopics left: {len(topics)}\tPercentage: {round((number_of_correct/number_of_completed)*100, 1)}%',
                )
            except ZeroDivisionError:
                print_commands_list(
                    description=description,
                    topics=topics,
                    show_topics=show_list_of_topics,
                    progress=f'\tCorrect: {number_of_correct}/{number_of_completed}\tTopics left: {len(topics)}\tPercentage: 0%',
                )

            if multiple_choices:
                for count, answere_or_not in enumerate(answer_and_possible_answers,start=1):
                    print(f'{count}. {answere_or_not}')
                # {"_" * len(topic)}
            input_call = ''
            console_input = []

            if not showed_notes:
                show_notes(paths_to_notes=paths_to_notes, topic=topics)
                showed_notes = True
            # Get user input
            # while True:
            #     x = msvcrt.getch()
            #     if x == b'\r': # Enter breaks the loop and heads onward with the input.
            #         break
            #     elif x == b'\x08': # Backspace
            #         try:
            #             console_input.pop(-1)
            #         except:
            #             console_input = []
            #     else:
            #         try:
            #             console_input.append(x.decode("utf-8"))
            #         except UnicodeDecodeError:
            #             pass
            #     input_call = ''.join(console_input)
            #     clear()
            #     try:
            #         print_commands_list(
            #             description=description,
            #             topics=topics,
            #             show_topics=show_list_of_topics,
            #             progress=f'\tCorrect: {number_of_correct}/{number_of_completed}\tTopics left: {len(topics)}\tPercentage: {round((number_of_correct/number_of_completed)*100, 1)}%',
            #         )
            #     except ZeroDivisionError:
            #         print_commands_list(
            #             description=description,
            #             topics=topics,
            #             show_topics=show_list_of_topics,
            #             progress=f'\tCorrect: {number_of_correct}/{number_of_completed}\tTopics left: {len(topics)}\tPercentage: 0%',
            #         )

            #     if multiple_choices:
            #         for count, answere_or_not in enumerate(answer_and_possible_answers,start=1):
            #             print(f'{count}. {answere_or_not}')
            #     if len(input_call) > len(topic):
            #         CURSOR: str = bcolors.FAIL + "█" + bcolors.ENDC
            #     elif len(input_call) == len(topic):
            #         CURSOR: str = bcolors.OKGREEN + "█" + bcolors.ENDC
            #     else:
            #         CURSOR: str = bcolors.OKBLUE + "█" + bcolors.ENDC
            #     CURSOR: str = "█"
            #         # {"_" * (len(topic)-len(console_input)-1)}
                # print(f'This topic is? {input_call}{CURSOR}')

            input_call = input("This topic is?\nAnswer: ")

            try:
                if multiple_choices:
                    input_call = answer_and_possible_answers[(int(input_call)-1)]
                input_call = number_and_ordered_topics[(int(input_call)-1)]
            except:
                pass
            input_call = input_call.replace(' ', '')
            if input_call == 'exit':
                break
            elif compare_words(input_call.lower(), answer.lower()) <= (len(answer)/4) and input_call.lower() != 'd' and input_call.lower() != 's'  and input_call.lower() != 'notes' and input_call != '' and input_call != '\n':
                END_TOPIC_TIME = datetime.now()
                elapsed = END_TOPIC_TIME - START_TOPIC_TIME
                minutes, seconds = divmod(elapsed.days * seconds_in_day + elapsed.seconds, 60)
                seconds += minutes*60
                sum_of_time += seconds
                if (isTimed):
                    if elapsed > timedelta(seconds=30):
                        print(f'{bcolors.OKBLUE}That is correct! But you didn\'t complete it within 30 seconds.{bcolors.ENDC}')
                        completed_topics.append(bcolors.OKBLUE + topics[i] + ' - ' + str(seconds) + 's' + bcolors.ENDC)
                    else:
                        print(f'{bcolors.OKGREEN}That is correct!\n\nThe answer was indeed {topic}.{bcolors.ENDC}')
                        completed_topics.append(bcolors.OKGREEN + topics[i] + ' - ' + str(seconds) + 's' + bcolors.ENDC)
                        number_of_correct+=1
                else:
                    print(f'{bcolors.OKGREEN}That is correct!\n\nThe answer was indeed {topic}.{bcolors.ENDC}')
                    completed_topics.append(bcolors.OKGREEN + topics[i] + ' - ' + str(seconds) + 's' + bcolors.ENDC)
                    number_of_correct+=1
                topics.pop(i)
                showed_notes = False
                shuffle(topics)
                input_call = input('Press ENTER to proceed...')
            elif input_call == 'd':
                if description:
                    print(f'The descripion for {"_" * len(topic)} is: "{description[0]}"')
                else:
                    print('No description available.')
                input_call = input('Press ENTER to proceed...')
            elif input_call == 's':
                print('Looking up sources. Please wait.')
                sources = search(f"\"defination\" of \"{topic}\" \"wikipedia\"", num_results=5)
                print(f'Found {len(sources)} sources!')
                for source in sources:
                    print(source)
                input_call = input('Press ENTER to proceed...')
            elif input_call == 'notes':
                show_notes(paths_to_notes=paths_to_notes, topic=topic)
            elif input_call in ['', 'next', 'idk', '\n', '\r']:
                END_TOPIC_TIME = datetime.now()
                elapsed = END_TOPIC_TIME - START_TOPIC_TIME
                minutes, seconds = divmod(elapsed.days * seconds_in_day + elapsed.seconds, 60)
                seconds += minutes*60
                sum_of_time += seconds

                print(f'{bcolors.FAIL}The answer was: {topic}{bcolors.ENDC}')
                completed_topics.append(bcolors.WARNING + topics[i] + ' - ' + str(seconds) + 's' + bcolors.ENDC)
                topics.pop(i)
                showed_notes = False
                shuffle(topics)
                input_call = input('Press ENTER to proceed...')
            else:
                END_TOPIC_TIME = datetime.now()
                elapsed = END_TOPIC_TIME - START_TOPIC_TIME
                minutes, seconds = divmod(elapsed.days * seconds_in_day + elapsed.seconds, 60)
                seconds += minutes*60
                sum_of_time += seconds

                print(f'{bcolors.FAIL}That is incorrect!\nThe correct answer is: {topic}{bcolors.ENDC}')
                completed_topics.append(bcolors.FAIL  + topics[i] + ' - ' + str(seconds) + 's' + bcolors.ENDC)
                topics.pop(i)
                showed_notes = False
                shuffle(topics)
                input_call = input('Press ENTER to proceed...')
        else:
            print_commands_list(description=description, topics=topics)
            print(f'What is {topic}?')
            input_call = input('Command: ')
            if input_call == 'd':
                if description:
                    print(f'The descripion for {topic} is: "{description[0]}"')
                else:
                    print('No description available.')
                input_call = input('Press ENTER to proceed...')
            elif input_call == 's':
                print('Looking up sources. Please wait.')
                sources = search(f"\"defination\" of \"{topic}\" \"wikipedia\"", num_results=5)
                print(f'Found {len(sources)} sources!')
                for source in sources:
                    print(source)

                input_call = input('Press ENTER to proceed...')
            elif input_call == 'notes':
                for path in paths_to_notes:

                    img = cv2.imread(path)

                    # Rescale the image so it fits to the screen.
                    screen_res = 1280, 920
                    scale_width = screen_res[0] / img.shape[1]
                    scale_height = screen_res[1] / img.shape[0]
                    scale = min(scale_width, scale_height)
                    window_width = int(img.shape[1] * scale)
                    window_height = int(img.shape[0] * scale)

                    cv2.namedWindow(topic, cv2.WINDOW_NORMAL)
                    cv2.resizeWindow(topic, window_width, window_height)

                    cv2.imshow(topic, img)
                    cv2.waitKey(0)
                    cv2.destroyAllWindows()
                input_call = input('Press ENTER to proceed...')
            elif input_call in ['next', 'idk', '']:
                END_TOPIC_TIME = datetime.now()
                elapsed = END_TOPIC_TIME - START_TOPIC_TIME
                completed_topics.append(bcolors.OKGREEN + topics[i] + ' - ' + elapsed + bcolors.ENDC)
                topics.pop(i)
                shuffle(topics)
            elif input_call == 'exit':
                break

def start_up():
    global WORK_FROM_DEFINATION, START_STUDYING_TIME
    clear()
    table = PrettyTable()
    json_file_location = PATH_TO_THIS_FOLDER + '/flashcards/JSON_DATA/'
    all_file_names = os.listdir(json_file_location)
    all_file_names = [json_file_location + file for file in all_file_names]
    creation_date = []
    for file in all_file_names:
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)
        creation_date.append(time.ctime(mtime))
    print('Please select what you want to study by typing its index.')
    table.field_names = ['Index', 'Study topics', 'Last edited', 'Date created']
    for i, cdate in enumerate(creation_date, start=1):
        file_name = all_file_names[i-1].split('/')[-1].replace('.json', '')
        table.add_row([i,file_name,str(differnce_in_time(cdate)) + ' ago', cdate])
    print(table)
    while True:
        index = input('Index: ')
        if index == 'exit':
            sys.exit()
        try:
            index = int(index)
            break
        except ValueError:
            print('That\'s not a number. Try again.')
    selected_json = all_file_names[index-1]
    t = selected_json.split('/')[-1]

    with open(selected_json) as f:
        d = json.load(f)
    '''
    This mess of code is responsible for selecting different modules, if they are available.
    The way it works is complicated and not well documented, but it works, (just bearly)
    and its a really hacky solution.

    The reason it's so complicated, is because I didn't want to change the `generate_flashcards.py` script
    because I would have to recompile it to exe on my system, and change how it compiles the json files.

    I would still have to do some re-compiling and what not to get the desired result, but I chose to do this,
    it like this, because it was the first idea I had in mind, and seemed a good one untill I actually tried
    to implment it.
    '''
    try:
        chapter_sorted_json = {}
        # We get all the modules from the json file.
        '''
        This is hacky, because modules are denoted as just a NUMBER. an empty page, with a title
        as a number. So what were looking for here, is for those titles that are just numbers or floats.
        '''
        for key in d.keys():
            try:
                key = int(key)
            except ValueError:
                try:
                    key = float(key)
                except:
                    continue
            '''
            This if statement might seem pointless, and thats right, it seems pointless.

            But due to the nature of OCR and how wrong it can be sometimes, we have to set this boundry.
            '''
            if key > 20:
                continue
            chapter_sorted_json.update({str(key):{}})
        '''
        We need to set the last key as the first one we found if there are any.
        If there are NO numbers in the json file. the program breaks right here, and
        skips to the exception
        '''
        last_key = list(chapter_sorted_json.keys())[0]

        '''
        What were doing here is were creating a new dictionary, but sorting the topics 'MODULE' wise.

        The way it does that is, if were at module 1, were going to add everything from module 1, until
        we hit the next number, or module, say 2, then we will switch over to using module 2, and addying
        everything to module 2, and so on.

        It's hard to explain but were addying everything to the module from the original dictionary, untill
        we hit the next module number.
        '''
        for i, key in enumerate(d.keys()):
            try:
                key = int(key)
                if key < 20: # Not a pointless if statement
                    last_key = key
            except ValueError:
                try:
                    key = float(key)
                    if key < 20: # Not a pointless if statement
                        last_key = key
                except:
                    pass
            # addying the dictionarys to the module organized dictionary.
            chapter_sorted_json[str(last_key)].update({str(key):[]})
            chapter_sorted_json[str(last_key)][str(key)].append(d[str(key)][0])
            chapter_sorted_json[str(last_key)][str(key)].append(d[str(key)][1])

        # We show the user how many modules there are, and how many topics each one has. and were wating for user input.
        clear()
        table = PrettyTable()
        table.field_names = ['Module Index', 'Module', '# of topics']
        for i, module in enumerate(list(chapter_sorted_json.keys())):
            table.add_row([i+1,module,len(chapter_sorted_json[module])])
        print(table)
        module_select = str(input(f"Studying: {t.replace('.json','')}\nPlease type the modules index you want to study.\nType 'all' for all.\nType '1,2,3' for module's 1 through 3.\n\nInput: "))

        # If we select 'all', we want to generate the list of index for all of those indexes.
        if 'a' in module_select.lower():
            module_select = list(range(1,len(list(chapter_sorted_json.keys()))+1))
            module_select = ','.join(str(x) for x in module_select)
        # If we want to go through modules 2-6, its not fun to type, 2,3,4,5,6. So this option is great
        # It basically generatess the string 2,3,4,5,6 for later processing because thats what its based on
        elif '-' in module_select:
            starting_module = int(module_select.split('-')[0])
            ending_module = int(module_select.split('-')[-1])
            module_select = str(starting_module)
            for i in range((ending_module-1)-starting_module):
                module_select += f",{i+starting_module+1}"
            module_select += ',' + str(ending_module)
        '''
        So what were doing here is were creating a temporary dictionary to ONLY add
        the selected module indexes that were inputed. By default ALL modules are selected.

        If you only want module 2 and 6, we add those to this temporary dictionary and head onward.
        '''
        module_select = module_select.split(',')
        temp_module_select = {}
        for i, module_index in enumerate(module_select):
            for key in list(chapter_sorted_json.keys()):
                try:
                    if list(chapter_sorted_json.keys())[int(module_index)-1] == str(key):
                        temp_module_select.update({key:[]})
                        temp_module_select[key] = chapter_sorted_json[key]
                except IndexError:
                    pass
        # Recompile back to standard simplified format using only the selected modules.
        chapter_sorted_json = temp_module_select
        final_compiled_json = {}

        '''
        This is mess

        I'm not sure if there is a better way to do this, but what were doing is, re-parsing
        the json file back to its orginal form.

        What this means is, we just want ALL the topics, and there information, we dont want
        the module, and what topics it includes.

        Essentially were just taking everything from each module and compiling it into one whole.

        for example. If we have module 1 and 2. Module 1 and 2 have different topics, we just merge
        everything from module 1 and 2, into one whole dictionary with no module index or chapters.

        So its basiclly just like it was in the orignal json format, except with less topics.
        '''
        total_topics: int = 0
        for i, key in enumerate(list(chapter_sorted_json.keys())):
            for topic in chapter_sorted_json[key]:
                total_topics += 1
                final_compiled_json.update({topic:[{'dictionary':[]},{'paths':[]}]})
                try:
                    description = (chapter_sorted_json[key][topic][0]['dictionary'][0])
                except IndexError:
                    description = []
                try:
                    path = chapter_sorted_json[key][topic][1]['paths'][0]
                except IndexError:
                    path = []
                final_compiled_json[topic][0]['dictionary'].append(description)
                final_compiled_json[topic][1]['paths'].append(path)
        studying_modules: list(str) = list(chapter_sorted_json.keys())
        if len(studying_modules) == 1:
            studying_modules = studying_modules[0]
        else:
            studying_modules = ', '.join(studying_modules)
        minutes_to_complete = int(total_topics/(0.0751*60))
        seconds_to_complete = int(((total_topics/(0.0751*60)) - minutes_to_complete)*100)
        yes_or_no = input(f"Studying: {t.replace('.json','')}\nModule(s): {studying_modules}\nTotal topics: {total_topics}\nApproximately will take: {minutes_to_complete} minutes and {seconds_to_complete} seconds to compelte.\nWould you like to work from definitions to find the keyword?\nyes or no?)")
    # This is because sometimes the study material doesnt have modules.
    except IndexError:
        # Set it back to the original json file.
        final_compiled_json = d
        yes_or_no = input(f"Studying: {t.replace('.json','')}\nWould you like to work from definitions to find the keyword?\nyes or no?)")
    if 'y' in yes_or_no.lower():
        WORK_FROM_DEFINATION = True
    elif 'n' in yes_or_no.lower():
        WORK_FROM_DEFINATION = False
        isTimed = False
    elif 'exit' in yes_or_no.lower():
        sys.exit()
    # I have decided that this will be a default setting.
    # if WORK_FROM_DEFINATION:
    #     isTimed = input(f"\nDo you want to be timed?\nyes or no?")
    #     if 'y' in isTimed.lower():
    #         isTimed = True
    #         print('\nYou will have 30 seconds to complete each topic.\n')
    #     elif 'n' in isTimed.lower():
    #         isTimed = False
    #     elif 'exit' in isTimed.lower():
    #         sys.exit()
    show_topics = input(f"\nDo you want to see the list of all the topics?\nyes or no?")
    if 'y' in show_topics.lower():
        show_topics = True
    elif 'n' in show_topics.lower():
        show_topics = False
    elif 'exit' in show_topics.lower():
        sys.exit()
    show_multiple_choices = input(f"\nDo you want to be shown multiple choices?\nyes or no?")
    if 'y' in show_multiple_choices.lower():
        show_multiple_choices = True
    elif 'n' in show_multiple_choices.lower():
        show_multiple_choices = False
    elif 'exit' in show_multiple_choices.lower():
        sys.exit()
    print(f'\n\nLoading {t}')
    START_STUDYING_TIME = datetime.now()
    main(final_compiled_json, True, show_topics, show_multiple_choices)
start_up()


import asyncio
from utils.constants import CSV_HEADERS
from utils.csv import extract_rncs_from_csv, write_row
from utils.automation import look_up_rnc, extract_required_data, open_look_up_page
from classes.browser_navigation import BrowserNavigation

async def main():
    rnc_list = extract_rncs_from_csv("data/rn.csv")

    rnc = "1-32-42125-6"

    page, browser = await open_look_up_page()
    await look_up_rnc(rnc, page)
    extracted_data = await extract_required_data(rnc, page)

    print(extracted_data)
    write_row(extracted_data, CSV_HEADERS)

    await browser.close()

print("Starting...")
asyncio.get_event_loop().run_until_complete(main())
print("Done ✅")