"""Shared decoder by different models

Yao Fu, Columbia University
yao.fu@columbia.edu
MAY 02ND 2019
"""

import tensorflow as tf
import numpy as np

def beamsearch_decoding():
  """The beam search decoding algorithm, used for inference"""
  return

def multi_source_attention(query, memory, mem_lens, max_mem_len, state_size):
  """Attention to multiple sources

  query: a query vector [B, S]
  memory: a list of memory vectors [[B, M, S], [B, M, S], ... ]
  mem_lens: a list of memory length
  max_mem_len: a list of maximum memory length
  """
  with tf.variable_scope("multi_source_attention"):
    num_memory = len(memory)
    context_vector = []

    for i in range(num_memory):
      query = tf.layers.dense(query, state_size, name="query_proj",
        kernel_initializer=tf.random_normal_initializer(stddev=0.05),
        bias_initializer=tf.constant_initializer(0.), reuse=tf.AUTO_REUSE)
      context, _ = attention(query, memory[i], mem_lens[i], max_mem_len[i])
      context_vector.append(context)

    context_vector = tf.concat(context_vector, axis=1)
    # [B, num_mem * S] -> [B, S]
    context_vector = tf.layers.dense(context_vector, state_size,
      name="mem_output_proj",
      kernel_initializer=tf.random_normal_initializer(stddev=0.05),
      bias_initializer=tf.constant_initializer(0.), reuse=tf.AUTO_REUSE)

  return (context_vector, None)

def attention(query, memory, mem_lens, max_mem_len):
  """The attention layer, we use the dot product attention

  Args:
    query: the query vector, [B, S]
    memory: the memory matrix, [B, M, S]
  """
  # We use the scaled dot product attention. Essentially, this method does not
  # include more parameters
  # TODO: other attention methods

  with tf.variable_scope("attention"):
    batch_size = tf.shape(memory)[0]
    state_size = tf.cast(tf.shape(query)[1], tf.float32)
    query = tf.expand_dims(query, 1) # [B, 1, S]
    memory_ = tf.transpose(memory, [0, 2, 1]) # [B, S, M]
    weights = tf.matmul(query, memory_) / tf.sqrt(state_size) # [B, 1, M]
    weights = tf.reshape(weights, [batch_size, max_mem_len])

    mem_pad = 1 - tf.sequence_mask(mem_lens, max_mem_len, tf.float32)
    mem_pad *= -10000000
    weights += mem_pad
    dist = tf.nn.softmax(weights) # [B, M]

    dist_ = tf.expand_dims(dist, 2) # [B, M, 1]
    context = tf.reduce_sum(dist_ * memory, 1) # [B, S]
  return context, dist

def decoding_infer(start_id,
                   dec_cell,
                   dec_proj,
                   embedding_matrix,
                   enc_state,
                   memory,
                   batch_size,
                   max_dec_len,
                   mem_lens,
                   max_mem_len,
                   is_attn=True,
                   sampling_method="greedy",
                   topk_size=3,
                   state_size=500,
                   multi_source=False,
                   source_bow=None,
                   copy=False,
                   copy_ind=None,
                   dec_ptr_g_proj=None,
                   dec_ptr_k_proj=None,
                   bow_cond=None,
                   bow_cond_gate_proj=None):
  """The greedy decoding algorithm, used for inference"""
  dec_outputs = tf.TensorArray(tf.float32, size=max_dec_len)
  dec_out_index = tf.TensorArray(tf.int32, size=max_dec_len)
  start_id = tf.zeros([batch_size], dtype=tf.int32) + start_id
  dec_state = enc_state

  def _dec_loop_fn(i, prev_id, dec_state, dec_outputs, dec_out_index):
    dec_in = tf.nn.embedding_lookup(embedding_matrix, prev_id)
    dec_out, dec_state = dec_cell(dec_in, dec_state)
    dec_outputs = dec_outputs.write(i, dec_out)

    # project to the vocabulary
    dec_logits = dec_proj(dec_out)
    dec_index = tf.argmax(dec_logits, axis=1, output_type=tf.int32)

    dec_out_index = dec_out_index.write(i, dec_index)
    return (i + 1, dec_index, dec_state, dec_outputs, dec_out_index)

  def _dec_loop_attn_fn(i, prev_id, dec_state, dec_outputs, dec_out_index):
    dec_in = tf.nn.embedding_lookup(embedding_matrix, prev_id)

    query = dec_state[-1].h
    if(multi_source):
      context, _ = multi_source_attention(
        query, memory, mem_lens, max_mem_len, state_size)
    else:
      context, dist = attention(query, memory, mem_lens, max_mem_len)
    attn_vec = context + query

    bow_cond_g = 1.0
    if(bow_cond_gate_proj is not None):
      bow_cond_g = bow_cond_gate_proj(query + bow_cond)
    if(bow_cond is not None): dec_in = dec_in + bow_cond_g * bow_cond

    dec_out, dec_state = dec_cell(dec_in + attn_vec, dec_state)
    dec_outputs = dec_outputs.write(i, dec_out)

    # project to the vocabulary
    dec_logits = dec_proj(dec_out)
    vocab_dist = tf.nn.softmax(dec_logits)

    if(copy):
      print('Using copy mechanism in inference')
      pointers = []
      for proj_i in dec_ptr_k_proj:
        ptr_query = proj_i(dec_out)
        _, ptr = attention(ptr_query, memory[0], mem_lens[0], max_mem_len[0])
        pointers.append(ptr)
      pointers = tf.stack(pointers) # [num_pointers, batch_size, max_mem_len]
      pointers = tf.reduce_mean(tf.transpose(pointers, [1, 0, 2]), 1)

      g = dec_ptr_g_proj(dec_out)
      mixed_dist, ptr_dist = _mix_dist(vocab_dist, pointers, copy_ind, g)
      dec_dist = mixed_dist
    else:
      print('Not using copy mechanism in inference')
      dec_dist = vocab_dist

    if(sampling_method == "greedy"):
      dec_index = tf.argmax(dec_dist, axis=1, output_type=tf.int32)
    elif(sampling_method == "topk"):
      dec_prob = tf.nn.softmax(dec_logits)
      dec_prob_topk, dec_id_topk = tf.nn.top_k(dec_prob, topk_size) # [B, K]
      dec_prob_topk /= tf.expand_dims(tf.reduce_sum(dec_prob_topk, 1), [1])

      sample = tf.multinomial(tf.log(dec_prob_topk), 1) # [B, K] -> [B, 1]
      sample = tf.one_hot(tf.reshape(sample, [batch_size]), topk_size)
      dec_index = tf.reduce_sum(tf.cast(sample, tf.int32) * dec_id_topk, axis=1)
    elif(sampling_method == "topk_rejection"):
      pass

    dec_out_index = dec_out_index.write(i, dec_index)
    return (i + 1, dec_index, dec_state, dec_outputs, dec_out_index)

  if(is_attn):
    print("Attention decoding ... ")
    _dec_loop = _dec_loop_attn_fn
  else:
    print("Not using attention ...")
    _dec_loop = _dec_loop_fn
  print("Sampling method: %s, topk size: %d" % (sampling_method, topk_size))

  loop_len = max_dec_len
  start_time = 0

  finish_time, _, dec_state, dec_outputs, dec_out_index = tf.while_loop(
    cond= lambda i, _1, _2, _3, _4 : tf.less(i, loop_len),
    body=_dec_loop,
    loop_vars=(start_time, start_id, dec_state, dec_outputs, dec_out_index))

  dec_outputs = tf.transpose(dec_outputs.stack(), [1, 0, 2])
  dec_out_index = tf.transpose(dec_out_index.stack(), [1, 0])
  return dec_outputs, dec_out_index

def _mix_dist(vocab_dist, pointers, memory, g):
  """Given previous decoder state, and current pointers, return the mixed
  distribution.

  Mapping the pointer distribution to the vocabulary distribution by the
  word indices in the memory is a little bit tricky. We implement it by
  the function scatter_nd

  Args:
    vocab_dist: shape = [batch_zize, vocab_size]
    pointers: shape = [batch_size, mem_size]
    memory: shape = [batch_size, max_mem_len]
    g: shape = [batch_size], the mix gate

  Returns:
    mixed_dist: the mixed distribution
    ptr_dist: the pointer distribution
  """

  dshape = tf.shape(vocab_dist)

  # from the pointer and the memory, get the new distribution
  mshape = tf.shape(memory)

  batch_nums = tf.transpose(tf.reshape(tf.tile(
    tf.range(0, mshape[0]), [mshape[1]]), (mshape[1], mshape[0])), [1, 0])
  indices = tf.concat(
    [tf.expand_dims(batch_nums, 2), tf.expand_dims(memory, 2)],
    2)

  ptr_dist = tf.scatter_nd(indices = indices, updates = pointers,
    shape = dshape)
  mixed_dist = (1 - g) * vocab_dist + g * ptr_dist
  return mixed_dist, ptr_dist

def decoding_train( dec_inputs,
                    dec_cell,
                    dec_proj,
                    enc_state,
                    memory,
                    max_dec_len,
                    mem_lens,
                    max_mem_len,
                    is_attn,
                    state_size,
                    multi_source=False,
                    copy=False,
                    copy_ind=None,
                    dec_ptr_g_proj=None,
                    dec_ptr_k_proj=None,
                    bow_cond=None,
                    bow_cond_gate_proj=None):
  """The greedy decoding algorithm, used for training"""
  dec_outputs = tf.TensorArray(tf.float32, size=max_dec_len)
  dec_logits_train = tf.TensorArray(tf.float32, size=max_dec_len)
  dec_pointers = tf.TensorArray(tf.float32, size=max_dec_len)
  dec_prob_train = tf.TensorArray(tf.float32, size=max_dec_len)
  dec_g_train = tf.TensorArray(tf.float32, size=max_dec_len)
  dec_inputs = tf.transpose(dec_inputs, [1, 0, 2]) # [T, B, S]
  dec_state = enc_state

  if(copy): print('Using copy mechanism in decoding')
  else: print('Not using copy mechanism')
  if(bow_cond is not None): print('Using bow condition vector')
  else: print('Not using bow condition vector')
  if(bow_cond_gate_proj is not None): print('Using bow condition gate')
  else: print('Not using bow condition gate')

  def _dec_loop_fn(i, dec_state, dec_outputs):
    dec_in = dec_inputs[i]
    dec_out, dec_state = dec_cell(dec_in, dec_state)
    dec_outputs = dec_outputs.write(i, dec_out)
    return i + 1, dec_state, dec_outputs

  def _dec_loop_attn_fn(i, dec_state,
    dec_outputs, dec_logits_train, dec_pointers, dec_prob_train, dec_g_train):
    # TODO: the copy mechanism
    dec_in = dec_inputs[i]
    query = dec_state[-1].h
    if(multi_source):
      context, _ = multi_source_attention(
        query, memory, mem_lens, max_mem_len, state_size)
    else:
      context, dist = attention(query, memory, mem_lens, max_mem_len)
    attn_vec = context + query

    bow_cond_g = 1.0
    if(bow_cond_gate_proj is not None):
      bow_cond_g = bow_cond_gate_proj(query + bow_cond)
    if(bow_cond is not None): dec_in = dec_in + bow_cond_g * bow_cond

    # dec_out, dec_state = dec_cell(tf.concat([dec_in, attn_vec], 1), dec_state)
    dec_out, dec_state = dec_cell(dec_in + attn_vec, dec_state)
    dec_logits = dec_proj(dec_out)
    vocab_dist = tf.nn.softmax(dec_logits)

    if(copy):
      # the attention source is the
      pointers = []
      print('use %d pointers' % len(dec_ptr_k_proj))
      for proj_i in dec_ptr_k_proj:
        ptr_query = proj_i(dec_out)
        _, ptr = attention(ptr_query, memory[0], mem_lens[0], max_mem_len[0])
        pointers.append(ptr)
      pointers = tf.stack(pointers) # [num_pointers, batch_size, max_mem_len]
      pointers = tf.reduce_mean(tf.transpose(pointers, [1, 0, 2]), 1)

      g = dec_ptr_g_proj(dec_out)

      mixed_dist, ptr_dist = _mix_dist(vocab_dist, pointers, copy_ind, g)

      dec_pointers = dec_pointers.write(i, pointers)
      dec_prob_train = dec_prob_train.write(i, mixed_dist)
      dec_g_train = dec_g_train.write(i, g)

    dec_outputs = dec_outputs.write(i, dec_out)
    dec_logits_train = dec_logits_train.write(i, dec_logits)
    return (i + 1, dec_state,
      dec_outputs, dec_logits_train, dec_pointers, dec_prob_train, dec_g_train)

  if(is_attn):
    print("Attention decoding ... ")
    _dec_loop = _dec_loop_attn_fn
  else:
    print("Not using attention ... ")
    _dec_loop = _dec_loop_fn

  loop_len = max_dec_len
  start_time = 0

  (finish_time, dec_state, dec_outputs, dec_logits_train, dec_pointers,
    dec_prob_train, dec_g_train) = tf.while_loop(
    cond= lambda i, _1, _2, _3, _4, _5, _6 : tf.less(i, loop_len),
    body=_dec_loop,
    loop_vars=(start_time, dec_state,
      dec_outputs, dec_logits_train, dec_pointers, dec_prob_train, dec_g_train))

  dec_logits_train = tf.transpose(dec_logits_train.stack(), [1, 0, 2])
  dec_pointers = tf.transpose(dec_pointers.stack(), [1, 0, 2])
  dec_prob_train = tf.transpose(dec_prob_train.stack(), [1, 0, 2])
  dec_g_train = tf.transpose(dec_g_train.stack(), [1, 0, 2])

  avg_max_ptr = tf.reduce_max(dec_pointers, 2)
  pointer_ent = tf.reduce_sum(-dec_pointers * tf.log(dec_pointers + 1e-10), 2)
  avg_num_copy = tf.squeeze(dec_g_train)

  return dec_logits_train, dec_prob_train, pointer_ent, avg_max_ptr, avg_num_copy

def decode( dec_start_id,
            dec_inputs,
            dec_cell,
            dec_proj,
            embedding_matrix,
            init_state,
            memory,
            mem_lens,
            max_mem_len,
            batch_size,
            max_dec_len,
            sampling_method,
            topk_sampling_size,
            state_size,
            multi_source=False,
            copy=False,
            copy_ind=None,
            dec_ptr_g_proj=None,
            dec_ptr_k_proj=None,
            bow_cond=None,
            bow_cond_gate_proj=None):
  """The decoder, create the decoding graph for both training and inference"""
  # greedy decoding
  _, dec_outputs_predict = decoding_infer(dec_start_id,
                                          dec_cell,
                                          dec_proj,
                                          embedding_matrix,
                                          init_state,
                                          memory,
                                          batch_size,
                                          max_dec_len,
                                          mem_lens,
                                          max_mem_len,
                                          True,
                                          sampling_method,
                                          topk_sampling_size,
                                          state_size,
                                          multi_source=multi_source,
                                          copy=copy,
                                          copy_ind=copy_ind,
                                          dec_ptr_g_proj=dec_ptr_g_proj,
                                          dec_ptr_k_proj=dec_ptr_k_proj,
                                          bow_cond=bow_cond,
                                          bow_cond_gate_proj=bow_cond_gate_proj)

  dec_logits_train, dec_prob_train, pointer_ent, avg_max_ptr, avg_num_copy =\
                       decoding_train(
                                      dec_inputs,
                                      dec_cell,
                                      dec_proj,
                                      init_state,
                                      memory,
                                      max_dec_len,
                                      mem_lens,
                                      max_mem_len,
                                      is_attn=True,
                                      state_size=state_size,
                                      multi_source=multi_source,
                                      copy=copy,
                                      copy_ind=copy_ind,
                                      dec_ptr_g_proj=dec_ptr_g_proj,
                                      dec_ptr_k_proj=dec_ptr_k_proj,
                                      bow_cond=bow_cond,
                                      bow_cond_gate_proj=bow_cond_gate_proj)
  return (dec_outputs_predict, dec_logits_train, dec_prob_train, pointer_ent,
  avg_max_ptr, avg_num_copy)


"""
------------------------------------------------------------
RETIRED - DO NOT USE
TRANSFERED TO timetable.py
RETIRED - DO NOT USE
------------------------------------------------------------

Lesson scheduler cog for Vitaly Petrov

This cog reads and writes to the database, getting
lesson information and displaying it to any user.

Uses APScheduler to run lesson_sender and lesson_embed
at the time when the lesson is. Not suitable for large
scale deployment

Working commands:

Reload Timetable
Set alert
Schedule
Next Lesson
Current Lesson
Weekly Schedule

Non working commands:

Add lesson
Add Teacher
Add School

To be added:

ID system to add groups, teachers and lessons:

showgroups
showteachers <group>
showlessons [group] [teacher]

"""
__title__ = "Lesson Scheduler"
__author__ = "Bspoones"
__version__ = "1.0.4"
__copyright__ = "Bspoones 2021"

import datetime, discord

from ..db import db
from lib.bot import OWNER_IDS, get_prefix_value

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from discord.errors import InvalidArgument
from discord.ext.commands.errors import BadArgument
from discord.ext.commands import Cog, command
from humanfriendly import format_timespan
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont,ImageColor

# timetable = 0: LessonID, 1: TeacherID, GroupID, DayOfWeek, StartHour, StartMin, Endhour, Endmin, Room

class LessonTimetable(Cog):
    def __init__(self, bot):
        self.bot = bot
        self.WEEK = ('MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN')
        self.DAYS_OF_WEEK = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]
        self.FMT = "%H:%M"
        self.__version__ = __version__

        self.lesson_scheduler = AsyncIOScheduler()
        self.load_schedule()

    def load_schedule(self) -> AsyncIOScheduler:

        try: # Prevents multiple schedulers running at the same time
            if (self.lesson_scheduler.state) == 1:
                self.lesson_scheduler.shutdown(wait=False)
            self.lesson_scheduler = AsyncIOScheduler()
        except:
            self.lesson_scheduler = AsyncIOScheduler()
        self.timetable = db.records("SELECT * FROM timetable ORDER BY LessonID")
        for item in self.timetable:
            Type_ID = item[1]
            Type_Info = db.record("SELECT * FROM teachers WHERE TeacherID = ?", Type_ID)
            Group_ID = Type_Info[1]
            Group_Info = db.record("SELECT * FROM groups WHERE GroupID = ?", Group_ID)
            start_hr = item[4]
            start_min = item[5]
            warming_times = Group_Info[5].split()
            warming_times = list(map(int, warming_times))
            actual_warning_times = []
            for element in warming_times:
                start_time = datetime.time(start_hr,start_min)
                tdelta = datetime.timedelta(minutes=element)
                element_time = (datetime.datetime.combine(datetime.date(2, 1, 1), start_time)- tdelta).time()
                if not self.is_time_in_lesson(element_time, item[3], Group_ID):
                    actual_warning_times.append(element)
            if 0 not in actual_warning_times:
                actual_warning_times.append(0)

            for element in actual_warning_times:
                start_time = datetime.time(start_hr,start_min)
                updated_time = (datetime.datetime.combine(datetime.date(2, 1, 1), start_time) - datetime.timedelta(minutes=element)).time()
                hour = updated_time.hour
                minute = updated_time.minute
                if element == actual_warning_times[0]:
                    self.lesson_scheduler.add_job(
                        self.lesson_embed,
                        CronTrigger(
                            day_of_week=item[3],
                            hour=hour,
                            minute=minute,
                            second=0
                        ),
                        args=[item, Type_Info, Group_Info, element, actual_warning_times]
                    )
                else:
                    self.lesson_scheduler.add_job(
                        self.lesson_sender,
                        CronTrigger(
                            day_of_week=item[3],
                            hour=hour,
                            minute=minute,
                            second=0
                        ),
                        args=[item, Type_Info, Group_Info, element, actual_warning_times]
                    )
                # print(item)
                # print(hour,minute)
                # print(Group_Info)
                # print(Type_Info)
                # print(actual_warning_times)
                # print("")

        self.lesson_scheduler.start()

    async def lesson_sender(self,*args) -> discord:
        Timetable_Info = args[0]
        Type_Info = args[1]
        Group_Info = args[2]
        element = args[3]
        actual_warning_times = args[4]

        output_channel = int(Group_Info[3])
        output_role = Group_Info[4]

        if element == 0:
            await self.bot.get_channel(output_channel).send(f"<@&{output_role}> your lesson is now!")
            await self.update_time_channels(Timetable_Info,Group_Info)
        else:
            """
            Deletes message just before the next message is shown.
            Time is calculated from the time between that message and the next one.
            E.g if the warning was 10 min and the next was 5 min, 10-5 = 5 so it would stay for 5 min
            """
            element_index = actual_warning_times.index(element)
             # Shouldn't create an error when finding +1 th in a list as the last element is always 0 and is handled by the above statement.
            delete_after = (actual_warning_times[element_index] - actual_warning_times[element_index+1]) * 60 # deleteafter works in seconds
            await self.bot.get_channel(output_channel).send(f"<@&{output_role}> your lesson is in {element} minutes!", delete_after=delete_after)

    async def lesson_embed(self,*args) -> discord.Embed:
        Timetable_Info = args[0]
        Type_Info = args[1]
        Group_Info = args[2]
        element = args[3]
        actual_warning_times = args[4]
        FMT = '%H:%M'
        start_time = datetime.time(Timetable_Info[4],Timetable_Info[5]).strftime(FMT)
        end_time = datetime.time(Timetable_Info[6],Timetable_Info[7]).strftime(FMT)
        duration = (datetime.datetime.strptime(end_time,FMT)-datetime.datetime.strptime(start_time,FMT)).total_seconds()
        duration = format_timespan(duration)
        title = f"***{Type_Info[3]} lesson with {Type_Info[2]}***"
        description=f"***In {Timetable_Info[8]}***\n\nStart time: `{start_time}`\nEnd time: `{end_time}`\nLesson duration: `{duration}`"
        colour = int(Type_Info[6],16)
        colour = int(hex(colour),0)
        if Type_Info[5] == "F":
            thumbnail = Group_Info[6]
        else:
            thumbnail = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Google_Meet_icon_%282020%29.svg/934px-Google_Meet_icon_%282020%29.svg.png"
        embed = self.bot.auto_embed(
            type="lesson",
            title=title,
            description=description,
            colour=colour,
            thumbnail=thumbnail,
            iconurl= thumbnail,
            schoolname = Group_Info[2]
            )
        output_channel = int(Group_Info[3])
        output_role = Group_Info[4]
        await self.bot.get_channel(output_channel).send(embed=embed)
        if element == 0:
            await self.bot.get_channel(output_channel).send(f"<@&{output_role}> your lesson is now!")
            await self.update_time_channels(Timetable_Info,Group_Info)
        else:
            """
            Deletes message just before the next message is shown.
            Time is calculated from the time between that message and the next one.
            E.g if the warning was 10 min and the next was 5 min, 10-5 = 5 so it would stay for 5 min
            """
            element_index = actual_warning_times.index(element)
             # Shouldn't create an error when finding +1 th in a list as the last element is always 0 and is handled by the above statement.
            delete_after = (actual_warning_times[element_index] - actual_warning_times[element_index+1]) * 60 # deleteafter works in seconds
            await self.bot.get_channel(output_channel).send(f"<@&{output_role}> your lesson is in {element} minutes!", delete_after=delete_after)

    def is_time_in_lesson(self, time, day, Group_ID) -> bool:
        timetable_on_day = db.records("SELECT * FROM timetable WHERE DayOfWeek = ? AND GroupID = ?",day,Group_ID)
        for item in timetable_on_day:
            start_time = datetime.time(item[4], item[5])
            end_time = datetime.time(item[6], item[7])
            if start_time <= end_time:
                if start_time <= time < end_time: # result = True if time is within lesson
                    return True
            # else:
            #     return start_time <= time or time < end_time
        return False

    def return_time_in_lesson(self,time,day,Group_ID) -> str:
        timetable_on_day = db.records("SELECT * FROM timetable WHERE DayOfWeek = ? AND GroupID = ?",day,Group_ID)
        for item in timetable_on_day:
            start_time = datetime.time(item[4], item[5])
            end_time = datetime.time(item[6], item[7])
            if start_time <= end_time:
                if start_time <= time < end_time: # result = True if time is within lesson
                    return item
        else:
            return False

    async def update_time_channels(self, Timetable_Info, Group_Info) -> discord:
        GroupID = Group_Info[0]
        group_timetable = db.records("SELECT * FROM timetable WHERE GroupID = ? ORDER BY DayOfWeek ASC, StartHour ASC, StartMin ASC", GroupID)

        index = group_timetable.index(Timetable_Info)
        try:
            next_lesson = (group_timetable[index+1])
        except:
            next_lesson = (group_timetable[0])
        day_channel = int(Group_Info[7])
        time_channel = int(Group_Info[8])
        time = datetime.time(next_lesson[4],next_lesson[5]).strftime("%H:%M")
        day = self.DAYS_OF_WEEK[next_lesson[3]]

        await self.bot.get_channel(day_channel).edit(name=f"Next lesson: {day.capitalize()}")
        await self.bot.get_channel(time_channel).edit(name=f"Next lesson time: {time}")

    @command(name="Reload timetable",brief="Reloads the timetable database.", aliases=["reload"])
    async def reloadtimetable(self,ctx) -> str:
        self.load_schedule()
        await ctx.send("Lesson schedule reloaded!")
        self.bot.log_command(ctx,"reloadtimetable")

    @command(name="Change Alert",brief="Changes the alert times before your group's lessons.", aliases=["changealert","setalert"])
    async def changealert(self,ctx,*,times) -> str:
        if ctx.author.id in OWNER_IDS:
            times_list = times.split()
            if all(x.isnumeric() for x in times_list):
                if times_list[-1] != "0": # Final item must be 0
                    print(times_list[-1])
                    times_list.append("0")
                warning_times = " ".join(times_list)
                user_id = ctx.author.id

                User_Info = db.record("SELECT * FROM students WHERE DiscordID = ?", user_id)
                Group_ID = User_Info[1]

                db.execute("UPDATE groups SET AlertTimes = ? WHERE GroupID = ?", warning_times,Group_ID)
                db.commit()

                Group_Info = db.record("SELECT * FROM groups where GroupID = ?", Group_ID)

                title = "**Alert times updated!**"
                description = f"Alert times updated for {Group_Info[2]} to be: \n`{warning_times}`."

                embed = self.bot.auto_embed(
                    type="info",
                    title=title,
                    description=description,
                    ctx=ctx
                )

                await ctx.send(embed=embed)
            else:
                raise BadArgument
        else:
            raise PermissionError
        self.load_schedule()
        self.bot.log_command(ctx,"changealert",Group_ID, warning_times)

    @command(name="Schedule",brief="Shows the schedule for a day and a school.",aliases=["schedule","sched"])
    async def schedule(self,ctx,day=None,school=None) -> discord.Embed:
        if day is not None:
            try:
                day.lower() # Used to find index of self.DAYS_OF_WEEK
            except:
                raise InvalidArgument
            if day == "today":
                day = (datetime.datetime.today().weekday())
            elif day == "tomorrow":
                day = datetime.datetime.today().weekday() + 1
                if day == 7:
                    day = 0
            elif day == "yesterday":
                day = datetime.datetime.today().weekday() - 1
                if day == -1:
                    day - 6
            else:
                try:
                    day = self.DAYS_OF_WEEK.index(day)
                except:
                    raise BadArgument
            current_day_of_week = self.DAYS_OF_WEEK[(datetime.datetime.today().weekday())]
            date_day_of_week = self.DAYS_OF_WEEK[day]
            difference = self.DAYS_OF_WEEK.index(date_day_of_week)-self.DAYS_OF_WEEK.index(current_day_of_week)
            if difference < 0:
                difference += 7
            full_date = (datetime.datetime.today()) + datetime.timedelta(days=difference)
            dateformat = full_date.strftime('%d %B')
        else:
            day = (datetime.datetime.today().weekday())
            dateformat = datetime.datetime.today().strftime('%d %B')

        if school is not None:
            try:
                school = school.lower()
            except:
                raise BadArgument
            Group_Info = db.record("SELECT * FROM groups WHERE GroupCode = ?", school)
            if Group_Info == ():
                raise InvalidArgument
            Group_ID = Group_Info[0]
        else:
            user_id = ctx.author.id
            User_Info = db.record("SELECT * FROM students WHERE DiscordID = ?", user_id)
            Group_ID = User_Info[1]
        lessons = db.records("SELECT * FROM timetable WHERE GroupID = ? AND DayOfWeek = ?", Group_ID, day)
        title=f"Schedule for {self.DAYS_OF_WEEK[day].capitalize()}, {dateformat}"
        fields = []
        for i, lesson in enumerate(lessons,start=1):
            Teacher_info = db.record("SELECT * FROM teachers where TeacherID = ?", lesson[1])
            Group_Info = db.record("SELECT * FROM groups where GroupID = ?", lesson[2])

            start_time = datetime.time(lesson[4],lesson[5]).strftime("%H:%M")
            end_time = datetime.time(lesson[6],lesson[7]).strftime("%H:%M")

            duration = (datetime.datetime.strptime(end_time,self.FMT)-datetime.datetime.strptime(start_time,self.FMT)).total_seconds()
            duration = format_timespan(duration)
            room = lesson[8]
            teacher = Teacher_info[2]
            subject = Teacher_info[3]
            name = f"**Lesson {i}**"
            value = f"Subject: `{subject}`\nTeacher: `{teacher}`\nStart time: `{start_time}`\nEnd time: `{end_time}`\nDuration: `{duration}`\nRoom: `{room}`"
            inline = False
            fields.append((name,value,inline))
        if len(lessons) == 0:
            title = f"**No lessons found**"
            description = f"You don't have any lessons for today\nUse `{get_prefix_value(ctx)}nextlesson` to show your next lesson"
            embed = self.bot.auto_embed(
                type="error",
                title=title,
                description=description,
                ctx=ctx
            )
        else:
            embed = self.bot.auto_embed(
                type="schedule",
                title=title,
                fields=fields,
                ctx=ctx
            )
        await ctx.send(embed=embed)
        self.bot.log_command(ctx,"schedule", day, Group_ID)

    @command(name="Next lesson",brief="Shows the next lesson for a school.", aliases=["nl","nextlesson"])
    async def nextlesson(self,ctx, school=None) -> discord.Embed:
        if school is None:
            User_ID = ctx.author.id
            Group_ID = db.record("SELECT GroupID FROM students where DiscordID = ?", User_ID)[0]
            group_timetable = db.records("SELECT * FROM timetable WHERE GroupID = ?", Group_ID)
        else:
            school = school.lower()
            try:
                Group_Info = db.record("SELECT * FROM groups WHERE GroupCode = ?", school)
            except:
                raise InvalidArgument
            Group_ID = Group_Info[0]
            group_timetable = db.records("SELECT * FROM timetable WHERE GroupID = ?", Group_ID)
        if group_timetable == []:
            title = f"**No lessons**"
            description = f"You don't have any lessons in the databse."
            embed = self.bot.auto_embed(
                type="error",
                title=title,
                description= description,
                ctx=ctx
            )
            await ctx.send(embed=embed)
            return
        group_timetable_weekdays = [x[3] for x in group_timetable]
        group_timetable_start_times = [(datetime.time(x[4],x[5])) for x in group_timetable]

        def next_day(current_time, current_date, weekday, start_time):
            day_shift = (weekday - current_date.weekday()) % 7
            if datetime.datetime.today().weekday() == weekday and current_time > start_time: # If lesson is today and has already happened
                day_shift += 7
            return current_date + datetime.timedelta(days=day_shift)

        current_date = datetime.datetime.today().date()
        current_time = datetime.datetime.now().time()
        next_lessons = [next_day(current_time, current_date,weekday,start_time) for weekday,start_time in zip(group_timetable_weekdays,group_timetable_start_times)]
        next_lesson_date = next_lessons.index(min(next_lessons, key=lambda d: abs(d - current_date)))
        lesson = (group_timetable[next_lesson_date])

        Teacher_info = db.record("SELECT * FROM teachers where TeacherID = ?", lesson[1])
        Group_Info = db.record("SELECT * FROM groups where GroupID = ?", lesson[2])

        start_time = datetime.time(lesson[4],lesson[5]).strftime("%H:%M")
        end_time = datetime.time(lesson[6],lesson[7]).strftime("%H:%M")
        duration = (datetime.datetime.strptime(end_time,self.FMT)-datetime.datetime.strptime(start_time,self.FMT)).total_seconds()
        duration = format_timespan(duration)
        lesson_day_of_week_str = self.DAYS_OF_WEEK[lesson[3]].capitalize()
        room = lesson[8]
        teacher = Teacher_info[2]
        subject = Teacher_info[3]

        current_time = datetime.datetime.now().strftime(self.FMT)
        next_lesson_date_object = next_lessons[next_lesson_date]
        next_lesson_datetime_object = datetime.datetime(
            next_lesson_date_object.year,
            next_lesson_date_object.month,
            next_lesson_date_object.day,
            lesson[4],
            lesson[5]
            )
        time_until_end = format_timespan((next_lesson_datetime_object-datetime.datetime.today()).total_seconds())

        if datetime.datetime.today().weekday() == lesson[3]: # If the current weekday is the same as the lesson weekday
            title = f"**Next lesson is at {start_time}\n({time_until_end} from now)**"
        else:
            title = f"**Next lesson is at {start_time} on {lesson_day_of_week_str}\n({time_until_end} from now)**"
        description = f"Day of week: `{lesson_day_of_week_str}`\nSubject: `{subject}`\nTeacher: `{teacher}`\nStart time: `{start_time}`\nEnd time: `{end_time}`\nDuration: `{duration}`\nRoom: `{room}`"
        embed = self.bot.auto_embed(
            type="schedule",
            title=title,
            description= description,
            ctx=ctx
        )
        await self.update_time_channels((group_timetable[next_lesson_date-1]),Group_Info)
        await ctx.send(embed=embed)
        self.bot.log_command(ctx,"nextlesson",Group_ID)

    @command(name="Current lesson",brief="Shows information about the current lesson.", aliases=["currentlesson"])
    async def currentlesson(self,ctx, school=None) -> discord.Embed:
        if school is None:
            User_ID = ctx.author.id
            Group_ID = db.record("SELECT GroupID FROM students where DiscordID = ?", User_ID)[0]
        else:
            school = school.lower()
            Group_Info = db.record("SELECT * FROM groups WHERE GroupCode = ?", school)
            if Group_Info == ():
                raise InvalidArgument
            Group_ID = Group_Info[0]
        current_time = datetime.datetime.now().time()
        current_weekday = datetime.datetime.today().weekday()
        day_timetable = db.records("SELECT * FROM timetable WHERE GroupID = ? AND DayOfWeek = ?", Group_ID, current_weekday)
        for each_lesson in day_timetable:
            weekday = each_lesson[3]
            lesson = self.return_time_in_lesson(current_time, weekday, Group_ID)
            if lesson != False:
                break
        else:
            title = f"**Not in a lesson**"
            description = f"You are not currently in a lesson.\nTry `{get_prefix_value(ctx)}schedule` to show the list of lessons for today"
            embed = self.bot.auto_embed(
                type="error",
                title=title,
                description= description,
                ctx=ctx
            )
            await ctx.send(embed=embed)
            return
        start_time = datetime.time(lesson[4],lesson[5]).strftime("%H:%M")
        end_time = datetime.time(lesson[6],lesson[7]).strftime("%H:%M")
        duration = (datetime.datetime.strptime(end_time,self.FMT)-datetime.datetime.strptime(start_time,self.FMT)).total_seconds()
        duration = format_timespan(duration)
        lesson_day_of_week = self.DAYS_OF_WEEK[lesson[3]].capitalize()
        room = lesson[8]
        Teacher_info = db.record("SELECT * FROM teachers where TeacherID = ?", lesson[1])
        teacher = Teacher_info[2]
        subject = Teacher_info[3]

        current_time = datetime.datetime.now().strftime(self.FMT)
        time_until_end = format_timespan((datetime.datetime.strptime(end_time,self.FMT)-datetime.datetime.strptime(current_time,self.FMT)).total_seconds())

        title = f"**This lesson finishes at {end_time}.\n({time_until_end} from now)**"
        description = f"Day of week: `{lesson_day_of_week}`\nSubject: `{subject}`\nTeacher: `{teacher}`\nStart time: `{start_time}`\nEnd time: `{end_time}`\nDuration: `{duration}`\nRoom: `{room}`"
        embed = self.bot.auto_embed(
            type="schedule",
            title=title,
            description= description,
            ctx=ctx
        )

        await ctx.send(embed=embed)
        self.bot.log_command(ctx,"currentlesson",Group_ID)

    @command(name="Weekly schedule",brief="Shows an auto generated image of the timetable for the week.", aliases=["ws","weeklyschedule"])
    async def weeklyschedule(self,ctx,school=None) -> discord.Embed and Image:
        if school is None:
            User_ID = ctx.author.id
            Group_ID = db.record("SELECT GroupID FROM students where DiscordID = ?", User_ID)[0]
        else:
            school = school.lower()
            try:
                Group_Info = db.record("SELECT * FROM groups WHERE GroupCode = ?", school)
            except:
                raise InvalidArgument
            Group_ID = Group_Info[0]

        Group_Info = db.record("SELECT * FROM groups WHERE GroupID = ?", Group_ID)

        wait_message = await ctx.send("Creating image, please wait.....")
        img = Image.new('RGB', size=(7000, 6000), color=(255,255,255))
        font = ImageFont.truetype("./data/fonts/arial.ttf", 90)
        draw = ImageDraw.Draw(img)

        draw.line((0, 500, 7000, 500), fill=(0, 0, 0), width=10) # Draws line at top to seperate day titles and content
        for i in range(20): # Going from 08:00 to 18:00 with lines, totalling 20 half hour increments
            if i % 2 == 0:
                draw.line((0,(1000+(250*i)),7000,(1000+(250*i))),fill=(0,0,0),width=1)
            else:
                draw.line((0,(1000+(250*i)),7000,(1000+(250*i))),fill=(128,128,128),width=1)
        for i,val  in enumerate(self.WEEK):
            # Draw day of week at top
            draw.text((i*1000+400, 200), val, fill=(0,0,0), font=font)
            # Select all lessons for day
            daily_timetable = db.records("SELECT * FROM timetable WHERE GroupID = ? AND DayOfWeek = ?", Group_ID, i)

            if i != 6:
                # Prevents extra black line on the right
                draw.line((i*1000+1000, 0, i*1000+1000, 7000), fill=(0, 0, 0), width=10)

            for lesson in daily_timetable:
                start_time = datetime.time(lesson[4],lesson[5]).strftime("%H:%M")
                end_time = datetime.time(lesson[6],lesson[7]).strftime("%H:%M")
                lesson_room = lesson[8]
                teacherID = (lesson[1])
                teacher_info = db.record("SELECT * FROM teachers WHERE TeacherID = ?", teacherID)
                teacher = teacher_info[2]
                hexcolour = str(teacher_info[6])
                colour = ImageColor.getrgb("#"+hexcolour)
                # Starting pad of 1000px, then each hour block counts as 500px. Starting at 08:00
                # E.g - 09:00 is 1500px down, 09:30 is 1750px down
                # Each half an hour increment is 250px, with an initial start point of 1000px down
                start_time_px = 1000+(500*(lesson[4]-8) + 500*(lesson[5]/60))
                end_time_px = 1000+(500*(lesson[6]-8) + 500*(lesson[7]/60))
                block_height = end_time_px-start_time_px
                print(teacher,block_height)
                message =  f"{start_time} - {end_time}\n{lesson_room}\n{teacher}"
                vertical_pad = 30
                if block_height <= 250:
                    # If the lesson is too short for the text to fit into a block
                    message = f"{start_time} - {end_time}\n{lesson_room} {teacher}"
                    vertical_pad = 1

                draw.rectangle((i*1000+100, start_time_px, i*1000+1000-100, end_time_px), fill=(colour), outline=(0, 0, 0)) # Draws rectangle with a 100px padding from the daily line seperators
                if (sum(colour)) <= 254:
                    # If the background colour is too dark then the text changes to white
                    draw.text((i*1000+130, start_time_px+vertical_pad), message, fill=(255,255,255), font=font, stroke_width = 2)
                else:
                    draw.text((i*1000+130, start_time_px+vertical_pad), message, fill=(0,0,0), font=font, stroke_width = 2)

        with BytesIO() as image_binary: # Used to turn Pillow image to binary for discord to upload
            img.save(image_binary, 'PNG')
            image_binary.seek(0)
            file=discord.File(fp=image_binary, filename='image.png')

        title=f"__**Weekly schedule**__"
        description= f"Use `{get_prefix_value(ctx)}schedule DAY` to find out more information for each day"
        school = Group_Info[2]
        iconurl = Group_Info[6]
        embed = self.bot.auto_embed(
            type="lesson",
            title=title,
            description= description,
            schoolname=school,
            iconurl=iconurl,
            colour = ctx.author.colour,
            ctx=ctx
        )
        embed.set_image(url=f"attachment://image.png")

        await ctx.send(file=file,embed=embed)
        await wait_message.delete()
        self.bot.log_command(ctx,"weeklyschedule",Group_ID)

    # @command()
    # async def addschool(self,ctx, group_code, output_channel, output_role, image_link, NLDayChannel, NLTimeChannel,*,School_Name):
    #     pass

    # @command()
    # async def addteacher(self,ctx, school, teacher_foremane, teacher_surname, lesson_subject, lesson_link = None):
    #     pass

    # @command()
    # async def addlesson(self, ctx, schoolcode, teacher_forename, teacher_surname, day_of_week, start_time, end_time, room):
    #     pass

    @Cog.listener()
    async def on_ready(self):
        if not self.bot.ready:
            self.bot.cogs_ready.ready_up("lesson timetable")
        print("lesson timetable cog ready!")

def setup(bot):
    # bot.add_cog(LessonTimetable(bot))
    pass
if __name__ == "__main__":
    print("I am an external cog, please run launcher.py to load me")
    exit()


import os
import scipy

import matplotlib.pyplot as plt
import numpy as np
import wave
import soundfile as sf
import scipy.fftpack as fft
import scipy.signal as sig
from scipy import signal
from scipy.io import wavfile
from scipy.io.wavfile import read
from scipy.io.wavfile import write
import sounddevice as sd
import cleanEnMasse

class CompTool():
    cleanOriginal = None
    cleanAttempt = None

    def __init__(self, attempt, word):
        # The original word
        self.attempt = attempt
        self.word = word
        # How many morphemes should be used as the prefix for the new portmanteau
        self.output = '' #
        self.compare()

    def compare(self):
        # REMOVE BACKGROUND NOISE
        self.clean()
        self.spectrogram()

    def clean(self):
        print("Clean")
        # CLEAN THE ORIGINAL
        freq, array = read("../database/" + self.word + ".wav")
        plt.subplot(2, 2, 1)
        plt.plot(array)
        plt.title("Original")
        plt.ylabel("Amplitude")
        # HIGH PASS FILTER
        b, a = signal.butter(5, 1000/(freq / 2), btype = 'highpass')
        filtered1 = signal.lfilter(b, a, array)
        # LOW PASS FILTER
        plt.subplot(2, 2, 3)
        c, d = signal.butter(5, 380 / (freq / 2), btype = 'lowpass')
        filtered2 = signal.lfilter(c, d, filtered1)
        plt.plot(filtered2)
        plt.title("Cleaned Low")
        plt.xlabel("Frequency (Hz)")
        plt.ylabel("Amplitude")

        # CLEAN THE ATTEMPT
        freq, array = read("out.wav")
        plt.subplot(2, 2, 2)
        plt.plot(array)
        plt.title("Attempt")
        plt.ylabel("Amplitude")
        # HIGH PASS FILTER
        b, a = signal.butter(5, 1000/(freq / 2), btype = 'highpass')
        filtered3 = signal.lfilter(b, a, array)
        # LOW PASS THE HIGH PASS
        plt.subplot(2, 2, 4)
        c, d = signal.butter(5, 380 / (freq / 2), btype = 'lowpass')
        filtered4 = signal.lfilter(c, d, filtered3)
        plt.plot(filtered4)
        plt.title("Cleaned Low")
        plt.xlabel("Frequency (Hz)")
        plt.ylabel("Amplitude")
        print("defos")

        # ASSIGN GLOBAL VARIABLES
        print("Here")
        self.cleanOriginal = filtered2
        self.cleanAttempt = filtered4
        # WRITE .WAV OF CLEANED
        # TODO: DELETE
        fs = 16000
        filename = "clean.wav"
        print(filename)
        sf.write(filename, self.cleanAttempt, fs)

        print("clean mass")
        self.finalClean()
        print("finish")
        plt.show()

    def finalClean(self):
        cleanEnMasse.actOnce(actions = ["find_silence", "sox_denoise", "cleanup", "nothing"], file = "clean.wav")

    def spectrogram(self):
        dt = 0.0005
        t = np.arange(0, 20, dt)
        s1 = np.sin(2 * np.pi * 100 * t)
        s2 = 2 * np.sin(2 * np.pi * 400 * t)
        s2 = s2 * self.cleanAttempt

        x = s1 + s2 + self.cleanAttempt
        nfft = 1024
        fs = int(1.0 / dt)

        ax1 = plt.sublplot(2, 1, 1)
        plt.plot(t, x)
        plt.subplot(2, 1, 2, sharex = ax1)
        pxx, freqs, bins, im = plt.specgram(x, NFFT = nfft, Fs = fs, noverlap = 900)
        plt.show()

# STEP 1
# eliminate background noise
# use: scipy

'''
# STEP 2
# distance metric
# compares two speech patterns
# .1 - spectogram of audio
# .2 - represent w/ 2D array
# use: OpenCV
        blob detection (the big vocal area)
# .3 - normalize two blob (speed adapt)
# .4 - normalize by intensity also (volume accounted for)
'''

'''
# STEP 3
# direct comparison time
# various methods possible
# opt 1 - cosine
# opt 2 - subtraction
# set difference threshold and see if it meets
'''


from django import forms
from .models import Book
from django.contrib.auth.forms import UserCreationForm

class BookForm(forms.ModelForm):
    class Meta:
        model = Book
        exclude = ['Arweave']
        widgets = {
            'intro': forms.Textarea(attrs={'rows': 4}),
        }


"""The code in this file is used for creating different
policies for recommending health interventions/nudges.
"""
import abc
from entities.key_entities import NudgeOptimizationDataSpec
from dataset.dataset import NudgeOptimizationDataset
import pandas as pd
import numpy as np
from typing import Dict
from models.base_models import BaseTabularPredictor
from dataset.dataset import TimeSeriesDataset
from pydantic import parse_file_as
from runners.model_runner import Runner
from models.models import get_forecasting_model

def get_nudge_optimizer_model(model_name):
    """
    This function is called to fetch the appropriate model based on the training config.
    """
    return eval(model_name)

class BaseNudgeOptimizer(BaseTabularPredictor):
    def __init__(self, _dataspec):
        super().__init__(_dataspec)
        assert isinstance(_dataspec, NudgeOptimizationDataSpec)

    def __call__(self, data: NudgeOptimizationDataset):
        """
        Asssume data contains state columns, and attempts to propose a policy based on the state.

        Args:
            data (NudgeOptimizationDataset): The nudge optimization dataset.

        Returns:
            NudgeOptimizationDataset: Populated NudgeOptimizationDataset with num_actions columns extra.
                                      These columns denote the probability distribution pi(a|s) recommended by the considered policy.
                                      For deterministic policies, for a state s0, only one of pi(a|s0) is 1, while rest are
                                      all 0.
        """
        possible_actions = self._dataspec.sampling_policy # The number of sampling policy columns is same as number of actions
        predicted_policy = ["policy_%s_predicted"%str(action) for action in range(len(possible_actions))]
        data.data[predicted_policy] = self._predict(
            self._get_data(data.data, self._dataspec.state_columns)
        )
        return data

class NudgeOptimizerToy(BaseNudgeOptimizer):
    """
    Used ONLY in a very simple contextual bandit scenario - meant for testing the estimators
    Do not use in real code.
    The scenario where this was used when state itself contained the predicted rewards, and the policy
    was simply picking the best action with probability p, and the rest with probability (1-p)/(num_actions-1)
    """
    def _predict(self, state):
        out = np.full((len(state), len(self._dataspec.sampling_policy)), (1-self.p)/(len(self._dataspec.sampling_policy)-1))
        out[np.arange(len(state)),state.argmax(axis=1)] = self.p
        return out

    def set_params(self, params):
        self.p = params["p"]

class UniformActionRecommender(BaseNudgeOptimizer):
    """
    Uniform random action, assigned with a probability of 1/num_actions.
    """
    def _predict(self, state):
        out = np.full((len(state), len(self._dataspec.sampling_policy)), 1.0/len(self._dataspec.sampling_policy))
        return out

    def set_params(self, params):
        pass

class NudgeOptimizerFromTimeSeries(BaseNudgeOptimizer):
    """
    Almost identical to RewardPredictorFromTimeSeries in reward_predictor_models.py.
    Please refer to the comments there before coming to this class.
    """
    def convert_to_timeseries(self,df): # df consists of only single point
        ret_dataset = TimeSeriesDataset(self.model.dataspec, blank_dataset=True)
        ret_df = pd.DataFrame()
        prediction_config = self.params["prediction_config"]
        total_window_size = (prediction_config.context_window
                            + prediction_config.lead_gap
                            + prediction_config.forecast_horizon)
        ret_df[self.model.dataspec.time_column] = list(range(total_window_size))
        ret_df[self.model.dataspec.series_id_column] = 1 #HACK since current time series models
        # do not use series_id_column, just adding a dummy input
        columns = (self.model.dataspec.dependent_state_columns + self.model.dataspec.independent_state_columns + self.model.dataspec.series_attribute_columns)

        d = {column: [] for column in columns}
        d["nudge"] = [] #HACK: althought time series model has a general control input, assuming that there is a single control input
        # called nudge and changing/passing this control input into the
        for time in range(prediction_config.context_window):
            for column in columns:
                d[column].append(df[column+"_"+str(time)])

            d["nudge"].append(0)

        for time in range(prediction_config.lead_gap + prediction_config.forecast_horizon):
            for column in columns:
                d[column].append(0)
            d["nudge"].append(0)

        for column, item in d.items():
            ret_df[column] = item
        ret_dataset.data = ret_df
        return ret_dataset

    def _predict(self, data: np.array):
        """
        This function could be optimized further.
        Currently performing a very naive interconversion from nudge dataset to time series dataset.
        There are also many hacks employed here, it is not a general function as it assumes that nudge is only
        a single column in the dataset.
        """
        nudge_optimizer_data= pd.DataFrame(data=data, columns=self._dataspec.state_columns)
        for idx, series in nudge_optimizer_data.iterrows():
            t = self.convert_to_timeseries(series)
            self.model.load_model(self.params["model_save_folder"], self.params["prediction_config"], t)
            break

        policy = np.zeros((len(nudge_optimizer_data), len(self._dataspec.sampling_policy)))
        i = 0
        for idx, series in nudge_optimizer_data.iterrows():
            ts = self.convert_to_timeseries(series)
            reward_row =  []
            for nudge in range(len(self._dataspec.sampling_policy)):
                ts.data.loc[ts.data[self.model.dataspec.time_column]>=self.params["prediction_config"].context_window,"nudge"] = nudge + 1
                predictions = self.model.simple_predict(ts, self.params["prediction_config"])
                reward = (predictions.data.iloc[0,predictions.data.columns.get_loc("VariableName.sbp_horizon_5_predict")]
                        - predictions.data.iloc[self.params["prediction_config"].context_window-1,predictions.data.columns.get_loc("VariableName.sbp")])
                reward_row.append(reward)
            policy[i,reward_row.index(min(reward_row))] = 1
            i += 1
        return policy

    def set_params(self, params):
        self.params = params
        runner_config = parse_file_as(Runner, params["train_file_path"])
        params["training_config"] = runner_config.training_config
        params["prediction_config"] = runner_config.training_config
        params["dataspec"] = runner_config.dataset_spec
        params["model_save_folder"] = runner_config.training_config.model_save_folder
        self.model = get_forecasting_model(params["prediction_config"].model_class)(params["dataspec"])
        self.model.set_params(params["training_config"].model_parameters)


class CoordiNa:
    '''
    CoordiNa는 위경도 좌표체계의 상등조건을 추가하기 위해
    만든 것입니다.

    상등조건
    Xint와 Yint가 동일해야 합니다.

    '''
    EPSILON = pow(10, -7)
    INVSILON = int(pow(10, 7))
    INTEGERPART_FOR_X= 3
    INTEGERPART_FOR_Y = 2

    Xint = 0
    Yint = 0

    XYstring = ""
    def __init__(self, XYstr):
        xstr = XYstr[0:XYstr.index(',')]
        ystr = XYstr[XYstr.index(',') + 1:XYstr.__len__()]
        Xfloat = float (xstr)
        Yfloat =    float (ystr)
        self.Xint = int(Xfloat * self.INVSILON)
        self.Yint = int(Yfloat * self.INVSILON)
        self.adjustXYstring()

    def adjustXYstring(self):
        self.XYstring = (
                str(str(self.Xint)[0:self.INTEGERPART_FOR_X] + '.'
                + str(self.Xint)[3:]) + ','
                + str(self.Yint)[0:self.INTEGERPART_FOR_Y] + '.' +
                str( self.Yint)[2:])

if __name__ == '__main__':
    Coord = CoordiNa(" 126.8111197001, 37.4991234")
    print(str(Coord.Xint))
    print(str(Coord.Xint))
    Coord.adjustXYstring()
    print(Coord.XYstring)


from matplotlib.pyplot import axis
import drawBarPlot
from globalVars import DIR_PATH, FEATURE_NAMES, N_CHANNELS, STACKING_WIDTH
import featureSelectionProbe
from datasetManipulation import mergeDataset
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif
import time

def featureSelection(nFeatures, method, trainFeatures, testFeatures, trainLabels, probeName):
    # This function performs the feature selection and also saves a table with the selected features sorted by the score given to them.

    t0 = time.time()

    if method == 'f_classif':
        scoreFunction = f_classif
    else:
        scoreFunction = mutual_info_classif

    # Gather all train features and labels from all utterances to fit the selector
    allTrainFeatures, allTrainLabels = mergeDataset(trainFeatures,trainLabels)

    selector = SelectKBest(scoreFunction,k = nFeatures)
    selector = selector.fit(allTrainFeatures,allTrainLabels) # Select the features and reduce the features on the train dataset in consonance
    # Reduce the features of the train and the test set in consonance
    newTrainFeatures = []
    newTestFeatures = []
    for i in range(len(trainFeatures)):
        newTrainFeatures.append(selector.transform(trainFeatures[i]))
    for i in range(len(testFeatures)):
        newTestFeatures.append(selector.transform(testFeatures[i]))

    colNames = drawBarPlot.getColNames() # Obtain the name of all the features
    ranking = drawBarPlot.printRanking(selector.scores_,colNames,nFeatures,method) # Obtain the ranking of the selected features as a string

    t1 = time.time()

    # Write the ranking into a text file
    with open(f"{DIR_PATH}/results/{probeName}/{method}{nFeatures}Ranking.txt","w+") as file:
        file.write(ranking)

    print("Feature selection results:")
    print("Shape of train features before feature selection: ",np.shape(trainFeatures[0]))
    print("Shape of train features after feature selection: ",np.shape(newTrainFeatures[0])) # This is displayed to make sure that the dimensions of the features have changed
    print("Execution time",time.strftime('%H:%M:%S', time.gmtime(t1-t0)))
    print("\n")

    return newTrainFeatures, newTestFeatures, selector

def featureLDAReduction(nComponents, trainFeatures, testFeatures, trainLabels):
    # This function performons the LDA reduction of the features

    t0 = time.time()

    # Gather all train features and labels from all utterances to fit the selector
    allTrainFeatures, allTrainLabels = mergeDataset(trainFeatures,trainLabels)

    LDASelector = LinearDiscriminantAnalysis(n_components=nComponents)

    LDASelector = LDASelector.fit(allTrainFeatures,allTrainLabels) # Train the selector with all the training features
    # Reduce the dimensionality of the train and test features in consonance
    newTrainFeatures = []
    newTestFeatures = []
    for i in range(len(trainFeatures)):
        newTrainFeatures.append(LDASelector.transform(trainFeatures[i]))

    for i in range(len(testFeatures)):
        newTestFeatures.append(LDASelector.transform(testFeatures[i]))

    t1 = time.time()

    print("LDA results:")
    print("Shape of train features before LDA transform: ",np.shape(trainFeatures[0]))
    print("Shape of train features after LDA transform: ",np.shape(newTrainFeatures[0])) # This is displayed to make sure that the dimensions of the features have changed
    print("Execution time",time.strftime('%H:%M:%S', time.gmtime(t1-t0)))
    print("\n")

    return newTrainFeatures, newTestFeatures, LDASelector


import numpy as np
import torch
import itertools

from scipy.spatial.transform import Rotation as R

from networks import PoseCNN, PoseEncoderDecoder
from .base_model import BaseModel
from . import networks
from utils.image_pool import ImagePool
import torch.nn.functional as F
from utils import dataset_util
from .layers import BackprojectDepth, Project3D, transformation_from_parameters

class FSModel(BaseModel):
    def name(self):
        return 'FSModel'

    @staticmethod
    def modify_commandline_options(parser, is_train=True):

        parser.set_defaults(no_dropout=True)
        if is_train:
            parser.add_argument('--lambda_R_Depth', type=float, default=1.0, help='weight for reconstruction loss')
            parser.add_argument('--lambda_S_Depth', type=float, default=0.01, help='weight for smooth loss')

            parser.add_argument('--lambda_R_Pose', type=float, default=1.0, help='weight for reconstruction loss')
            parser.add_argument('--lambda_S_Pose', type=float, default=0.01, help='weight for smooth loss')

            parser.add_argument('--lambda_R_Img', type=float, default=1.0,help='weight for image reconstruction')

            parser.add_argument('--g_src_premodel', type=str, default=" ",help='pretrained G_Src model')

            parser.add_argument('--s_pose_premodel', type=str, default='/media/data1/results/pgasda',
                                help='models are saved here')
            parser.add_argument('--s_depth_premodel', type=str, default='/media/data1/results/pgasda', help='models are saved here')

        return parser

    def initialize(self, opt):
        BaseModel.initialize(self, opt)
        self.src_img ={}
        self.tgt_img = {}
        self.fake_src_img = {}
        self.fake_tgt_img = {}

        self.fake_tgt_gen = {}
        self.tgt_gen = {}

        self.src_gt = {}
        self.tgt_gt = {}

        if self.isTrain:
            self.loss_names = ['R_Depth_Src',  'R_Pose_Src', 'S_Depth_Tgt', 'R_Img_Tgt' ]

        if self.isTrain:
            self.visual_names = ['src_img', 'fake_tgt_img', 'src_gt', 'fake_tgt_gen', 'tgt_img', 'tgt_gen', 'warp_tgt_img']
        else:
            self.visual_names = ['pred', 'img']

        if self.isTrain:
            self.model_names = ['G_Depth_S', 'G_Pose_S', 'G_Src']

        else:
            self.model_names = ['G_Depth_S', 'G_Src']

        self.num_pose_frames = 2

        self.netG_Depth_S = networks.init_net(networks.UNetGenerator(norm='batch'), init_type='normal',
                                              gpu_ids=opt.gpu_ids)
        # self.netG_Pose_T = networks.init_net(PoseCNN(opt.num_frames), init_type='normal', gpu_ids=opt.gpu_ids)
        # self.netG_Pose_S = networks.init_net(PoseCNN(opt.num_frames), init_type='normal', gpu_ids=opt.gpu_ids)

        self.netG_Pose_T = networks.init_net(PoseEncoderDecoder(num_layers=opt.num_layers, pretrained=False, num_input_images=self.num_pose_frames, num_frames_to_predict_for=1, stride=1), init_type='normal', gpu_ids=opt.gpu_ids)
        self.netG_Pose_S = networks.init_net(PoseEncoderDecoder(num_layers=opt.num_layers, pretrained=False, num_input_images=self.num_pose_frames, num_frames_to_predict_for=1, stride=1), init_type='normal', gpu_ids=opt.gpu_ids)

        self.netG_Src = networks.init_net(networks.ResGenerator(norm='instance'), init_type='kaiming',gpu_ids=opt.gpu_ids)

        if self.isTrain:
            self.init_with_pretrained_model('G_Src', self.opt.g_src_premodel)
            # self.init_with_pretrained_model('G_Pose_S', self.opt.s_pose_premodel)
            # self.init_with_pretrained_model('G_Depth_S', self.opt.s_depth_premodel)

            self.netG_Src.eval()

        if self.isTrain:
            # define loss functions
            self.criterionDepthReg = torch.nn.L1Loss()
            self.criterionPoseReg = torch.nn.MSELoss()

            self.criterionSmooth = networks.SmoothLoss()
            self.criterionImgRecon = networks.ReconImgLoss()

            self.optimizer_G_task = torch.optim.Adam(itertools.chain(self.netG_Depth_S.parameters(), self.netG_Pose_S.parameters()),
                                                lr=opt.lr_task, betas=(0.9, 0.999))
            self.optimizers = []
            self.optimizers.append(self.optimizer_G_task)

    def set_input(self, src_data, tgt_data):

        if self.isTrain:
            self.src_img[('color', 0, -1)] = src_data[('color', 0, -1)].to(self.device)
            self.src_img[('color', -1, -1)] = src_data[('color', -1, -1)].to(self.device)
            self.src_img[('color', 1, -1)] = src_data[('color', 1, -1)].to(self.device)

            self.src_gt['depth'] = src_data['depth'].to(self.device)
            self.src_gt[("axisangle", 0, -1)] = src_data[("axisangle", 0, -1)].to(self.device)
            self.src_gt[("translation", 0, -1)] = src_data[("translation", 0, -1)].to(self.device)

            self.src_gt[("axisangle", 0, 1)] = src_data[("axisangle", 0, 1)].to(self.device)
            self.src_gt[("translation", 0, 1)] = src_data[("translation", 0, 1)].to(self.device)

            self.tgt_img[('color', -1, -1)] = tgt_data[('color', -1, -1)].to(self.device)
            self.tgt_img[('color', 0, -1)] = tgt_data[('color', 0, -1)].to(self.device)
            self.tgt_img[('color', 1, -1)] = tgt_data[('color', 1, -1)].to(self.device)

            if 'depth_gt' in tgt_data.keys():
                self.tgt_gen['depth_gt'] = tgt_data['depth_gt'].to(self.device)

            self.tgt_img_K = tgt_data['K'].to(self.device)
            self.tgt_img_inv_K = tgt_data['inv_K'].to(self.device)

            self.num = self.src_img[('color', 0, -1)].shape[0]

            b, _, h, w = tgt_data[('color', 0, -1)].shape

            self.backproject_depth = BackprojectDepth(b, h, w)
            self.backproject_depth.to(self.device)

            self.project_3d = Project3D(b, h, w)
            self.project_3d.to(self.device)

        # else:
        #     self.img = input['left_img'].to(self.device)

    def forward(self):

        if self.isTrain:
            self.fake_tgt_img[('color', -1, -1)] = self.netG_Src(self.src_img[('color', -1, -1)]).detach()
            self.fake_tgt_img[('color', 0, -1)] = self.netG_Src(self.src_img[('color', 0, -1)]).detach()
            self.fake_tgt_img[('color', 1, -1)] = self.netG_Src(self.src_img[('color', 1, -1)]).detach()

            self.out = self.netG_Depth_S(
                torch.cat((self.fake_tgt_img[('color', 0, -1)], self.tgt_img[('color', 0, -1)]), 0))
            # src image generated depth
            self.fake_tgt_gen['depth'] = self.out[-1].narrow(0, 0, self.num)
            # r2s image generate depth
            self.tgt_gen['depth'] = self.out[-1].narrow(0, self.num, self.num)

            for f_i in self.opt.frame_idxs[1:]:
                if f_i<0:
                    fake_tgt_pose_inputs = torch.cat([self.fake_tgt_img[('color', f_i, -1)], self.fake_tgt_img[('color', 0, -1)]], axis=1)
                    tgt_pose_inputs = torch.cat([self.tgt_img[('color', f_i, -1)], self.tgt_img[('color', 0, -1)]],axis=1)
                else:
                    fake_tgt_pose_inputs = torch.cat([self.fake_tgt_img[('color', 0, -1)], self.fake_tgt_img[('color', f_i, -1)]], axis=1)
                    tgt_pose_inputs = torch.cat([self.tgt_img[('color', 0, -1)], self.tgt_img[('color', f_i, -1)]],axis=1)
                # generate src pose
                self.fake_tgt_gen[('axisangle', 0, f_i)], self.fake_tgt_gen[('translation', 0, f_i)] = self.netG_Pose_S(fake_tgt_pose_inputs)
                # generate r2s pose
                self.tgt_gen[('axisangle', 0, f_i)], self.tgt_gen[('translation', 0, f_i)] = self.netG_Pose_T(tgt_pose_inputs)
                # generate cam_T_cam
                # self.tgt_gen[("cam_T_cam", 0, f_i)] = transformation_from_parameters(self.tgt_gen[("axisangle", 0, f_i)][:, 0], self.tgt_gen[("translation", 0, f_i)][:, 0])

        else:
            self.pred = self.netG_Depth_S(self.img)[-1]

    def backward_G(self):

        lambda_R_Depth = self.opt.lambda_R_Depth
        lambda_R_Img = self.opt.lambda_R_Img
        lambda_S_Depth = self.opt.lambda_S_Depth
        lambda_R_Pose = self.opt.lambda_R_Pose

        # Depth L1 loss for s2r image gen depth - src image depth gt
        self.loss_R_Depth_Src = 0.0
        real_depths = dataset_util.scale_pyramid(self.src_gt['depth'], 4)
        for (fake_tgt_gen_depth, real_depth) in zip(self.out, real_depths):
            self.loss_R_Depth_Src += self.criterionDepthReg(fake_tgt_gen_depth[:self.num,:,:,:], real_depth) * lambda_R_Depth

        # Pose L2 loss for s2r image gen depth - src image depth gt
        self.loss_R_Pose_Src = 0.0
        for f_i in self.opt.frame_idxs[1:]:
            fake_tgt_gen_q = []
            for ind, i in enumerate(self.tgt_gen[("axisangle", 0, f_i)].squeeze()):
                row = R.from_rotvec(i.cpu().detach().numpy()).as_quat()
                fake_tgt_gen_q.append(row)
            fake_tgt_gen_q = torch.from_numpy(np.array(fake_tgt_gen_q)).to(self.device)
            fake_tgt_gen_t = self.tgt_gen[("translation", 0, f_i)].view([-1, 3])
            self.src_gt[("translation", 0, f_i)].requires_grad = True
            # if torch.from_numpy(k).shape == self.src_gt[('axisangle', 0, f_i)].shape and ts.shape == self.src_gt[('translation', 0, f_i)].shape:
            self.loss_R_Pose_Src += lambda_R_Pose * (self.criterionPoseReg(fake_tgt_gen_q,self.src_gt[("axisangle", 0, f_i)]) +
                self.criterionPoseReg(fake_tgt_gen_t, self.src_gt[("translation", 0, f_i)]))

        # Recon loss
        # tgt image image depthmap, t-1 img , pose 정보 활용해서  -> t img로 wrap
        self.loss_R_Img_Tgt, self.warp_tgt_img = self.criterionImgRecon(self.backproject_depth, self.project_3d,self.tgt_img, self.tgt_gen, self.tgt_img_K,self.tgt_img_inv_K, self.device)

        # Depth Smoothness loss for s2r image gen depth - tgt_img[('color', 0, -1)]
        tgt_imgs = dataset_util.scale_pyramid(self.tgt_img[('color', 0, -1)], 4)
        i = 0
        self.loss_S_Depth_Tgt = 0.0
        for (gen_depth, img) in zip(self.out, tgt_imgs):
            self.loss_S_Depth_Tgt += self.criterionSmooth(gen_depth[self.num:, :, :, :],img) * lambda_S_Depth / 2 ** i
            i += 1

        self.loss_G_Src = lambda_R_Img*self.loss_R_Img_Tgt + self.loss_S_Depth_Tgt + self.loss_R_Depth_Src+ self.loss_R_Pose_Src
        self.loss_G_Src = self.loss_G_Src.type(torch.DoubleTensor)
        self.loss_G_Src.backward()

    def optimize_parameters(self):

        self.forward()
        self.optimizer_G_task.zero_grad()
        self.backward_G()
        self.optimizer_G_task.step()


# !/usr/bin/python
# -*- coding: utf-8 -*-
# @time    : 2020/5/7 21:06
# @author  : Mo
# @function: TextRCNN  [Recurrent Convolutional Neural Networks for TextClassiﬁcation](http://www.nlpr.ia.ac.cn/cip/~liukang/liukangPageFile/Recurrent%20Convolutional%20Neural%20Networks%20for%20Text%20Classification.pdf)

from macadam.base.graph import graph
from macadam import keras, K, L, M, O

class RCNNGraph(graph):
    def __init__(self, hyper_parameters):
        self.rnn_layer_repeat = hyper_parameters.get("graph", {}).get("rnn_layer_repeat", 2)
        super().__init__(hyper_parameters)

    def build_model(self, inputs, outputs):
        # rnn type, RNN的类型
        if self.rnn_type == "LSTM":
            layer_cell = L.LSTM
        else:
            layer_cell = L.GRU
        # backword, 反向
        x_backwords = layer_cell(units=self.rnn_unit,
                                 return_sequences=True,
                                 kernel_regularizer=keras.regularizers.l2(0.32 * 0.1),
                                 recurrent_regularizer=keras.regularizers.l2(0.32),
                                 go_backwards=True)(outputs)
        x_backwords_reverse = L.Lambda(lambda x: K.reverse(x, axes=1))(x_backwords)
        # fordword, 前向
        x_fordwords = layer_cell(units=self.rnn_unit,
                                 return_sequences=True,
                                 kernel_regularizer=keras.regularizers.l2(0.32 * 0.1),
                                 recurrent_regularizer=keras.regularizers.l2(0.32),
                                 go_backwards=False)(outputs)
        # concatenate, 拼接
        x_feb = L.Concatenate(axis=2)([x_fordwords, outputs, x_backwords_reverse])
        # dropout, 随机失活
        x_feb = L.Dropout(self.dropout)(x_feb)
        # Concatenate, 拼接后的embedding_size
        dim_2 = K.int_shape(x_feb)[2]
        x_feb_reshape = L.Reshape((self.length_max, dim_2, 1))(x_feb)
        # n-gram, conv, maxpool, 使用n-gram进行卷积和池化
        conv_pools = []
        for filter in self.filters_size:
            conv = L.Conv2D(filters=self.filters_num,
                            kernel_size=(filter, dim_2),
                            padding='valid',
                            kernel_initializer='normal',
                            activation='relu',
                            )(x_feb_reshape)
            pooled = L.MaxPooling2D(pool_size=(self.length_max - filter + 1, 1),
                                    strides=(1, 1),
                                    padding='valid',
                                    )(conv)
            conv_pools.append(pooled)
        # concatenate, 拼接TextCNN
        x = L.Concatenate()(conv_pools)
        x = L.Dropout(self.dropout)(x)
        # dense-mid, 中间全连接到中间的隐藏元
        x = L.Flatten()(x)
        x = L.Dense(units=min(max(self.label, 64), self.embed_size), activation=self.activate_mid)(x)
        x = L.Dropout(self.dropout)(x)
        # dense-end, 最后一层, dense到label
        self.outputs = L.Dense(units=self.label, activation=self.activate_end)(x)
        self.model = M.Model(inputs=inputs, outputs=self.outputs)
        self.model.summary(132)


from pprint import pprint

from typing import Dict

import pandas as pd

from utils import *
from nltk.corpus import framenet as fn
from nltk.corpus import wordnet as wn

def get_frame_set_elements(frame_set: Dict[int, str]):
    """
    Funzione per la raccolta di Name, FEs, LUs di ogni frame del frame set

    :param frame_set: il frame set con i frame ed i loro ID

    :return: due dizionari con gli elementi (frame_words) e le loro definizioni (frame_words_def)
    """
    frame_words = {}
    frame_words_def = {}
    for id in frame_set.keys():
        f = fn.frame_by_id(id)
        frame_words[id] = {'NAME': frame_set[id], 'FE': list(f.FE.keys()), 'LU': list(f.lexUnit.keys())}
        frame_words_def[id] = {'NAME DEF': f.definition, 'FE DEFS': [f.FE[fe].definition for fe in f.FE.keys()],
                               'LU DEFS': [f.lexUnit[lu].definition for lu in f.lexUnit.keys()]}
    return frame_words, frame_words_def

def get_ctx(s):
    """
    Prende gli iperonimi e gli iponimi del synset s e ritorna una lista con tutti i termini delle loro definizioni e dei loro esempi

    :param s: il synset di WN

    :return: lista di termini che fungono da contesto per s
    """
    ctx_s = []
    for hype in s.hypernyms():
        ctx_s += hype.definition().split()
        [ctx_s.extend(ex.split()) for ex in hype.examples()]
    for hypo in s.hyponyms():
        ctx_s += hypo.definition().split()
        [ctx_s.extend(ex.split()) for ex in hypo.examples()]
    return ctx_s

def find_sense(word, ctx_w, pos:str=None):
    """
    Trova il senso in WN per la parola word tramite un approccio bag of words.

    :param word: il termine da disambiguare
    :param ctx_w: il contesto del termine word composto dalle definizioni del suo frame in FrameNet
    :param pos: l'eventuale POS del termine (usato solo per le lexical units)

    :return: il senso più adatto per il termine word
    """
    multiwords = {'Particular_iteration': 'Iteration',
                  'Part_1': 'Part',
                  'Part_2': 'Part',
                  'band together': 'band',
                  'come together': 'come',
                  'Aggregate_property': 'Property',
                  'Source_symbol': 'Symbol',
                  'Source_representation': 'Representation',
                  'Target_symbol': 'Symbol',
                  'Target_representation': 'Representation',
                  'Hidden_object': 'Object',
                  'Potential_observer': 'Observer'
                  }
    if word in multiwords.keys():
        # in caso di multiwords usiamo un dizionario per ricondurle al termine reggente
        word = multiwords[word]
    max = 0
    best_sense = None
    for s in wn.synsets(word, pos=pos):
        ctx_s = s.definition().lower().split()
        [ctx_s.extend(ex.lower().split()) for ex in s.examples()]
        ctx_s += get_ctx(s)
        ctx_s = set(ctx_s)
        score = len(ctx_s.intersection(ctx_w)) + 1
        if score > max:
            max = score
            best_sense = s
    return best_sense

# getFrameSetForStudent('pettinato')
# getFrameSetForStudent('Donini')

# frame set costruito con i frame ottenuti da getFrameSetForStudent(), la chiave è il frameID e il valore è il nome del frame eventualmente disambiguato
# i termini sono già disambiguati ed alcuni frame non presenti su WordNet sono stati sostituiti
frame_set = {
    2021: 'Conquering',
    2010: 'Hunting',
    550: 'Claim',
    2481: 'Erasing',
    2566: 'Trajectory',
    790: 'Amalgamation',
    2597: 'Convoy',
    1655: 'Be',
    2916: 'Abundance',
    1673: 'Hiding'
}

# build_corpus_gold(frame_set, 2021)

# prendiamo gli elementi ed i loro contesti di disambiguazione da FrameNet
frame_words, frame_words_def = get_frame_set_elements(frame_set)
# pprint(frame_words)
# pprint(frame_words_def)

# con gli elementi trovati estraiamo i sensi da WN
words = []
senses = []
for id in frame_set.keys():
    elems = frame_words[id]

    # disambiguiamo il nome del frame
    w = elems['NAME']
    ctx_w = frame_words_def[id]['NAME DEF'].lower().split()
    words.append(w)
    senses.append(find_sense(w, ctx_w).name())

    # disambiguiamo i FEs del frame
    for w, ctx_w in zip(elems['FE'], frame_words_def[id]['FE DEFS']):
        ctx_w = ctx_w.lower().split()
        words.append(w)
        senses.append(find_sense(w, ctx_w).name())

    # disambiguiamo le lexical units del frame
    for w, ctx_w in zip(elems['LU'], frame_words_def[id]['LU DEFS']):
        word, pos = w.split('.')
        ctx_w = ctx_w.lower().split()
        words.append(w)
        senses.append(find_sense(word, ctx_w, pos).name())

df = pd.DataFrame({'word': words,
                   'sense': senses})
df_gold = pd.read_csv('corpus gold/gold_corpus.csv', names=['word', 'gold sense'])
df['gold sense'] = df_gold['gold sense']

# stampiamo i risultati
print(df.to_string())

# calcoliamo l'accuracy
accuracy = (len(df[df['sense'] == df['gold sense']]), len(df))
print(f'\n\nAccuracy: {accuracy[0]}/{accuracy[1]}')

'''
############################
##         NOTE           ##
############################

L'annotazione manuale è avvenuta utilizzando l'output della funzione build_corpus_gold(), che ritorna i possibili synset
di WN ed una descrizione degli elementi del frame (Name, FEs, LUs). Usando la descrizione, è stato scelto il synset più
adatto.

In caso di multiwords usiamo una mappa per ricondurle al termine corretto, un approccio più generale potrebbe essere
quello di usare un parser e delle regole per scegliere fra le varie parole.

Nei risultati notiamo che il programma funziona bene per le LU (il POS tag aiuta molto).
'''


import re
# regex = re.compile(r'\+\*\?')
# mo = regex.search('I learned about +*? regex syntax')
# print(mo.group())
# Here we are literally searching for the characters +*? in the string and we are escaping the special characters by using a backslash\

# Escaping and printing one or more occurrences of the same character
regex = re.compile(r'(\+\*\?)+')
mo = regex.search("I learned about +*?+*?+*? regex syntax")
# print(mo.group())
# prints +*?+*?+*?
print(mo.group(1))
# prints +*?


import pred_soh
import matplotlib.pyplot as plt
import pandas as pd
soh_seq=(pd.read_csv(r'C:\Users\ycc\Desktop\SOH\car7_LSTM.csv')).values

data_y = soh_seq[1:,0]
a = soh_seq[:,1]#读取第二列
b = list(a)
train_size = 173

fig,ax= plt.subplots(figsize=(4,4),facecolor='w',dpi=600)
ax.set_ylim(0,1.2)
plt.scatter(b[-3], pred_y[train_size + 63], marker='*', s=100, zorder=30)
print('after 180days:', pred_y[train_size + 63])
plt.plot(b[train_size-3:-2], pred_y[train_size-3:], 'r', label='pred', linewidth=1.5, zorder=20)
plt.plot(b[:train_size-1],data_y[:train_size-1], 'b', label='real', alpha=0.6, linewidth=1.5)
# plt.plot([train_size, train_size], [data_y[train_size-1]-0.008, data_y[train_size-1]+0.008], linestyle='--', color='k', label='train | pred')
plt.title('Car_{}'.format('7'), fontsize=12)
plt.xticks(fontsize=8)
plt.yticks(fontsize=8)
plt.xlabel('Timestamp', fontsize=10)
plt.ylabel('RUL', fontsize=10)
plt.legend(loc='best')
 # plt.savefig('lstm_reg.png')


import pandas as pd
import csv
from pandas import read_csv
from matplotlib import pyplot
import numpy as np
from statsmodels.tsa.ar_model import AR
from statsmodels.tsa.arima_model import ARIMA
from statsmodels.tsa.arima_model import ARMA
from matplotlib import style
from matplotlib import pyplot as plt

matplotlib.rcParams.update({'font.size': 10})
matplotlib.rc('xtick', labelsize=16)
matplotlib.rc('ytick', labelsize=16)

from sklearn.metrics import mean_squared_error
local_path='D:\\3rd_year\\Objective1\\pythonscript\\'
#data_xls = pd.read_excel(local_path+'AirQualityUCI2.xlsx', 'AirQualityUCI', index_col=None)
#data_xls.to_csv(local_path+'tempfile1.csv', encoding='utf-8')

print("hhhh")
series = pd.read_csv(local_path+'tempfile1.csv',encoding='utf-8', header=0, index_col=0)
X = series['CO(GT)']
#print(X)
"""with open(local_path+'tempfile2.csv', 'w', newline='') as file:
     writer = csv.writer(file)
     writer.writerow(["'Data'","'Value'"])
     j = 0
     for i in X:
        writer.writerow(["'str(value)'",X[j]])
        j=j+1
"""

series = pd.read_csv(local_path+'tempfile1.csv',encoding='utf-8', header=0, index_col=0)
X = series.values
print(X)
print (series['CO(GT)'])

#Missing values or outliers are found out and replaced with the medians

print (series['CO(GT)'].isnull())
missing_values=["n/a","na","-200"]
series = read_csv("tempfile1.csv", na_values=missing_values)
print (series['CO(GT)'].isnull())
median = series['CO(GT)'].median()
print("median:"," ", median)
series['CO(GT)'].fillna(median, inplace=True)
print(series['CO(GT)'])
X = series.values
print('Here')
print(X)
y = series['CO(GT)']
print("hellllo")

my_list = []

for i in range(len(y)):
    my_list.append(y[i])

#print(my_list)

train, test = my_list[1:len(X)-100], my_list[len(X)-100:]
test1= test
k=1
# using list comprehension
# adding K to each element
test2 = [x + k for x in test1]

#print(train)
#6
#25

# train autoregression
### AR Model#####
model1 = AR(train)
model_fit1 = model1.fit()
print('Lag: %s' % model_fit1.k_ar)
print('Coefficients: %s' % model_fit1.params)
# make predictions
predictions1 = model_fit1.predict(start=len(train), end=len(train)+len(test2)-1, dynamic=False)
for i in range(len(predictions1)):
	print('predicted=%f, expected=%f' % (predictions1[i], test2[i]))
error1 = mean_squared_error(test2, predictions1)
print('Test MSE in AR: %.3f' % error1)

# ARIMA model
# train autoregression
model2 = ARIMA(train, order=(10,1,1))
model_fit2 = model2.fit()
print('Lag: %s' % model_fit2.k_ar)
print('Coefficients: %s' % model_fit2.params)
# make predictions
predictions2 = model_fit2.predict(start=len(train), end=len(train)+len(test2)-1, dynamic=False)
for i in range(len(predictions2)):
	print('predicted=%f, expected=%f' % (predictions2[i], test2[i]))
error2 = mean_squared_error(test2, predictions2)
print('Test MSE ARIMA: %.3f' % error2)

#MA model
# train autoregression
model3 = ARMA(train,order=(0,1))
model_fit3 = model3.fit()
print('Lag: %s' % model_fit3.k_ar)
print('Coefficients: %s' % model_fit3.params)
# make predictions
predictions3 = model_fit3.predict(start=len(train), end=len(train)+len(test2)-1, dynamic=False)
for i in range(len(predictions3)):
	print('predicted=%f, expected=%f' % (predictions3[i], test2[i]))
error3 = mean_squared_error(test2, predictions3)
print('Test MSE in MA: %.3f' % error3)
# plot results
#pyplot.plot(test)

#ARMA model
# train autoregression
#for k in range(5)
model4 = ARMA(train, order=(2,1))
model_fit4 = model4.fit()
print('Lag: %s' % model_fit4.k_ar)
print('Coefficients: %s' % model_fit4.params)
# make predictions
predictions4 = model_fit4.predict(start=len(train), end=len(train)+len(test2)-1, dynamic=False)
for i in range(len(predictions4)):
	print('predicted=%f, expected=%f' % (predictions4[i], test2[i]))
error4 = mean_squared_error(test2, predictions4)
print('Test MSE in ARMA: %.3f' % error4)
# plot results
pyplot.grid()
pyplot.plot(test2, linestyle='--', label ='Expected Value', linewidth= 2)
pyplot.plot(predictions1 ,color='red',marker="o", label='AR Model', linewidth= 2)
pyplot.plot(predictions2, color='green', marker="^", label='ARIMA Model', linewidth= 2)
pyplot.plot(predictions3, color='blue', marker="+", label='MA Model', linewidth= 2)
pyplot.plot(predictions4, color='black', marker="v", label='ARMA Model', linewidth= 2)
pyplot.ylabel('CO Index')
pyplot.xlabel('Observations')
pyplot.ylim(-2,6)
pyplot.xlim(0,100)
pyplot.legend()

#xlim(right=10)
#xlim(left=1)
#yplot.show()
pyplot.savefig("out13.png")


import argparse
from functools import partial
import logging
import os
import json

from tvm import meta_schedule as ms
from tvm.target import Target

from tvm.meta_schedule import postproc
from tvm.meta_schedule import schedule_rule as M
from tvm.meta_schedule.tune import TuneConfig

logging.basicConfig(
    format="%(asctime)s.%(msecs)03d %(levelname)s %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logging.getLogger("tvm.meta_schedule").setLevel(logging.INFO)

def parse_args(workload_candidates, default_trials=20000):
    args = argparse.ArgumentParser()
    args.add_argument(
        "-w",
        "--workload",
        nargs="+",
        type=str,
        choices=workload_candidates,
        required=True,
    )
    args.add_argument("-bs", "--batch-size", nargs="+", type=int, default=[1])
    args.add_argument("-t", "--target", type=str)
    args.add_argument("-n", "--num-trials", type=int, default=default_trials)
    args.add_argument("--work-dir", type=str)
    use_rpc = args.add_mutually_exclusive_group()
    use_rpc.add_argument("--local", action="store_false", dest="use_rpc", default=False)
    use_rpc.add_argument("--rpc", action="store_true", dest="use_rpc")
    args.add_argument("--rpc-host", type=str)
    args.add_argument("--rpc-port", type=int)
    args.add_argument("--rpc-key", type=str)
    args.add_argument("--workers", type=int)
    args.add_argument("--alloc-repeat", type=int, default=1)
    args.add_argument("--out-dtype", type=str, default="float16")

    parsed = args.parse_args()
    parsed.target = parsed.target or os.environ.get("TVM_TARGET")
    parsed.target = Target(parsed.target)
    parsed.work_dir = parsed.work_dir or f"logs/"
    if parsed.use_rpc:
        rpc_host = parsed.rpc_host or os.environ.get("TVM_RPC_HOST")
        rpc_port = parsed.rpc_port or int(os.environ.get("TVM_RPC_PORT"))
        rpc_key = parsed.rpc_key or os.environ.get("TVM_RPC_KEY")
        rpc_config = ms.runner.RPCConfig(
            tracker_host=rpc_host,
            tracker_port=rpc_port,
            tracker_key=rpc_key,
            session_timeout_sec=60,
        )
        workers = parsed.workers or rpc_config.count_num_servers(allow_missing=False)
        parsed.runner = partial(
            ms.runner.RPCRunner, rpc_config=rpc_config, max_workers=workers
        )
    else:
        parsed.runner = ms.runner.LocalRunner
    parsed.runner = parsed.runner(
        evaluator_config=ms.runner.EvaluatorConfig(
            number=3,
            repeat=1,
            min_repeat_ms=100,
            enable_cpu_cache_flush=False,
        )
    )
    return parsed

def load_config():
    cur_path = os.path.dirname(__file__)
    config_path = os.path.join(cur_path, "configs")
    with open(config_path) as f:
        return json.load(f)

def sch_rules_tensor_core():
    return [
        M.MultiLevelTiling(
            structure="SSSRRSRS",
            tile_binds=["blockIdx.y", "blockIdx.x", "threadIdx.y"],
            use_tensor_core=True,
            max_innermost_factor=4,
            vector_load_lens=[1, 2, 4, 8],
            reuse_read=M.ReuseType(
                req="must",
                levels=[4],
                scope="shared.dyn",
            ),
            reuse_write=M.ReuseType(
                req="no",
                levels=[3],
                scope="shared.dyn",
            ),
        ),
        M.AutoInline(
            into_producer=True,
            into_consumer=True,
            inline_const_tensor=True,
            disallow_if_then_else=False,
            require_injective=False,
            require_ordered=False,
            disallow_op=None,
        ),
        M.AutoInline(
            into_producer=True,
            into_consumer=True,
            inline_const_tensor=True,
            disallow_if_then_else=False,
            require_injective=False,
            require_ordered=False,
            disallow_op=None,
        ),
        M.CrossThreadReduction(thread_extents=[4, 8, 16, 32, 64, 128, 256, 512]),
        M.ParallelizeVectorizeUnroll(
            max_jobs_per_core=-1,  # disable parallelize
            max_vectorize_extent=-1,  # disable vectorize
            unroll_max_steps=[0, 16, 64, 512, 1024],
            unroll_explicit=True,
        ),
    ]

def postprocs_tensor_core():
    return [
        postproc.RewriteCooperativeFetch(),
        postproc.RewriteUnboundBlock(),
        postproc.RewriteParallelVectorizeUnroll(),
        postproc.RewriteReductionBlock(),
        postproc.RewriteTensorCore(),
        postproc.VerifyGPUCode(),
    ]

def get_search_config(n_trails, trails_per_task=2000):
    return TuneConfig(
        num_trials_per_iter=64,
        max_trials_per_task=trails_per_task,
        max_trials_global=n_trails,
        search_strategy_config={
            "population_size": 2048,
            "init_measured_ratio": 0.2,
            "init_min_unmeasured": 50,
            "genetic_num_iters": 3,
            "genetic_mutate_prob": 0.85,
            "genetic_max_fail_count": 10,
            "eps_greedy": 0.05,
        },
    )


from time import sleep

def titulo(msg):
    tam = len(msg) + 4
    print('=' * tam)
    print(f'  {msg}  ')
    print('=' * tam)
    sleep(1)
    cor('limpa')

def cor(cod):
    if cod == 'verde':
        print('\033[0;30;42m', end='')
    elif cod == 'limpa':
        print('\033[m', end='')
    elif cod == 'azul':
        print('\033[0;30;44m', end='')
    elif cod == 'branco':
        print('\033[7;30m', end='')
    elif cod == 'verm':
        print('\033[0;30;41m', end='')

def doc(sos):
    cor('azul')
    titulo(f"Acessando o manual do comando '{ajuda}'")
    cor('branco')
    help(sos)
    sleep(1)

while True:
    cor('verde')
    titulo('SISTEMA DE AJUDA PyHELP')
    ajuda = str(input('Função ou Biblioteca > ')).lower().strip()
    if ajuda == 'fim':
        break
    doc(ajuda)
cor('verm')
titulo('ATÉ LOGO')


testcase_count = int(input())

for _ in range(testcase_count):
    left_side, right_side = list(map(int, input().split()))

    # 2차원 dp 사용
    # dp = [[0 for _ in range(right_side + 1)] for _ in range(left_side + 1)]
    #
    # # left_side, right_side가 동일한 부분을 1로 채움
    # for left_idx in range(1, left_side + 1):
    #     dp[left_idx][left_idx] = 1
    #
    # # left_side가 1인 부분들은 right_idx 값으로 각각 채움
    # # 해당 부분은 좌측 다리 포인트가 1이므로 우측 다리 포인트 개수에 따라
    # # 다리를 놓을 수 있는 경우의 수가 주어짐
    # for right_idx in range(right_side + 1):
    #     dp[1][right_idx] = right_idx
    #
    # for left_idx in range(2, left_side + 1):  # left_idx를 2 이상인 부분 계산 시작
    #     for right_idx in range(left_idx + 1, right_side + 1):
    #         # right_idx는 left_idx보다 1 큰 위치부터 시작
    #         # 왜냐하면, left_idx, right_idx가 동일한 위치는 이미 1로
    #         # 채워져 있기 때문에 수정할 필요가 없음
    #
    #         # 다리 포인트가 좌측은 left_idx개, 우측은 right_idx개인 경우 놓을 수 있는 다리의 수는
    #         # 좌측 다리 포인트가 1 작을 때, 우측 다리 포인트가 1부터 right_idx인 경우의 수의 합과 동일
    #         dp[left_idx][right_idx] = sum([dp[left_idx - 1][val] for val in range(1, right_idx)])
    #
    # print(dp[left_side][right_side])

    # 1차원 dp 이용
    # m! / (n! * (m-n)!)
    dp = [0] * (right_side + 1)
    dp[0] = 1

    for right_idx in range(1, right_side + 1):
        for left_idx in range(min(right_idx, left_side), 0, -1):
            dp[left_idx] += dp[left_idx - 1]

    print(dp[left_side])


# Time: 2023/4/21 1:20

def kaisa_decode(c:str,k:int):
    result = ''
    for word in c:
        if word.isalpha():
            if word.isupper():
                tword = chr((ord(word)-ord('A') - k) % 26 + ord('A'))
            else:
                tword = chr((ord(word)-ord('a') - k) % 26 + ord('a'))
        else:
            tword = word
        result += tword
    return result

def main():
    c = input("输入密文: ")
    k = int(input("输入密钥: "))
    decrypted_message = kaisa_decode(c, k)
    print("明文: ", decrypted_message)

if __name__ == "__main__":
    main()

凯撒密码/凯撒密码解密.py


import numpy as np
import cv2
import os

image_extensions = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.tiff', '.pbm', '.pgm', '.ppm']

def splitfn(fn):
    path, fn = os.path.split(fn)
    name, ext = os.path.splitext(fn)
    return path, name, ext

def anorm2(a):
    return (a*a).sum(-1)
def anorm(a):
    return np.sqrt( anorm2(a) )

def homotrans(H, x, y):
    xs = H[0, 0]*x + H[0, 1]*y + H[0, 2]
    ys = H[1, 0]*x + H[1, 1]*y + H[1, 2]
    s  = H[2, 0]*x + H[2, 1]*y + H[2, 2]
    return xs/s, ys/s

def to_rect(a):
    a = np.ravel(a)
    if len(a) == 2:
        a = (0, 0, a[0], a[1])
    return np.array(a, np.float64).reshape(2, 2)

def rect2rect_mtx(src, dst):
    src, dst = to_rect(src), to_rect(dst)
    cx, cy = (dst[1] - dst[0]) / (src[1] - src[0])
    tx, ty = dst[0] - src[0] * (cx, cy)
    M = np.float64([[ cx,  0, tx],
                    [  0, cy, ty],
                    [  0,  0,  1]])
    return M

def lookat(eye, target, up = (0, 0, 1)):
    fwd = np.asarray(target, np.float64) - eye
    fwd /= anorm(fwd)
    right = np.cross(fwd, up)
    right /= anorm(right)
    down = np.cross(fwd, right)
    R = np.float64([right, down, fwd])
    tvec = -np.dot(R, eye)
    return R, tvec

def mtx2rvec(R):
    w, u, vt = cv2.SVDecomp(R - np.eye(3))
    p = vt[0] + u[:,0]*w[0]    # same as np.dot(R, vt[0])
    c = np.dot(vt[0], p)
    s = np.dot(vt[1], p)
    axis = np.cross(vt[0], vt[1])
    return axis * np.arctan2(s, c)