from scrapy.spider import BaseSpider
from scrapy.http import Request
from scrapy.selector import HtmlXPathSelector
from dateutil.parser import parse
from datetime import date
from carib_stockex_scrapers.items import (
    MarketSummaryItem, TickerItem, CapValueItem, MarketCapValueItem)
from dateutil import rrule
from dateutil.rrule import MO, TU, WE, TH, FR

def clean_str(instr):
    s = instr.strip()
    s = s.replace(u'\xa0', u'')
    return s.replace(",", "").replace('$', "")

def bdate_range(start, end):
    return rrule.rrule(
        rrule.DAILY,
        byweekday=(MO, TU, WE, TH, FR),
        dtstart=start,
        until=end
    )

class TTSESpider(BaseSpider):
    name = 'ttse_equity_data'
    allowed_domains = ["http://www.stockex.co.tt"]

    def __init__(self, start_date=None, end_date=None):
        #super(TTSESpider, self).__init__(name, **kwargs)
        #import ipdb; ipdb.set_trace()
        if start_date:
            self.start = parse(start_date)
        else:
            self.start = date.today()
        if end_date:
            self.end = parse(end_date)
        else:
            self.end = None

    def start_requests(self):
        domain = "http://www.stockex.co.tt"
        controller = "controller.php?action"
        if self.end:
            for dd in bdate_range(start=self.start, end=self.end):
                url = "%s/%s=view_quote&TradingDate=%s" % (
                    domain, controller, dd.strftime("%m/%d/%Y"))
                yield Request(url, self.parse_equity_summary)

                url = "%s/%s=view_daily_index_summary&TradingDate=%s" % (
                    domain, controller, dd.strftime("%m/%d/%Y"))
                yield Request(url, self.parse_index_summary)
        else:
            url = "%s/%s=view_quote&TradingDate=%s" % (
                domain, controller, self.start.strftime("%m/%d/%Y")
            )
            yield Request(url, self.parse_equity_summary)
            url = "%s/%s=view_daily_index_summary&TradingDate=%s" % (
                domain, controller, self.start.strftime("%m/%d/%Y")
            )
            yield Request(url, self.parse_index_summary)

    def parse_index_summary(self, response):
        hxs = HtmlXPathSelector(response)
        date_tab = hxs.select('//table')[1]
        dateix = parse(
            " ".join(
            date_tab.select(
            'tr/td/p/text()').extract()[0].split(',')[1:]))

        self.log("Spider Index Summary for %s" % dateix.strftime(
            "%Y-%m-%d")
        )
        data_rows = hxs.select('//table')[2].select('tr')
        for r in range(1, len(data_rows)):
            try:
                ticker = data_rows[r].select('td')[0].select(
                    'p/a').select(
                        'text()'
                    ).extract()[0].strip()
                issued_capital = clean_str(data_rows[r].select('td')[1].select(
                    'p/text()').extract()[0])
                captial_value = clean_str(data_rows[r].select('td')[2].select(
                    'p/text()').extract()[0])
                trade_count = clean_str(data_rows[r].select('td')[3].select(
                    'p/text()').extract()[0])
                traded_value = clean_str(data_rows[r].select('td')[5].select(
                    'p/text()').extract()[0])
                if traded_value:
                    yield CapValueItem(
                        exchange='TTSE',
                        dateix=dateix.strftime("%Y-%m-%d"),
                        ticker=ticker,
                        issued_capital=issued_capital,
                        capital_value=captial_value,
                        trade_count=trade_count,
                        traded_value=traded_value)
            except IndexError:
                tick_data = data_rows[r].select(
                    'td')[0].select('p/b/text()').extract()

                if 'Composite Totals' in tick_data:
                    ticker = 'market_overview'
                    capital_value = clean_str(
                        data_rows[r].select('td')[1].select(
                            'p/text()').extract()[0])
                    trade_count = clean_str(
                        data_rows[r].select('td')[2].select(
                            'p/text()').extract()[0])
                    traded_value = clean_str(
                        data_rows[r].select('td')[4].select(
                            'p/text()').extract()[0])
                    yield MarketCapValueItem(
                        exchange='TTSE',
                        dateix=dateix.strftime('%Y-%m-%d'),
                        capital_value=capital_value,
                        trade_count=trade_count,
                        traded_value=traded_value)
                else:
                    continue

    def parse_equity_summary(self, response):
        hxs = HtmlXPathSelector(response)
        #from scrapy.shell import inspect_response
        #inspect_response(response)
        date_tab = hxs.select('//table')[1]
        dateix = parse(
            " ".join(
            date_tab.select(
            'tr/td/p/text()').extract()[0].split(',')[1:]))
        self.log("Spider Equity Summary for %s" % dateix.strftime(
            "%Y-%m-%d")
        )
        sum_tab = hxs.select('//table')[3]
        composite_ix = clean_str(
            sum_tab.select('tr')[1].select('td/p/text()')[1].extract())
        total_volume = clean_str(
            sum_tab.select('tr')[1].select('td/p/text()')[4].extract())
        total_value = clean_str(
            sum_tab.select('tr')[1].select('td/p/text()')[5].extract())
        num_trades = clean_str(
            sum_tab.select('tr')[1].select('td/p/text()')[6].extract())

        alltt_ix = clean_str(
            sum_tab.select('tr')[2].select('td/p/text()')[1].extract())
        tt_volume = clean_str(
            sum_tab.select('tr')[2].select('td/p/text()')[4].extract())
        tt_value = clean_str(
            sum_tab.select('tr')[2].select('td/p/text()')[5].extract())
        tt_trades = clean_str(
            sum_tab.select('tr')[2].select('td/p/text()')[6].extract())

        cross_ix = clean_str(
            sum_tab.select('tr')[3].select('td/p/text()')[1].extract())
        cross_volume = clean_str(
            sum_tab.select('tr')[3].select('td/p/text()')[4].extract())
        cross_value = clean_str(
            sum_tab.select('tr')[3].select('td/p/text()')[5].extract())
        cross_trades = clean_str(
            sum_tab.select('tr')[3].select('td/p/text()')[6].extract())
        yield MarketSummaryItem(
            exchange='TTSE',
            dateix=dateix.strftime("%Y-%m-%d"),
            composite_ix=composite_ix,
            total_market_volume=total_volume,
            total_market_value=total_value,
            total_trades=num_trades,
            alltt_ix=alltt_ix,
            tt_volume=tt_volume,
            tt_value=tt_value,
            tt_trades=tt_trades,
            cross_ix=cross_ix,
            cross_volume=cross_volume,
            cross_value=cross_value,
            cross_trades=cross_trades
        )
        log_str = "%s composite_ix:%s volume:%s value %s num_trades %s"
        self.log(
            log_str % (
                dateix, composite_ix, total_volume,
                total_value, num_trades)
        )
        log_str = "%s ticker:%s open:%s high %s low %s close %s volume: %s"
        for t in [4, 5, 6]:
            ord_rows = hxs.select('//table')[t].select('tr')
            for r in range(2, len(ord_rows)):
                ticker = ord_rows[r].select('td')[1].select(
                    'p/a').select(
                        'text()').extract()[0].strip()
                open_price = clean_str(ord_rows[r].select(
                    'td')[2].select(
                        'p/text()').extract()[0])
                high_price = clean_str(ord_rows[r].select('td')[3].select(
                    'p/text()').extract()[0])
                low_price = clean_str(ord_rows[r].select('td')[4].select(
                    'p/text()').extract()[0])
                volume = clean_str(ord_rows[r].select('td')[11].select(
                    'p/text()').extract()[0])
                close_price = clean_str(ord_rows[r].select('td')[12].select(
                    'p/text()').extract()[0])

                self.log(log_str % (
                    dateix.strftime("%m/%d/%y"), ticker,
                    open_price, high_price,
                    low_price, close_price, volume)
                )
                yield TickerItem(dateix=dateix.strftime("%Y-%m-%d"),
                                 exchange='TTSE',
                                 ticker=ticker,
                                 open_price=open_price,
                                 high_price=high_price,
                                 low_price=low_price,
                                 close_price=close_price,
                                 volume=volume)


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if not root: return root
        node, parent, right = root, None, False
        while root:
            if root.val == key: break
            if root.val > key: parent, root, right = root, root.left, False
            else: parent, root, right = root, root.right, True
        if not root: return node
        if (not root.right and not root.left) and not parent and root.val == key: return
        if not root.right or not root.left:
            if root.right and not root.left: chosen = root.right
            elif root.left and not root.right: chosen = root.left
            elif not root.left and not root.right: chosen = None
            if not parent: return chosen
            if right: parent.right = chosen
            else: parent.left = chosen
            return node
        else:
            leftChild = root.left
            rightChild = root.right
            while rightChild.left: rightChild = rightChild.left
            rightChild.left = leftChild
            if root == node: return root.right
            else:
                chosen = root.right
                if right: parent.right = chosen
                else: parent.left = chosen
                return node


def create_single_fc_model(fingerprint_input, model_settings, is_training):
  """Builds a model with a single hidden fully-connected layer.

  This is a very simple model with just one matmul and bias layer. As you'd
  expect, it doesn't produce very accurate results, but it is very fast and
  simple, so it's useful for sanity testing.

  Here's the layout of the graph:

  (fingerprint_input)
          v
      [MatMul]<-(weights)
          v
      [BiasAdd]<-(bias)
          v

  Args:
    fingerprint_input: TensorFlow node that will output audio feature vectors.
    model_settings: Dictionary of information about the model.
    is_training: Whether the model is going to be used for training.

  Returns:
    TensorFlow node outputting logits results, and optionally a dropout
    placeholder.
  """
  if is_training:
    dropout_prob = tf.placeholder(tf.float32, name='dropout_prob')
  fingerprint_size = model_settings['fingerprint_size']
  label_count = model_settings['label_count']
  weights = tf.get_variable(
      name='weights',
      initializer=tf.truncated_normal_initializer(stddev=0.001),
      shape=[fingerprint_size, label_count])
  bias = tf.get_variable(
      name='bias', initializer=tf.zeros_initializer, shape=[label_count])
  logits = tf.matmul(fingerprint_input, weights) + bias
  if is_training:
    return logits, dropout_prob
  else:
    return logits

def create_conv_model(fingerprint_input, model_settings, is_training):
  """Builds a standard convolutional model.

  This is roughly the network labeled as 'cnn-trad-fpool3' in the
  'Convolutional Neural Networks for Small-footprint Keyword Spotting' paper:
  http://www.isca-speech.org/archive/interspeech_2015/papers/i15_1478.pdf

  Here's the layout of the graph:

  (fingerprint_input)
          v
      [Conv2D]<-(weights)
          v
      [BiasAdd]<-(bias)
          v
        [Relu]
          v
      [MaxPool]
          v
      [Conv2D]<-(weights)
          v
      [BiasAdd]<-(bias)
          v
        [Relu]
          v
      [MaxPool]
          v
      [MatMul]<-(weights)
          v
      [BiasAdd]<-(bias)
          v

  This produces fairly good quality results, but can involve a large number of
  weight parameters and computations. For a cheaper alternative from the same
  paper with slightly less accuracy, see 'low_latency_conv' below.

  During training, dropout nodes are introduced after each relu, controlled by a
  placeholder.

  Args:
    fingerprint_input: TensorFlow node that will output audio feature vectors.
    model_settings: Dictionary of information about the model.
    is_training: Whether the model is going to be used for training.

  Returns:
    TensorFlow node outputting logits results, and optionally a dropout
    placeholder.
  """
  if is_training:
    dropout_prob = tf.placeholder(tf.float32, name='dropout_prob')
  input_frequency_size = model_settings['fingerprint_width']
  input_time_size = model_settings['spectrogram_length']
  fingerprint_4d = tf.reshape(fingerprint_input,
                              [-1, input_time_size, input_frequency_size, 1])
  first_filter_width = 8
  first_filter_height = 20
  first_filter_count = 64
  first_weights = tf.get_variable(
      name='first_weights',
      initializer=tf.truncated_normal_initializer(stddev=0.01),
      shape=[first_filter_height, first_filter_width, 1, first_filter_count])
  first_bias = tf.get_variable(
      name='first_bias',
      initializer=tf.zeros_initializer,
      shape=[first_filter_count])
  first_conv = tf.nn.conv2d(fingerprint_4d, first_weights, [1, 1, 1, 1],
                            'SAME') + first_bias
  first_relu = tf.nn.relu(first_conv)
  if is_training:
    first_dropout = tf.nn.dropout(first_relu, dropout_prob)
  else:
    first_dropout = first_relu
  max_pool = tf.nn.max_pool(first_dropout, [1, 2, 2, 1], [1, 2, 2, 1], 'SAME')
  second_filter_width = 4
  second_filter_height = 10
  second_filter_count = 64
  second_weights = tf.get_variable(
      name='second_weights',
      initializer=tf.truncated_normal_initializer(stddev=0.01),
      shape=[
          second_filter_height, second_filter_width, first_filter_count,
          second_filter_count
      ])
  second_bias = tf.get_variable(
      name='second_bias',
      initializer=tf.zeros_initializer,
      shape=[second_filter_count])
  second_conv = tf.nn.conv2d(max_pool, second_weights, [1, 1, 1, 1],
                             'SAME') + second_bias
  second_relu = tf.nn.relu(second_conv)
  if is_training:
    second_dropout = tf.nn.dropout(second_relu, dropout_prob)
  else:
    second_dropout = second_relu
  second_conv_shape = second_dropout.get_shape()
  second_conv_output_width = second_conv_shape[2]
  second_conv_output_height = second_conv_shape[1]
  second_conv_element_count = int(
      second_conv_output_width * second_conv_output_height *
      second_filter_count)
  flattened_second_conv = tf.reshape(second_dropout,
                                     [-1, second_conv_element_count])
  label_count = model_settings['label_count']
  final_fc_weights = tf.get_variable(
      name='final_fc_weights',
      initializer=tf.truncated_normal_initializer,
      shape=[second_conv_element_count, label_count])
  final_fc_bias = tf.get_variable(
      name='final_fc_bias',
      initializer=tf.zeros_initializer,
      shape=[label_count])
  final_fc = tf.matmul(flattened_second_conv, final_fc_weights) + final_fc_bias
  if is_training:
    return final_fc, dropout_prob
  else:
    return final_fc


import sys
input = lambda: sys.stdin.readline().rstrip()

def resolve():
    N = int(input())

    def rec(n, maxchr, ans):
        if n==N:
            print(ans)
        else:
            for i in range(maxchr):
                rec(n+1, maxchr, ans+chr(i+97))
            rec(n+1, maxchr+1, ans+chr(maxchr+97))

    rec(0, 0, '')

if __name__ == '__main__':
    resolve()


from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from aiogram.dispatcher.filters import Command
import requests
from bs4 import BeautifulSoup

import config

bot_token = config.Token
bot = Bot(token=bot_token)
dp = Dispatcher(bot)
hello = 'Приветик, напиши /help чтоб узнать что я умею'

def city_checking(message):
    request = requests.get('https://www.meteoservice.ru/location/search?q=' + message + '&force=0')
    request_soup = BeautifulSoup(request.text, "html.parser")
    if request.status_code != 200:
        return request_soup, 1
    else:
        if len(request_soup.findAll('i', class_='material-icons font-4x color-alert float-left')) != 0:
            return request_soup, 2
        found_refs = request_soup.find_all('a', href=True)
        found_city_request = requests.get('https://www.meteoservice.ru' + found_refs[30]['href'])
        found_city_request_soup = BeautifulSoup(found_city_request.text, "html.parser")
    return found_city_request_soup, 0

@dp.message_handler(commands=["weather"])
async def weather(message: types.Message, command: Command):
    soup, checker = city_checking(command.args)
    if checker == 0:
        temp = soup.findAll('span', {"class": 'value'})[0].text.strip()
        await message.reply(f"В городе {command.args} сейчас {temp}")
    elif checker == 1:
        await message.reply('Упс, случилась ошибочка во время запроса')
    elif checker == 2:
        await message.reply('Напишите /weather название существующего города')

@dp.message_handler(commands=['start'])
async def process_start_command(message: types.Message):
    await message.answer(hello)

@dp.message_handler(commands=['help'])
async def process_help_command(message: types.Message):
    await message.answer("Я умею рассказывать температуру по команде weather!")

if __name__ == '__main__':
    executor.start_polling(dp)


import discord
import asyncio
import re
from discord.utils import get
from discord.ext import commands
from datetime import datetime, timezone,timedelta
from bfunc import callAPI, db, traceBack, timeConversion, calculateTreasure, roleArray,timeConversion
from pymongo import UpdateOne
from pymongo.errors import BulkWriteError

logChannel = 73707667723806312 # 728456783466725427 73707667723806312

async def generateLog(self,  num : int, sessionInfo=None, guildDBEntriesDic=None, characterDBentries=None, userDBEntriesDic=None, ):
    logData =db.logdata
    if sessionInfo == None:
        sessionInfo = logData.find_one({"Log ID": int(num)})

    channel = self.bot.get_channel(logChannel) # 728456783466725427 737076677238063125
    editMessage = await channel.fetch_message(num)

    if not editMessage or editMessage.author != self.bot.user:
        print("Invalid Message")
        return None

    # get the collections of characters
    playersCollection = db.players
    guildCollection = db.guilds
    statsCollection = db.stats
    usersCollection = db.users

    sessionLogEmbed = editMessage.embeds[0]
    summaryIndex = sessionLogEmbed.description.find('Summary:')
    description = sessionLogEmbed.description[summaryIndex:]+"\n"

    role = sessionInfo["Role"]
    game = sessionInfo["Game"]
    start = sessionInfo["Start"]
    end = sessionInfo["End"]
    tierNum = sessionInfo["Tier"]

    guilds = sessionInfo["Guilds"]

    players = sessionInfo["Players"]
    #dictionary indexed by user id
    # {cp, magic items, consumables, inventory, partial, status, user id, character id, character name, character level, character cp, double rewards, guild, }

    dm = sessionInfo["DM"]
    # {cp, magic items, consumables, inventory, partial, status, user id, character id, character name, character level, character cp, double rewards, guild, noodles}

    maximumCP = dm["CP"]

    deathChars = []
    allRewardStrings = {}

    characterIDs = [p["Character ID"] for p in players.values()]
    userIDs = list(players.keys())
    guildIDs = list(guilds.keys())
    for gt in guildIDs:
        if guilds[gt]["Status"] == False:
            del guilds[gt]

    guildIDs = list(guilds.keys())

    userIDs.append(str(dm["ID"]))

    print(userIDs)
    # the db entry of every character
    if characterDBentries == None:
        characterDBentries = playersCollection.find({"_id": {"$in": characterIDs}})

    # the db entry of every user
    if userDBEntriesDic == None:
        userDBentries = usersCollection.find({"User ID": {"$in": userIDs}})
        userDBEntriesDic = {}
        for u in userDBentries:
            userDBEntriesDic[u["User ID"]] = u

    # the db entry of every guild
    if guildDBEntriesDic == None:
        guildDBEntriesDic = {}
        guildDBentries = guildCollection.find({"Name": {"$in": guildIDs}})
        for g in guildDBentries:
            guildDBEntriesDic[g["Name"]] = g

    print(userDBEntriesDic)

    for k, player in players.items():
        # this indicates that the character had died
        if player["Status"] == "Dead":
            deathChars.append(player)

        duration = player["CP"] * 3600

        guildDouble = False
        playerDouble = False
        dmDouble = False

        if role != "":
            guild_valid =("Guild" in player and
                            player["Guild"] in guilds and
                            guilds[player["Guild"]]["Status"])
            guildDouble = (guild_valid and
                            guilds[player["Guild"]]["Rewards"] and
                            guildDBEntriesDic[player["Guild"]]["Reputation"]>20)
            if((guild_valid and
                guilds[player["Guild"]]["Items"] and
                guildDBEntriesDic[player["Guild"]]["Reputation"]>15+20*guildDouble)):
                print("A", player["Double Items"])
                if player["Double Items"][0] == "Magic Items":
                    player["Magic Items"].append(player["Double Items"][1])
                else:
                    player[player["Double Items"][0]]["Add"].append(player["Double Items"][1])

            player["Double"] = "Double" in userDBEntriesDic[k] and userDBEntriesDic[k]["Double"] >0
            playerDouble = player["Double"]

            dmDouble = False

            treasureArray  = calculateTreasure(player["Level"], player["Character CP"], tierNum, duration, (player in deathChars), num, guildDouble, playerDouble, dmDouble)
            treasureString = f"{treasureArray[0]} CP, {sum(treasureArray[1].values())} TP, {treasureArray[2]} GP"

        else:
            # if there were no rewards we only care about the time
            treasureString = timeConversion(duration)
        groupString = ""
        groupString += guildDouble * "Guild "
        groupString += playerDouble * "Fanatic "

        groupString += f'{role} Friend {"Full"*(player["CP"]==dm["CP"])+"Partial"*(not player["CP"]==dm["CP"])} Rewards:\n{treasureString}'

        # if the player was not present the whole game and it was a no rewards game
        if role == "" and not (player["CP"]==dm["CP"]):
            # check if the reward has already been added to the reward dictionary
            if treasureString not in allRewardStrings:
                # if not, create the entry
                allRewardStrings[treasureString] = [player]
            else:
                # otherwise add the new players
                allRewardStrings[treasureString] += [player]
        else:
            print(groupString, "\n", allRewardStrings)
            # check if the full rewards have already been added, if yes create it and add the players
            if groupString in allRewardStrings:
                allRewardStrings[groupString] += [player]
            else:
                allRewardStrings[groupString] = [player]

    datestart = datetime.fromtimestamp(start).strftime("%b-%d-%y %I:%M %p")
    dateend = datetime.fromtimestamp(end).strftime("%b-%d-%y %I:%M %p")
    totalDuration = timeConversion(end - start)
    sessionLogEmbed.title = f"Timer: {game} [END] - {totalDuration}"
    sessionLogEmbed.description = f"Start: {datestart} CDT\nEnd: {dateend} CDT\n"

    dm_double_string = ""
    dmRewardsList = []
    #DM REWARD MATH STARTS HERE
    if("Character ID" in dm):
        charLevel = int(dm['Level'])
        player = dm
        # calculate the tier of the DM character
        if charLevel < 5:
            dmRole = 'Junior'
        elif charLevel < 11:
            dmRole = 'Journey'
        elif charLevel < 17:
            dmRole = 'Elite'
        elif charLevel < 20:
            dmRole = 'True'
        else:
            dmRole = 'Ascended'
        duration = player["CP"]*3600
        print(duration)
        if role != "":
            guild_valid =("Guild" in player and
                            player["Guild"] in guilds and
                            guilds[player["Guild"]]["Status"])
            guildDouble = (guild_valid and
                            guilds[player["Guild"]]["Rewards"] and
                            guildDBEntriesDic[player["Guild"]]["Reputation"]>20)
            player["Double"] = "Double" in userDBEntriesDic[k] and userDBEntriesDic[k]["Double"] >0
            playerDouble = player["Double"]
            if((guild_valid and
                guilds[player["Guild"]]["Items"] and
                guildDBEntriesDic[player["Guild"]]["Reputation"]>15+20*guildDouble)):
                print("A", player["Double Items"][0])
                if player["Double Items"][0] == "Magic Items":
                    player["Magic Items"].append(player["Double Items"][1])
                else:
                    player[player["Double Items"][0]]["Add"].append(player["Double Items"][1])

            dmDouble = player["DM Double"]

            dm_double_string += guildDouble * "Guild "
            dm_double_string += playerDouble * "Fanatic "
            dm_double_string += dmDouble * "DDMRW "

            dmtreasureArray  = calculateTreasure(player["Level"], player["CP"], tierNum, duration, (player in deathChars), num, guildDouble, playerDouble, dmDouble)

        # add the items that the DM awarded themselves to the items list
        for d in dm["Magic Items"]+dm["Consumables"]["Add"]+dm["Inventory"]["Add"]:
            dmRewardsList.append("+"+d)

    # get the collections of characters
    playersCollection = db.players

    noodles = dm["Noodles"]
    # Noodles Math

    # calculate the hour duration and calculate how many 3h segements were played
    hoursPlayed = maximumCP
    # that is the base line of sparkles and noodles gained
    noodlesGained = sparklesGained = int(hoursPlayed) // 3
    print("Noodles:", noodlesGained)
    # add the noodles to the record or start a record if needed

    #update noodle role if dm
    noodleString = "Current :star:: " + str(noodles)

    # if the game received rewards
    if role != "":
        # clear the embed message to repopulate it
        sessionLogEmbed.clear_fields()
        # for every unique set of TP rewards
        for key, value in allRewardStrings.items():
            temp = ""
            # for every player of this reward
            for v in value:
                vRewardList = []
                # for every brough consumable for the character
                for r in  v["Magic Items"]+ v["Consumables"]["Add"]+ v["Inventory"]["Add"]:
                    # add the awarded items to the list of rewards
                    vRewardList.append("+"+r)
                # if the character was not dead at the end of the game
                if v not in deathChars:
                    temp += f"{v['Mention']} | {v['Character Name']} {', '.join(vRewardList).strip()}\n"
                else:
                    # if they were dead, include that death
                    temp += f"~~{v['Mention']} | {v['Character Name']}~~ **DEATH** {', '.join(vRewardList).strip()}\n"

            # since if there is a player for this reward, temp will have text since every case above results in changes to temp
            # this informs us that there were no players
            if temp == "":
                temp = 'None'
            # add the information about the reward to the embed object
            sessionLogEmbed.add_field(name=f"**{key}**\n", value=temp, inline=False)
            # add temp to the total output string

        # list of all guilds
        guildsListStr = ""

        guildRewardsStr = ""
        players[dm["ID"]] = dm
        # passed in parameter, check if there were guilds involved
        if guilds != dict():
            guildsListStr = "Guilds: "
            # for every guild in the game
            for name, g in guilds.items():

                guildsListStr += "\n"+g["Mention"]
                # get the DB record of the guild
                #filter player list by guild
                print(players.values())
                gPlayers = [p for p in players.values() if "Guild" in p and p["Guild"]==name]
                if(len(gPlayers)>0):
                    gain = sparklesGained*len(gPlayers) + int("Guild" in dm and dm["Guild"]==name)
                    guildRewardsStr += f"{g['Name']}: +{gain} :sparkles:\n"

        sessionLogEmbed.title = f"\n**{game}**\n*Tier {tierNum} Quest* \n{sessionInfo['Channel']}"
        sessionLogEmbed.description = f"{guildsListStr}\nStart: {datestart} CDT\nEnd: {dateend} CDT\nRuntime: {totalDuration}\n"+description
        status_text = "Log is being processed! Characters are currently on hold."
        if sessionInfo["Status"] == "Approved":
            status_text = "✅ Log approved! Players have received their rewards."
        elif sessionInfo["Status"] == "Denied":
            status_text = "❌ Log Denied! Characters have been cleared"
        sessionLogEmbed.set_footer(text=f"Game ID: {num}\n{status_text}")

        # add the field for the DM's player rewards
        dm_text = "**No Character**"
        dm_name_text = "**No Rewards**"
        # if no character signed up then the character parts are excluded
        if("Character ID" in dm):
            dm_text = f"{dm['Character Name']} {', '.join(dmRewardsList)}"
            dm_name_text = f"DM {dm_double_string}Rewards (Tier {roleArray.index(dmRole) + 1}):\n**{dmtreasureArray[0]} CP, {sum(dmtreasureArray[1].values())} TP, {dmtreasureArray[2]} GP**\n"
        sessionLogEmbed.add_field(value=f"{dm['Mention']} | {dm_text}\n{'Gained :star:: ' + str(noodlesGained)} \n{noodleString}", name=dm_name_text)

        # if there are guild rewards then add a field with relevant information
        if guildRewardsStr != "":
            sessionLogEmbed.add_field(value=guildRewardsStr, name=f"Guild Rewards", inline=False)
        await editMessage.edit(embed=sessionLogEmbed)

    pass

class Log(commands.Cog):
    def __init__ (self, bot):
        self.bot = bot
        self.logChannel = 73707667723806312 # 728456783466725427 73707667723806312

    @commands.group()
    async def session(self, ctx):
        pass

    async def cog_command_error(self, ctx, error):
        msg = None

        if isinstance(error, commands.MissingAnyRole):
            await ctx.channel.send("You do not have the required permissions for this command.")
            bot.get_command(ctx.invoked_with).reset_cooldown(ctx)
            return
        else:
            if isinstance(error, commands.MissingRequiredArgument):
                msg = "Your command was missing an argument! "
            elif isinstance(error, commands.UnexpectedQuoteError) or isinstance(error, commands.ExpectedClosingQuoteError) or isinstance(error, commands.InvalidEndOfQuotedStringError):
              msg = ""

            if msg:
                ctx.command.reset_cooldown(ctx)
                await ctx.channel.send(msg)
                await traceBack(ctx,error)
            else:
                ctx.command.reset_cooldown(ctx)
                await traceBack(ctx,error)

    @commands.Cog.listener()
    async def on_raw_reaction_remove(self,payload):

        pass

    @commands.Cog.listener()
    async def on_raw_reaction_add(self,payload):
        pass

    @commands.has_any_role('Mod Friend', 'Admins')
    @session.command()
    async def approve(self,ctx,  num : int):
        channel = self.bot.get_channel
        logData = db.logdata
        sessionInfo = logData.find_one({"Log ID": int(num)})
        channel = self.bot.get_channel(self.logChannel) # 728456783466725427 737076677238063125

        editMessage = await channel.fetch_message(num)

        if not editMessage or editMessage.author != self.bot.user:
            return ctx.channel.send("Session has no corresponding message in the log channel.")

        guilds = sessionInfo["Guilds"]
        tierNum = sessionInfo["Tier"]
        role = sessionInfo["Role"]

        #dictionary indexed by user id
        players = sessionInfo["Players"]
        # {cp, magic items, consumables, inventory, status, character id, character name, character level, character cp, double rewards, guild, }

        dm = sessionInfo["DM"]
        # {cp, magic items, consumables, inventory, character id, character name, character level, character cp, double rewards, guild, noodles, dm double}

        maximumCP = dm["CP"]
        deathChars = []
        playerUpdates = []

        # get the collections of characters
        playersCollection = db.players
        guildCollection = db.guilds
        statsCollection = db.stats
        usersCollection = db.users

        characterIDs = [p["Character ID"] for p in players.values()]
        userIDs = list(players.keys())
        guildIDs = list(guilds.keys())

        userIDs.append(str(dm["ID"]))

        # the db entry of every character
        characterDBentries = playersCollection.find({"_id": {"$in": characterIDs}})

        # the db entry of every user
        userDBentries = usersCollection.find({"User ID": {"$in": userIDs}})

        # the db entry of every guild
        guildDBentries = guildCollection.find({"Name": {"$in": guildIDs}})
        print("GUILD", guildDBentries)
        guildDBEntriesDic = {}
        for g in guildDBentries:
            guildDBEntriesDic[g["Name"]] = g

        userDBEntriesDic = {}
        for u in userDBentries:
            userDBEntriesDic[u["User ID"]] = u

        #print(characterDBentries)
        print(players)

        for character in characterDBentries:
            player = players[str(character["User ID"])]
            # this indicates that the character had died
            if player["Status"] == "Dead":
                deathChars.append(player)

            duration = player["CP"] * 3600
            guildDouble = False
            playerDouble = False
            dmDouble = False

            if role != "":
                guild_valid =("Guild" in player and
                                player["Guild"] in guilds and
                                guilds[player["Guild"]]["Status"])
                guildDouble = (guild_valid and
                                guilds[player["Guild"]]["Rewards"] and
                                guildDBEntriesDic[player["Guild"]]["Reputation"]>20)

                player["Double"] = "Double" in userDBEntriesDic[str(character["User ID"])] and userDBEntriesDic[str(character["User ID"])]["Double"] >0
                playerDouble = player["Double"]
                dmDouble = False

                treasureArray  = calculateTreasure(player["Level"], player["Character CP"] , tierNum, duration, (player in deathChars), num, guildDouble, playerDouble, dmDouble)

                if((guild_valid and
                        guilds[player["Guild"]]["Items"] and
                        guildDBEntriesDic[player["Guild"]]["Reputation"]>15+20*guildDouble)):
                    if player["Double Items"][0] == "Inventory":
                        player["Inventory"].append(player["Double Items"][1])
                    else:
                        player[player["Double Items"][0]]["Add"].append(player["Double Items"][1])

                # create a list of all items the character has
                consumableList = character["Consumables"].split(", ")
                consumablesString = ""

                #if we know they didnt have any items, we know that changes could only be additions
                if(character["Consumables"]=="None"):
                    # turn the list of added items into the new string
                    consumablesString = ", ".join(player["Consumables"]["Add"])
                else:
                    #remove the removed items from the list of original items and then combine the remaining and the new items
                    for i in player["Consumables"]["Remove"]:
                        consumableList.remove(i)
                    consumablesString = ", ".join(player["Consumables"]["Add"]+consumableList)

                # if the string is empty, turn it into none
                consumablesString += "None"*(consumablesString=="")

                # magic items cannot be removed so we only care about addtions
                # if we have no items and no additions, string is None

                magicItemString = "None"
                if(character["Magic Items"]=="None"):
                    if(len(player["Magic Items"])==0):
                        pass
                    else:
                        # otherwise it is just the combination of new items
                        magicItemString = ", ".join(player["Magic Items"])
                else:
                    # otherwise connect up the old and new items
                    magicItemString = character["Magic Items"]+(", "+ ", ".join(player["Magic Items"]))*(len(player["Magic Items"])>0)

                # increase the relevant inventory entries and create them if necessary
                for i in player["Inventory"]["Add"]:
                    if i in character["Inventory"]:
                        character["Inventory"][i] += 1
                    else:
                        character["Inventory"][i] = 1

                # decrement the relevant items and delete the entry when necessary
                for i in player["Inventory"]["Remove"]:
                    character["Inventory"][i] -= 1
                    if int(character["Inventory"][i]) <= 0:
                        del character["Inventory"][i]

                # set up all db values that need to be incremented
                increment = {"CP":  treasureArray[0], "GP":  treasureArray[2],"Games": 1}
                # for every TP tier value that was gained create the increment field
                for k,v in treasureArray[1].items():
                    increment[k] = v
                print("IIIIIIIIIIIIIIIIIIIIII", increment)
                charRewards = {'_id': player["Character ID"],
                                    "fields": {"$unset": {f"GID": 1} ,"$inc": increment,
                                    "$set": {"Consumables": consumablesString, "Magic Items": magicItemString, "Inventory" : character["Inventory"]}}}
                if(player["Status"] == "Dead"):
                    del charRewards["fields"]["$inc"]["Games"]
                    print("charRewards 1", charRewards)
                    deathDic = {"inc": increment.copy(), "set": charRewards["fields"]["$set"]}
                    charRewards["fields"]["$inc"] = {"Games": 1}
                    charRewards["fields"]["$set"] = {"Death": deathDic}
                    print("death", deathDic)
                    print("charRewards 2", charRewards)
                playerUpdates.append(charRewards)

            else:
                # if there were no rewards we only care about the time
                pass

        dmRewardsList = []
        dm["Double"] = False
        #DM REWARD MATH STARTS HERE
        if("Character ID" in dm):

            player = dm

            duration = player["CP"] * 3600
            # the db entry of every character
            print(dm)
            character = playersCollection.find_one({"_id": dm["Character ID"]})
            charLevel = int(dm['Level'])
            # calculate the tier of the DM character
            dmTierNum= 5
            dmRole = "Ascended"
            if charLevel < 5:
                dmRole = 'Junior'
                dmTierNum = 1
            elif charLevel < 11:
                dmRole = 'Journey'
                dmTierNum = 2
            elif charLevel < 17:
                dmRole = 'Elite'
                dmTierNum = 3
            elif charLevel < 20:
                dmRole = 'True'
                dmTierNum = 4

            guild_valid =("Guild" in player and
                                player["Guild"] in guilds and
                                guilds[player["Guild"]]["Status"])
            guildDouble = (guild_valid and
                            guilds[player["Guild"]]["Rewards"] and
                            guildDBEntriesDic[player["Guild"]]["Reputation"]>20)

            player["Double"] = "Double" in userDBEntriesDic[dm["ID"]] and userDBEntriesDic[dm["ID"]]["Double"] >0
            playerDouble = player["Double"]

            dmDouble = player["DM Double"]

            treasureArray  = calculateTreasure(charLevel, player["Character CP"] , dmTierNum, duration, (player in deathChars), num, guildDouble, playerDouble, dmDouble)

            # create a list of all items the character has
            consumableList = character["Consumables"].split(", ")
            consumablesString = ""

            #if we know they didnt have any items, we know that changes could only be additions
            if(character["Consumables"]=="None"):
                # turn the list of added items into the new string
                consumablesString = ", ".join(player["Consumables"]["Add"])
            else:
                #remove the removed items from the list of original items and then combine the remaining and the new items
                for i in player["Consumables"]["Remove"]:
                    consumableList.remove(i)
                consumablesString = ", ".join(player["Consumables"]["Add"]+consumableList)

            # if the string is empty, turn it into none
            consumablesString += "None"*(consumablesString=="")

            # magic items cannot be removed so we only care about addtions
            # if we have no items and no additions, string is None
            magicItemString = "None"
            if(character["Magic Items"]=="None"):
                if(len(player["Magic Items"])==0):
                    pass
                else:
                    # otherwise it is just the combination of new items
                    magicItemString = ", ".join(player["Magic Items"])
            else:
                # otherwise connect up the old and new items
                magicItemString = character["Magic Items"]+(", "+ ", ".join(player["Magic Items"]))*(len(player["Magic Items"])>0)

            # increase the relevant inventory entries and create them if necessary
            for i in player["Inventory"]["Add"]:
                if i in character["Inventory"]:
                    character["Inventory"][i] += 1
                else:
                    character["Inventory"][i] = 1

            # decrement the relevant items and delete the entry when necessary
            for i in player["Inventory"]["Remove"]:
                character["Inventory"][i] -= 1
                if int(character["Inventory"][i]) <= 0:
                    del character["Inventory"][i]

            # set up all db values that need to be incremented
            increment = {"CP":  treasureArray[0], "GP":  treasureArray[2],"Games": 1}
            # for every TP tier value that was gained create the increment field
            for k,v in treasureArray[1].items():
                increment[k] = v

            charRewards = {'_id': player["Character ID"],  "fields": {"$unset": {f"GID": 1} ,
                            "$inc": increment,
                             "$set": {"Consumables": consumablesString, "Magic Items": magicItemString, "Inventory" : character["Inventory"]}}}
            playerUpdates.append(charRewards)

        noodles = dm["Noodles"]
        # Noodles Math
        hoursPlayed = maximumCP
        # that is the base line of sparkles and noodles gained
        noodlesGained = sparklesGained = int(hoursPlayed) // 3

        timerData = list(map(lambda item: UpdateOne({'_id': item['_id']}, item['fields']), playerUpdates))

        players[dm["ID"]] = dm
        guildsData = []
        # if the game received rewards
        if role != "":
            guildsRecordsList = []
            # passed in parameter, check if there were guilds involved
            if guilds != list():
                # for every guild in the game
                for g in guildDBentries:
                    name = g["Name"]
                    # get the DB record of the guild
                    #filter player list by guild
                    gPlayers = [p for p in players.values() if "Guild" in p and p["Guild"] == name]
                    p_count = len(gPlayers) - int("Guild" in dm and dm["Guild"] == name)
                    guilds[g["Name"]]["Player Sparkles"] = sparklesGained*p_count
                    guilds[g["Name"]]["DM Sparkles"] = sparklesGained*int("Guild" in dm and dm["Guild"] == name)
                    if(len(gPlayers)>0):
                        reputationCost = (20*guilds[g["Name"]]["Rewards"]+15*guilds[g["Name"]]["Items"])*guilds[g["Name"]]["Status"]
                        gain = sparklesGained*len(gPlayers) + int("Guild" in dm and dm["Guild"] == name)
                        guildsData.append(UpdateOne({"Name": name},
                                                   {"$inc": {"Games": 1, "Reputation": gain- reputationCost, "Total Reputation": gain}}))

        try:
            end = sessionInfo["End"]
            start = sessionInfo["Start"]

            totalDuration = end - start

            # get the stats for the month and create an entry if it doesnt exist yet
            statsIncrement ={"Games": 1, "Playtime": totalDuration, 'Players': len(players)}
            statsAddToSet = {}
            for name in [g["Name"] for g in guilds.values() if g["Status"]]:
                statsIncrement["Guilds."+name] = 1# Track how many guild quests there were
                statsIncrement["Guilds."+name+".GQ"] = 1
                statsIncrement["Guilds."+name+".Player Sparkles"] = guilds[g["Name"]]["Player Sparkles"]
                statsIncrement["Guilds."+name+".DM Sparkles"] = guilds[g["Name"]]["DM Sparkles"]
                # track how many games had a guild memeber with rewards and how many have not
                if guilds[g["Name"]]["Player Sparkles"] > 0:
                    statsIncrement["Guilds."+name+".GQM"] = 1
                elif guilds[g["Name"]]["DM Sparkles"] > 0:
                    statsIncrement["Guilds."+name+".GQDM"] = 1
                else:
                    statsIncrement["Guilds."+name+".GQNM"] = 1
            # Total Number of Games for the Month / Life

            # increment or create the stat entry for the DM of the game
            statsIncrement[f"DM.{dm['ID']}.T{tierNum}"] = 1
            statsIncrement[f"T{tierNum}"] = 1

            # track a list of unique players
            statsAddToSet = { 'Unique Players': { "$each":  list([p for p in players.keys()])} }

            # track playtime and players

            dateyear = datetime.fromtimestamp(start).strftime("%b-%y")
            # update all the other data entries
            # update the DB stats
            statsCollection.update_one({'Date': dateyear}, {"$set": {'Date': dateyear}, "$inc": statsIncrement, "$addToSet": statsAddToSet}, upsert=True)
            statsCollection.update_one({'Life': 1}, {"$inc": statsIncrement, "$addToSet": statsAddToSet}, upsert=True)
            # update the DM' stats

            usersCollection.update_one({'User ID': str(dm["ID"])}, {"$set": {'User ID':str(dm["ID"])}, "$inc": {'Games': 1, 'Noodles': noodlesGained, 'Double': -1*dm["Double"]}}, upsert=True)
            playersCollection.bulk_write(timerData)

            usersData = list([UpdateOne({'User ID': key}, {'$inc': {'Games': 1, 'Double': -1*item["Double"] }}, upsert=True) for key, item in players.items()])
            usersCollection.bulk_write(usersData)

            logData.update_one({"_id": sessionInfo["_id"]}, {"$set" : {"Status": "Approved"}})
            sessionInfo["Status"]="Approved"

            if guildsRecordsList != list():
                # do a bulk write for the guild data
                guildsCollection.bulk_write(guildsData)
            del players[dm["ID"]]

            await  generateLog(ctx, num, sessionInfo=sessionInfo, userDBEntriesDic=userDBEntriesDic, guildDBEntriesDic=guildDBEntriesDic, characterDBentries=characterDBentries)

            del players[dm["ID"]]
            game = sessionInfo["Game"]
            for k,p in players.items():
                c = ctx.guild.get_member(int(k))
                if(c):
                    await c.send(f"The session log for **{game}** has been approved. **{p['Character Name']}** has received their rewards.")
            dm_text = ""
            if("Character ID" in dm):
                dm_text += f" **{dm['Character Name']}** has received their rewards."

            c = ctx.guild.get_member(int(dm["ID"]))
            if(c):
                await c.send(f"Your session log for **{game}** has been approved."+dm_text)
            #logData.delete_one({"Log ID": num})

        except BulkWriteError as bwe:
            print(bwe.details)
            charEmbedmsg = await ctx.channel.send(embed=None, content="Uh oh, looks like something went wrong. Please try the timer again.")
        #except Exception as e:
        #    print ('MONGO ERROR: ' + str(e))
        #    charEmbedmsg = await ctx.channel.send(embed=None, content="Uh oh, looks like something went wrong. Please try the timer again.")
        else:
            print('Success')
        guild = ctx.guild
        dmUser = ctx.guild.get_member(int(dm["ID"]))
        if dmUser:
            dmRoleNames = [r.name for r in dmUser.roles]
            # for each noodle roll cut-off check if the user would now qualify for the roll and if they do not have it and remove the old roll
            if noodles >= 150:
                if 'Immortal Noodle' not in dmRoleNames:
                    noodleRole = get(guild.roles, name = 'Immortal Noodle')
                    await dmUser.add_roles(noodleRole, reason=f"Hosted 150 sessions. This user has 150+ Noodles.")
                    if 'Ascended Noodle' in dmRoleNames:
                        await dmUser.remove_roles(get(guild.roles, name = 'Ascended Noodle'))
                    noodleString += "\n**Immortal Noodle** role received! :tada:"
            elif noodles >= 100:
                if 'Ascended Noodle' not in dmRoleNames:
                    noodleRole = get(guild.roles, name = 'Ascended Noodle')
                    await dmUser.add_roles(noodleRole, reason=f"Hosted 100 sessions. This user has 100+ Noodles.")
                    if 'True Noodle' in dmRoleNames:
                        await dmUser.remove_roles(get(guild.roles, name = 'True Noodle'))
                    noodleString += "\n**Ascended Noodle** role received! :tada:"

            elif noodles >= 60:
                if 'True Noodle' not in dmRoleNames:
                    noodleRole = get(guild.roles, name = 'True Noodle')
                    await dmUser.add_roles(noodleRole, reason=f"Hosted 60 sessions. This user has 60+ Noodles.")
                    if 'Elite Noodle' in dmRoleNames:
                        await dmUser.remove_roles(get(guild.roles, name = 'Elite Noodle'))
                    noodleString += "\n**True Noodle** role received! :tada:"

            elif noodles >= 30:
                if 'Elite Noodle' not in dmRoleNames:
                    noodleRole = get(guild.roles, name = 'Elite Noodle')
                    await dmUser.add_roles(noodleRole, reason=f"Hosted 30 sessions. This user has 30+ Noodles.")
                    if 'Good Noodle' in dmRoleNames:
                        await dmUser.remove_roles(get(guild.roles, name = 'Good Noodle'))
                    noodleString += "\n**Elite Noodle** role received! :tada:"

            elif noodles >= 10:
                if 'Good Noodle' not in dmRoleNames:
                    noodleRole = get(guild.roles, name = 'Good Noodle')
                    await dmUser.add_roles(noodleRole, reason=f"Hosted 10 sessions. This user has 10+ Noodles.")
                    noodleString += "\n**Good Noodle** role received! :tada:"

    @commands.has_any_role('Mod Friend', 'Admins')
    @session.command()
    async def deny(self,ctx,  num : int):
        channel = self.bot.get_channel
        logData = db.logdata
        sessionInfo = logData.find_one({"Log ID": int(num)})
        channel = self.bot.get_channel(self.logChannel) # 728456783466725427 737076677238063125

        editMessage = await channel.fetch_message(num)

        if not editMessage or editMessage.author != self.bot.user:
            return ctx.channel.send("Session has no corresponding message in the log channel.")

        sessionLogEmbed = editMessage.embeds[0]
        #dictionary indexed by user id
        players = sessionInfo["Players"]
        # {cp, magic items, consumables, inventory, status, character id, character name, character level, character cp, double rewards, guild, }

        playerUpdates = []
        dm = sessionInfo["DM"]
        # {cp, magic items, consumables, inventory, character id, character name, character level, character cp, double rewards, guild, noodles, dm double}
        if "Character ID" in dm:
            charRewards = {'_id': dm["Character ID"],
                                "fields": {"$unset": {f"GID": 1} }}
            playerUpdates.append(charRewards)

        role = sessionInfo["Role"]

        # get the collections of characters
        playersCollection = db.players

        for player in players.values():
            if role != "":
                charRewards = {'_id': player["Character ID"],
                                    "fields": {"$unset": {f"GID": 1} }}
                playerUpdates.append(charRewards)

            else:
                # if there were no rewards we only care about the time
                pass

        timerData = list(map(lambda item: UpdateOne({'_id': item['_id']}, item['fields']), playerUpdates))

        try:
            playersCollection.bulk_write(timerData)
            logData.update_one({"_id": sessionInfo["_id"]}, {"$set" : {"Status": "Denied"}})

            game = sessionInfo["Game"]
            for k,p in players.items():
                c = ctx.guild.get_member(int(k))
                if(c):
                    await c.send(f"The session log for **{game}** has been denied. **{p['Character Name']}** has been cleared.")
            dm_text = ""
            if("Character ID" in dm):
                dm_text += f" **{dm['Character Name']}** has been cleared."

            c = ctx.guild.get_member(int(dm["ID"]))
            if(c):
                await c.send(f"Your session log for **{game}** has been denied."+dm_text)
            #logData.delete_one({"Log ID": num})
            sessionLogEmbed.set_footer(text=f"Game ID: {num}\n❌ Log Denied! Characters have been cleared.")
            await editMessage.edit(embed=sessionLogEmbed)
        except BulkWriteError as bwe:
            print(bwe.details)
            charEmbedmsg = await ctx.channel.send(embed=None, content="Uh oh, looks like something went wrong. Please try the timer again.")
        #except Exception as e:
        #    print ('MONGO ERROR: ' + str(e))
        #    charEmbedmsg = await ctx.channel.send(embed=None, content="Uh oh, looks like something went wrong. Please try the timer again.")
        else:
            print('Success')

    @session.command()
    async def denyGuild(self, ctx,  num : int, *, guilds):
        await self.guildPermission(ctx, num, "Status", False, 0)
    @session.command()
    async def permitGuild(self, ctx,  num : int, *, guilds):
        await self.guildPermission(ctx, num, "Status", True, 0)

    @session.command()
    async def denyRewards(self, ctx,  num : int, *, guilds):
        await self.guildPermission(ctx, num, "Rewards", False, 0)
    @session.command()
    async def permitRewards(self, ctx,  num : int, *, guilds):
        await self.guildPermission(ctx, num, "Rewards", True, 3)

    @session.command()
    async def denyItems(self, ctx,  num : int, *, guilds):
        await self.guildPermission(ctx, num, "Items", False, 0)
    @session.command()
    async def permitItems(self, ctx,  num : int, *, guilds):
        await self.guildPermission(ctx, num, "Items", True, 3)

    async def guildPermission(self, ctx, num : int, target, goal, min_members):
        logData = db.logdata
        sessionInfo = logData.find_one({"Log ID": int(num)})
        if( sessionInfo):
            if( (str(ctx.author.id) == sessionInfo["DM"]["ID"]) or "Mod Friend" in [r.name for r in ctx.author.roles]):
            # if the game received rewards
                if sessionInfo["Role"] != "":
                    players = sessionInfo["Players"]
                    players[sessionInfo["DM"]["ID"]] = sessionInfo["DM"]
                    guilds = sessionInfo["Guilds"]
                    guild_dic = {}
                    for g in guilds.values():
                        guild_dic[g["Mention"]] = g
                    err_message = ""
                    for guildChannel in ctx.message.channel_mentions:
                        m = guildChannel.mention
                        # filter player list by guild
                        gPlayers = [p for p in players.values() if "Guild" in p and guilds[p["Guild"]]["Mention"] == m]
                        if(len(gPlayers) >= min_members):
                            print(guilds)
                            if guildChannel.mention in guild_dic:
                                try:
                                    db.logdata.update_one({"_id": sessionInfo["_id"]}, {"$set": {"Guilds."+guild_dic[m]["Name"]+"."+target: goal}})
                                except BulkWriteError as bwe:
                                    print(e)
                            else:
                                err_message += m +" not found in game.\n"
                        else:
                            err_message += "Not enough members to apply this change for "+ m +"\n"
                    if err_message != "":
                        await ctx.channel.send(err_message)
                    else:
                        await ctx.channel.send("Session updated.")
                        await generateLog(self, num)
            else:
                await ctx.channel.send("You do not have the permissions to perform this change to the session.")
        else:
            await ctx.channel.send("The session could not be found, please double check your number or if the session has already been approved.")

    @commands.has_any_role('Mod Friend', 'Admins')
    @session.command()
    async def denyDDMRW(self, ctx,  num : int):
        await self.session_set(ctx, num, "DDMRW", False)

    @commands.has_any_role('Mod Friend', 'Admins')
    @session.command()
    async def permitDDMRW(self, ctx,  num : int):
        await self.session_set(ctx, num, "DDMRW", True)

    async def session_set(self, ctx, num : int, target, goal):
        logData = db.logdata
        sessionInfo = logData.find_one({"Log ID": int(num)})
        if( sessionInfo):
            try:
                db.logdata.update_one({"_id": sessionInfo["_id"]}, {"$set": {target: goal}})
            except BulkWriteError as bwe:
                print(e)
            await ctx.channel.send("Session updated.")
            await generateLog(self, num)
        else:
            await ctx.channel.send("The session could not be found, please double check your number or if the session has already been approved.")

    @session.command()
    async def optout(self, ctx, num):
        await self.opt(ctx, num, False)
    @session.command()
    async def optin(self, ctx, num):
        await self.opt(ctx, num, True)

    # allows DMs to opt in or out of DDMRW
    async def opt(self, ctx, num : int, goal):
        logData =db.logdata
        sessionInfo = logData.find_one({"Log ID": int(num)})
        if( sessionInfo):
            if (str(ctx.author.id) == sessionInfo["DM"]["ID"] or "Mod Friend" in [r.name for r in ctx.author.roles] and sessionInfo["DDMRW"]):

                try:
                    db.logdata.update_one({"_id": sessionInfo["_id"]}, {"$set": {"DM.DM Double": goal}})
                    await generateLog(self, num)
                except BulkWriteError as bwe:
                    print(e)
            else:
                await ctx.channel.send("You were not the DM of that session or it was not DDMRW.")
        else:
            await ctx.channel.send("The session could not be found, please double check your number or if the session has already been approved.")

    @session.command()
    async def log(self, ctx,  num : int, *, editString=""):
        # The real Bot
        botUser = self.bot.user

        # Logs channel
        # channel = self.bot.get_channel(577227687962214406)
        channel = self.bot.get_channel(self.logChannel) # 728456783466725427 737076677238063125

        limit = 100
        msgFound = False
        async with channel.typing():
            async for message in channel.history(oldest_first=False, limit=limit):
                if int(num) == message.id and message.author == botUser:
                    editMessage = message
                    msgFound = True
                    break

        if not msgFound:
            delMessage = await ctx.channel.send(content=f"I couldn't find your game with ID - `{num}` in the last {limit} games. Please try again, I will delete your message and this message in 10 seconds.")
            await asyncio.sleep(10)
            await delMessage.delete()
            await ctx.message.delete()
            return

        logData =db.logdata
        sessionInfo = logData.find_one({"Log ID": int(num)})
        if( sessionInfo):
            if (str(ctx.author.id) == sessionInfo["DM"]["ID"] or "Mod Friend" in [r.name for r in ctx.author.roles] and sessionInfo["DDMRW"]):
                pass

            else:
                await ctx.channel.send("You were not the DM of that session.")
                return
        else:
            await ctx.channel.send("The session could not be found, please double check your number or if the session has already been approved.")
            return
        sessionLogEmbed = editMessage.embeds[0]

        summaryIndex = sessionLogEmbed.description.find('Summary:')
        sessionLogEmbed.description = sessionLogEmbed.description[:summaryIndex] + "Summary: " + editString+"\n"

        await editMessage.edit(embed=sessionLogEmbed)
        delMessage = await ctx.channel.send(content=f"I've edited the summary for quest #{num}.\n```{editString}```\nPlease double-check that the edit is correct. I will now delete your message and this one in 30 seconds.")

        await editMessage.edit(embed=sessionLogEmbed)
        await asyncio.sleep(30)
        await delMessage.delete()
        await ctx.message.delete()

def setup(bot):
    bot.add_cog(Log(bot))


# canvasTest.py...

from kivy.app import App
from kivy.graphics import Rectangle
from kivy.graphics import Color, Line
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from random import random as r

class rootCanvas(Widget):
    def __init__(self, **kwargs):
        super(rootCanvas, self).__init__(**kwargs)
        with self.canvas:
            Color(1, 1, 1, mode='rgb')
            self.rect = Rectangle(pos=self.pos, size=self.size)
        self.bind(size=self.update_rect)
        self.bind(size=self.draw)

    def update_rect(self, *args):
        self.rect.pos = self.pos
        self.rect.size = (self.size[0], self.size[1])

    def draw(self,*args):
        self.canvas.clear()
        with self.canvas:
            Color(1,1,1, mode='rgb')
            Line(points=[0,0,self.rect.size[0],self.rect.size[1]], width=1)

    def change_color(self, *args):

        with self.canvas:
            Color(r(), 1, 1, mode='hsv')
            self.rect = Rectangle(pos=self.pos, size=self.size)

            Color(0,0,0, mode='rgb')
            Line(points=[0,0,self.rect.size[0],self.rect.size[1]], width=1)

class canvasTest(App):
    def build(self):
        root = BoxLayout()
        sideBar = BoxLayout(size=(200, 100), size_hint=(None, 1))
        widget = rootCanvas()
        root.add_widget(widget)
        btn_1 = Button(
            text='Change Canvas Color',
            width=200,
            on_press=widget.change_color)
        sideBar.add_widget(btn_1)
        root.add_widget(sideBar)
        return root

if __name__ == '__main__':
    canvasTest().run()


import streamlit as st
from utils.utils import *
import pandas as pd

def create_filters():
    col1, col2=st.columns(2)
    with col1:
        surname=st.text_input(label="Trainer surname", max_chars=20, placeholder="Rossi")
    with col2:
        min_datetime=execute_query(conn=st.session_state["connection"],
                                   query="SELECT MIN(DataNascita) FROM Istruttore;").one()[0]
        max_datetime=execute_query(conn=st.session_state["connection"],
                                   query="SELECT MAX(DataNascita) FROM Istruttore;").one()[0]
        (min_born_date, max_born_date)=st.date_input(label="Birth date range", min_value=min_datetime, max_value=max_datetime, value=(min_datetime, max_datetime))
    return (surname, (min_born_date, max_born_date))

def create_trainer_info(surname, min_datetime, max_datetime):
    found_trainers=execute_query(conn=st.session_state["connection"],
                        query=f"SELECT CONCAT(Cognome, ' ', Nome) as Trainer, CodFisc as ID_Code, DataNascita as Birth_date, Email, Telefono as Mobile FROM Istruttore WHERE Cognome LIKE '{surname}%' AND DataNascita BETWEEN '{min_datetime}' AND '{max_datetime}';")
    df_found_trainers=pd.DataFrame(found_trainers)
    if df_found_trainers.empty:
        st.error(body="No trainers found.")
    else:
        with st.container():
            for index, row in df_found_trainers.iterrows():
                st.markdown(body=f"{index+1}.  **:green[{row['Trainer']}]** - CF: {row['ID_Code']} - Email: - {row['Email']} - Tel: {row['Mobile']}")

if __name__ == "__main__":
    st.title(":green[Trainer search]")

    if st.session_state["connection"] is not False:
        (surname, (min_datetime, max_datetime))=create_filters()
        create_trainer_info(surname, min_datetime, max_datetime)
    else:
        st.error('Connect to the database first!', icon="⚠")


r=9.2
h=3
pi=22/7
volume=(1/3)*pi*(r**2)*h
area=int(volume)
print("volume of the cone=",area)

n=5
p=5
m=5*12
r=13
s=13/100
t=(n*m*s)
u=float(t)
print("output of npr=",t)
print(type(u))

r=3.2
pi=22/7
area=(22/7)*(r**2)
print("Area of the circle is", area)
print(type(area))

r=3.8
pi=22/7
circum=2*(22/7)*r
print("Circumference of the circle is", circum)
print(type(circum))


operadores = {
    '+': 'ADD',
    '-': 'SUB',
    '/': 'DIV',
    '*': 'MLT',
    '=': 'ATRIBUICAO'
}
op_logico = {
    '==': 'IGUAL',
    '!=': 'DIFERENTE',
    '<': 'MENOR_QUE',
    '>': 'MAIOR_QUE'
}

comandos = ('if', 'else', 'while', 'return')
sinc = ('(', ')', ';')

class pilha_comando:
    'Classe para uma pilha'

    def __init__(self):
        self.pilha = []
        self.instr = []
        self.funcao = {}

    def empilha(self, comando):
        return self.pilha.append(comando)

    def desempilha(self, id):
        atual = self.pilha.pop()
        fila = []  # fila de comandos
        temp = ""  # variavel ou numero imediatamente anterior
        operador = ""  # armazena o operador a ser tratado
        opLog = ""  # armazena o operador lógico a ser tratado
        acmldr = [None, None, None]  # acumula variavel na 0 e 1 e na 2 um operador
        temporarios = []  # cria registradores temporarios
        while (len(self.pilha) != 0):
            if atual == '}':
                fila.insert(0, ['}', None, None, None])
                acmldr = zera(acmldr)
            elif atual == '=':
                if operador != "":
                    atual = self.pilha.pop()
                    fila.insert(0,
                                [operadores[operador], atual,
                                 temp, acmldr[0]])
                    acmldr = zera(acmldr)
                    temp = ""
                    operador = ""
                elif acmldr[2] is not None:
                    atual = self.pilha.pop()
                    fila.insert(0,
                                [operadores[acmldr[2]], atual,
                                 temp, acmldr[1]])
                    acmldr = zera(acmldr)
                    temp = ""
                    operador = ""
                elif temp in self.funcao:
                    atual = self.pilha.pop()
                    fila.insert(0,
                                ['MOV', atual, 'JMP', self.funcao[temp]])
                    acmldr = zera(acmldr)
                    temp = ""
                else:
                    atual = self.pilha.pop()
                    fila.insert(0,
                                ['MOV', atual, temp, None])
                    acmldr = zera(acmldr)
                    temp = ""
            elif atual == ')' and temp and operador != "":
                acmldr[1] = temp
                acmldr[2] = operador
                operador = ""
                temp = ""
            elif atual in operadores:
                acmldr[0] = temp
                operador = atual
            elif atual in op_logico:
                acmldr[1] = temp
                opLog = atual
            elif atual == '(':
                i = len(temporarios)
                temporarios.append('t'+str(i+1))
                fila.insert(0,
                            [operadores[operador], temporarios[i-1],
                             temp, acmldr[0]])
                acmldr = zera(acmldr)
                temp = temporarios[i-1]
                operador = ""
            elif atual == 'return':
                fila.insert(0,
                            ['RTN', temp, None, None])
            elif atual == 'else':
                fila.insert(0, ['else', None, None, None])
            elif atual == 'if':
                if opLog == '<':
                    fila.insert(0, ['JGT', temp, acmldr[1], 'PULAR'])
                elif opLog == '>':
                    fila.insert(0, ['JGT', acmldr[1], temp, 'PULAR'])
                elif opLog == '==':
                    fila.insert(0, ['JDT', temp, acmldr[1], 'PULAR'])
                elif opLog == '!=':
                    fila.insert(0, ['JET', temp, acmldr[1], 'PULAR'])
            elif atual == 'while':
                if opLog == '<':
                    fila.insert(0, ['JGT', temp, acmldr[1], 'WPULAR'])
                elif opLog == '>':
                    fila.insert(0, ['JGT', acmldr[1], temp, 'WPULAR'])
                elif opLog == '==':
                    fila.insert(0, ['JDT', temp, acmldr[1], 'WPULAR'])
                elif opLog == '!=':
                    fila.insert(0, ['JET', temp, acmldr[1], 'WPULAR'])
            else:
                temp = atual
            if len(self.pilha) > 0:
                atual = self.pilha.pop()
        i = 0
        j = 0
        fila.pop(-1)
        for iten in fila:
            if fila[i][0] == '}':
                j = i
                while i >= 0:
                    i -= 1
                    if fila[i][0] is 'else':
                        fila.insert(i, ['JMP', j, None, None])
                        fila.pop(j)
                        break
                    elif fila[i][3] == 'PULAR':
                        fila[i][3] = j+1
                        fila.pop(j)
                        break
                    elif fila[i][3] == 'WPULAR':
                        fila[i][3] = j+2
                        fila[j] = ['JNP', i+1, None, None]
                        break
                i = j
            i += 1
        self.funcao[id] = len(self.instr)
        self.pilha.clear()
        return self.instr.append(fila)

    def imprime_instrucoes(self):
        i = 0
        print(self.funcao)
        for item in self.instr:
            print(i)
            print(self.instr[i])
            i += 1

def zera(vetor):
    vetor[0] = None
    vetor[1] = None
    vetor[2] = None
    return vetor
# varrer a FILA atras dos } pa poder colcoar os GOTO e substituir os PULAR

# transformar isso no gerador de codigo intermediaro
# a medida que for desempilhando as instruções,
# que acontece sempre ao fim do BLOCO
# acrescentar a instrução completa numa FILA
# depois imprimir essa filha
# pq na pilha nos vamos desempilha do final para o começo
# pela gramatica obrigatoriamente tem um else depois do if
# porem eu nao tratei disso no sintatico, [ja tratei]
# aqui eu preciso esperar um else apos desempilhar os comandos do if
# para ser a instruçao do GOTO
# o GOTO vai ser para o final da fila de instruções
# que vai ser onde o inicio do outro comando vai ser anexado
# fazer uma fila por bloco
# ao fim do bloco desempilhar e colocar numa fila temporaria
# pegar a len()+1+filareal.len() dessa fila
# e sinalizar ao GOTO onde deve ser o pulo
# anexar a fila temporaria a fila real
# filareal é a fila do programa como um todo
# marcar o inicio do bloco de cada funcao


# =======================================================================================================
# PROBLEM C3
#
# Build a CNN based classifier for Cats vs Dogs dataset.
# Your input layer should accept 150x150 with 3 bytes color as the input shape.
# This is unlabeled data, use ImageDataGenerator to automatically label it.
# Don't use lambda layers in your model.
#
# The dataset used in this problem is originally published in https://www.kaggle.com/c/dogs-vs-cats/data
#
# Desired accuracy and validation_accuracy > 72%
# ========================================================================================================

import tensorflow as tf
import urllib.request
import zipfile
import tensorflow as tf
import os
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.preprocessing.image import ImageDataGenerator

def solution_C3():
    #data_url = 'https://dicodingacademy.blob.core.windows.net/picodiploma/Simulation/machine_learning/cats_and_dogs.zip'
    #urllib.request.urlretrieve(data_url, 'cats_and_dogs.zip')
    #local_file = 'cats_and_dogs.zip'
    #zip_ref = zipfile.ZipFile(local_file, 'r')
    #zip_ref.extractall('data/')
    #zip_ref.close()

    BASE_DIR = 'data\cats_and_dogs'
    train_dir = r'C:\Users\ASUS\PycharmProjects\TF_Exam_Simulation1\Submission C\data\cats_and_dogs_filtered\train'
    validation_dir = r'C:\Users\ASUS\PycharmProjects\TF_Exam_Simulation1\Submission C\data\cats_and_dogs_filtered\validation'

    train_datagen =  ImageDataGenerator(
        rescale = 1./255.,
        rotation_range = 40,
        width_shift_range = 0.2,
        height_shift_range = 0.2,
        shear_range = 0.2,
        zoom_range = 0.2,
        horizontal_flip = True,
        validation_split=0.2
    )
    # YOUR CODE HERE
    test_datagen = ImageDataGenerator(rescale=1.0/255.)

    train_generator = train_datagen.flow_from_directory(train_dir,
                                                        batch_size = 32,
                                                        class_mode = 'binary',
                                                        target_size = (150, 150))

    validation_generator =  test_datagen.flow_from_directory(validation_dir,
                                                              batch_size  = 32,
                                                              class_mode  = 'binary',
                                                              target_size = (150, 150))

    model = tf.keras.models.Sequential([
        tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
        tf.keras.layers.MaxPooling2D(2,2),

        tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),

        tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),

        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(512, activation='relu'),
        # YOUR CODE HERE, end with a Neuron Dense, activated by 'sigmoid'
        tf.keras.layers.Dense(1, activation='sigmoid')
    ])

    model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['acc'])
    model.fit(
        train_generator,
        validation_data = validation_generator,
        epochs = 30,
        verbose = 1)
    return model

# The code below is to save your model as a .h5 file.
# It will be saved automatically in your Submission folder.
if __name__ == '__main__':
    model = solution_C3()
    model.save("model_C3.h5")


from project.hardware.heavy_hardware import HeavyHardware
from project.hardware.power_hardware import PowerHardware
from project.software.express_software import ExpressSoftware
from project.software.light_software import LightSoftware

class System:
    _hardware = []
    _software = []

    @staticmethod
    def register_power_hardware(name: str, capacity: int, memory: int):
        hardware = PowerHardware(name, capacity, memory)
        System._hardware.append(hardware)

    @staticmethod
    def register_heavy_hardware(name: str, capacity: int, memory: int):
        hardware = HeavyHardware(name, capacity, memory)
        System._hardware.append(hardware)

    @staticmethod
    def register_express_software(hardware_name: str, name: str, capacity_consumption: int, memory_consumption: int):
      #  software = ExpressSoftware(name, capacity_consumption, memory_consumption)

        try:
            hardware = [h for h in System._hardware if h.name == hardware_name][0]
        except IndexError:
            return "Hardware does not exist"

        try:
            software = ExpressSoftware(name, capacity_consumption, memory_consumption)
            hardware.install(software)
            System._software.append(software)
        except Exception:
            return "Software cannot be installed"

    @staticmethod
    def register_light_software(hardware_name: str, name: str, capacity_consumption: int, memory_consumption: int):
     #   software = LightSoftware(name, capacity_consumption, memory_consumption)

        try:
            hardware = [h for h in System._hardware if h.name == hardware_name][0]
        except IndexError:
            return "Hardware does not exist"

        try:
            software = LightSoftware(name, capacity_consumption, memory_consumption)
            hardware.install(software)
            System._software.append(software)
        except Exception:
            return "Software cannot be installed"

    @staticmethod
    def release_software_component(hardware_name: str, software_name: str):
        try:
            hardware = [h for h in System._hardware if h.name == hardware_name][0]
            software = [s for s in System._software if s.name == software_name][0]

            hardware.uninstall(software)
            System._software.remove(software)

        except IndexError:
            return "Some of the components do not exist"

    @staticmethod
    def analyze():
        used_memory = sum([s.memory_consumption for s in System._software])
        used_capacity = sum([s.capacity_consumption for s in System._software])
        total_memory = sum([h.memory for h in System._hardware])
        total_capacity = sum([h.capacity for h in System._hardware])

        result = "System Analysis\n"
        result += f"Hardware Components: {len(System._hardware)}\n"
        result += f"Software Components: {len(System._software)}\n"
        result += f"Total Operational Memory: {int(used_memory)} / {int(total_memory)}\n"
        result += f"Total Capacity Taken: {int(used_capacity)} / {int(total_capacity)}"

        return result

    @staticmethod
    def system_split():
        result = ""
        for h in System._hardware:
            number_express = len([s for s in h.software_components if s.software_type == "Express"])
            number_light = len([s for s in h.software_components if s.software_type == "Light"])
            used_memory = sum([s.memory_consumption for s in h.software_components])
            used_capacity = sum([s.capacity_consumption for s in h.software_components])
            all_software = ', '.join([s.name for s in h.software_components])

            result += f"Hardware Component - {h.name}\n"
            result += f"Express Software Components: {number_express}\n"
            result += f"Light Software Components: {number_light}\n"
            result += f"Memory Usage: {int(used_memory)} / {int(h.memory)}\n"
            result += f"Capacity Usage: {int(used_capacity)} / {int(h.capacity)}\n"
            result += f"Type: {h.hardware_type}\n"
            if h.software_components:
                result += f"Software Components: {all_software}\n"
            else:
                result += f"Software Components: 'None'\n"
        return result

Python OOP/11. Exam prep/Exam 6/project 16082020/system.py


'''
Caleb Ha
23/03/2023
LUNA Cipher

This is the LUNA Cipher. LUNA is an acryonym for Layered Unbreakable Numerical Algorithm. This cipher will be based upon a previous cipher I created named the TESS Cipher.
The TESS Cipher (Transformational Enhanced Shift System) used a modified version of the Ceaser Cipher which increases
the shift used as it encrypts the message. The LUNA Cipher is based on the same principle, however, at the very end of the
encryption, the whole message is put through the Polybius Cipher.

No value greater than 1 billion may be entered for the shift value due to limitations in the code
'''

# Just to use sys.exit(1)
import sys

# Encryptes using TESS1
def encryptionTess(phrase, shift, verbose=False):

    # Contains the encrypted phrase
    encryptedPhrase = ""

    # Contains the total shift
    numberShift = 0

    # Loops through all the characters in the phrase
    for character in phrase:

        # Keeps track of the character
        i = 0

        # Finds the character in the character set
        while character != characterSet[i]:

            i += 1

        # Increases the number shift
        numberShift += shift

        # Increments the character
        i += numberShift

        # Checks to see if the i variable is outside of the range of the characte set
        while i > len(characterSet) - 1:

            i -= len(characterSet)

        # Adds the encrypted character the the encrypted phrase
        encryptedPhrase += characterSet[i]

        # Debugging info
        if verbose:
            print(f"Character: {character}, Shift: {numberShift}, Encrypted: {characterSet[i]}")

    # Returns the final encrypted phrase
    return encryptedPhrase

# Decrypts using TESS
def decryptionTess(phrase, shift, verbose=False):

    # Contains the decrypted phrase
    decryptedPhrase = ""

    # Contains the total number shift
    numberShift = 0

    # Loops through all the characters in the phrase
    for character in phrase:

        # Keeps tract in the character
        i = 0

        # Finds the character in the character set
        while character != characterSet[i]:

            i += 1

        # Increases the number shift
        numberShift += shift

        # Decrements the character
        i -= numberShift

        # Checks to see if the variable i is outside of the range of the character set
        while i < 0:

            i += len(characterSet)

        # Adds the decrypted character to the decrypted phrase
        decryptedPhrase += characterSet[i]

        # Debugging info
        if verbose:
            print(f"Encrypted Character: {character}, Shift: {numberShift}, Decrypted Character: {characterSet[i]}")

    # Returns the final decrypted phrase
    return decryptedPhrase

# Encrypts using Polybius Cipher
def encryptionPolybius(phrase, verbose=False):

    # Contains the encrypted phrase
    encryptedPhrase = ""

    # Loops through all the characters in the phrase
    for character in phrase:

        # Loops through all the rows in the polybius square
        for row in range(len(polybiusSquare)):

            # Loops through all the columns in the polybius square
            for column in range(len(polybiusSquare[0])):

                # Checks to see if the character is the same as the item in the list
                if character == polybiusSquare[row][column]:

                    # Debugging info
                    if verbose:
                        print(f"Character: {character}, Row: {row}, Column: {column}, Encrypted: {rowEncryptionCharacters[row] + columnEncryptionCharacters[column]}")

                    # Adds the encrypted characters to the encrypted phrase
                    encryptedPhrase += rowEncryptionCharacters[row]
                    encryptedPhrase += columnEncryptionCharacters[column]

    # Returns the encrypted phrase
    return encryptedPhrase

# Decrypts using Polybius Cipher
def decryptionPolybius(phrase, verbose=False):

    # Contains the decrypted phrase
    decryptedPhrase = ""

    # Loops to iterate through all the characters (+1 to ensure that the try-except block is ran)
    for i in range(len(phrase)):

        j = i * 2

        # Checks to see if the phrase[j] is out of the index
        try:

            phrase[j]

        # Runs at the end of the characters
        except IndexError:

            # Returns the decypted phrase
            return decryptedPhrase

        # Creates the encypted block
        encryptedBlock = phrase[j] + phrase[j + 1]

        # Loops through the all the characters in polybius square row string
        for row in range(len(polybiusSquare)):

            # Finds the row of the character
            if rowEncryptionCharacters[row] == encryptedBlock[0]:

                # Loops through all the characters in the polybius square column string
                for column in range(len(polybiusSquare[0])):

                    # Finds the column of the character
                    if columnEncryptionCharacters[column] == encryptedBlock[1]:

                        # Adds the decrypted character to the decrypted phrase
                        decryptedPhrase += polybiusSquare[row][column]

                        # Debugging info
                        if verbose:

                            print(f"Encrypted Block: {encryptedBlock}, Row: {row}, Column: {column}, Decrypted: {polybiusSquare[row][column]}")

# Uses both methods of encryption, as well as tacking on the shift value as well as how long the shift is
def totalEncryption(phrase, shift, verbose=False):

    # Checks if the shift value is greater or equal to 1 billion. If it is, it stops the program
    if shift > 999999999:

        print("Error: Please do not attempt to encrypt with a shift equal or greater than 1 billion")
        sys.exit(1)

    # Encrypts the phrase using TESS
    encryptedPhraseTess = encryptionTess(phrase, shift, verbose)

    # Encrypts the phrase using the Polybius Square and adds the shift value as well as how long the shift is
    encryptedPhrasePolybius = encryptionPolybius(encryptedPhraseTess, verbose) + str(shift) + str(len(str(shift)))

    # Returns the totally encrypted phrase
    return encryptedPhrasePolybius

# Uses both methods of decryption
def totalDecryption(phrase, verbose=False):

    # Gets how long the shift value is
    charLength = int(phrase[-1])

    # Keeps track of the shift value
    shiftValue = ""

    # Finds the shift value from the end of the phrase
    for i in range(charLength - 1, -1, -1):

        shiftValue += phrase[-(i +2)]

    # Translates the shift value from a string to an integer
    shiftValue = int(shiftValue)

    # Checks if the shift value is greater or equal to 1 billion. If it is, it stops the program
    if shiftValue > 999999999:

        print("\n---------- ERROR ----------")
        print("Do not enter a number greater than 999,999,999 as the shift value")
        print("---------- ERROR ----------")

        sys.exit(1)

    # Removes the tacked on shift value as well as how long the shift is from the phrase
    phrase = phrase.replace(str(shiftValue) + str(charLength), "")

    # Decrypts using the Polybius Square
    polybiusDecryption = decryptionPolybius(phrase, verbose)

    # Decrypts using TESS
    tessDecryption = decryptionTess(polybiusDecryption, shiftValue, verbose)

    # Returns the decrypted phrase
    return tessDecryption

# A list of 95 characters, organized in the order on a keyboard
characterSet = [
    '`', '~', '1', '!', '2', '@', '3', '#', '4', '$', '5', '%', '6', '^', '7', '&', '8', '*', '9', '(', '0', ')', '-', '_', '=', '+',
    'q', 'Q', 'w', 'W', 'e', 'E', 'r', 'R', 't', 'T', 'y', 'Y', 'u', 'U', 'i', 'I', 'o', 'O', 'p', 'P', '[', '{', ']', '}',
    'a', 'A', 's', 'S', 'd', 'D', 'f', 'F', 'g', 'G', 'h', 'H', 'j', 'J', 'k', 'K', 'l', 'L', ';', ':', '\'', '\"', '\\', '|',
    'z', 'Z', 'x', 'X', 'c', 'C', 'v', 'V', 'b', 'B', 'n', 'N', 'm', 'M', ',', '<', '.', '>', '/', '?',
    ' '
]

# A 5x19 Polybius Square (Rectangle)
polybiusSquare = [
    # 0    1    2    3    4
    ['`', '~', '1', '!', '2'], # 0
    ['@', '3', '#', '4', '$'], # 1
    ['5', '%', '6', '^', '7'], # 2
    ['&', '8', '*', '9', '('], # 3
    ['0', ')', '-', '_', '='], # 4
    ['+', 'q', 'Q', 'w', 'W'], # 5
    ['e', 'E', 'r', 'R', 't'], # 6
    ['T', 'y', 'Y', 'u', 'U'], # 7
    ['i', 'I', 'o', 'O', 'p'], # 8
    ['P', '[', '{', ']', '}'], # 9
    ['a', 'A', 's', 'S', 'd'], # A 10
    ['D', 'f', 'F', 'g', 'G'], # B 11
    ['h', 'H', 'j', 'J', 'k'], # C 12
    ['K', 'l', 'L', ';', ':'], # D 13
    ['\'', '\"', '\\', '|', 'z'], # E 14
    ['Z', 'x', 'X', 'c', 'C'], # F 15
    ['v', 'V', 'b', 'B', 'n'], # G 16
    ['N', 'm', 'M', ',', '<'], # H 17
    ['.', '>', '/', '?', ' '] # I 18
]

# Holders of the characters used to encode in the polybius square
rowEncryptionCharacters = "0123456789ABCDEFGHI"
columnEncryptionCharacters = "01234"

# Takes user input to encrypt or decrypt
encryptOrDecrypt = input("Do you want to encrypt or decrypt? (e/d): ")

# Option to encrypt
if encryptOrDecrypt == "e":

    # Takes the user message to encrypt
    message = input("\nPlease input message: ")

    # Takes user input for the shift value
    shift = int(input("\nPlease input shift: "))

    # Checks if the shift value is invalid
    if shift > 999999999:

        print("\n---------- ERROR ----------")
        print("Do not enter a number greater than 999,999,999 as the shift value")
        print("---------- ERROR ----------")

        sys.exit(1)

    # Determines if to print verbose output
    verboseInput = input("\nVerbose? (y/n): ")

    if verboseInput == "y":

        verbose = True

    elif verboseInput == "n":

        verbose = False

    else:

        print("Invalid input. Verbose is set to false.")
        verbose = False

    encryptedMessage = totalEncryption(message, shift, verbose)

    print(f"\nEncrypted Message: {encryptedMessage}")

# Option to decrypt
elif encryptOrDecrypt == "d":

    message = input("\nPlease input message: ")

    # Determines if to print verbose output
    verboseInput = input("\nVerbose? (y/n): ")

    if verboseInput == "y":

        verbose = True

    elif verboseInput == "n":

        verbose = False

    else:

        print("Invalid input. Verbose is set to false.")
        verbose = False

    decryptedMessage = totalDecryption(message, verbose)

    print(f"\nDecrypted Message: {decryptedMessage}")

else:

    print("\nInvalid input.")