import pygame

# drawing the hole machine
def draw_enigma(enigma,  path, sc, width, heigth, mar, gap, font):
    TITLE_FONT = pygame.font.SysFont("FreeMono", 30, bold=True)
    massage_FONT = pygame.font.SysFont("FreeMono", 17, bold=True)

    # width and heigth of components
    w = (width - mar["left"] - mar["right"] - 5 * gap) / 6
    h = heigth - mar["top"] - mar["bottom"]

    # path coordinates
    y = [mar["top"]+(signal+1)*h/27 for signal in path] # y coordinates
    x = [width-mar["right"]-w/2] # keyboard

    for i in [4, 3, 2, 1, 0]: # forward pass
        x.append(mar["left"]+i*(w+gap)+w*3/4)
        x.append(mar["left"]+i*(w+gap)+w*1/4)

    x.append(mar["left"]+w/2) # reflector

    for i in [1, 2, 3, 4]: # backward pass
        x.append(mar["left"]+i*(w+gap)+w*1/4)
        x.append(mar["left"]+i*(w+gap)+w*3/4)

    x.append(width-mar["right"]-w/2) # lamboard

    # draw the path
    if len(path) > 0:
        for i in range(1, 21):
            if i < 10:
                color = "#43aa3b"
            elif i < 12:
                color = "#f9c74f"
            else:
                color = "#e63946"
            start = (x[i-1], y[i-1])
            end = (x[i], y[i])
            pygame.draw.line(sc, color, start, end, width=3)

    # base coordinates
    x = mar["left"]
    y = mar["top"]

    # draw enigma components
    for comp in [enigma.re, enigma.r1, enigma.r2, enigma.r3, enigma.pb, enigma.kb]:
        comp.draw(sc, x, y, w, h, font)
        x += w + gap

    # Applications messages

    # Application name
    x = width/2
    y = 20
    message = TITLE_FONT.render("ENIGMA SIMULATION", True, "white")
    text_box = message.get_rect(center=(x, y))
    sc.blit(message, text_box)

    # exit info
    x = width-300
    y = heigth-20
    message = massage_FONT.render("PRESS ESC BUTTON TO EXİT THE APP.", True, "white")
    text_box = message.get_rect(center=(x, y))
    sc.blit(message, text_box)

    # text cleaning info
    x = width - 300
    y = heigth - 40
    message= massage_FONT.render("PRESS BACKSPACE BUTTON TO DELLETE THE MESSAGE.", True, "white")
    text_box = message.get_rect(center=(x, y))
    sc.blit(message, text_box)

    # write the names of components
    names = ["Reflector", "Left", "Middle", "Right", "Plugboard", "Key/Lamb"]
    y = mar["top"]*0.8
    for i in range(len(names)):
        x = mar["left"] + w/2 + i*(w+gap)
        title = font.render(names[i], True, "white")
        text_box = title.get_rect(center=(x, y))
        sc.blit(title, text_box)


from model.bert import BERT
import bert_pytorch
import torch
import torch.nn as nn
from dataset.dataset import BERTDataset
from dataset.vocab import WordVocab
from torch.utils.data import DataLoader
import csv
import random
from torch.utils.data import TensorDataset, DataLoader
import numpy as np
from ENSEMBLE_BERT import DeepSEA_BERT
import os
import argparse
from sklearn.metrics import recall_score,precision_score,f1_score,accuracy_score
base_dict = {'A':0, 'G':1, 'C':2, 'T':3}
def seq2idxseq(seq, base_dict):
    idx_list = [np.zeros(len(seq)) for i in range(len(base_dict))]
    for i in range(len(seq)):
        idx_list[base_dict[seq[i]]][i] = 1
    return idx_list
def tensor_generate(data_set):
    x = torch.FloatTensor(np.array([seq2idxseq(x[0], base_dict) for x in data_set])).cuda()
    y = torch.FloatTensor(np.array([x[1] for x in data_set])).cuda()
    return x,y

def seq_split(seq):
    head = list(seq)
    return ' '.join(head)
def sentence_processing(sentence1):
    out1 = [vocab.sos_index] + vocab.to_seq(sentence1) + [vocab.eos_index]
    seg_labels = [1 for x in out1]
    return (out1, seg_labels)
def data_generate(data_set):
    seq_list= []
    label_list = []
    target_list = []
    for row in data_set:
        curr_seq = seq_split(row[0])
        bert_seq,bert_label = sentence_processing(curr_seq)
        seq_list.append(bert_seq)
        label_list.append(bert_label)
        target_list.append(row[1])
    return torch.LongTensor(seq_list).cuda(),torch.LongTensor(label_list).cuda(),torch.FloatTensor(target_list).cuda()

def test_model_out(xy_set, model, batch_size, th=0.5):
    model.eval()
    data_loader = DataLoader(dataset=xy_set,batch_size=batch_size,shuffle=True)
    true_out = []
    model_out = []
    for i,data in enumerate(data_loader):
        bert_seq,bert_label,sea,target = data
        out = model(bert_seq,bert_label,sea)
        out = out.detach().cpu().numpy()
        target = target.cpu().numpy()
        for j in range(len(target)):
            curr = 0
            if out[j] > th:
                curr = 1
            true_out.append(target[j])
            model_out.append(curr)
    return model_out, true_out

parser = argparse.ArgumentParser()
parser.add_argument("--test_corpus",
                        default=None,
                        type=str,
                        required=True,
                        help="The input test corpus.")

parser.add_argument("--batch_size",
                        default=32,
                        type=int,
                        required=False,
                        help="batch size")

parser.add_argument("--vocab",
                        default='vocab.small',
                        type=str,
                        required=False,
                        help="vocab path.")

parser.add_argument("--model_path",
                        default=None,
                        type=str,
                        required=True,
                        help="model path.")

args = parser.parse_args()

test_set = []
with open(args.test_corpus, 'r') as csvfile:
    csv_reader = csv.reader(csvfile, delimiter='\t',
                            quotechar='|', quoting=csv.QUOTE_MINIMAL)
    for row in csv_reader:
        test_set.append([row[1], int(row[0])])

vocab = WordVocab.load_vocab(args.vocab)

sea_test,_ = tensor_generate(test_set)

seq_list,label_list,target_list = data_generate(test_set)
test_dataset = TensorDataset(seq_list,label_list,sea_test,target_list)

classifiy_model = torch.load(args.model_path).cuda()
model_out, true_out = test_model_out(test_dataset, classifiy_model, args.batch_size)
print('Recall:' + str(recall_score(true_out, model_out)))
print('Precision:' + str(precision_score(true_out, model_out)))
print('F1:' + str(f1_score(true_out, model_out)))
print('Accuracy:' + str(accuracy_score(true_out, model_out)))


import re

def Ej6(secuencias):
	print("Secuencias formidables: ")
	formidables = []
	l = 0
	index = -1
	for i in secuencias:
		val = re.findall("[0,1]+", i)
		if len(val)==1:
			if es_formidable(i):
				l += 1
				print(str(l) + ". " + i)
				formidables.append(i)
#			else:
#				print(i + " no es formidable")
#		else:
#			print("La secuencia " + i + " no es valida")
	if l>0:
		index = int(input("Cual secuencia desea enviar: "))
		while index<1 or l<index:
			index = int(input("Ingrese un numero valido: "))
		return (formidables[index-1],l)
	else:
		return ('',0)

def es_formidable(string):
	zero = string.split("1")[::-1]
	len_zero = []
	temp_zero = 0
	flag_zero = True

	one = string.split("0")
	len_one = []
	temp_one = 0
	flag_one = True

	for i in zero:
		if len(i)>0:
			if len(i)>temp_zero:
				temp_zero = len(i)
			else:
				flag_zero = False

	for i in one:
		if len(i)>0:
			if len(i)>temp_one:
				temp_one = len(i)
			else:
				flag_one = False

	return flag_zero and flag_one

print("Ejercicio 6:\nTest case:\nInput 1: 000100110 00 10 5 6\nInput 2: 2\nReturn: ('00',3)\n")
string = str(input("Ingrese las secuencias separadas por un espacio : ")).split(" ")
print(Ej6(string))


# coding: utf-8

import os
from setuptools import setup, Command

class CleanCommand(Command):
    """Custom clean command to tidy up the project root."""
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        os.system('rm -vrf ./build ./dist ./*.pyc ./*.tgz ./*.egg-info')

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
    name='django-explain',
    version='1.1.1',
    py_modules=[
        'django_explain',
    ],
    include_package_data=True,
    install_requires=['Django>=1.7', 'requests', 'sqlparse'],
    license='MIT License',
    description='A helper to get EXPLAIN and EXPLAIN ANALYZE OUTPUT for django queryset.',
    author='Egor Orlov',
    author_email='oeegor@gmail.com',
    platforms='any',
    url='https://github.com/oeegor/django-explain',
    classifiers=[
        'Environment :: Web Environment',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3.5',
        'Topic :: Internet :: WWW/HTTP',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
    ],
    cmdclass={
        'clean': CleanCommand,
    },
)


from datetime import datetime
import pytest
from unittest.mock import MagicMock, patch, PropertyMock

from __app__.processor.adscorer import score_ad
from __app__.processor.scorers.functions import (
    frequency_scores,
    frequency_score_helper,
    twilio_score,
)
from tests.fixtures.no_network import *
from tests.fixtures.spam_cache import test_cache

@patch("__app__.processor.scorers.functions.twilio_request")
@pytest.mark.parametrize(
    "pnumber, tstatus, tscore, expected_score",
    [
        ("+1-347-687-5904", "successful", 0.1, 0.1),
        ("+1-347-687-5904", "successful", 0.2, 0.2),
        ("+1-347-687-5905", "failure", None, None),
        ("+1-347-687-5906", "successful", 0.5, 0.5),
        (None, "successful", None, None),
    ],
)
def test_twilio_result(
    mock_twilio, test_cache, pnumber, tstatus, tscore, expected_score
):
    msg = {"primary-phone-number": pnumber}
    mock_twilio_resp = MagicMock()
    mock_twilio_resp.add_ons = {
        "status": tstatus,
        "results": {"truecnam_truespam": {"result": {"spam_score": tscore}}},
    }

    mock_twilio.return_value = mock_twilio_resp

    score = twilio_score(msg, "TEST_SID", "TEST_TOKEN", test_cache)
    assert score == expected_score
    assert (
        test_cache.get_cached_score(
            phone_number=msg["primary-phone-number"], score_key="truespam"
        )
        == expected_score
    )

def test_twilio_cached_result(test_cache):
    msg = {"primary-phone-number": "+1-347-687-5904"}
    test_cache.put_cached_score(
        phone_number=msg["primary-phone-number"], score_key="truespam", score=10
    )

    score = twilio_score(msg, "TEST_SID", "TEST_TOKEN", test_cache)
    assert score == 10


# -*- coding: utf-8 -*-
"""
Created on Tue Apr  9 13:46:06 2019

Figure 5. Seasonal terrestrial and oceanic contributions to Australian precipitation in (a) relative terms [%]
and (b) absolute terms [mm].

@author: z3131380
"""

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import FormatStrFormatter

#==============================================================================
# Definitions
#==============================================================================
dir_in = r'/srv/ccrc/data19/z3131380/PartB/Output/Australia/100parcels/TS10min/exp01/Processed/Rainfall_recycling/'
dir_out = r'/home/z3131380/hdrive/PhD/PartB/Reporting/Paper_1/Figures/'

region = 'Australia'
df = pd.read_csv(dir_in+'Seasonal/'+region+'_seasonal_rainfall_recycling_1979-2013.csv')
groups = df.groupby('Season')
#yr_groups = df.groupby(df.Year.astype(int))
#RR_seas_mean = [groups.get_group('DJF')['RR'].mean(),\
#                            groups.get_group('MAM')['RR'].mean(),\
#                            groups.get_group('JJA')['RR'].mean(),\
#                            groups.get_group('SON')['RR'].mean()]
#RR_seas_min = [groups.get_group('DJF')['RR'].min(),\
#                            groups.get_group('MAM')['RR'].min(),\
#                            groups.get_group('JJA')['RR'].min(),\
#                            groups.get_group('SON')['RR'].min()]
#RR_seas_max = [groups.get_group('DJF')['RR'].max(),\
#                            groups.get_group('MAM')['RR'].max(),\
#                            groups.get_group('JJA')['RR'].max(),\
#                            groups.get_group('SON')['RR'].max()]
#RR_seas_25th = [groups.get_group('DJF')['RR'].quantile(q=0.25),\
#                            groups.get_group('MAM')['RR'].quantile(q=0.25),\
#                            groups.get_group('JJA')['RR'].quantile(q=0.25),\
#                            groups.get_group('SON')['RR'].quantile(q=0.25)]
#RR_seas_75th = [groups.get_group('DJF')['RR'].quantile(q=0.75),\
#                            groups.get_group('MAM')['RR'].quantile(q=0.75),\
#                            groups.get_group('JJA')['RR'].quantile(q=0.75),\
#                            groups.get_group('SON')['RR'].quantile(q=0.75)]

# Open annual df to plot comparison
df2 = pd.read_csv(dir_in+'Yearly/'+region+'_annual_rainfall_recycling_1979-2013.csv')

fsize = 14

#==============================================================================
# Functions
#==============================================================================
def make_patch_spines_invisible(ax):
    ax.set_frame_on(True)
    ax.patch.set_visible(False)
    for sp in ax.spines.values():
        sp.set_visible(False)

#==============================================================================
# Plot seasonal RR for selected years
#==============================================================================
#
#
## Pick wet and dry years
#sel_yrs = [1986,1989,1994,1999,2002,2010]
#
#fig, ax1 = plt.subplots()#figsize=(16,11.7))
#for y in sel_yrs:#np.arange(1979,2014):
#    ax1.plot(range(4),yr_groups.get_group(y)['RR'],':o', label=y,markersize=4,linewidth=2)
#    plt.annotate(y,xy=(3.0,yr_groups.get_group(y)['RR'].reset_index(drop=True)[3]),fontsize=12)#xycoords='data',

#==============================================================================
# Figure 5. Seasonal terrestrial and oceanic contributions to Australian precipitation in (a) relative terms
# [%] and (b) absolute terms [mm], (c) mean seasonal precipitation recycling.
#==============================================================================

fig, ax2 = plt.subplots(figsize=(16,11.7))
ax2 = plt.subplot2grid((3, 1), (0, 0), rowspan=1)
plt.annotate('(a)',xy=(1980,1.3),xycoords='data',fontsize=fsize)
#p3, = ax2.plot(df['Year'],df['P_anom'],'-k', label="Rainfall anomaly [%]",linewidth=1.5)#,alpha=0.7)
ax2.fill_between(df['Year'],df['P_anom'],0,alpha=0.5,edgecolor='None',facecolor='gray')
# NOTE: the annual time series has been moved to show its values in the MIDDLE of each year. This differs
# from the purely annual plots, with show values at the START of each year. Careful when analysing.
#p2, = ax1.plot(df2['Year']+0.5,df2['P_anom'],':', label="Rainfall anomaly [%]",markersize=4,linewidth=2,color='gray')
ax2.set_ylim(-1.5,1.5)
ax2.grid(axis='x')
ax2.tick_params(labelbottom=False,labeltop=True)
ax2.axhline(0,color='gray',linestyle=':')
ax2.set_yticklabels(ax2.get_yticks(),fontsize=fsize)
ax2.yaxis.set_major_formatter(FormatStrFormatter('%.1f'))
plt.annotate('Precipitation anomaly [%]',xy=(-0.06,0.85),xycoords='axes fraction',fontsize=fsize,rotation=90)
ax2a = ax2.twinx()
ax2b = ax2.twinx()
p2a, = ax2a.plot(df['Year'],df['outregion_ocean_pct'],'-bo', label="Ocean water vapour contribution [%]",markersize=3,markeredgecolor='b',markerfacecolor='b',alpha=0.4)
p2b, = ax2b.plot(df['Year'],df['RR'],'-gs', label="Land water vapour contribution [%]",markersize=3,markeredgecolor='g',markerfacecolor='g')#,alpha=0.5)
ax2a.spines["right"].set_position(("axes", 1.01))
ax2b.spines["right"].set_position(("axes", 1.07))
make_patch_spines_invisible(ax2a)
make_patch_spines_invisible(ax2b)
ax2a.spines["right"].set_visible(True)
ax2b.spines["right"].set_visible(True)
tkw = dict(size=4, width=1.5)
ax2a.tick_params(axis='y', colors=p2a.get_color(), **tkw)
ax2b.tick_params(axis='y', colors=p2b.get_color(), **tkw)
ax2a.set_yticklabels(ax2a.get_yticks(),fontsize=fsize)
ax2a.yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
ax2b.set_yticklabels(ax2b.get_yticks(),fontsize=fsize)
ax2b.yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
ax2a.set_ylim(70,95)
ax2b.set_ylim(5,25)
plt.annotate("Oceanic contribution [%]",xy=(1.04,0.85),xycoords = 'axes fraction',rotation=90,color='blue',fontsize=fsize)
plt.annotate("Terrestrialcontribution [%]",xy=(1.1,0.9),xycoords = 'axes fraction',rotation=90,color='green',fontsize=fsize)
ax2.set_xlim(1979,2014)
ax2.set_xticks(np.arange(min(df['Year']), max(df['Year'])+1, 1))
ax2.set_xticklabels(ax2.get_xticks(),rotation=90,fontsize=fsize)
ax2.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))

ax1 = plt.subplot2grid((3, 1), (1, 0), sharex=ax2)
plt.annotate('(b)',xy=(1980,1800),xycoords='data',fontsize=fsize)
#p1, = ax1.plot(df['Year'],df['P_total']/1000,'-k', label="Rainfall total [m]",linewidth=1.5)#,alpha=0.7)
ax1.fill_between(df['Year'],df['P_total']/1000,0,alpha=0.5,edgecolor='None',facecolor='gray')
# NOTE: the annual time series has been moved to show its values in the MIDDLE of each year. This differs
# from the purely annual plots, with show values at the START of each year. Careful when analysing.
#p2, = ax1.plot(df2['Year']+0.5,df2['P_total']/1000,':', label="Rainfall total [m]",markersize=4,linewidth=2,color='gray')
ax1.set_ylim(0,2000)
ax1.grid(axis='x')
ax1.set_xlim(1979,2014)
ax1.set_xticks(np.arange(min(df['Year']), max(df['Year'])+1, 1))
ax1.set_xticklabels(ax1.get_xticks(),rotation=90,fontsize=fsize)
ax1.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
ax1.set_yticklabels(ax1.get_yticks(),fontsize=fsize)
ax1.yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
plt.annotate('Precipitation total [m]',xy=(-0.06,0.75),xycoords='axes fraction',fontsize=fsize,rotation=90)
ax1a = ax1.twinx()
ax1b = ax1.twinx()
p1a, = ax1a.plot(df['Year'],df['outregion_ocean_mm']/1000,'-bo', label="Ocean water vapour contribution [m]",markersize=3,markeredgecolor='b',markerfacecolor='b',alpha=0.4)
p1b, = ax1b.plot(df['Year'],df['region_land_mm']/1000,'-gs', label="Land water vapour contribution [m]",markersize=3,markeredgecolor='g',markerfacecolor='g')#,alpha=0.5)
ax1a.spines["right"].set_position(("axes", 1.01))
ax1b.spines["right"].set_position(("axes", 1.07))
make_patch_spines_invisible(ax1a)
make_patch_spines_invisible(ax1b)
ax1a.spines["right"].set_visible(True)
ax1b.spines["right"].set_visible(True)
tkw = dict(size=4, width=1.5)
ax1a.tick_params(axis='y', colors=p1a.get_color(), **tkw)
ax1b.tick_params(axis='y', colors=p1b.get_color(), **tkw)
ax1a.set_yticklabels(ax1a.get_yticks(),fontsize=fsize)
ax1a.yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
ax1b.set_yticklabels(ax1b.get_yticks(),fontsize=fsize)
ax1b.yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
plt.annotate("Oceanic contribution [m]",xy=(1.05,0.85),xycoords = 'axes fraction',rotation=90,color='blue',fontsize=fsize)
plt.annotate("Terrestrial contribution [m]",xy=(1.105,0.9),xycoords = 'axes fraction',rotation=90,color='green',fontsize=fsize)

#ax3  = fig.add_axes([0.5,0.42,0.25,0.15])
ax3 = plt.subplot2grid((3, 1), (2, 0))#, sharex=ax2)
plt.annotate('(c)',xy=(0.6,22),xycoords='data',fontsize=fsize)
#ax3.bar(range(4),RR_seas_mean,width=0.6,align='center',facecolor='g',alpha=0.8)
data = np.column_stack([groups.get_group('DJF')['RR'][1:],\
                        groups.get_group('MAM')['RR'][1:],\
                        groups.get_group('JJA')['RR'][1:],\
                        groups.get_group('SON')['RR'][1:]])
bp = ax3.boxplot(data,whis='range')
plt.setp(bp['boxes'], color='black')
plt.setp(bp['whiskers'], color='black')
plt.setp(bp['medians'], color='black')
for line in bp['medians']:
    # get position data for median line
    x = bp['medians'].index(line)+1#line.get_xydata()[1][0]-line.get_xydata()[0][0]
    y = line.get_xydata()[0,1] + 0.1
    mn = line.get_xydata()[0,1]
    # overlay median value
    plt.text(x, y, '%.1f' % mn,
         horizontalalignment='center',fontsize=12) # draw above, centered
ax3.set_ylim(5,25)
#plt.tick_params(
#    axis='both',          # changes apply to the x-axis
#    which='both',      # both major and minor ticks are affected
#    bottom=False,      # ticks along the bottom edge are off
#    top=False,         # ticks along the top edge are off
#    left=False,
#    labelleft=False,
#    labelbottom=False) # labels along the bottom edge are off
ax3.set_xticklabels(['Summer','Fall','Winter','Spring'],fontsize=fsize)
ax3.set_yticklabels(ax3.get_yticks(),fontsize=fsize)
ax3.yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
plt.annotate('Precipitation recycling [%]',xy=(-0.06,0.9),xycoords='axes fraction',fontsize=fsize,rotation=90)
#plt.annotate('Summer',xy=(0,11),xycoords='data',fontsize=12,rotation=90)
#plt.annotate('Fall',xy=(1,4),xycoords='data',fontsize=12,rotation=90)
#plt.annotate('Winter',xy=(2,8),xycoords='data',fontsize=12,rotation=90)
#plt.annotate('Spring',xy=(3,9),xycoords='data',fontsize=12,rotation=90)
#plt.annotate(str(round(RR_seas_mean[0],1))+'%',xy=(-0.2,20),xycoords='data',fontsize=12)
#plt.annotate(str(round(RR_seas_mean[1],1))+'%',xy=(0.8,17),xycoords='data',fontsize=12)
#plt.annotate(str(round(RR_seas_mean[2],1))+'%',xy=(1.8,13),xycoords='data',fontsize=12)
#plt.annotate(str(round(RR_seas_mean[3],1))+'%',xy=(2.8,17.9),xycoords='data',fontsize=12)

plt.tight_layout()
fig.savefig(dir_out+'Fig5.png',bbox_inches='tight')
plt.close()


import datetime
import pickle

class GlobalKVS(object):
    """
    (c) Aleksei Tiulpin, Unversity of Oulu, 2018-

    """
    _instance = None
    _d = dict()

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(GlobalKVS, cls).__new__(cls, *args, **kwargs)
        return cls._instance

    def update(self, tag, value, dtype=None):
        """
        Updates the internal state of the logger.
        Parameters
        ----------
        tag : str
            Tag, of the variable, which we log.
        value : object
            The value to be logged
        dtype :
            Container which is used to store the values under the tag
        Returns
        -------
        """
        if tag not in self._d:
            if dtype is None:
                self._d[tag] = (value, str(datetime.datetime.now()))
            else:
                self._d[tag] = dtype()
        else:
            if isinstance(self._d[tag], list):
                self._d[tag].append((value, str(datetime.datetime.now())))
            elif isinstance(self._d[tag], dict):
                self._d[tag].update((value, str(datetime.datetime.now())))
            else:
                self._d[tag] = (value, str(datetime.datetime.now()))

    def __getitem__(self, tag):
        if not isinstance(self._d[tag], (list, dict)):
            return self._d[tag][0]
        else:
            return self._d[tag]

    def tag_ts(self, tag):
        return self._d[tag]

    def save_pkl(self, filename):
        with open(filename, 'wb') as f:
            pickle.dump(self._d, f)


import pandas as pd
from datetime import datetime

now = datetime.now() # current date and time
today = now.strftime('%Y-%m-%d')

''' https://fixturedownload.com/results/epl-2020 '''

# show complete records by changing rules
# pd.set_option('display.max_rows', None)
# pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)

def triangle(n):
    """Calculates the triangle number. ie. 1+2+...+n"""
    return sum([x for x in range(n+1)])

# Scrape tables from the webpage
dfs = pd.read_html('https://fixturedownload.com/results/epl-2020')

# Load the dataset into the dataframe
df = dfs[0]

# Split into results and fixtures
df_r = df.copy()[df['Result'].str.contains(" - ")]
df_f = df.copy()[~df['Result'].str.contains(" - ")]

# Create Home and Away Result Columns
splits = df_r['Result'].str.split()
df_r['Home_Score'] = splits.str[0]
df_r['Away_Score'] = splits.str[2]

df_r['Home_Result'] = ['win' if x > y else 'loss' if y > x else 'draw'
                       for x,y in zip(df_r['Home_Score'],df_r['Away_Score'])]
df_r['Away_Result'] = ['win' if x > y else 'loss' if y > x else 'draw'
                       for x,y in zip(df_r['Away_Score'],df_r['Home_Score'])]

# Print dataframes for viewing
# print(df_r)
# print('---------------')
# print(df_f)

# The set of stats that will be recorded for all teams
stats = {'Home_games': 0,
         'Away_games': 0,
         'Home_points': 0,
         'Away_points': 0,
         'Total_points': 0,
         'Game_mp': 0,
         'Home_mp': 0,
         'Away_mp': 0,
         'H_form_mp': 0,
         'A_form_mp': 0,
         'Home_form': 0,
         'Away_form': 0}

# Each of the teams as key to a dictionary holding their respective stats
teams = {'Arsenal': stats.copy(),
         'Aston Villa': stats.copy(),
         'Brighton': stats.copy(),
         'Burnley': stats.copy(),
         'Chelsea': stats.copy(),
         'Crystal Palace': stats.copy(),
         'Everton': stats.copy(),
         'Fulham': stats.copy(),
         'Leeds': stats.copy(),
         'Leicester': stats.copy(),
         'Liverpool': stats.copy(),
         'Newcastle': stats.copy(),
         'Man City': stats.copy(),
         'Man Utd': stats.copy(),
         'Sheffield Utd': stats.copy(),
         'Southampton': stats.copy(),
         'Spurs': stats.copy(),
         'West Brom': stats.copy(),
         'West Ham': stats.copy(),
         'Wolves': stats.copy()}

# Update the stats as per the results dataframe
for index, row in df_r.iterrows():
    if row['Home_Result'] == 'win':
        teams[row['Home Team']]['Home_games'] += 1
        teams[row['Home Team']]['Home_points'] += 3
        teams[row['Away Team']]['Away_games'] += 1
        teams[row['Away Team']]['Away_points'] += 0
        teams[row['Home Team']]['Total_points'] += 3
        teams[row['Home Team']]['Home_form'] += teams[row['Home Team']]['Home_games'] * 3   # Home Form
    elif row['Home_Result'] == 'loss':
        teams[row['Home Team']]['Home_games'] += 1
        teams[row['Home Team']]['Home_points'] += 0
        teams[row['Away Team']]['Away_games'] += 1
        teams[row['Away Team']]['Away_points'] += 3
        teams[row['Away Team']]['Total_points'] += 3
        teams[row['Away Team']]['Away_form'] += teams[row['Away Team']]['Away_games'] * 3   # Away Form
    else:
        teams[row['Home Team']]['Home_games'] += 1
        teams[row['Home Team']]['Home_points'] += 1
        teams[row['Away Team']]['Away_games'] += 1
        teams[row['Away Team']]['Away_points'] += 1
        teams[row['Home Team']]['Total_points'] += 1
        teams[row['Away Team']]['Total_points'] += 1
        teams[row['Home Team']]['Home_form'] += teams[row['Home Team']]['Home_games'] * 1   # Home Form
        teams[row['Away Team']]['Away_form'] += teams[row['Away Team']]['Away_games'] * 1   # Away Form

# Calculate the Game, home/away and form multipliers for each team
for team, stats in teams.items():
    stats['Game_mp'] = stats['Total_points'] / (stats['Home_games'] + stats['Away_games'])
    stats['Home_mp'] = stats['Home_points'] / stats['Home_games']
    stats['Away_mp'] = stats['Away_points'] / stats['Away_games']
    stats['H_form_mp'] = stats['Home_form'] * stats['Home_games'] / triangle(stats['Home_games']) / stats['Home_points']
    stats['A_form_mp'] = stats['Away_form'] * stats['Away_games'] / triangle(stats['Away_games']) / stats['Away_points']

avg_points = 0

# Print stats for viewing
for team, stats in teams.items():
    # print(team, ': ', stats)
    avg_points += stats['Game_mp']

# This calculates the average points per game for the season
avg_points /= 10

# Update stats for fixtures
for index, row in df_f.iterrows():
    home_temp = 0.5 * teams[row['Home Team']]['H_form_mp'] * (
                      teams[row['Home Team']]['Game_mp'] + teams[row['Home Team']]['Home_mp'])
    away_temp = 0.5 * teams[row['Home Team']]['H_form_mp'] * (
                      teams[row['Away Team']]['Game_mp'] + teams[row['Away Team']]['Away_mp'])
    point_update_h = teams[row['Home Team']]['Game_mp'] * avg_points / (home_temp + away_temp)
    # print('point_update_h: ', point_update_h)
    point_update_a = avg_points - point_update_h
    # print('point_update_a: ', point_update_a)

    # Update Stats for Home team
    teams[row['Home Team']]['Home_games'] += 1
    teams[row['Home Team']]['Home_points'] += point_update_h
    teams[row['Home Team']]['Total_points'] += point_update_h
    # Update Stats for Away team
    teams[row['Away Team']]['Away_games'] += 1
    teams[row['Away Team']]['Away_points'] += point_update_a
    teams[row['Away Team']]['Total_points'] += point_update_a

result_list = []

for team, stats in teams.items():
    result_list.append((team, round(stats['Total_points'])))

print('PL predictions on ', today, '\n')

result_list.sort(key = lambda x: x[1], reverse=True)

for team, pts in result_list:
    print(team, pts)

result_list.sort()

df_output = pd.DataFrame(result_list, columns=['team', today])

df_output = df_output.drop(['team'], axis=1)

df_output = df_output.transpose()

df_output.to_csv('predict.csv', mode='a', header=False)

print('\n PL Predictions have been written to csv')


import sys
import pygame
from game.Button import Button
import utils.solver as solver
import copy

from pygame.locals import (
    K_0,
    K_1,
    K_2,
    K_3,
    K_4,
    K_5,
    K_6,
    K_7,
    K_8,
    K_9,
    K_ESCAPE,
    K_RETURN,
    K_BACKSPACE,
    K_SPACE,
    QUIT,
    K_s
)

SCREEN_WIDTH = 500
SCREEN_HEIGHT = 600

def create_buttons(screen, grid):

    buttons = []
    fake_buttons = []
    x = 21
    for i in range(9):
        y = 18
        for j in range(9):
            if (j % 3 == 0):
                y += 3
            if (grid[j][i] == '0'):
                buttons.append( Button((x, y), coordinates=(j, i)) )
            else:
                fake_buttons.append( Button((x, y), grid[j][i], pygame.Color("Grey")) )
            y += 50
        if ((i+1) % 3 == 0):
            x += 3
        x += 50

    for fake_button in fake_buttons:
        fake_button.show(screen, update=False)
    for button in buttons:
        button.show(screen, update=False)
    pygame.display.update()
    return (buttons)

def init_screen():

    pygame.init()
    # for performance, pygame doesn't register mouse motion as an event
    pygame.event.set_blocked(pygame.MOUSEMOTION)
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    screen.fill(pygame.Color("White"))
    pygame.draw.rect(screen, pygame.Color("Black"), pygame.Rect(16, 16, 464, 464), border_radius=3)
    print_message(screen, 'Let the game begin')
    pygame.display.flip()
    return (screen)

def print_message(screen, message):

    font = pygame.font.SysFont("Arial", 35)
    text = font.render(message, 1, pygame.Color("Black"))
    size = text.get_size()
    surface = pygame.Surface((400, 100))
    surface.fill(pygame.Color("White"))
    screen.blit(surface, (50, 500))
    pygame.draw.rect(surface, pygame.Color("Orange"), pygame.Rect(0, 0, size[0], size[1]))
    pygame.draw.rect(surface, pygame.Color("White"), pygame.Rect(5, 5, size[0] - 10, size[1] - 10))

    screen.blit(surface, (SCREEN_WIDTH/2 - size[0]/2, 510))
    screen.blit(text, (SCREEN_WIDTH/2 - size[0]/2, 510))
    pygame.display.update()

def main(grid_file):

    screen = init_screen()
    keys_possible = {K_BACKSPACE: '0', K_0: '0', K_1: '1', K_2: '2', K_3: '3',
                K_4: '4', K_5: '5', K_6: '6', K_7: '7', K_8: '8', K_9: '9'}
    original_grid = solver.make_grid(grid_file)
    if (solver.check_grid_format(original_grid) is False):
        print('\nPlease use a well formatted map')
        print('(9x9 grid, separated by spaces with only numbers between 0 and 9)')
        exit()
    solver.print_instructions_cli()
    grid = copy.deepcopy(original_grid)
    buttons = create_buttons(screen, grid)
    running = True
    current_button = None

    while running:

        event = pygame.event.wait()
        if event.type == QUIT:
            running = False

        # event to select a tile with the mouse
        if event.type == pygame.MOUSEBUTTONDOWN:
            for button in buttons:
                if button.check_click(screen) == True:
                    if (current_button is not None and current_button != button):
                        current_button.draw_borders(screen, pygame.Color("White"))
                    current_button = button

        if event.type == pygame.KEYDOWN:
            # ESC quits the game
            if event.key == K_ESCAPE:
                running = False

            # enter (return) checks if the player's solution is valid
            if event.key == K_RETURN:
                if (solver.grid_is_valid(grid) == True):
                    print_message(screen, "Success")
                else:
                    print_message(screen, "Not quite right!")

            # space solves the sudoku automatically
            if event.key == K_SPACE or event.key == K_s:
                if (not solver.precheck_grid(original_grid)):
                    print_message(screen, 'No solution exists')
                else:
                    if (event.key == K_SPACE):
                        ret = solver.solver(original_grid, 0, 0, buttons, screen, visu=True)
                    elif (event.key == K_s):
                        ret = solver.solver(original_grid, 0, 0, buttons, screen, visu=False)
                    if (ret == False):
                        print_message(screen, 'No solution exists')
                    elif (ret == 2):
                        running = False
                    else:
                        print_message(screen, 'Solved')
                    pygame.display.update()

            # number keys to fill the tiles
            if event.key in keys_possible.keys() and current_button is not None:
                number_pressed = keys_possible[event.key]
                current_button.change_text(number_pressed)
                current_button.show(screen)
                grid[current_button.coord[0]][current_button.coord[1]] = number_pressed

        # pygame.display.flip()

    pygame.quit()

if __name__ == '__main__':
    if (len(sys.argv) != 2):
        print('\nOne argument required: location to grid file')
    else:
        main(sys.argv[1])


import os
import util
import random
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
from torch.autograd import Variable
from batchnorm import SynchronizedBatchNorm2d as SynBN2d
from dataset import pad_tensor,pad_tensor_back
from collections import OrderedDict

class Unet_resize_conv(nn.Module):
    def __init__(self, opt):
        super(Unet_resize_conv, self).__init__()

        self.opt = opt
        p = 1
        self.conv1_1 = nn.Conv2d(4, 32, 3, padding=p)
        self.downsample_1 = nn.MaxPool2d(2)
        self.downsample_2 = nn.MaxPool2d(2)
        self.downsample_3 = nn.MaxPool2d(2)
        self.downsample_4 = nn.MaxPool2d(2)

        self.LReLU1_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn1_1 = SynBN2d(32) if self.opt.syn_norm else nn.BatchNorm2d(32)
        self.conv1_2 = nn.Conv2d(32, 32, 3, padding=p)
        self.LReLU1_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn1_2 = SynBN2d(32) if self.opt.syn_norm else nn.BatchNorm2d(32)
        self.max_pool1 = nn.AvgPool2d(2) if self.opt.use_avgpool == 1 else nn.MaxPool2d(2)

        self.conv2_1 = nn.Conv2d(32, 64, 3, padding=p)
        self.LReLU2_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn2_1 = SynBN2d(64) if self.opt.syn_norm else nn.BatchNorm2d(64)
        self.conv2_2 = nn.Conv2d(64, 64, 3, padding=p)
        self.LReLU2_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn2_2 = SynBN2d(64) if self.opt.syn_norm else nn.BatchNorm2d(64)
        self.max_pool2 = nn.AvgPool2d(2) if self.opt.use_avgpool == 1 else nn.MaxPool2d(2)

        self.conv3_1 = nn.Conv2d(64, 128, 3, padding=p)
        self.LReLU3_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn3_1 = SynBN2d(128) if self.opt.syn_norm else nn.BatchNorm2d(128)
        self.conv3_2 = nn.Conv2d(128, 128, 3, padding=p)
        self.LReLU3_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn3_2 = SynBN2d(128) if self.opt.syn_norm else nn.BatchNorm2d(128)
        self.max_pool3 = nn.AvgPool2d(2) if self.opt.use_avgpool == 1 else nn.MaxPool2d(2)

        self.conv4_1 = nn.Conv2d(128, 256, 3, padding=p)
        self.LReLU4_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn4_1 = SynBN2d(256) if self.opt.syn_norm else nn.BatchNorm2d(256)
        self.conv4_2 = nn.Conv2d(256, 256, 3, padding=p)
        self.LReLU4_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn4_2 = SynBN2d(256) if self.opt.syn_norm else nn.BatchNorm2d(256)
        self.max_pool4 = nn.AvgPool2d(2) if self.opt.use_avgpool == 1 else nn.MaxPool2d(2)

        self.conv5_1 = nn.Conv2d(256, 512, 3, padding=p)
        self.LReLU5_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn5_1 = SynBN2d(512) if self.opt.syn_norm else nn.BatchNorm2d(512)
        self.conv5_2 = nn.Conv2d(512, 512, 3, padding=p)
        self.LReLU5_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn5_2 = SynBN2d(512) if self.opt.syn_norm else nn.BatchNorm2d(512)

        # self.deconv5 = nn.ConvTranspose2d(512, 256, 2, stride=2)
        self.deconv5 = nn.Conv2d(512, 256, 3, padding=p)
        self.conv6_1 = nn.Conv2d(512, 256, 3, padding=p)
        self.LReLU6_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn6_1 = SynBN2d(256) if self.opt.syn_norm else nn.BatchNorm2d(256)
        self.conv6_2 = nn.Conv2d(256, 256, 3, padding=p)
        self.LReLU6_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn6_2 = SynBN2d(256) if self.opt.syn_norm else nn.BatchNorm2d(256)

        # self.deconv6 = nn.ConvTranspose2d(256, 128, 2, stride=2)
        self.deconv6 = nn.Conv2d(256, 128, 3, padding=p)
        self.conv7_1 = nn.Conv2d(256, 128, 3, padding=p)
        self.LReLU7_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn7_1 = SynBN2d(128) if self.opt.syn_norm else nn.BatchNorm2d(128)
        self.conv7_2 = nn.Conv2d(128, 128, 3, padding=p)
        self.LReLU7_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn7_2 = SynBN2d(128) if self.opt.syn_norm else nn.BatchNorm2d(128)

        # self.deconv7 = nn.ConvTranspose2d(128, 64, 2, stride=2)
        self.deconv7 = nn.Conv2d(128, 64, 3, padding=p)
        self.conv8_1 = nn.Conv2d(128, 64, 3, padding=p)
        self.LReLU8_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn8_1 = SynBN2d(64) if self.opt.syn_norm else nn.BatchNorm2d(64)
        self.conv8_2 = nn.Conv2d(64, 64, 3, padding=p)
        self.LReLU8_2 = nn.LeakyReLU(0.2, inplace=True)
        self.bn8_2 = SynBN2d(64) if self.opt.syn_norm else nn.BatchNorm2d(64)

        # self.deconv8 = nn.ConvTranspose2d(64, 32, 2, stride=2)
        self.deconv8 = nn.Conv2d(64, 32, 3, padding=p)
        self.conv9_1 = nn.Conv2d(64, 32, 3, padding=p)
        self.LReLU9_1 = nn.LeakyReLU(0.2, inplace=True)
        self.bn9_1 = SynBN2d(32) if self.opt.syn_norm else nn.BatchNorm2d(32)
        self.conv9_2 = nn.Conv2d(32, 32, 3, padding=p)
        self.LReLU9_2 = nn.LeakyReLU(0.2, inplace=True)

        self.conv10 = nn.Conv2d(32, 3, 1)
        if self.opt.tanh:
            self.tanh = nn.Tanh()

    def forward(self, input, gray):
        flag = 0
        if input.size()[3] > 2200:
            avg = nn.AvgPool2d(2)
            input = avg(input)
            gray = avg(gray)
            flag = 1
            # pass
        input, pad_left, pad_right, pad_top, pad_bottom = pad_tensor(input)
        gray, pad_left, pad_right, pad_top, pad_bottom = pad_tensor(gray)

        gray_2 = self.downsample_1(gray)
        gray_3 = self.downsample_2(gray_2)
        gray_4 = self.downsample_3(gray_3)
        gray_5 = self.downsample_4(gray_4)

        x = self.bn1_1(self.LReLU1_1(self.conv1_1(torch.cat((input, gray), 1))))
        # x = self.bn1_1(self.LReLU1_1(self.conv1_1(input)))

        conv1 = self.bn1_2(self.LReLU1_2(self.conv1_2(x)))
        x = self.max_pool1(conv1)

        x = self.bn2_1(self.LReLU2_1(self.conv2_1(x)))
        conv2 = self.bn2_2(self.LReLU2_2(self.conv2_2(x)))
        x = self.max_pool2(conv2)

        x = self.bn3_1(self.LReLU3_1(self.conv3_1(x)))
        conv3 = self.bn3_2(self.LReLU3_2(self.conv3_2(x)))
        x = self.max_pool3(conv3)

        x = self.bn4_1(self.LReLU4_1(self.conv4_1(x)))
        conv4 = self.bn4_2(self.LReLU4_2(self.conv4_2(x)))
        x = self.max_pool4(conv4)

        x = self.bn5_1(self.LReLU5_1(self.conv5_1(x)))
        x = x*gray_5 if self.opt.self_attention else x
        conv5 = self.bn5_2(self.LReLU5_2(self.conv5_2(x)))

        conv5 = F.upsample(conv5, scale_factor=2, mode='bilinear')
        conv4 = conv4*gray_4 if self.opt.self_attention else conv4
        up6 = torch.cat([self.deconv5(conv5), conv4], 1)
        x = self.bn6_1(self.LReLU6_1(self.conv6_1(up6)))
        conv6 = self.bn6_2(self.LReLU6_2(self.conv6_2(x)))

        conv6 = F.upsample(conv6, scale_factor=2, mode='bilinear')
        conv3 = conv3*gray_3 if self.opt.self_attention else conv3
        up7 = torch.cat([self.deconv6(conv6), conv3], 1)
        x = self.bn7_1(self.LReLU7_1(self.conv7_1(up7)))
        conv7 = self.bn7_2(self.LReLU7_2(self.conv7_2(x)))

        conv7 = F.upsample(conv7, scale_factor=2, mode='bilinear')
        conv2 = conv2*gray_2 if self.opt.self_attention else conv2
        up8 = torch.cat([self.deconv7(conv7), conv2], 1)
        x = self.bn8_1(self.LReLU8_1(self.conv8_1(up8)))
        conv8 = self.bn8_2(self.LReLU8_2(self.conv8_2(x)))

        conv8 = F.upsample(conv8, scale_factor=2, mode='bilinear')
        conv1 = conv1*gray if self.opt.self_attention else conv1
        up9 = torch.cat([self.deconv8(conv8), conv1], 1)
        x = self.bn9_1(self.LReLU9_1(self.conv9_1(up9)))
        conv9 = self.LReLU9_2(self.conv9_2(x))

        latent = self.conv10(conv9)

        if self.opt.times_residual:  #True
            latent = latent*gray

        # output = self.depth_to_space(conv10, 2)
        if self.opt.tanh:  #false
            latent = self.tanh(latent)
        if self.opt.skip:
            if self.opt.linear_add: #false
                if self.opt.latent_threshold:  #false
                    latent = F.relu(latent)
                elif self.opt.latent_norm: #false
                    latent = (latent - torch.min(latent))/(torch.max(latent)-torch.min(latent))
                input = (input - torch.min(input))/(torch.max(input) - torch.min(input))
                output = latent + input*self.opt.skip
                output = output*2 - 1
            else:
                if self.opt.latent_threshold:
                    latent = F.relu(latent)
                elif self.opt.latent_norm:
                    latent = (latent - torch.min(latent))/(torch.max(latent)-torch.min(latent))
                output = latent + input*self.opt.skip
        else:
            output = latent

        if self.opt.linear:  #false
            output = output/torch.max(torch.abs(output))

        output = pad_tensor_back(output, pad_left, pad_right, pad_top, pad_bottom)
        latent = pad_tensor_back(latent, pad_left, pad_right, pad_top, pad_bottom)
        gray = pad_tensor_back(gray, pad_left, pad_right, pad_top, pad_bottom)
        if flag == 1:
            output = F.upsample(output, scale_factor=2, mode='bilinear')
            gray = F.upsample(gray, scale_factor=2, mode='bilinear')
        if self.opt.skip:
            return output, latent
        else:
            return output

class NoNormDiscriminator(nn.Module):
    def __init__(self, input_nc, ndf=64, n_layers=3, use_sigmoid=False):
        super(NoNormDiscriminator, self).__init__()
        kw = 4
        padw = int(np.ceil((kw-1)/2))
        sequence = [
            nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw),
            nn.LeakyReLU(0.2, True)
        ]

        nf_mult = 1
        nf_mult_prev = 1
        for n in range(1, n_layers):
            nf_mult_prev = nf_mult
            nf_mult = min(2**n, 8)
            sequence += [
                nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult,
                          kernel_size=kw, stride=2, padding=padw),
                nn.LeakyReLU(0.2, True)
            ]

        nf_mult_prev = nf_mult
        nf_mult = min(2**n_layers, 8)
        sequence += [
            nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult,
                      kernel_size=kw, stride=1, padding=padw),
            nn.LeakyReLU(0.2, True)
        ]

        sequence += [nn.Conv2d(ndf * nf_mult, 1, kernel_size=kw, stride=1, padding=padw)]

        if use_sigmoid:
            sequence += [nn.Sigmoid()]

        self.model = nn.Sequential(*sequence)

    def forward(self, input):
        # if len(self.gpu_ids) and isinstance(input.data, torch.cuda.FloatTensor):
        #     return nn.parallel.data_parallel(self.model, input, self.gpu_ids)
        # else:
        return self.model(input)

class ImagePool(object):
    def __init__(self, pool_size):
        self.pool_size = pool_size
        if self.pool_size > 0:
            self.num_imgs = 0
            self.images = []

    def query(self, images):
        if self.pool_size == 0:
            return images
        return_images = []
        for image in images.data:
            image = torch.unsqueeze(image, 0)
            if self.num_imgs < self.pool_size:
                self.num_imgs = self.num_imgs + 1
                self.images.append(image)
                return_images.append(image)
            else:
                p = random.uniform(0, 1)
                if p > 0.5:
                    random_id = random.randint(0, self.pool_size-1)
                    tmp = self.images[random_id].clone()
                    self.images[random_id] = image
                    return_images.append(tmp)
                else:
                    return_images.append(image)
        return_images = Variable(torch.cat(return_images, 0))
        return return_images

def vgg_preprocess(batch, opt):
    tensortype = type(batch)
    (r, g, b) = torch.chunk(batch, 3, dim = 1)
    batch = torch.cat((b, g, r), dim = 1) # convert RGB to BGR
    batch = (batch + 1) * 255 * 0.5 # [-1, 1] -> [0, 255]
    # if True:#if opt.vgg_mean: #false
    #     mean = tensortype(batch.size())
    #     mean[:, 0, :, :] = 103.939
    #     mean[:, 1, :, :] = 116.779
    #     mean[:, 2, :, :] = 123.680
    #     batch = batch.sub(mean.cuda()) # subtract mean
    return batch
class PerceptualLoss(nn.Module):
    def __init__(self, opt):
        super(PerceptualLoss, self).__init__()
        self.opt = opt
        self.instancenorm = nn.InstanceNorm2d(512, affine=False)

    def compute_vgg_loss(self, vgg, img, target):
        img_vgg = vgg_preprocess(img, self.opt)
        target_vgg = vgg_preprocess(target, self.opt)
        img_fea = vgg(img_vgg, self.opt)
        target_fea = vgg(target_vgg, self.opt)
        if self.opt.no_vgg_instance:
            return torch.mean((img_fea - target_fea) ** 2)
        else:
            return torch.mean((self.instancenorm(img_fea) - self.instancenorm(target_fea)) ** 2)

class Vgg16(nn.Module):
    def __init__(self):
        super(Vgg16, self).__init__()
        self.conv1_1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)

        self.conv2_1 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)

        self.conv3_1 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.conv3_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.conv3_3 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)

        self.conv4_1 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)
        self.conv4_2 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.conv4_3 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)

        self.conv5_1 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.conv5_2 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.conv5_3 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)

    def forward(self, X, opt):
        h = F.relu(self.conv1_1(X), inplace=True)
        h = F.relu(self.conv1_2(h), inplace=True)
        # relu1_2 = h
        h = F.max_pool2d(h, kernel_size=2, stride=2)

        h = F.relu(self.conv2_1(h), inplace=True)
        h = F.relu(self.conv2_2(h), inplace=True)
        # relu2_2 = h
        h = F.max_pool2d(h, kernel_size=2, stride=2)

        h = F.relu(self.conv3_1(h), inplace=True)
        h = F.relu(self.conv3_2(h), inplace=True)
        h = F.relu(self.conv3_3(h), inplace=True)
        # relu3_3 = h
        if opt.vgg_choose != "no_maxpool":
            h = F.max_pool2d(h, kernel_size=2, stride=2)

        h = F.relu(self.conv4_1(h), inplace=True)
        relu4_1 = h
        h = F.relu(self.conv4_2(h), inplace=True)
        relu4_2 = h
        conv4_3 = self.conv4_3(h)
        h = F.relu(conv4_3, inplace=True)
        relu4_3 = h

        if opt.vgg_choose != "no_maxpool":
            if opt.vgg_maxpooling:
                h = F.max_pool2d(h, kernel_size=2, stride=2)

        relu5_1 = F.relu(self.conv5_1(h), inplace=True)
        relu5_2 = F.relu(self.conv5_2(relu5_1), inplace=True)
        conv5_3 = self.conv5_3(relu5_2)
        h = F.relu(conv5_3, inplace=True)
        relu5_3 = h
        if opt.vgg_choose == "conv4_3":
            return conv4_3
        elif opt.vgg_choose == "relu4_2":
            return relu4_2
        elif opt.vgg_choose == "relu4_1":
            return relu4_1
        elif opt.vgg_choose == "relu4_3":
            return relu4_3
        elif opt.vgg_choose == "conv5_3":
            return conv5_3
        elif opt.vgg_choose == "relu5_1":
            return relu5_1
        elif opt.vgg_choose == "relu5_2":
            return relu5_2
        elif opt.vgg_choose == "relu5_3" or "maxpool":
            return relu5_3

def load_vgg16(model_dir):
    """ Use the model from https://github.com/abhiskk/fast-neural-style/blob/master/neural_style/utils.py """
    if not os.path.exists(os.path.join(model_dir,'vgg16.weight')):
        print("vgg16.weight not find")
        vgg = None
    else:
        vgg = Vgg16()
        vgg.eval()
        vgg.cuda()
        vgg.load_state_dict(torch.load(os.path.join(model_dir, 'vgg16.weight')))
    return vgg

def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        m.weight.data.normal_(0.0, 0.02)
    elif classname.find('BatchNorm2d') != -1:
        m.weight.data.normal_(1.0, 0.02)
        m.bias.data.fill_(0)

class GANLoss(nn.Module):
    def __init__(self, use_lsgan=True, target_real_label=1.0, target_fake_label=0.0,
                 tensor=torch.FloatTensor):
        super(GANLoss, self).__init__()
        self.real_label = target_real_label
        self.fake_label = target_fake_label
        self.real_label_var = None
        self.fake_label_var = None
        self.Tensor = tensor
        if use_lsgan:
            self.loss = nn.MSELoss()
        else:
            self.loss = nn.BCELoss()

    def get_target_tensor(self, input, target_is_real):
        target_tensor = None
        if target_is_real:
            create_label = ((self.real_label_var is None) or
                            (self.real_label_var.numel() != input.numel()))
            if create_label:
                real_tensor = self.Tensor(input.size()).fill_(self.real_label)
                self.real_label_var = Variable(real_tensor, requires_grad=False)
            target_tensor = self.real_label_var
        else:
            create_label = ((self.fake_label_var is None) or
                            (self.fake_label_var.numel() != input.numel()))
            if create_label:
                fake_tensor = self.Tensor(input.size()).fill_(self.fake_label)
                self.fake_label_var = Variable(fake_tensor, requires_grad=False)
            target_tensor = self.fake_label_var
        return target_tensor

    def __call__(self, input, target_is_real):
        target_tensor = self.get_target_tensor(input, target_is_real)
        return self.loss(input, target_tensor)

class GANModel(object):
    def __init__(self,opt):
        self.opt = opt
        self.Tensor = torch.cuda.FloatTensor
        self.netG = Unet_resize_conv(opt)
        self.netD = NoNormDiscriminator(opt.output_nc, opt.ndf, opt.n_layers_D, use_sigmoid=opt.no_lsgan)
        self.netD_P = NoNormDiscriminator(opt.output_nc, opt.ndf, opt.n_layers_patchD, use_sigmoid=opt.no_lsgan)
        self.optimizer_G = torch.optim.Adam(self.netG.parameters(),lr=opt.lr, betas=(opt.beta1, 0.999))
        self.optimizer_D = torch.optim.Adam(self.netD.parameters(),lr=opt.lr, betas=(opt.beta1, 0.999))
        self.optimizer_D_P = torch.optim.Adam(self.netD_P.parameters(), lr=opt.lr, betas=(opt.beta1, 0.999))
        self.netG.cuda()
        self.netD.cuda()
        self.netD_P.cuda()
        self.netG.apply(weights_init)
        self.netD.apply(weights_init)
        self.netD_P.apply(weights_init)
        self.vgg_loss = PerceptualLoss(opt)
        self.vgg_loss.cuda()
        self.vgg = load_vgg16("./model")
        for param in self.vgg.parameters():
            param.requires_grad = False

        if self.opt.isTrain:
            self.old_lr = opt.lr
            # self.fake_A_pool = ImagePool(opt.pool_size)
            self.fake_B_pool = ImagePool(opt.pool_size)
            # define loss functions
            self.criterionGAN = GANLoss(use_lsgan=not opt.no_lsgan, tensor=self.Tensor)

        if self.opt.isTrain:
            self.netG.train()
            self.netD.train()
            if opt.patchD:
                self.netD_P.train()
        else:
            self.netG.eval()
            self.netD.eval()
            if opt.patchD:
                self.netD_P.eval()
        # print("G_A:")
        # print(self.netG)
        # print("D_A:")
        # print(self.netD)
        # print("D_P:")
        # print(self.netD_P)

    def forward(self,data):
        self.real_A = data['A'].cuda()
        self.real_B = data['B'].cuda()
        self.real_A_gray = data['A_gray'].cuda()
        self.real_img = data['input_img'].cuda()

        self.fake_B, self.latent_real_A = self.netG.forward(self.real_img, self.real_A_gray)
        if self.opt.patchD:  #True
            w = self.real_A.size(3)
            h = self.real_A.size(2)
            w_offset = random.randint(0, max(0, w - self.opt.patchSize - 1))
            h_offset = random.randint(0, max(0, h - self.opt.patchSize - 1))

            self.fake_patch = self.fake_B[:,:, h_offset:h_offset + self.opt.patchSize,
                   w_offset:w_offset + self.opt.patchSize]
            self.real_patch = self.real_B[:,:, h_offset:h_offset + self.opt.patchSize,
                   w_offset:w_offset + self.opt.patchSize]
            self.input_patch = self.real_A[:,:, h_offset:h_offset + self.opt.patchSize,
                   w_offset:w_offset + self.opt.patchSize]
        if self.opt.patchD_3 > 0:  #=5
            self.fake_patch_1 = []
            self.real_patch_1 = []
            self.input_patch_1 = []
            w = self.real_A.size(3)
            h = self.real_A.size(2)
            for _ in range(self.opt.patchD_3):
                w_offset_1 = random.randint(0, max(0, w - self.opt.patchSize - 1))
                h_offset_1 = random.randint(0, max(0, h - self.opt.patchSize - 1))
                self.fake_patch_1.append(self.fake_B[:,:, h_offset_1:h_offset_1 + self.opt.patchSize,
                    w_offset_1:w_offset_1 + self.opt.patchSize])
                self.real_patch_1.append(self.real_B[:,:, h_offset_1:h_offset_1 + self.opt.patchSize,
                    w_offset_1:w_offset_1 + self.opt.patchSize])
                self.input_patch_1.append(self.real_A[:,:, h_offset_1:h_offset_1 + self.opt.patchSize,
                    w_offset_1:w_offset_1 + self.opt.patchSize])

    def backward_G(self, epoch):
        pred_fake = self.netD.forward(self.fake_B)
        pred_real = self.netD.forward(self.real_B)

        self.loss_G_A = (self.criterionGAN(torch.sigmoid(pred_real - torch.mean(pred_fake)), False) +
                                    self.criterionGAN(torch.sigmoid(pred_fake - torch.mean(pred_real)), True)) / 2

        loss_G_A = 0
        if self.opt.patchD:#True
            pred_fake_patch = self.netD_P.forward(self.fake_patch)
            if self.opt.hybrid_loss:#true
                loss_G_A += self.criterionGAN(pred_fake_patch, True)
            else:
                pred_real_patch = self.netD_P.forward(self.real_patch)

                loss_G_A += (self.criterionGAN(torch.sigmoid(pred_real_patch - torch.mean(pred_fake_patch)), False) +
                                      self.criterionGAN(torch.sigmoid(pred_fake_patch - torch.mean(pred_real_patch)), True)) / 2
        if self.opt.patchD_3 > 0:   #True
            for i in range(self.opt.patchD_3):
                pred_fake_patch_1 = self.netD_P.forward(self.fake_patch_1[i])
                if self.opt.hybrid_loss:
                    loss_G_A += self.criterionGAN(pred_fake_patch_1, True)
                else:
                    pred_real_patch_1 = self.netD_P.forward(self.real_patch_1[i])

                    loss_G_A += (self.criterionGAN(torch.sigmoid(pred_real_patch_1 - torch.mean(pred_fake_patch_1)), False) +
                                        self.criterionGAN(torch.sigmoid(pred_fake_patch_1 - torch.mean(pred_real_patch_1)), True)) / 2

            if not self.opt.D_P_times2: #false
                self.loss_G_A += loss_G_A/float(self.opt.patchD_3 + 1)
            else:
                self.loss_G_A += loss_G_A/float(self.opt.patchD_3 + 1)*2
        else:
            if not self.opt.D_P_times2:
                self.loss_G_A += loss_G_A
            else:
                self.loss_G_A += loss_G_A*2

        vgg_w = 1
        self.loss_vgg_b = self.vgg_loss.compute_vgg_loss(self.vgg,
                self.fake_B, self.real_A) * self.opt.vgg #if self.opt.vgg > 0 else 0
        if self.opt.patch_vgg:#true
            loss_vgg_patch = self.vgg_loss.compute_vgg_loss(self.vgg,
            self.fake_patch, self.input_patch) * self.opt.vgg
            if self.opt.patchD_3 > 0:
                for i in range(self.opt.patchD_3):
                    loss_vgg_patch += self.vgg_loss.compute_vgg_loss(self.vgg,
                        self.fake_patch_1[i], self.input_patch_1[i]) * self.opt.vgg
                self.loss_vgg_b += loss_vgg_patch/float(self.opt.patchD_3 + 1)
            else:
                self.loss_vgg_b += loss_vgg_patch
        self.loss_G = self.loss_G_A + self.loss_vgg_b*vgg_w
        self.loss_G.backward()

    def backward_D_basic(self, netD, real, fake, use_ragan):
        pred_real = netD.forward(real)
        pred_fake = netD.forward(fake.detach())
        if self.opt.use_ragan and use_ragan:
            loss_D = (self.criterionGAN(torch.sigmoid(pred_real - torch.mean(pred_fake)), True) +
                                      self.criterionGAN(torch.sigmoid(pred_fake - torch.mean(pred_real)), False)) / 2
        else:
            loss_D_real = self.criterionGAN(pred_real, True)
            loss_D_fake = self.criterionGAN(pred_fake, False)
            loss_D = (loss_D_real + loss_D_fake) * 0.5
        return loss_D

    def backward_D(self):
        fake_B = self.fake_B_pool.query(self.fake_B)
        fake_B = self.fake_B
        self.loss_D_A = self.backward_D_basic(self.netD, self.real_B, fake_B, True)
        self.loss_D_A.backward()

    def backward_D_P(self):
        loss_D_P = self.backward_D_basic(self.netD_P, self.real_patch, self.fake_patch, False)
        if self.opt.patchD_3 > 0:
            for i in range(self.opt.patchD_3):
                loss_D_P += self.backward_D_basic(self.netD_P, self.real_patch_1[i], self.fake_patch_1[i], False)
            self.loss_D_P = loss_D_P/float(self.opt.patchD_3 + 1)
        else:
            self.loss_D_P = loss_D_P

        if self.opt.D_P_times2:
            self.loss_D_P = self.loss_D_P*2
        self.loss_D_P.backward()

    def test(self):
        # self.real_A = Variable(self.input_A, volatile=True)
        # self.real_A_gray = Variable(self.input_A_gray, volatile=True)
        self.fake_B, self.latent_real_A = self.netG.forward(self.real_A, self.real_A_gray)
        self.real_B = Variable(self.input_B, volatile=True)

    def predict(self,data):
        self.real_A = data['A'].cuda()
        self.real_A_gray = data['A_gray'].cuda()
        self.fake_B, self.latent_real_A = self.netG.forward(self.real_A, self.real_A_gray)
        real_A = util.tensor2im(self.real_A.data)
        fake_B = util.tensor2im(self.fake_B.data)
        A_gray = util.atten2im(self.real_A_gray.data)
        return OrderedDict([('real_A', real_A), ('fake_B', fake_B),('A_gray',A_gray)])

    def get_current_visuals(self):
        real_A = util.tensor2im(self.real_A.data)
        fake_B = util.tensor2im(self.fake_B.data)
        real_B = util.tensor2im(self.real_B.data)

        latent_real_A = util.tensor2im(self.latent_real_A.data)
        latent_show = util.latent2im(self.latent_real_A.data)
        fake_patch = util.tensor2im(self.fake_patch.data)
        real_patch = util.tensor2im(self.real_patch.data)
        input_patch = util.tensor2im(self.input_patch.data)
        if not self.opt.self_attention:
            return OrderedDict([('real_A', real_A), ('fake_B', fake_B), ('latent_real_A', latent_real_A),
                    ('latent_show', latent_show), ('real_B', real_B), ('real_patch', real_patch),
                    ('fake_patch', fake_patch), ('input_patch', input_patch)])
        else:
            self_attention = util.atten2im(self.real_A_gray.data)
            return OrderedDict([('real_A', real_A), ('fake_B', fake_B), ('latent_real_A', latent_real_A),
                    ('latent_show', latent_show), ('real_B', real_B), ('real_patch', real_patch),
                    ('fake_patch', fake_patch), ('input_patch', input_patch), ('self_attention', self_attention)])

    def get_current_errors(self, epoch):
        D_A = self.loss_D_A.item()
        D_P = self.loss_D_P.item() if self.opt.patchD else 0
        G_A = self.loss_G_A.item()
        vgg = self.loss_vgg_b.item()/self.opt.vgg if self.opt.vgg > 0 else 0
        return OrderedDict([('D_A', D_A), ('G_A', G_A), ("vgg", vgg), ("D_P", D_P)])

    def load_network(self, network, network_label, epoch_label):
        save_filename = '%s_net_%s.pth' % (epoch_label, network_label)
        save_path = os.path.join('my_model', save_filename)
        network.load_state_dict(torch.load(save_path))

    def save_network(self, network, network_label, epoch_label):
        save_filename = '%s_net_%s.pth' % (epoch_label, network_label)
        save_path = os.path.join('my_model', save_filename)
        torch.save(network.cpu().state_dict(), save_path)

    def save(self, label):
        self.save_network(self.netG, 'G_A', label)
        self.save_network(self.netD, 'D_A', label)
        self.save_network(self.netD_P, 'D_P', label)

    def update_learning_rate(self):

        if self.opt.new_lr:
            lr = self.old_lr/2
        else:
            lrd = self.opt.lr / self.opt.niter_decay
            lr = self.old_lr - lrd
        for param_group in self.optimizer_D.param_groups:
            param_group['lr'] = lr
        if self.opt.patchD:
            for param_group in self.optimizer_D_P.param_groups:
                param_group['lr'] = lr
        for param_group in self.optimizer_G.param_groups:
            param_group['lr'] = lr

        print('update learning rate: %f -> %f' % (self.old_lr, lr))
        self.old_lr = lr

    def train(self,data,epoch):
        self.forward(data)
        self.optimizer_G.zero_grad()
        self.backward_G(epoch)
        self.optimizer_G.step()
        self.optimizer_D.zero_grad()
        self.backward_D()

        self.optimizer_D_P.zero_grad()
        self.backward_D_P()
        self.optimizer_D.step()
        self.optimizer_D_P.step()

class Visualizer(object):
    def display_current_results(self,value,epoch):
        dictlen = len(value)
        plt.figure(figsize=(15,5))
        plt.suptitle("disply_current_results")
        for i,key in enumerate(value):
            plt.subplot(1,dictlen,i+1)
            plt.title(key)
            plt.axis('off')
            plt.imshow(value[key])
        plt.show()
    def print_current_errors(self,epoch,i,errors,t):
        message = '(epoch: %d, iters: %d, time: %.3f) ' % (epoch, i, t)
        for k, v in errors.items():
            message += '%s: %.3f ' % (k, v)
        print(message)
    def plot_current_errors(self,epoch,percent,opt,errors):
        pass


my_list=[1,6,7,22,3,4,9,10]

#---exercise 1---
def filter_list(numbers:list, integer:int):
    newlist = [x for x in my_list if x > integer]
    return newlist

#print(filter_list(my_list,2))

#---exercise 2---
def count_vowels(text:str):
    count = 0
    for x in text:
        if x == 'a' or x == 'e' or x == 'i' or x == 'o' or x == 'u':
            count += 1
    return count

#print(count_vowels('hello'))

#---exercise 3---
def replace_char(text:str, char:str, replace_char:str):
    if char in text:
        return text.replace(char,replace_char)
    else:
        print(f"\"{char}\" is not an occurrence in \"{text}\". The string remains unchanged.")
        return text

#print(replace_char("catch","c","k"))

#---exercise 4---
def hamming(text1:str,text2:str):
    if len(text1) != len(text2):
        print("Strings must be of same length.")
        return 0
    else:
        dist = 0
        for i, x in enumerate(text1):
            if x != text2[i]:
                dist += 1
        return dist

#print(hamming("hat","cat"))

#---exercise 5---
class Book:

    def __init__(self, name, author):
        self.name = name
        self.author = author

    def __str__(self):
        return self.name + ", " + self.author

book1 = Book('Dune', 'Frank Herbert')
book2 = Book('The Dark Tower', 'Stephen King')
book3 = Book('Dune Messiah', 'Frank Herbert')
print(book2)

#---exercise 6---
class BookShelf:

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

    def add_book(self,book):
        self.books.append(book)

    def add_books(self,book):
        self.books.extend(book)

    def books_by_author(self, author):
        authors = []
        for i,x in enumerate(self.books):
            if self.books[i].author == author:
                authors.append(self.books[i].name)
        if authors == []:
            print("There are no books of this author in this bookshelf")
        else:
            print(f"Books by {author}:")
        return authors

    def get_books(self):
        book_names = []
        for i,x in enumerate(self.books):
            book_names.append(self.books[i].name)
        if book_names == []:
            print("Your bookshelf is currently empty:")
        else:
            print("Your books:")
        return book_names

    def clear_shelf(self):
        self.books.clear()
        print("Your bookshelf has been emptied.")

def having_fun_with_books():
    #creating a bookshelf and adding individual books to it
    shelf = BookShelf([])
    shelf.add_book(book1)
    shelf.add_book(book2)
    shelf.add_book(book3)

    #books by author
    print(shelf.books_by_author("Frank Herbert"))

    #show shelf before and after emptying it
    print(shelf.get_books())
    shelf.clear_shelf()
    print(shelf.get_books())

    #create a list of books
    booklist = []
    booklist.append(book1)
    booklist.append(book2)
    booklist.append(book3)

    #show shelf after adding multiple books at once
    shelf.add_books(booklist)
    print(shelf.get_books())

#having_fun_with_books()


from datetime import datetime
import csv
import os
import pandas as pd
from matplotlib import pyplot as plt

def get_dict(name_dict, key_dict):
    if name_dict.get(key_dict) is None:
        name_dict[key_dict] = [f'"cid","dt","dvalue"\n"{cid}","{dt}","{dvalue}"\n']
    else:
        temp = name_dict.get(key_dict)
        temp.append(f'"{cid}","{dt}","{dvalue}"\n')
        name_dict[key_dict] = temp

def create_csv(name_folder, name_dict):
    for key, value in name_dict.items():
        with open(f'task\\data\\{name_folder}\\{key}.csv', 'w') as new_file:
            new_file.writelines(value)

def create_png(way_folder):
    for dirs, folder, files in os.walk(f'task\\data\\{way_folder}'):
        for name in files:
            df = pd.read_csv(f'task\\data\\{way_folder}\\{name}')
            df.plot(y='dvalue', use_index=True)
            plt.savefig(f'task\\img\\{way_folder}\\{name[:-4]}.png')
            plt.close()

if __name__ == '__main__':
    dayAll, dayCid, weekAll, weekCid, cidAll = dict(), dict(), dict(), dict(), dict()
    with open('data1.csv', 'r') as file:
        reader = csv.reader(file)
        for cid, dt, dvalue in [*reader][1:]:
            date_and_time = dt.split(' ')
            date = date_and_time[0]
            day, month, year = date.split('.')
            cid_and_date = f'{cid} {date}'
            week_and_year = f'{datetime.strptime(f"{date}", "%d.%m.%Y").isocalendar()[1]} {year}'
            cid_and_week = f'{cid} {datetime.strptime(f"{date}", "%d.%m.%Y").isocalendar()[1]} {year}'
            get_dict(dayAll, date)
            get_dict(dayCid, cid_and_date)
            get_dict(weekAll, week_and_year)
            get_dict(weekCid, cid_and_week)
            get_dict(cidAll, cid)
    for way, name_dict in zip(['Every day all sensors', 'Every sensor every day', 'Every week all sensors',
                               'Every sensor is weekly', 'Each sensor for the entire time period'],
                              [dayAll, dayCid, weekAll, weekCid, cidAll]):
        create_csv(way, name_dict)
        create_png(way)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# (c) Shrimadhav U K & PR0FESS0R-99

import os
from pyrogram import Client, filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
from pyrogram.errors import FloodWait

CAPTION_TEXT = """
CHANNEL LINK ☞ https://youtube.com/c/MoTech_YT
"""

@Client.on_message(filters.media & filters.channel)
async def caption(client, message: Message):
    kopp, _ = get_file_id(message)
    await message.edit(f"{kopp.file_name}\n\n{CAPTION_TEXT}")

def get_file_id(msg: Message):
    if msg.media:
        for message_type in (
            "photo",
            "animation",
            "audio",
            "document",
            "video",
            "video_note",
            "voice",
            # "contact",
            # "dice",
            # "poll",
            # "location",
            # "venue",
            "sticker"
        ):
            obj = getattr(msg, message_type)
            if obj:
                return obj, obj.file_id


from collections import defaultdict

def is_phrase_valid2(phrase):
	words = phrase.split(' ')
	seen = set()
	for word in words:
		if contains_anagram(seen, word):
			return False
		seen.add(word)
	return True

def is_phrase_valid1(phrase):
	words = phrase.split(' ')
	seen = set()
	for word in words:
		if word in seen:
			return False
		seen.add(word)
	return True

def contains_anagram(wordset, word):
	word_char_count = get_char_count(word)
	for testword in wordset:
		test_char_count = get_char_count(testword)
		if counts_equal(word_char_count, test_char_count):
			return True
	return False

def counts_equal(char_count_1, char_count_2):
	if len(char_count_1.keys()) != len(char_count_2.keys()):
		return False

	for key in char_count_1.keys():
		if key not in char_count_2.keys():
			return False
		if char_count_1[key] != char_count_2[key]:
			return False
	return True

def get_char_count(word):
	counts = defaultdict(int)
	for c in word:
		counts[c] += 1
	return counts

def count_valid_passphrases(in_text):
	phrases = in_text.split('\n')
	count = 0
	for phrase in phrases:
		if(is_phrase_valid2(phrase)):
			count += 1

	return count

with open('input.txt', 'r') as f:
	in_text = f.read()


import pandas as pd
import numpy as np
import pickle
import sys
import os

CORRECT = 0
CLEAVAGE_POSITION_ERR = -1
UPP_SIDE_ERR = -2
DWN_SIDE_ERR = -3
BOTH_SIDE_ERR = -5

OUTPUT_DIR = "../../processed_data/TCGA/local_cleavage_nucleotides/"
MIRBASE_PATH = "../../processed_data/miRBase/miRBase.pkl"
with open(MIRBASE_PATH, 'rb') as handle:
    miRBase = pickle.load(handle)

MAIN_CONSENSUS_MIRNA_PATH = "../../processed_data/TCGA/main_consensus_isomiRs.tsv"
consensus_tb = pd.read_csv(MAIN_CONSENSUS_MIRNA_PATH, sep="\t")

def parse_description(description):
    miRNA = description.split(" ")[0]
    side_count = len(description.split("[")) - 1

    sides = []
    for ind in range(side_count):
        side_desc = description.split("[")[1 + ind].split("]")[0]
        side = side_desc.split(":")[0]
        indexes = [int(el) - 1 for el in side_desc.split(":")[1].split("-")]
        sides.append((side, indexes[0], indexes[1]))
    return sides

def merge_cycle_stem(cycle, stem):
    result = ""
    for ind in range(len(stem)):
        if (stem[ind] != " "):
            result += stem[ind]
        else:
            result += cycle[ind]
    return result

def get_local_nucleotides(pri_miRNA, side, termini, shift, radius, gap_flag=True):
    # rotate second structure so that side termini position
    # is on the upper left part of miRNA structure
    second_structure = miRBase[pri_miRNA]

    if (side == "3p"):
        second_structure = second_structure[::-1]
        second_structure = [line[::-1] for line in second_structure]
    if (termini == "3prime"):
        second_structure = [line[::-1] for line in second_structure]

    upp_side = merge_cycle_stem(second_structure[0], second_structure[1])
    dwn_side = merge_cycle_stem(second_structure[-1], second_structure[-2])

    # find index of the canonicial cleavage position
    canon_cleavage_pos = -1
    for ind in range(len(upp_side)):
        if upp_side[ind].isupper():
            canon_cleavage_pos = ind
            break
    if (canon_cleavage_pos == -1):
        return CLEAVAGE_POSITION_ERR, []

    # find index of the main (shifted) cleavage position
    main_cleavage_pos = canon_cleavage_pos
    if (abs(shift) > 0):
        direction = shift // abs(shift)
    nucleotides_count = 0
    while (nucleotides_count < abs(shift)):
        if (upp_side[main_cleavage_pos] != "-"):
            nucleotides_count += 1
        main_cleavage_pos += direction

    # extract cleavage local nucleotides with gaps
    if (gap_flag):
        return_code = CORRECT
        upp_gap_wrd = upp_side[max(0, main_cleavage_pos - radius):
                               min(len(upp_side), main_cleavage_pos + radius)]
        dwn_gap_wrd = dwn_side[max(0, main_cleavage_pos - radius):
                               min(len(dwn_side), main_cleavage_pos + radius)][::-1]

        if (len(upp_gap_wrd) < 2 * radius):
            return_code += UPP_SIDE_ERR

        if (len(dwn_gap_wrd) < 2 * radius):
            return_code += DWN_SIDE_ERR

        return return_code, [upp_gap_wrd, dwn_gap_wrd]

    # extract cleavage local nucleotides without gaps for the upp side
    return_code = CORRECT
    start_pos = main_cleavage_pos - 1
    nucleotides_count = 0
    while (start_pos >= 0 and nucleotides_count < radius):
        if (upp_side[start_pos] != "-"):
            nucleotides_count += 1
        start_pos -= 1
    start_pos += 1

    end_pos = main_cleavage_pos
    nucleotides_count = 0
    while (end_pos < len(upp_side) and nucleotides_count < radius):
        if (upp_side[end_pos] != "-"):
            nucleotides_count += 1
        end_pos += 1

    upp_ungap_wrd = upp_side[start_pos:end_pos]
    upp_ungap_wrd = "".join([nucl for nucl in upp_ungap_wrd if (nucl != "-")])

    # extract cleavage local nucleotides without gaps for the dwn side
    start_pos = main_cleavage_pos - 1
    nucleotides_count = 0
    while (start_pos >= 0 and nucleotides_count < radius):
        if (dwn_side[start_pos] != "-"):
            nucleotides_count += 1
        start_pos -= 1
    start_pos += 1

    end_pos = main_cleavage_pos
    nucleotides_count = 0
    while (end_pos < len(dwn_side) and nucleotides_count < radius):
        if (dwn_side[end_pos] != "-"):
            nucleotides_count += 1
        end_pos += 1

    dwn_ungap_wrd = dwn_side[start_pos:end_pos][::-1]
    dwn_ungap_wrd = "".join([nucl for nucl in dwn_ungap_wrd if (nucl != "-")])

    if (len(upp_ungap_wrd) < 2 * radius):
        return_code += UPP_SIDE_ERR

    if (len(dwn_ungap_wrd) < 2 * radius):
        return_code += DWN_SIDE_ERR

    return return_code, [upp_ungap_wrd, dwn_ungap_wrd]

terminis = ["5prime", "3prime"]
radiuses = list(range(1, 5))

for radius in radiuses:
    for termini in terminis:
        print("Progress:", termini, radius)
        table_gap = pd.DataFrame(columns=[
            "pri_miRNA",
            "miRNA",
            "source",
            "opposite",
            "heterogenous"
        ])

        table_ungap = pd.DataFrame(columns=[
            "pri_miRNA",
            "miRNA",
            "source",
            "opposite",
            "heterogenous"
        ])

        for index, miRNA_row in consensus_tb.iterrows():
            # extracting pri_miRNA identifier
            miRNA = miRNA_row["miRNA"]
            shift = miRNA_row["shift_" + termini[0]]
            heterogenous = miRNA_row["heterogenous_" + termini[0]]
            if (miRNA.rfind("-3p") != -1):
                side = "3p"
            elif (miRNA.rfind("-5p") != -1):
                side = "5p"
            else:
                print(miRNA, "is not sided")
            pri_miRNA = miRNA[:miRNA.rfind("-" + side)].lower()

            # if pri_miRNA in miRBase then extract its
            # cleavage local nucleotides
            if (pri_miRNA in miRBase):
                # extract gapped local cleavage nucleotides
                code, features = get_local_nucleotides(pri_miRNA, side, termini,
                                                       shift, radius, gap_flag=True)
                if (code != CORRECT):
                    print(pri_miRNA, miRNA, side, termini,
                          shift, radius, "gapped", code)
                else:
                    table_gap.loc[len(table_gap)] = [
                        pri_miRNA,
                        miRNA,
                        features[0],
                        features[1],
                        heterogenous
                    ]

                # extract ungapped local cleavage nucleotides
                code, features = get_local_nucleotides(pri_miRNA, side, termini,
                                                       shift, radius, gap_flag=False)
                if (code != CORRECT):
                    print(pri_miRNA, miRNA, side, termini,
                          shift, radius, "ungapped", code)
                else:
                    table_ungap.loc[len(table_ungap)] = [
                        pri_miRNA,
                        miRNA,
                        features[0],
                        features[1],
                        heterogenous
                    ]

            # otherwise miRBase consider other stem pri_miRNA variants of miRNA
            # extract them all
            for variant in miRBase:
                if not variant.startswith(pri_miRNA + "-"):
                    continue

                # extract gapped local cleavage nucleotides
                code, features = get_local_nucleotides(variant, side, termini,
                                                       shift, radius, gap_flag=True)
                if (code != CORRECT):
                    print(variant, miRNA, side, termini,
                          shift, radius, "gapped", code)
                else:
                    table_gap.loc[len(table_gap)] = [
                        variant,
                        miRNA,
                        features[0],
                        features[1],
                        heterogenous
                    ]

                # extract ungapped local cleavage nucleotides
                code, features = get_local_nucleotides(variant, side, termini,
                                                       shift, radius, gap_flag=False)
                if (code != CORRECT):
                    print(variant, miRNA, side, termini,
                          shift, radius, "ungapped", code)
                else:
                    table_ungap.loc[len(table_ungap)] = [
                        variant,
                        miRNA,
                        features[0],
                        features[1],
                        heterogenous
                    ]
        table_gap_3p = table_gap[table_gap["miRNA"].str.endswith("-3p")]
        table_gap_5p = table_gap[table_gap["miRNA"].str.endswith("-5p")]
        table_gap_3p.to_csv(OUTPUT_DIR + "{}term_3p_gap_{}.tsv".format(
            termini[0], radius
        ), sep="\t", index=None)
        table_gap_5p.to_csv(OUTPUT_DIR + "{}term_5p_gap_{}.tsv".format(
            termini[0], radius
        ), sep="\t", index=None)

        table_ungap_3p = table_ungap[table_ungap["miRNA"].str.endswith("-3p")]
        table_ungap_5p = table_ungap[table_ungap["miRNA"].str.endswith("-5p")]
        table_ungap_3p.to_csv(OUTPUT_DIR + "{}term_3p_ungap_{}.tsv".format(
            termini[0], radius
        ), sep="\t", index=None)
        table_ungap_5p.to_csv(OUTPUT_DIR + "{}term_5p_ungap_{}.tsv".format(
            termini[0], radius
        ), sep="\t", index=None)


#!/opt/bin/lv_micropython
import lvgl as lv
import init_gui
import time

# Create a Tab view object
tabview = lv.tabview(lv.scr_act(), None)

# Add 3 tabs (the tabs are page (lv_page) and can be scrolled
tab1=tabview.add_tab("Tab 1")
tab2=tabview.add_tab("Tab 2")
tab3=tabview.add_tab("Tab 3")

# Add content to the tabs
label = lv.label(tab1,None)
label.set_text('''
This the first tab\n
If the content
of a tab
become too long
then it
automatically
becomes
scrollable.
''')

label = lv.label(tab2,None)
label.set_text("Second tab")

label = lv.label(tab3, None)
label.set_text("Third tab");

while True:
    lv.task_handler()
    time.sleep_ms(5)


from typing import List
from node import Node

class TimeTable:
    """
    Структура, показывающая занятость ресурсов при определенном обходе дерева
    Используется для оценки времени каждого пути
    """
    def __init__(self, requirements, max_size=200, verbose=False):
        self.requirements = requirements
        self.max_size = max_size
        self.taken = {req: [0] * self.max_size for req in requirements}
        self.last_index = {req: 0 for req in requirements}
        self.verbose = verbose

    def add_node(self, node: Node):
        requirements = node.requirements
        last_index = max(self.last_index[req] for req in requirements)
        last_index = max(last_index, self.last_index['h'])
        for req in requirements:
            self.last_index[req] = last_index + node.time
            self.taken[req][last_index: last_index + node.time] = [1] * node.time

        if self.verbose:
            print(node, node.time)
            for req in self.requirements:
                if self.time() > 0:
                    print(self.taken[req][:self.time()], req)
                else:
                    print(self.taken[req], req)

    def __call__(self, path: List[Node]):
        for node in path:
            self.add_node(node)
        return self

    def time(self):
        return max(self.last_index[req] for req in self.requirements)

    def print(self):
        for req in self.requirements:
            print(self.taken[req][:self.time()], req)


# -*- coding: cp950 -*-
import numpy as np
from scipy.integrate import dblquad

def make_C_matrix(DELTA, site_size):
    def eps(Px,Py):
        value = 2*((np.cos(Px))**2+(np.cos(Py))**2)
        return value

    def pow(Px,Py,ri=[0,0],rj=[0,0]):
        value = Px*(ri[0]-rj[0])+Py*(ri[1]-rj[1])
        return value

    def R(Px,Py,delta):
        value = (eps(Px,Py)**2+4*(delta**2)*((np.sin(Px))**2+(np.sin(Py))**2))**0.5
        return value

    def Cij(delta,mode,ri=[0,0],rj=[0,0]):

        const = 1.0/(4*np.pi**2)

        def Nonedeg_real():
            value = dblquad(lambda Px,Py:((-np.cos(pow(Px,Py,ri,rj))*np.sin(Py)
                                          -np.sin(pow(Px,Py,ri,rj))*np.sin(Px))*const
                            *delta/(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi,
                            )
            return value[0]
        def Nonedeg_imag():
            value = dblquad(lambda Px,Py:((-np.sin(pow(Px,Py,ri,rj))*np.sin(Py)
                                          +np.cos(pow(Px,Py,ri,rj))*np.sin(Px))*const
                            *delta/(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]

        def leftdeg_real():
            value = dblquad(lambda Px,Py:((-np.cos(pow(Px,Py,ri,rj))*np.sin(Py)
                                          +np.sin(pow(Px,Py,ri,rj))*np.sin(Px))*const
                            *delta/(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]
        def leftdeg_imag():
            value = dblquad(lambda Px,Py:((-np.cos(pow(Px,Py,ri,rj))*np.sin(Px)
                                          -np.sin(pow(Px,Py,ri,rj))*np.sin(Py))*const
                            *delta/(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]

        def rightdeg_real():
            value = dblquad(lambda Px,Py:(np.cos(pow(Px,Py,ri,rj))*(1+eps(Px,Py))*const
                            /(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]
        def rightdeg_imag():
            value = dblquad(lambda Px,Py:(np.sin(pow(Px,Py,ri,rj))*(1+eps(Px,Py))*const
                            /(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]

        def twodeg_real():
            value = dblquad(lambda Px,Py:(np.cos(pow(Px,Py,ri,rj))*(1-eps(Px,Py))*const
                            /(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]
        def twodeg_imag():
            value = dblquad(lambda Px,Py:(np.sin(pow(Px,Py,ri,rj))*(1-eps(Px,Py))*const
                            /(2.0*R(Px,Py,delta))),
                            0,
                            2*np.pi,
                            lambda Py:0,
                            lambda Py:2*np.pi)
            return value[0]

        def choose(mode):
            value = {
                0 : complex(Nonedeg_real(),Nonedeg_imag()),
                1 : complex(leftdeg_real(),leftdeg_imag()),
                2 : complex(rightdeg_real(),rightdeg_imag()),
                3 : complex(twodeg_real(),twodeg_imag())
            }
            return value.get(mode,None)

        return choose(mode)

    '''
    print(Cij(DELTA ,0,[2,3],[3,2]))
    print(Cij(DELTA ,1,[2,3],[3,2]))
    print(Cij(DELTA ,2,[2,3],[3,2]))
    print(Cij(DELTA ,3,[2,3],[3,2]))
    '''
    def make_Cij(DELTA, ri=[0, 0], rj=[0, 0]):
        Cij_cell = [[0, 0], [0, 0]]
        Cij_cell[0][1]= Cij(DELTA ,0,ri,rj)
        Cij_cell[1][1]= Cij(DELTA ,1,ri,rj)
        Cij_cell[0][0]= Cij(DELTA ,2,ri,rj)
        Cij_cell[1][0]= Cij(DELTA ,3,ri,rj)
        return Cij_cell

    total_site = site_size[0]*site_size[1]

    C_matrix = np.zeros([total_site*2, total_site*2], dtype=complex)
    for i in range(total_site):
        for j in range(total_site):
            ri = [i/site_size[1],i%site_size[1]]
            rj = [j/site_size[1],j%site_size[1]]
            #print(i, j, ri, rj)
            Cij_cell = make_Cij(DELTA, ri, rj)
            for a in range(2):
                for b in range(2):
                    C_matrix[i*2+a, j*2+b] = Cij_cell[a][b]
    return C_matrix

DELTA = 4
site_size = (2, 2)
A = make_C_matrix(DELTA, site_size)
print(A)

import matplotlib.pylab as plt
plt.imshow(np.abs(A))
plt.show()


####=======================  THread ==================
from time import sleep
from threading import *

class hello(Thread):
    def run(self):
        for i in range(10):
            print('Hello')
            sleep(1)

class hi(Thread):
    def run(self):
        for i in range(10):
            print('Hi')
            sleep(1)

t1 = hello()
t2 = hi()

# t1.start()
# sleep(0.2)
# t2.start()
#
# t1.join()
# t2.join()

print('Bye....')

###  ================= Working with File =======================================
## Read File
file = open('data/MyData', mode='r')
# print(file.readline(), end='#')
# print(file.readline())

## Write File
file1 = open('data/Mydata2', 'a')
# file1.write(' Hello.....')

for data in file:
    # print(data)
    file1.write(data)

try:
    with open('data/data.txt') as fileObj:
        lines = fileObj.readlines()
        print(lines)
        # for num, line in enumerate(lines):
        #     print(num+1, line.upper())

except Exception as e:
       print("File Error :  ", e)

##=======================================  BINARY DATA ==========================================================

## write a binary data to file
with open('data/binary', 'wb') as binObj:
    binObj.write(b'life is good') # not saving as binary data

##read  a binary file
with open('data/binary', 'rb') as binObj:
    binary_data = binObj.read()
    # encoded_data = binary_data.encode('utf-8')
    print(binary_data)

#### read  write a  imaage file as binary file  #####
imgFile = open('data/Image.jpg', 'rb')
print(imgFile)

imgFile1 = open('data/image2.jpg', 'wb')
for i in imgFile:
    imgFile1.write(i)

### --------------- Bangla writing-------------------
with open('data/bangla.txt', 'a', encoding='UTF-8') as fileObj:
    fileObj.write('আমার সোনার বাংলাদেশ \n')
    fileObj.write('আমি তোমায় ভাল বাসি  \n')

### --------------- redirect ot file -------------------
## it will redirect to file directly instead printing in the consosle
with open('data/print.txt', 'w') as fileObj:
    print('Nothing Goes unpaid:  ', file=fileObj)

##==============Check if fle exist ================
import os
if os.path.exists('data/bangla.txt'):
    print("Yes....exist ")

###=======================temporary file ========================
from tempfile import TemporaryFile
with TemporaryFile('w+') as obj:
     obj.write(" Life is cool . \n")

     obj.seek(5)  ## 0 indicate the posttion of controoll
     data = obj.read()
     print(data)

##===================== Serialize python object to a byte stream=============================
import pickle

dict_data = {'name' : 'Joytun nur', 'Country' : 'Bangladesh'}
with open('data/serialize', 'wb') as obj:
    pickle.dump(dict_data, obj)

with open('data/serialize', 'rb') as obj:
    dict_data = pickle.load(obj)
    print(dict_data)

###================= CSV fle read and write ==================
### read csv
import csv
# with open('data/data.csv','r') as obj:
#     fcsv = csv.reader(obj)
#
#     sum = 0
#     for i, row in enumerate(fcsv):
#         print(i, row[0], row[1])
#         sum += int(row[1]) if i > 0 else 0
#     print("Total Cost: ", sum)

## write csv
list_item = [ ["Name",'Age','Country'],
              ['Bill Gets', 55,'US'],
              ['Mark Zucarbarg', 34,'US'],
              ['Swift',35,'CANADA']
            ]

with open('data/ppl.csv','w') as obj:
    file_csv = csv.writer(obj)
    file_csv.writerows(list_item)

###=========  JSON data ==================
data ={'Name':'Bill Gets', 'age': 55,'Country':'US'},
    # {'Mark Zucarbarg', 34,'US'],
    # {'Swift',35,'CANADA']

import json
with open('data/json_data.json','w') as obj:
    json.dump(data, obj)

with open('data/json_data.json','r') as obj:
  jsondata = json.load(obj)
  print(jsondata)


#!/usr/bin/python3
"""Module that defines endpoints for articles"""

from typing import List
from sqlalchemy.orm import Session
from . import crud, models, schemas
from .database import SessionLocal, engine
from fastapi import Depends, HTTPException, APIRouter

models.Base.metadata.create_all(bind=engine)

router = APIRouter()

# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@router.get("/get_subsection_by_country/{country_id}",
            response_model=List[schemas.Subsection])
async def read_subsections_by_country(
        country_id: int, skip: int = 0, limit: int = 100,
        db: Session = Depends(get_db)):
    """Endpoint to read subsection based on country id"""

    subsections = crud.get_subsections_by_country(
            db, country_id=country_id, skip=skip, limit=limit)
    return subsections

@router.get("/get_subsection_by_cc/{country_id}/{chapter_id}",
            response_model=List[schemas.Subsection])
async def read_subsections_by_country_and_chapter(
        country_id: int, chapter_id: int, skip: int = 0,
        limit: int = 100, db: Session = Depends(get_db)):
    """Endpoint to read subsection based on country id and chapter id"""

    subsections = crud.get_subsections_by_country_chapter(
            db, country_id=country_id, chapter_id=chapter_id,
            skip=skip, limit=limit)
    return subsections

@router.get("/get_subsection_by_cca/{country_id}/{chapter_id}/{article_id}",
            response_model=List[schemas.Subsection])
async def read_subsections_by_country_chapter_and_article(
        country_id: int, chapter_id: int, article_id: int, skip: int = 0,
        limit: int = 100, db: Session = Depends(get_db)):
    """Endpoint to read subsection based on country, chapter and article"""

    subsections = crud.get_subsections_by_country_chapter_article(
            db, country_id=country_id, chapter_id=chapter_id,
            article_id=article_id, skip=skip, limit=limit)
    return subsections

@router.get("/get_subsection_by_ccas/{country}/{chapter}/{article}/{section}",
            response_model=List[schemas.Subsection])
async def read_subsections_by_country_chapter_article_and_section(
        country_id: int, chapter_id: int, article_id: int, section_id: int,
        skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    """Endpoint to read subsection based on country, chapter, article..."""

    subsections = crud.get_subsections_by_country_chapter_article_section(
            db, country_id=country_id, chapter_id=chapter_id,
            article_id=article_id, section_id=section_id,
            skip=skip, limit=limit)
    return subsections

@router.get("/get_subsection/{subsection_id}",
            response_model=schemas.Subsection)
async def read_subsection(subsection_id: int, db: Session = Depends(get_db)):
    """Endpoint to read subsection based on its id"""

    db_subsection = crud.get_subsection(db, subsection_id=subsection_id)
    if db_subsection is None:
        raise HTTPException(status_code=404, detail="Subsection not found")
    return db_subsection

@router.post("/create_subsection/{country}/{chapter}/{article}/{section}",
             response_model=schemas.Subsection)
async def create_subsection(country_id: int,
                            chapter_id: int,
                            article_id: int,
                            section_id: int,
                            subsection: schemas.SubsectionCreate,
                            db: Session = Depends(get_db)):
    """Endpoint to create subsection with country, chapter, article..."""

    return crud.create_subsection(db=db,
                                  subsection=subsection,
                                  country_id=country_id,
                                  chapter_id=chapter_id,
                                  article_id=article_id,
                                  section_id=section_id)

@router.put("/update_subsection/{subsection_id}",
            response_model=schemas.Subsection)
async def update_subsection(subsection_id: int,
                            subsection_update: schemas.SubsectionBase,
                            db: Session = Depends(get_db)):
    """Endpoint to update a subsection based on its id"""

    return crud.update_subsection(db, subsection_id, subsection_update)

@router.delete("/delete_subsection/{subsection_id}", response_model=None)
async def delete_subsection(subsection_id: int,
                            db: Session = Depends(get_db)):
    """Endpoint to delete a subsection based on its id"""

    crud.delete_subsection(db, subsection_id)
    return {"message": "Subsection deleted successfully"}