import json

from ryu.lib.packet import packet, ethernet, ipv4

from utils import log

MULTIJET_IP_PROTO = 143

class Dispatcher:

    def __init__(self, multijet):
        self.multijet = multijet
        self.dp = multijet.dp
        self.seq = 0

    def dispatch(self, msg):
        cp = self.multijet.get_cp_by_id(msg['cpid'])
        self.multijet.verify_pool.work(cp, msg)

    def unicast(self, msg, port, seq):
        size = len(msg)
        buf_size = 1200
        offset = 0
        count = size / buf_size + 1
        while size > 0:
            frag = msg[offset:offset + buf_size]
            offset = offset + buf_size
            size = size - buf_size
            pkt_data = json.dumps({
                'seq': seq,
                'count': count,
                'data': frag
            })
            p = packet.Packet()
            eth_header = ethernet.ethernet()
            ip_header = ipv4.ipv4(proto=MULTIJET_IP_PROTO)
            ip_header.serialize(pkt_data, eth_header)
            p.add_protocol(eth_header)
            p.add_protocol(ip_header)
            p.add_protocol(pkt_data)

            ofp = self.dp.ofproto
            parser = self.dp.ofproto_parser
            p.serialize()
            data = p.data
            actions = [parser.OFPActionOutput(port)]
            out = parser.OFPPacketOut(datapath=self.dp, buffer_id=ofp.OFP_NO_BUFFER, in_port=ofp.OFPP_CONTROLLER,
                                      actions=actions, data=data)
            self.dp.send_msg(out)
        log('unicast finished')

    def flood(self, msg, seq, except_port=None):
        size = len(msg)
        buf_size = 1200
        offset = 0
        count = size / buf_size + 1
        while size > 0:
            frag = msg[offset:offset + buf_size]
            offset = offset + buf_size
            size = size - buf_size
            pkt_data = json.dumps({
                'seq': seq,
                'count': count,
                'data': frag
            })
            p = packet.Packet()
            eth_header = ethernet.ethernet()
            ip_header = ipv4.ipv4(proto=MULTIJET_IP_PROTO)
            ip_header.serialize(pkt_data, eth_header)
            p.add_protocol(eth_header)
            p.add_protocol(ip_header)
            p.add_protocol(pkt_data)

            ofp = self.dp.ofproto
            parser = self.dp.ofproto_parser
            p.serialize()
            data = p.data
            actions = [parser.OFPActionOutput(ofp.OFPP_FLOOD)]
            if except_port is None:
                except_port = ofp.OFPP_CONTROLLER
            out = parser.OFPPacketOut(datapath=self.dp, buffer_id=ofp.OFP_NO_BUFFER, in_port=except_port,
                                      actions=actions, data=data)
            self.dp.send_msg(out)
        log('flood finished')

    def set_selector_table(self, next_table_id):

        dp = self.multijet.dp
        ofp = dp.ofproto
        parser = dp.ofproto_parser
        ofmatch = parser.OFPMatch()
        inst = [parser.OFPInstructionGotoTable(next_table_id)]
        msg = parser.OFPFlowMod(datapath=dp, priority=1, match=ofmatch, table_id=0,
                                command=ofp.OFPFC_ADD,
                                flags=ofp.OFPFF_SEND_FLOW_REM,
                                instructions=inst)
        dp.send_msg(msg)


import importlib
from pyrogram import idle
from uvloop import install

from PyroKar.modules import ALL_MODULES
from PyroKar import BOTLOG_CHATID, LOGGER, LOOP, aiosession, app, bots, ids
from PyroKar.modules.basic import join
from PyroKar.helpers.misc import heroku

BOT_VER = "0.1.0"
CMD_HANDLER = ["." "," "?" "!"]
MSG_ON = """
💢 **PyroKar Telah Hidup** 💢
╼┅━━━━━━━━━━╍━━━━━━━━━━┅╾
❍▹ **Userbot Version -** `{}`
❍▹ **Ketik** `{}alive` **untuk Mengecek Bot**
╼┅━━━━━━━━━━╍━━━━━━━━━━┅╾
"""

async def main():
    await app.start()
    print("LOG: Founded Bot token Booting..")
    for all_module in ALL_MODULES:
        importlib.import_module("PyroKar.modules" + all_module)
        print(f"Successfully Imported {all_module} ")
    for bot in bots:
        try:
            await bot.start()
            ex = await bot.get_me()
            await join(bot)
            try:
                await bot.send_message(BOTLOG_CHATID, MSG_ON.format(BOT_VER, CMD_HANDLER))
            except BaseException:
                pass
            print(f"Started as {ex.first_name} | {ex.id} ")
            ids.append(ex.id)
        except Exception as e:
            print(f"{e}")
    await idle()
    await aiosession.close()

if __name__ == "__main__":
    LOGGER("PyroKar").info("PyroKar Telah Hidup")
    install()
    heroku()
    LOOP.run_until_complete(main())


'''Provides code to use Eigendocs, i.e. an adaption of Eigenfaces.
Most of the source code was provided by Dr. Christian Gruhl.'''

import numpy as np
import matplotlib as plt
from sklearn.decomposition import PCA

def rgb2gray(img: np.ndarray):
    '''returns array of greyscale values'''
    try:
        return 0.299*img[:,:,0] + 0.587*img[:,:,1] + 0.114*img[:,:,2]
    except IndexError: # already greyscale
        return img

def get_maximum_height_width(documents:list):
    '''returns maximum height and width of a list from images.'''
    max_w = 0
    max_h = 0
    for doc in documents:
        max_w = np.maximum(max_w, doc.shape[0])
        max_h = np.maximum(max_h, doc.shape[1])
    return max_w, max_h

def proprocess_docs(raw_documents:list, max_w:int, max_h:int):
    '''
    return same sized, greyscale documents as an array
    '''
    documents = []
    for doc in raw_documents:
        # same size for all documents
        C = np.ones((max_w,max_h))
        # convert to grayscale
        C[:doc.shape[0],:doc.shape[1]] = rgb2gray(doc) if len(doc.shape) == 3 else doc
        # 2d to 1d array
        documents.append(C.ravel())

    # list to array
    return np.asarray(documents)

def plot_expl_var(pca:PCA, save=False):
    '''plots (and saves) the explained variance for a trained pca model.'''
    fig, ax = plt.subplots(figsize=(10,6)) # type: ignore
    y = np.cumsum(pca.explained_variance_ratio_)
    plt.plot(y, label="cumulative explained variance") # type: ignore
    plt.title("Cumulative explained variance") # type: ignore
    plt.xlabel("Number of components") # type: ignore
    plt.ylabel("Explained variance") # type: ignore
    plt.axhline(y = 0.9, color = 'r', linestyle = '-', label="90% explained variance") # type: ignore
    interp = np.interp(0.9, y, list(range(len(y))))
    plt.axvline(x=interp,color='grey', label="{} components".format(int(interp))) # type: ignore
    plt.legend() # type: ignore
    if save:
        plt.savefig("results/cumulative_explained_variance.pdf", format="pdf") # type: ignore
    plt.show() # type: ignore

def plot_rec_err(X_train:np.ndarray, X_test:np.ndarray, n_max:int=10, save:bool=False):
    '''plots the reconstruction error for different number of components (PCA)'''
    reconstr_err = []
    for i in range(1, n_max):
        pca = PCA(n_components=i, whiten=True, svd_solver="randomized")
        pca.fit(X_train)
        X_test_pca = pca.transform(X_test)
        X_test_pca_inverse = pca.inverse_transform(X_test_pca)
        reconstr_err.append(np.mean((X_test - X_test_pca_inverse)**2))

    # plot reconstruction error
    plt.figure(figsize=(10,6)) # type: ignore
    plt.plot(reconstr_err, label="reconstruction error") # type: ignore
    plt.title("Reconstruction error") # type: ignore
    plt.xlabel("Number of components") # type: ignore
    plt.ylabel("Reconstruction error") # type: ignore
    plt.legend() # type: ignore
    if save:
        plt.savefig("results/reconstruction error.pdf", format="pdf") # type: ignore
    plt.show() # type: ignore


import logging
import socket

logging.basicConfig(level=logging.DEBUG, filename='log_server.log', format='server %(asctime)s - %(levelname)s - %(message)s')

try:
    my_socket = socket.socket()
    my_socket.bind(("127.0.0.1", 8000))
    my_socket.listen()
    logging.info("listening")
except Exception as err:
    logging.error(f"failed to start server")
else:
    while True:
        client_socket, client_address = my_socket.accept()
        logging.debug(f"server accept {client_address}")
        while True:
            try:
                info_from_client = client_socket.recv(1024)
            except Exception as error:
                logging.error(f"server couldn't receive 1024 b from {client_socket}")
                client_socket.close()
                break
            message_to_client = input("Send message to client, if you want break, input q:__")
            if message_to_client == "q":
                break
            logging.debug(f"Sending message to client")
            client_socket.send(message_to_client.encode())

        client_socket.close()
finally:
    logging.info(f"socket {my_socket} closing")
    my_socket.close()


from sqlalchemy import text
from app.db.env import Session
from app.entities import MovieEntity

def seed_movie_entities():
    with Session() as session:
        films = [
            MovieEntity(name="Blade Runner", director="Ridley Scott"),
            MovieEntity(name="Endgame", director="Anthony Russo"),
            MovieEntity(name="Rocky", director="John G. Avildsen"),
            MovieEntity(name="The Matrix", director="The Wachowskis"),
            MovieEntity(name="The Terminator", director="James Cameron"),
            MovieEntity(name="The Terminator 2: Judgement Day",
                        director="James Cameron"),
            MovieEntity(name="The Terminator 3: Rise of the Machines",
                        director="Jonathan Mostow"),
        ]
        session.execute(text("delete from movies"))
        session.add_all(films)
        session.commit()


from django.urls import path
from . import views
from django.views.generic import TemplateView
from todo.views import *
urlpatterns = [
    path('', views.index,name='index'),
    path('single_blog',views.Blog,name='single_blog'),
    path('blogs/<str:pk>',views.singleblog,name='blogs'),
    #path('post/<str:pk>', views.post, name='post'),
    #path('boss',views.boss_article,name='boss'),
    path('search',views.search,name='search'),
    path('shopInterface',views.shopInterface,name='shopInterface'),
    path('quiz',views.attemptQuiz,name='quiz'),
    path('contact',views.contact,name='contact'),
    path('service',views.service,name='service'),
    path('verse',views.showVerse,name='list_verse1'),
    path('hadith',views.showHadith,name='list_hadith1'),
    path('home',views.homepage,name='home'),
]


'''
Author: lmio 2091319361@qq.com
Date: 2023-07-28 18:01:23
LastEditors: lmio 2091319361@qq.com
Description: 2095. 删除链表的中间节点
'''

from typing import Optional

from utils.node import ListNode

class Solution:
    def deleteMiddle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode()
        dummy.next = head
        flow, fast = dummy, head
        while fast:
            fast = fast.next
            if fast:
                fast = fast.next
            else:
                break
            flow = flow.next
        flow.next = flow.next.next
        return dummy.next


VERSION = 1.1

import sys
import argparse

def process_file(options):

    file = options.cosmicfile
    outfile = options.outfile

    interesting_cell_lines = options.cells.split(",")

    mutations = {}
    mutation_details = {}

    line_count = 0
    with open(file) as fh:
        for line in fh:

            line_count += 1

            if line_count % 100000 == 0 and not options.quiet:
                print (f"Read {line_count} lines", flush=True)

            if line.startswith("Gene name"):
                continue

            sections = line.strip().split("\t")
            # Skip it if there's no position
            if len(sections[25]) == 0:
                continue

#            for i in range(len(sections)):
#                print(f"{i} {sections[i]}")
#            break

            cell_line = sections[4]
            mutation_type = sections[21]
            zygosity = sections[22]
            snp_id = sections[16]
            (chrom,position) = sections[25].split(":")
            (start,end) = position.split("-")
            strand = sections[26]
            snp = sections[19]
            ref = snp[-3]
            alt = snp[-1]

            if not cell_line in interesting_cell_lines:
                continue

            if not mutation_type.startswith("Substitution"):
                continue

            if not snp_id in mutations:
                mutations[snp_id] = []

            mutations[snp_id].append(cell_line)

            if not snp_id in mutation_details:
                mutation_details[snp_id] = {"id": snp_id, "chr":chrom, "start":start, "end":end, "strand": strand, "ref":ref, "alt":alt, "zygosity":zygosity}

    per_line_count = {}
    with open (outfile,"w") as out:
        headers = ["LINE","ID","CHR","START","END","STRAND","REF","ALT","ZYGOSITY"]
        out.write("\t".join(headers))
        out.write("\n")

        for snp in mutations.keys():
            if len(mutations[snp]) == 1:
                cell_line = mutations[snp][0]
                if cell_line not in per_line_count:
                    per_line_count[cell_line] = 0

                per_line_count[cell_line] += 1

                details = mutation_details[snp]

                out.write(cell_line)
                out.write("\t")
                out.write("\t".join([str(details[x]) for x in ["id","chr","start","end","strand","ref","alt","zygosity"]]))
                out.write("\n")

    for line in per_line_count.items():
        print("\t".join([str(x) for x in line]))

def read_options():
    parser = argparse.ArgumentParser(description="Prepare unique SNPS for cell line sleuth")

    parser.add_argument('--quiet', dest="quiet", action='store_true', default=False, help="Supress all but essential messages")
    parser.add_argument('--version', action='version', version=f"Cell Line Sleuth v{VERSION}")
    parser.add_argument('--cells', type=str, help="Comma delimited list of cells to identify", default="MCF7,HeLa,COLO-205,HCT-116,H9")
    parser.add_argument('cosmicfile', type=str, help="The COSMIC SNP file to process")
    parser.add_argument('outfile', type=str, help="Where to write the output")

    options = parser.parse_args()

    return options

def main():

    options = read_options()
    process_file(options)

if __name__ == "__main__":
    main()


# PV248 Python - exercise 01
# Author: Silvia Janikova 475938@muni.cz

import sys
import re
from collections import Counter

# regex definition
r_composer_name = re.compile(r"([^\(]*)(\(.*\))?")
r_year = re.compile(r".*([0-9]{4})(?!.*\d{4})")

# function for convert year to "xxth century" format
def year_to_century(year):
	return str((year // 100) + 1) + "th century"

# function for regex compile
def add_regex(key):
	text = ""
	if key == "composer":
		text = "Composer"
	elif key == "century":
		text = "Composition Year"

	result = re.compile(r"" + text + ":(.*)")

	return result

# function for printing result
def print_results(results):
	for k,v in Counter(result).items():
		print(k + ': ' + str(v))

if len(sys.argv) != 3:
	print('wrong number of arguments')
	sys.exit(1)

if sys.argv[2] == 'composer':
	result = []
	r_key = add_regex(sys.argv[2]);

	f = open(sys.argv[1], 'r')
	for line in f:
		match = r_key.match(line)

		if match != None:
			for composer_item in match.group(1).split(';'):
				composer_item = r_composer_name.match(composer_item)
				if composer_item.group(1).strip() != "":
					result.append(composer_item.group(1).strip())
	print_results(result)

elif sys.argv[2] == 'century':
	result = []
	r_key = add_regex(sys.argv[2]);

	f = open(sys.argv[1], 'r')
	for line in f:
		match = r_key.match(line)

		if match != None:
			century_item = match.group(1).strip()
			if "th century" in century_item:
				result.append(century_item)
			elif century_item !=  "":
				year = r_year.match(century_item).group(1)
				result.append(year_to_century(int(year)))
	print_results(result)

else:
	print('unknown argument')
	sys.exit(1)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
DEPRICATED:
    code is now in abstract_model under fitting

constructs the Theano optimization and trains a learning model,
optionally by initializing the network with pre-trained weights.

http://cs231n.github.io/neural-networks-3/#distr

Pretrained Models:
    https://github.com/fchollet/deep-learning-models
"""
import logging
from six.moves import input, zip, range  # NOQA
import utool as ut

print, rrr, profile = ut.inject2(__name__)
logger = logging.getLogger()

def _clean(model, theano_forward, X_list, y_list, min_conf=0.95):
    from wbia_cnn import batch_processing as batch
    import random

    # Perform testing
    clean_outputs = batch.process_batch(
        model,
        X_list,
        y_list,
        theano_forward,
        augment_on=False,
        randomize_batch_order=False,
    )
    prediction_list = clean_outputs['labeled_predictions']
    confidence_list = clean_outputs['confidences']
    enumerated = enumerate(zip(y_list, prediction_list, confidence_list))

    switched_counter = 0
    switched = {}
    for index, (y, prediction, confidence) in enumerated:
        if confidence < min_conf:
            continue
        if y == prediction:
            continue
        if random.uniform(0.0, 1.0) > confidence:
            continue
        # Perform the switching
        y_list[index] = prediction
        switched_counter += 1
        # Keep track of changes
        y = str(y)
        prediction = str(prediction)
        if y not in switched:
            switched[y] = {}
        if prediction not in switched[y]:
            switched[y][prediction] = 0
        switched[y][prediction] += 1

    total = len(y_list)
    ratio = switched_counter / total
    args = (
        switched_counter,
        total,
        ratio,
    )
    logger.info('[_clean] Cleaned Data... [ %d / %d ] ( %0.04f )' % args)
    for src in sorted(switched.keys()):
        for dst in sorted(switched[src].keys()):
            logger.info(
                '[_clean] \t%r -> %r : %d'
                % (
                    src,
                    dst,
                    switched[src][dst],
                )
            )

    return y_list


"""
Programmer: Laxmi Gurung
Project: Creating a file classList.txt file and prompts the user to enter three names.
Date: 06/01/2021
"""
import getpass
import datetime

current = datetime.datetime.now()
print("Lab 13.1:", current.strftime("%X on %A, %B %d, %Y \n"))

def main():
    # File input and output has three steps: Open the file | Process the file | Close the file
    # make sure all these three steps are fulfilled when opening a file
   #opening the file named classList.txt
    nameFile = open('classList.txt', 'w')

    #writing the three names in the classList file
    #Prompts the user to enter names.
    #In the line 14, 15 and 16 the user inputs the name and at the sametime
    #the data is also written in the file.
    print("Enter three names for Class 30")
    nameFile.write(input("Enter 1st Name: ") + '\n')
    nameFile.write(input("Enter 2nd Name: ") + '\n')
    nameFile.write(input("Enter 3rd Name: ") + '\n')

    # closing the file
    nameFile.close()

    # opening the file to read the names
    nameFile = open('classList.txt', 'r')

    print("These are the current names in the classList file. \n")
    #(file_name.read()) reads the file name line by line. It also saves the lines in the code than reading the names
    #using .readline() funciton.
    print(nameFile.read())

    nameFile.close()

    print("Add Ling Dong and Peter Mei to the class list.")
    #append mode let's the user add data into an existing file, while write mode deletes all
    #previous data. So using append to write on existing data is very useful.
    nameFile = open('classList.txt', 'a')
    nameFile.write('Ling Dong' + '\n')
    nameFile.write('Peter Mei' + '\n')
    nameFile.close()

    again = input("Enter 'Y' to continue: ")
    while again == 'y' or again == 'Y':
        print("These are the new names in the classList file. \n")
        # opening the file to read the names
        nameFile = open('classList.txt', 'r')
        # reading the file line by line
        print(nameFile.read())
        break

#calling the main function
main()


import asyncio
import collections
import random
import re

import discord
from discord.ext import commands
import utils.datamanager as manager

wallet = manager.wallet
config = manager.config

class Listener(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot
        self.user_timers = collections.defaultdict(asyncio.Event)

    async def create_user_timer(self, member: discord.Member) -> None:
        while not self.user_timers[str(member.id)].is_set():
            if config.VOICE_ENABLE:
                coins: int = random.randint(config.VOICE_MIN, config.VOICE_MAX)
            else:
                coins: int = config.VOICE_COINS
            await asyncio.sleep(delay=config.VOICE_COOLDOWN)
            wallet.give_money(user_id=member.id, amount=coins)

    @commands.Cog.listener()
    async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState) -> None:
        if after.channel:
            self.user_timers[str(member.id)].clear()
            await self.create_user_timer(member=member)
        elif before.channel:
            self.user_timers[f"{member.id}"].set()

    @commands.Cog.listener()
    async def on_message(self, message: discord.Message) -> None:
        if message.author.bot:
            return
        coins: float = 0
        if config.CHAT_ENABLE:
            if len(message.content) >= config.CHAT_CON_SYMBOLS:
                coins = len(message.content) * (config.CHAT_STABILIZER / config.CHAT_MULTIPLIER)
        elif not config.CHAT_BLACK_WORDS:
            if len(message.content) >= config.CHAT_DEF_SYMBOLS:
                coins = config.CHAT_COINS

        if config.CHAT_BLACK_WORDS:
            if len(message.content) >= config.CHAT_CON_SYMBOLS:
                for black_words in config.CHAT_BLACK_WORDS:
                    if re.search(re.escape(black_words.split("; ")[0]), message.content):
                        coins = (len(message.content) * (config.CHAT_STABILIZER / config.CHAT_MULTIPLIER)) - float(black_words.split("; ")[1])
                        break
                else:
                    coins = len(message.content) * (config.CHAT_STABILIZER / config.CHAT_MULTIPLIER)
        elif not config.CHAT_ENABLE:
            if len(message.content) >= config.CHAT_DEF_SYMBOLS:
                coins = config.CHAT_COINS
        wallet.give_money(user_id=message.author.id, amount=int(coins))

async def setup(bot: commands.Bot):
    await bot.add_cog(Listener(bot))


from __future__ import print_function
import gspread
import pandas as pd
import re

SERVICE_ACCOUNT_FILE = 'keys.json'
SCOPES = ['https://www.googleapis.com/auth/spreadsheets',
              'https://www.googleapis.com/auth/drive']

gc = gspread.service_account(SERVICE_ACCOUNT_FILE, SCOPES)
sh = gc.open("Pandemic Legacy Sheet")
worksheet = sh.get_worksheet(1)

def setup_dataframe():

    cards_df = pd.DataFrame(worksheet.get_all_records())
    cards_df.columns = cards_df.columns.str.lower().str.replace(' ', '_')
    cards_df.dropna(how='all', axis=1, inplace=True)
    cards_df.fillna('', inplace=True)
    cards_df['forsaken'].replace(
                     to_replace={'TRUE': True, 'FALSE': False},inplace=True)
    cards_df['sticker'] = cards_df.sticker.str.lower()
    n_mask = lambda x: '{} ({})'.format(x.card_name.lower(), x.sticker)
    cards_df['card_name'] = cards_df.apply(n_mask, axis=1)
    cards_df['card_name'].replace(
                      to_replace=" \(\)$", value='', inplace=True, regex=True)
    cards_df.sort_values(['card_name', 'location'], inplace=True, ignore_index=True)
    u_mask = lambda x: '{} {}'.format(x.card_name, x.name)
    cards_df['unique_name'] = cards_df.apply(u_mask, axis=1)
    cards_df.set_index('unique_name', inplace=True, drop=True)

    return(cards_df)

def update_spreadsheet(cards_df):
    clean_df = cards_df.copy()
    clean_df['card_name'].replace(
                      to_replace="( \(.*\))?", value='', inplace=True, regex=True)
    clean_df['card_name'] = clean_df.card_name.str.title()
    clean_df.columns = clean_df.columns.str.title().str.replace('_', ' ')
    worksheet.update([clean_df.columns.values.tolist()] + clean_df.values.tolist())
#creds = service_account.Credentials.from_service_account_file(
        #SERVICE_ACCOUNT_FILE, scopes=SCOPES)
#client = gspread.authorize(creds)

#spreadsheet = client.open('133EKlMVdgp8e-aEEiBsV4NPdttHHDfk0JKvQfNXASUw')


from colorama import Fore

from datetime import datetime
from message import good_bye, welcome
from modify_tables import insert_player, insert_score_board
from player_info import player_guess, game_players, get_player_info

import number_generator
import random

class Level:
    def __init__(self, level, points, attempts, prize_amount, total_hint):
        self.level = level
        self.points = points
        self.attempts = attempts
        self.prize_amount = prize_amount
        self.total_hint = total_hint

"""
- Created two objects and assigned attributes value of th Level Class.
- Stored them in a list to perform FOR loop to run the game.
"""

land_level = Level("land", 0, 5, "1 million dollar", 2)
sky_level = Level("sky", 0, 8, "5 million dollar", 4)

levels = [land_level, sky_level]

"""
- get_game_info() function collects the information of user before starting the game.
    : param player: List prompts user to input username and email.
    : param player_name, player_email: stores the value from the list into the variables.
- insert_player() stores the player information into "player" table in FortuneMakerGame database.
- start_game() takes two arguments to display them during the game run.
"""

def get_game_info():
    welcome()
    player = get_player_info()
    player_name, player_email = player[0], player[1]
    date_start = datetime.now()
    #insert_player(player_name, player_email, date_start)
    start_game(player_name, date_start)

"""
function start_game(): main game run in this function.
:params user_input: List  - to store the user input numbers during the game, inorder for user to see the history.
:params computer_number: List  - this stores the computer generated number from the api call.
:params player_number: List - this stores the user input number.
"""

def start_game(player_name, date_start):
    total_points = 0
    user_input = []

    # Performed FOR loop to begin the game from LAND level to SKY level.
    for game in levels:
        guess = False
        print(Fore.LIGHTGREEN_EX + f"Hi {player_name}, beginning {game.level} level.... ")
        print(Fore.CYAN + "********************************************")
        print(f"Time: {date_start}")
        print(Fore.CYAN + "********************************************")
        print(f"You have {game.attempts} attempts and {game.total_hint} hint available for {game.level} level.")

        computer_number = number_generator.computer_guess()
        print(computer_number)
        player_number = player_guess()

        user_input.append(player_number)
        game.attempts -= 1
        count_numbers, count_position = game_players(computer_number, player_number)
        print(Fore.LIGHTRED_EX + f"Correct Numbers:  {count_numbers} Correct Position: {count_position}")
        if (count_numbers == 1 or 2 or 3 or 4) and count_position == 4:
            print(f"Congratulations!! You completed level {game.level}")
            print(f"Hurray!! You won {game.prize_amount}")
            user_input.clear()
            continue
            if game.level == 'sky':
                good_bye()
                exit()

        print(f"Attempt Left: {game.attempts}")
        print(Fore.GREEN + "********************************************")
        print("YOU ENTERED")
        print_user_input(user_input)

        hint_list = [0, 1, 2, 3]

        while guess is False and game.attempts != 0:
            if game.total_hint > 0:
                ask_help = input(Fore.GREEN + "Do you need hint? Type 'y' for YES || type anything for NO ::  ")
                print(Fore.YELLOW + f"Hint Available Before hint: {game.total_hint}")
                if ask_help == 'y':
                    game.total_hint -= 1
                    game_hint(computer_number, game.level, hint_list)
                    print(Fore.YELLOW + f"Hint Available: {game.total_hint}")
            elif game.total_hint == 0:
                print(Fore.YELLOW + "No Hint Available")

            elif game.attempts == 0:
                print("You Lost")
                good_bye()
                exit()
            print(Fore.RED + f"Attempt Left: {game.attempts}")
            print(Fore.BLUE + "Guess again.....")
            player_number = player_guess()
            user_input.append(player_number)
            count_numbers, count_position = game_players(computer_number, player_number)
            print(Fore.GREEN + "********************************************")
            print(Fore.YELLOW + f"Correct Numbers:  {count_numbers} Correct Position: {count_position}")
            if (count_numbers == 1 or 2 or 3 or 4) and count_position == 4:
                print(f"Congratulations!! You completed level {game.level}")
                print(f"Hurray!! You won {game.prize_amount}")
                user_input.clear()
                if game.level == 'sky':
                    good_bye()
                    exit()
                else:
                    guess = True
            game.attempts -= 1
            print(Fore.RED + f"Attempt Left: {game.attempts}")
            print(Fore.GREEN + "********************************************")
            print("YOU ENTERED")
            print_user_input(user_input)
            print(Fore.GREEN + "********************************************")
            """if game.attempts == 0:
                print("You Lost")
                good_bye()
                exit()"""
            compare(player_number, computer_number, game.points, game.prize_amount)
            total_points += 1
        else:
            print("I am here")

            # insert_score_board(1, 1, game.level, game.points, game.prize_amount, date_start)

# Comparing player's guess to computer number,if this is true, it will take you to next SKY level. If false,
# Performed while loop to let player guess the number,until it matches with computer or no attempts left.
def compare(player_num, comp_num, points, amount):
    if player_num == comp_num:
        print(Fore.BLUE + f"Congratulations! You won {amount}.")
        points += 1
        print(Fore.YELLOW + f"Your points: {points}")

def game_hint(computer_number, level, hint_list):
    if level == "land":
        position = random.choice(hint_list)
        hint_number = computer_number[position]
        print(Fore.YELLOW + f"{computer_number.index(hint_number) + 1} : This position contains {hint_number}.")
        hint_list.remove(position)

    else:
        position = random.choice(hint_list)
        hint_number = computer_number[position]
        if int(hint_number) % 2 == 0:
            print(
                Fore.LIGHTMAGENTA_EX + f"{computer_number.index(hint_number) + 1} : This position contains an even number.")
            hint_list.remove(position)
        else:
            print(
                Fore.LIGHTMAGENTA_EX + f"{computer_number.index(hint_number) + 1} : This position contains an odd number.")
            hint_list.remove(position)

# This function helps to store the user input history is clear format.
def print_user_input(user_input):
    for i in range(len(user_input)):
        for j in user_input[i]:
            print(Fore.RED + j, end=" ")
        print()

# This function call starts the game
get_game_info()


from urllib.request import urlopen
from urllib.error import HTTPError, URLError
from bs4 import BeautifulSoup

def html_to_text_img(url):
    """
    reads html and parse into text and img data

    Parameter
    ------------
    url: str
        a url

    Returns
    -------------
    title: str
        title of an article
    text: str
        article contents
    img_url: str
        url of an image

    """
    if url is '' or url is None:
        return None
    try:
        html = urlopen(url)
    except HTTPError as e:
        print(e)
        return None
    except URLError as e:
        print(e)
        return None

    try:
        soup = BeautifulSoup(html.read(), "html.parser")
    except AttributeError as e:
        print(e)
        return None

    title = soup.find("h1").get_text()
    text = ''
    ps = soup.find("div", {"class": "article gtm-click"}).find_all('p')
    for p in ps:
        text += ''.join(p.get_text())
    imgs = soup.select("div.article__image img")
    img_url = imgs[0]['data-src']

    return title, text, img_url


from pathlib import Path

lines = Path('input.txt').read_text().split('\n')[:-1]

class A:
    meaning = 'rock'
    win_value = 1
    beats = 'scissors'
    outcomes = {'lose': 'scissors','draw': 'rock','win': 'paper'}

class B:
    meaning = 'paper'
    win_value = 2
    beats = 'rock'
    outcomes = {'lose': 'rock','draw': 'paper','win': 'scissors'}

class C:
    meaning = 'scissors'
    win_value = 3
    beats = 'paper'
    outcomes = {'lose': 'paper','draw': 'scissors','win': 'rock'}

outcomes = {
    'win': 6,
    'draw': 3,
    'lose': 0,
    }

items = {
    'A': A,
    'B': B,
    'C': C,
    'rock': A,
    'paper': B,
    'scissors': C,
    'X': 'lose',
    'Y': 'draw',
    'Z': 'win',
    }

def compare(me, them):
    if me.meaning == them.meaning:
        return 'draw'
    elif me.beats == them.meaning:
        return 'win'
    else:
        return 'lose'

total = 0
for line in lines:
    them, me = line.split()
    them = items[them]()
    me = items[me] #win or lose
    me = them.outcomes[me] #rock paper scissors
    me = items[me] #my object
    score = me.win_value + outcomes[compare(me,them)]
    total += score
print(f'total is {total}')


from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import strutils
import tenacity

from ironic.common import cinder
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import states
from ironic.drivers import base
from ironic.drivers import utils
from ironic import objects

CONF = cfg.CONF

LOG = log.getLogger(__name__)

# NOTE(TheJulia): Sets containing known valid types that align with
# _generate_connector() and the volume connection information spec.
VALID_ISCSI_TYPES = ('iqn',)
# TODO(TheJulia): FCoE?
VALID_FC_TYPES = ('wwpn', 'wwnn')

class CinderStorage(base.StorageInterface):
    """A storage_interface driver supporting Cinder."""

    def get_properties(self):
        """Return the properties of the interface.

        :returns: dictionary of <property name>:<property description> entries.
        """
        return {}

    def _fail_validation(self, task, reason,
                         exception=exception.InvalidParameterValue):
        msg = (_("Failed to validate cinder storage interface for node "
                 "%(node)s. %(reason)s") %
               {'node': task.node.uuid, 'reason': reason})
        LOG.error(msg)
        raise exception(msg)

    def _validate_connectors(self, task):
        """Validate connector information helper.

        Enumerates through all connector objects, and identifies if
        iSCSI or Fibre Channel connectors are present.

        :param task: The task object.
        :raises InvalidParameterValue: If iSCSI is identified and
                                       iPXE is disabled.
        :raises StorageError: If the number of wwpns is not equal to
                              the number of wwnns
        :returns: Dictionary containing iscsi_found and fc_found
                  keys with boolean values representing if the
                  helper found that connector type configured
                  for the node.
        """

        node = task.node
        iscsi_uuids_found = []
        wwpn_found = 0
        wwnn_found = 0
        ipxe_enabled = False
        if 'ipxe_boot' in task.driver.boot.capabilities:
            ipxe_enabled = True

        for connector in task.volume_connectors:
            if (connector.type in VALID_ISCSI_TYPES
                    and connector.connector_id is not None):
                iscsi_uuids_found.append(connector.uuid)
                if not ipxe_enabled:
                    msg = _("The [pxe]/ipxe_enabled option must "
                            "be set to True or the boot interface "
                            "must be set to ``ipxe`` to support network "
                            "booting to an iSCSI volume.")
                    self._fail_validation(task, msg)

            if (connector.type in VALID_FC_TYPES
                    and connector.connector_id is not None):
                # NOTE(TheJulia): Unlike iSCSI with cinder, we have no need
                # to warn about multiple IQN entries, since we are able to
                # submit multiple fibre channel WWPN entries.
                if connector.type == 'wwpn':
                    wwpn_found += 1
                if connector.type == 'wwnn':
                    wwnn_found += 1
        if len(iscsi_uuids_found) > 1:
            LOG.warning("Multiple possible iSCSI connectors, "
                        "%(iscsi_uuids_found)s found, for node %(node)s. "
                        "Only the first iSCSI connector, %(iscsi_uuid)s, "
                        "will be utilized.",
                        {'node': node.uuid,
                         'iscsi_uuids_found': iscsi_uuids_found,
                         'iscsi_uuid': iscsi_uuids_found[0]})
        if wwpn_found != wwnn_found:
            msg = _("Cinder requires both wwnn and wwpn entries for FCoE "
                    "connections. There must be a wwpn entry for every wwnn "
                    "entry. There are %(wwpn)d wwpn entries and %(wwnn)s wwnn "
                    "entries.") % {'wwpn': wwpn_found, 'wwnn': wwnn_found}
            self._fail_validation(task, msg, exception.StorageError)
        return {'fc_found': wwpn_found >= 1,
                'iscsi_found': len(iscsi_uuids_found) >= 1}

    def _validate_targets(self, task, found_types, iscsi_boot, fc_boot):
        """Validate target information helper.

        Enumerates through all target objects and identifies if
        iSCSI or Fibre Channel targets are present, and matches the
        connector capability of the node.

        :param task: The task object.
        :param found_types: Dictionary containing boolean values returned
                            from the _validate_connectors helper method.
        :param iscsi_boot: Boolean value indicating if iSCSI boot operations
                           are available.
        :param fc_boot: Boolean value indicating if Fibre Channel boot
                        operations are available.
        :raises: InvalidParameterValue
        """

        for volume in task.volume_targets:
            if volume.volume_id is None:
                msg = (_("volume_id missing from target %(id)s.") %
                       {'id': volume.uuid})
                self._fail_validation(task, msg)

            # NOTE(TheJulia): We should likely consider incorporation
            # of the volume boot_index field, however it may not be
            # relevant to the checks we perform here as in the end a
            # FC volume "attached" to a node is a valid configuration
            # as well.
            # TODO(TheJulia): When we create support in nova to record
            # that a volume attachment is going to take place, we will
            # likely need to match the driver_volume_type field to
            # our generic volume_type field. NB The LVM driver appears
            # to not use that convention in cinder, as it is freeform.
            if volume.volume_type == 'fibre_channel':
                if not fc_boot and volume.boot_index == 0:
                    msg = (_("Volume target %(id)s is configured for "
                             "'fibre_channel', however the capability "
                             "'fibre_channel_boot' is not set on node.") %
                           {'id': volume.uuid})
                    self._fail_validation(task, msg)
                if not found_types['fc_found']:
                    msg = (_("Volume target %(id)s is configured for "
                             "'fibre_channel', however no Fibre Channel "
                             "WWPNs are configured for the node volume "
                             "connectors.") %
                           {'id': volume.uuid})
                    self._fail_validation(task, msg)

            elif volume.volume_type == 'iscsi':
                if not iscsi_boot and volume.boot_index == 0:
                    msg = (_("Volume target %(id)s is configured for "
                             "'iscsi', however the capability 'iscsi_boot' "
                             "is not set for the node.") %
                           {'id': volume.uuid})
                    self._fail_validation(task, msg)
                if not found_types['iscsi_found']:
                    msg = (_("Volume target %(id)s is configured for "
                             "'iscsi', however no iSCSI connectors are "
                             "configured for the node.") %
                           {'id': volume.uuid})
                    self._fail_validation(task, msg)
            else:
                # NOTE(TheJulia); The note below needs to be updated
                # whenever support for additional volume types are added.
                msg = (_("Volume target %(id)s is of an unknown type "
                         "'%(type)s'. Supported types: 'iscsi' or "
                         "'fibre_channel'") %
                       {'id': volume.uuid, 'type': volume.volume_type})
                self._fail_validation(task, msg)

    def validate(self, task):
        """Validate storage_interface configuration for Cinder usage.

        In order to provide fail fast functionality prior to nodes being
        requested to enter the active state, this method performs basic
        checks of the volume connectors, volume targets, and operator
        defined capabilities. These checks are to help ensure that we
        should have a compatible configuration prior to activating the
        node.

        :param task: The task object.
        :raises: InvalidParameterValue If a misconfiguration or mismatch
                 exists that would prevent storage the cinder storage
                 driver from initializing attachments.
        """

        found_types = self._validate_connectors(task)
        node = task.node
        iscsi_boot = strutils.bool_from_string(
            utils.get_node_capability(node, 'iscsi_boot'))
        fc_boot = strutils.bool_from_string(
            utils.get_node_capability(node, 'fibre_channel_boot'))

        # Validate capability configuration against configured volumes
        # such that we raise errors for missing connectors if the
        # boot capability is defined.
        if iscsi_boot and not found_types['iscsi_found']:
            valid_types = ', '.join(VALID_ISCSI_TYPES)
            msg = (_("In order to enable the 'iscsi_boot' capability for "
                     "the node, an associated volume_connector type "
                     "must be valid for iSCSI (%(options)s).") %
                   {'options': valid_types})
            self._fail_validation(task, msg)

        if fc_boot and not found_types['fc_found']:
            valid_types = ', '.join(VALID_FC_TYPES)
            msg = (_("In order to enable the 'fibre_channel_boot' capability "
                     "for the node, an associated volume_connector type must "
                     "be valid for Fibre Channel (%(options)s).") %
                   {'options': valid_types})
            self._fail_validation(task, msg)

        self._validate_targets(task, found_types, iscsi_boot, fc_boot)

    def attach_volumes(self, task):
        """Informs the storage subsystem to attach all volumes for the node.

        :param task: The task object.
        :raises: StorageError If an underlying exception or failure
                              is detected.
        """
        node = task.node
        targets = [target.volume_id for target in task.volume_targets]

        # If there are no targets, then we have nothing to do.
        if not targets:
            return

        connector = self._generate_connector(task)
        try:
            connected = cinder.attach_volumes(task, targets, connector)
        except exception.StorageError as e:
            with excutils.save_and_reraise_exception():
                LOG.error("Error attaching volumes for node %(node)s: "
                          "%(err)s", {'node': node.uuid, 'err': e})
                self.detach_volumes(task, connector=connector,
                                    aborting_attach=True)

        if len(targets) != len(connected):
            LOG.error("The number of volumes defined for node %(node)s does "
                      "not match the number of attached volumes. Attempting "
                      "detach and abort operation.", {'node': node.uuid})
            self.detach_volumes(task, connector=connector,
                                aborting_attach=True)
            raise exception.StorageError(("Mismatch between the number of "
                                          "configured volume targets for "
                                          "node %(uuid)s and the number of "
                                          "completed attachments.") %
                                         {'uuid': node.uuid})

        for volume in connected:
            # Volumes that were already attached are
            # skipped. Updating target volume properties
            # for these volumes is nova's responsibility.
            if not volume.get('already_attached'):
                volume_uuid = volume['data']['ironic_volume_uuid']
                targets = objects.VolumeTarget.list_by_volume_id(task.context,
                                                                 volume_uuid)

                for target in targets:
                    target.properties = volume['data']
                    target.save()

    def detach_volumes(self, task, connector=None, aborting_attach=False):
        """Informs the storage subsystem to detach all volumes for the node.

        This action is retried in case of failure.

        :param task: The task object.
        :param connector: The dictionary representing a node's connectivity
                          as defined by _generate_connector(). Generated
                          if not passed.
        :param aborting_attach: Boolean representing if this detachment
                                was requested to handle aborting a
                                failed attachment
        :raises: StorageError If an underlying exception or failure
                              is detected.
        """
        # TODO(TheJulia): Ideally we should query the cinder API and reconcile
        # or add any missing volumes and initiate detachments.
        node = task.node
        targets = [target.volume_id for target in task.volume_targets]

        # If there are no targets, then we have nothing to do.
        if not targets:
            return

        if not connector:
            connector = self._generate_connector(task)

        @tenacity.retry(
            retry=tenacity.retry_if_exception_type(exception.StorageError),
            stop=tenacity.stop_after_attempt(CONF.cinder.action_retries + 1),
            wait=tenacity.wait_fixed(CONF.cinder.action_retry_interval),
            reraise=True)
        def detach_volumes():
            try:
                # NOTE(TheJulia): If the node is in ACTIVE state, we can
                # tolerate failures detaching as the node is likely being
                # powered down to cause a detachment event.
                allow_errors = (task.node.provision_state == states.ACTIVE
                                or aborting_attach and outer_args['attempt']
                                > 0)
                cinder.detach_volumes(task, targets, connector,
                                      allow_errors=allow_errors)
            except exception.StorageError as e:
                with excutils.save_and_reraise_exception():
                    # NOTE(TheJulia): In the event that the node is not in
                    # ACTIVE state, we need to fail hard as we need to ensure
                    # all attachments are removed.
                    if aborting_attach:
                        msg_format = ("Error on aborting volume detach for "
                                      "node %(node)s: %(err)s.")
                    else:
                        msg_format = ("Error detaching volume for "
                                      "node %(node)s: %(err)s.")
                    msg = (msg_format) % {'node': node.uuid,
                                          'err': e}
                    if outer_args['attempt'] < CONF.cinder.action_retries:
                        outer_args['attempt'] += 1
                        msg += " Re-attempting detachment."
                        LOG.warning(msg)
                    else:
                        LOG.error(msg)

        # NOTE(mjturek): This dict is used by detach_volumes to determine
        # if this is the last attempt. This is a dict rather than an int
        # so that it is mutable by the inner function. In python3 this is
        # possible with the 'nonlocal' keyword which is unfortunately not
        # available in python2.
        outer_args = {'attempt': 0}
        detach_volumes()

    def should_write_image(self, task):
        """Determines if deploy should perform the image write-out.

        :param task: The task object.
        :returns: True if the deployment write-out process should be
                  executed.
        """
        # NOTE(TheJulia): There is no reason to check if a root volume
        # exists here because if the validation has already been passed
        # then we know that there should be a volume. If there is an
        # image_source, then we should expect to write it out.
        instance_info = task.node.instance_info
        if 'image_source' not in instance_info:
            for volume in task.volume_targets:
                if volume['boot_index'] == 0:
                    return False
        return True

    def _generate_connector(self, task):
        """Generate cinder connector value based upon the node.

        Generates cinder compatible connector information for the purpose of
        attaching volumes. Translation: We need to tell the storage where and
        possibly how we can connect.

        Supports passing iSCSI information in the form of IP and IQN records,
        as well as Fibre Channel information in the form of WWPN addresses.
        Fibre Channel WWNN addresses are also sent, however at present in-tree
        Cinder drivers do not utilize WWNN addresses.

        If multiple connectors exist, the request will be filed with
        MultiPath IO being enabled.

        A warning is logged if an unsupported volume type is encountered.

        :params task: The task object.

        :returns: A dictionary data structure similar to:
                    {'ip': ip,
                     'initiator': iqn,
                     'multipath: True,
                     'wwpns': ['WWN1', 'WWN2']}
        :raises: StorageError upon no valid connector record being identified.
        """
        data = {}
        valid = False
        for connector in task.volume_connectors:
            if 'iqn' in connector.type and 'initiator' not in data:
                data['initiator'] = connector.connector_id
                valid = True
            elif 'ip' in connector.type and 'ip' not in data:
                data['ip'] = connector.connector_id
            # TODO(TheJulia): Translate to, or generate an IQN.
            elif 'wwpn' in connector.type:
                data.setdefault('wwpns', []).append(connector.connector_id)
                valid = True
            elif 'wwnn' in connector.type:
                data.setdefault('wwnns', []).append(connector.connector_id)
                valid = True
            else:
                # TODO(jtaryma): Add handling of type 'mac' with MAC to IP
                #                translation.
                LOG.warning('Node %(node)s has a volume_connector (%(uuid)s) '
                            'defined with an unsupported type: %(type)s.',
                            {'node': task.node.uuid,
                             'uuid': connector.uuid,
                             'type': connector.type})
        if not valid:
            valid_types = ', '.join(VALID_FC_TYPES + VALID_ISCSI_TYPES)
            msg = (_('Insufficient or incompatible volume connection '
                     'records for node %(uuid)s. Valid connector '
                     'types: %(types)s') %
                   {'uuid': task.node.uuid, 'types': valid_types})
            LOG.error(msg)
            raise exception.StorageError(msg)

        # NOTE(TheJulia): Hostname appears to only be used for logging
        # in cinder drivers, however that may not always be true, and
        # may need to change over time.
        data['host'] = task.node.uuid
        if len(task.volume_connectors) > 1 and len(data) > 1:
            data['multipath'] = True

        return data


'''
Get distribution of positive and negative sentiment probability for supplied text.
'''

from nltk.tokenize import casual_tokenize
import build_model, helpers

def get_sentiment(classifier, tweets, keep_status=True):
	'''
	Takes tweets as a list of dictionaries.
	Returns tweets as a list of dictionaries with sentiment labels.

	Arguments:
		classifier: NLTK Naive Bayes Classifier object.
		tweets: Tweets as a list of dictionaries.
		keep_status: Set as False to remove tweet status and reduce data size,
			keeping sentiment distribution and label.
	Returns:
		tweets: Tweets as a list of dictionaries.
	'''

	print('Starting text analysis...')
	print('Scoring tweets...')
	# get positive and negative probabilities for each tweet
	for tweet in tweets:
		custom_tokens = helpers.remove_noise(casual_tokenize(tweet['status']))
		dist = classifier.prob_classify(dict([token, True] for token in custom_tokens))

		# append probabilities to list
		pos_probability = dist.prob('Positive')
		neg_probability = dist.prob('Negative')

		# add sentiment probabilities to tweet dictionary
		try:
			tweet['positive'] = pos_probability
			tweet['negative'] = neg_probability
		except Exception as e:
			print(e)

		# add sentiment label to tweet dictionary
		if pos_probability >= 0.9:
			tweet['label'] = 'Very Positive'
		elif pos_probability >= 0.7:
			tweet['label'] = 'Positive'
		elif pos_probability > 0.3 and neg_probability > 0.3:
			tweet['label'] = 'Neutral'
		elif neg_probability >= 0.9:
			tweet['label'] = 'Very Negative'
		elif neg_probability >= 0.7:
			tweet['label'] = 'Negative'
		else:
			tweet['label'] = 'None'

		# optional: remove tweet status to reduce data size (keeping sentiment distribution and label)
		if tweet['status'] and keep_status == False:
			del tweet['status']

	print('Text analysis complete!\n')

	return tweets


'''
791. Custom Sort String
https://leetcode.com/problems/custom-sort-string/
'''
import collections
from typing import List

from test_tool import assert_value

class Solution:
    def customSortString(self, order: str, s: str) -> str:
        idx_order = collections.defaultdict(lambda: -1)
        idx_order.update({c: i for i, c in enumerate(order)})
        idx_s = [idx_order[c] for c in s]
        suffix = ''.join(s[idx] for idx, i in enumerate(idx_s) if i == -1)
        idx_s = sorted(i for i in idx_s if i != -1)
        prefix = ''.join(order[i] for i in idx_s)

        return prefix + suffix

assert_value("cbad", Solution().customSortString, order="cba", s="abcd")
assert_value("cbad", Solution().customSortString, order="cbafg", s="abcd")
assert_value("eexvw", Solution().customSortString, order="exv", s="xwvee")


import os

from datetime import datetime

from helix.database.database import DatabaseObject
from helix.database.show import Show
from helix.database.sequence import Sequence
from helix.database.shot import Shot
from helix.database.element import Element
from helix.database.person import Person
import helix.environment.environment as env
import helix.utils.utils as utils

class Fix(DatabaseObject):
	TABLE = 'fixes'
	PK = 'id'
	STATUS = {
		0: 'new',
		1: 'assigned',
		2: 'ip',
		3: 'review',
		4: 'done'
	}
	PRIORITY = {
		0: 'Very Low',
		1: '',
		2: '',
		3: 'Normal',
		4: '',
		5: 'Medium',
		6: '',
		7: '',
		8: '',
		9: '',
		10: 'Critical'
	}

	TYPE_FIX = 'fix'
	TYPE_TASK = 'task'
	TYPES = [TYPE_TASK, TYPE_FIX]

	def __init__(self, fixType, title, body, dept, show=None, sequence=None, shot=None, clipName=None, elementName=None, elementType=None, author=None, status=STATUS[0], priority=3, dummy=False):
		self.table = Fix.TABLE
		self.commentList = []

		if dummy:
			return

		self.title = title
		self.body = body
		self.show = show if show else env.getEnvironment('show')
		self.sequence = sequence
		self.shot = shot
		self.elementName = elementName
		self.elementType = elementType
		self._exists = None

		self.sequenceId = None
		self.shotId = None
		self.elementId = None

		if not title:
			raise ValueError('Must specify a fix title')

		if not body:
			raise ValueError('Must specify details for the fix in the body text')

		if not self.show:
			raise ValueError('Tried to fallback to environment-set show, but it was null.')

		fetched = self.exists(fetch=True)

		if fetched:
			self.unmap(fetched)
			self._exists = True
		else:
			creationInfo = env.getCreationInfo(format=False)
			self.type = fixType.lower()
			self.author = author if author else creationInfo[0]
			self.creation = creationInfo[1]
			self.status = status if status in Fix.STATUS.values() else Fix.STATUS[0]
			self.priority = priority if priority in Fix.PRIORITY.keys() else 3
			self.fixer = None
			self.fix_date = None
			self.deadline = None
			self.assign_date = None
			self.num = Fix.nextFixNum(self.show)
			self.for_dept = dept.lower()

			if self.type not in Fix.TYPES:
				raise ValueError('Type must be one of: {}, not: {}'.format(', '.join(Fix.TYPES, self.type)))

			if self.for_dept not in env.cfg.departments and self.for_dept != 'general':
				raise ValueError('Invalid department ({}) to assign fix to. Options are: {}'.format(self.for_dept, ', '.join(['general'] + env.cfg.departments)))

			s = Show.fromPk(self.show)

			if not s:
				raise ValueError('No such show: {}'.format(show))

			p = Person(self.author)

			if not p.exists():
				raise ValueError('No such user: {}'.format(self.author))

			if self.sequence is not None:
				try:
					self.sequence = int(self.sequence)
				except ValueError:
					raise ValueError('Sequence number must be a number, not: {}'.format(self.sequence))

				sq = Sequence(self.sequence, show=self.show)

				if not sq.exists():
					raise ValueError('No such sequence {} in show {}'.format(sq.num, sq.show))
				else:
					self.sequenceId = sq.id

			if self.shot is not None and self.sequence is not None:
				try:
					self.shot = int(shot)
				except ValueError:
					raise ValueError('Sequence number must be a number, not: {}'.format(shot))

				sh = Shot(self.shot, self.sequence, show=self.show, clipName=clipName)

				if not sh.exists():
					raise ValueError('No such shot {}{} in sequence {} in show {}'.format(sh.num, sh.clipName if sh.clipName else '', sh.sequence, sh.show))
				else:
					self.shotId = sh.id

			if self.elementType:
				el = Element(self.elementName, self.elementType, self.show, self.sequence, self.shot)

				if not el.exists():
					raise ValueError(
						'No such element {} ({}){}{} in show {}'.format(
							el.name,
							el.type,
							' in shot {}'.format(el.shot) if el.shot else '',
							' in sequence {}'.format(el.sequence) if el.sequence else '',
							el.show
						)
					)
				else:
					self.elementId = el.id

	def addComment(self, text):
		text = text.replace("'", '').replace('"', '') # Temp work around until I can either escape quotes or properly update the DB with the value
		self.commentList.append((env.USER, str(env.getCreationInfo(format=False)[1]), text))
		self.set('comments', self.comments)

	@property
	def comments(self):
		return str(self.commentList).replace("'", '"')

	@comments.setter
	def comments(self, comments):
		try:
			if comments:
				self.commentList = eval(comments)
			else:
				self.commentList = []
		except:
			self.commentList = []

	@property
	def id(self):
		return super(Fix, self)._id(
			'{}_{}_{}_{}_{}_{}'.format(
				self.show,
				self.sequence if self.sequence else '',
				self.shot if self.shot else '',
				self.elementId if self.elementId else '',
				self.title,
				self.body
			)
		)

	@property
	def pk(self):
		return Fix.PK

	@property
	def target(self):
		if self.elementId:
			return Element.fromPk(self.elementId)
		elif self.shotId:
			return Shot.fromPk(self.shotId)
		elif self.sequenceId:
			return Sequence.fromPk(self.sequenceId)
		elif self.show:
			return Show.fromPk(self.show)

	@property
	def bid(self):
		if self.deadline is not None and self.creation is not None:
			return (utils.dbTimetoDt(self.deadline) - utils.dbTimetoDt(self.creation)).days + 1

		return None

	@property
	def days(self):
		if self.deadline is not None:
			return (utils.dbTimetoDt(self.deadline) - datetime.now()).days + 1

		return None

	@staticmethod
	def nextFixNum(show=None):
		show = show if show else env.show
		if not show:
			raise ValueError('Tried to fallback to environment-set show, but it was null.')

		from helix.database.sql import Manager

		with Manager(willCommit=False) as mgr:
			res = mgr.connection().execute(
				'''
					SELECT MAX(num) from {} WHERE show='{}'
				'''.format(Fix.TABLE, show)
			).fetchone()

			if res and res[0]:
				return int(res[0]) + 1
			else:
				return 1

	@staticmethod
	def byNum(fixNum, show=None):
		show = show if show else env.show
		if not show:
			raise ValueError('Tried to fallback to environment-set show, but it was null.')

		from helix.database.sql import Manager

		with Manager(willCommit=False) as mgr:
			res = mgr.connection().execute(
				'''
					SELECT * from {} WHERE num='{}' AND show='{}'
				'''.format(Fix.TABLE, fixNum, show)
			).fetchone()

			if res:
				return Fix('.', '.', show).unmap(res)
			else:
				return None

	@staticmethod
	def dummy():
		return Fix(None, None, None, None, dummy=True)