import math, json
import bpy, mathutils

# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty

class TransverseMercator:
    radius = 6378137

    def __init__(self, **kwargs):
        # setting default values
        self.lat = 0 # in degrees
        self.lon = 0 # in degrees
        self.k = 1 # scale factor

        for attr in kwargs:
            setattr(self, attr, kwargs[attr])
        self.latInRadians = math.radians(self.lat)

    def fromGeographic(self, lat, lon):
        lat = math.radians(lat)
        lon = math.radians(lon-self.lon)
        B = math.sin(lon) * math.cos(lat)
        x = 0.5 * self.k * self.radius * math.log((1+B)/(1-B))
        y = self.k * self.radius * ( math.atan(math.tan(lat)/math.cos(lon)) - self.latInRadians )
        return (x,y)

    def toGeographic(self, x, y):
        x = x/(self.k * self.radius)
        y = y/(self.k * self.radius)
        D = y + self.latInRadians
        lon = math.atan(math.sinh(x)/math.cos(D))
        lat = math.asin(math.sin(D)/math.cosh(x))

        lon = self.lon + math.degrees(lon)
        lat = math.degrees(lat)
        return (lat, lon)

class GeoJson(bpy.types.Operator, ExportHelper):
    bl_idname = "prk.export_geojson"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export GeoJSON"

    # ExportHelper mixin class uses this
    filename_ext = ".json"

    filter_glob = StringProperty(default="*.json", options={'HIDDEN'})

    # List of operator properties, the attributes will be assigned
    # to the class instance from the operator settings before calling.
    use_setting = BoolProperty(
            name="Example Boolean",
            description="Example Tooltip",
            default=True,
            )

    type = EnumProperty(
            name="Example Enum",
            description="Choose between two items",
            items=(('OPT_A', "First Option", "Description one"),
                   ('OPT_B', "Second Option", "Description two")),
            default='OPT_A',
            )

    def execute(self, context):
        features = []
        data = {
            "type": "FeatureCollection",
            "features": features
        }
        rotationMatrix = mathutils.Matrix.Rotation(math.radians(context.scene["heading"]), 4, "Z")
        projection = TransverseMercator(lat=context.scene["latitude"], lon=context.scene["longitude"])
        # iterate through all rooms
        for o in context.scene.objects:
            if not ("t" in o and o["t"] == "room"):
                continue
            coords = []
            # iterate through EMPTYs that control the vertices of polygon of the area
            for m in o.modifiers:
                e = m.object
                p = rotationMatrix * e.parent.matrix_world * e.location
                p = projection.toGeographic(p.x, p.y)
                coords.append( (p[1], p[0]) )
            features.append({
                "type": "Feature",
                "geometry": {
                    "type": "Polygon",
                    "coordinates": (coords,)
                },
                "properties": {
                    "name": o.name
                }
            })
        f = open(self.filepath, 'w', encoding="utf-8")
        f.write(json.dumps(data))
        f.close()

        return {'FINISHED'}


import numpy as np

def load_detections(det_file, num_frames):
    det_boxes = []
    det_scores = []
    raw_data = np.genfromtxt(det_file, delimiter=',')
    for frame_idx in range(num_frames):
        idx = np.where(raw_data[:, 0] == frame_idx+1)
        if idx[0].size:
            det_boxes.append(np.stack(raw_data[idx], axis=0)[:, 2:6])
            det_scores.append(np.stack(raw_data[idx], axis=0)[:, 6])
        else:
            det_boxes.append(np.empty(shape=(0, 4)))
            det_scores.append(np.empty(shape=(0,)))
    return det_boxes, det_scores


import asyncio

import aioconsole
import json
from abc import abstractmethod
from prompt_toolkit import print_formatted_text
from prompt_toolkit.formatted_text import FormattedText
from monstr.event.event import Event
from monstr.encrypt import Keys
from monstr.inbox import Inbox
from monstr.client.client import Client
from monstr.ident.profile import Profile, NIP5Helper, NIP5Error
from monstr.ident.event_handlers import ProfileEventHandler
from monstr.signing import SignerInterface
from monstr.entities import Entities
from monstr.util import util_funcs

class EventPrinter:

    # print the event
    @abstractmethod
    async def aprint_event(self, the_client: Client, sub_id, evt: Event):
        pass

    # any other msgs
    @abstractmethod
    async def astatus(self, status: str):
        pass

class WrappedEventPrinter(EventPrinter):
    """
        support events that have been wrapped inside a public inbox
        also adds method for getting the decrypted content of an event
    """
    def __init__(self,
                 as_sign: SignerInterface = None,
                 inboxes: [Inbox] = None,
                 # kinds to output - probably a filter would be more flexible here... None will not restrict at all
                 kinds: {int} = None,
                 # kinds that we'll be decrypted
                 encrypted_kinds: {int} = None
                 ):

        # if given will be used for decrypting
        self._as_sign = as_sign

        self._inboxes = inboxes
        # lookups for dealing with inboxes
        c_i: Inbox
        self._inbox_view_keys = {}
        self._inbox_decode_map = {}

        asyncio.create_task(self._ready_inboxes())

        self._kinds = kinds

        self._encrypted_kinds = {}
        # by default nothing will be decrypted - probably at least want to set as {Event.KIND_ENCRYPT}
        if encrypted_kinds is not None:
            self._encrypted_kinds = encrypted_kinds

        self._ready = False

    async def wait_ready(self):
        while not self._ready:
            await asyncio.sleep(0.1)

    async def _ready_inboxes(self):
        # lookups for dealing with inboxes
        if self._inboxes:
            c_i: Inbox
            self._inbox_view_keys = {await c_i.pub_key for c_i in self._inboxes}
            self._inbox_decode_map = {await c_i.pub_key: c_i for c_i in self._inboxes}
        self._ready = True

    def event_needs_unwrap(self, evt: Event) -> bool:
        return evt.pub_key in self._inbox_view_keys

    async def get_unwrapped_event(self, evt) -> Event:
        """
        unwraps evt, if unable you just get the orignal event back
        if you need to know that you have an event that needed unwrapping and it failed you can check

        if event_needs_unwrap(evt) is True and evt.id == ret.id then we failed to unwrap an event that needed unwrap

        :param evt:
        :return:
        """

        ret = evt
        if self.event_needs_unwrap(evt):
            unwrapped_evt = await self._inbox_decode_map[evt.pub_key].unwrap_event(evt=evt,
                                                                                user_sign=self._as_sign)
            if unwrapped_evt:
                ret = unwrapped_evt

        return ret

    def event_needs_decrypt(self, evt: Event) -> bool:
        # if we've been given a signer and the event is in kinds we have been told are encrypted
        # we'll attempt to decrypt it with _as_sign
        return self._as_sign and evt.kind in self._encrypted_kinds

    async def get_decrypted_event(self, evt: Event) -> Event:
        """
        decrypts evt, if unable you just get the orignal event back
        checking failed to decrypt works same as unwrap except you need to compare the event content
        :param evt:
        :return:
        """
        # copy the event so we're working on a new obj
        ret = Event.from_JSON(evt.event_data())

        if self.event_needs_decrypt(evt):
            # for the public key we'll try all p_tags and the events public key with
            # are priv key, so we have a chance to decrypt non standard events also
            # we just exit first time we don't cause exception
            # (Like we produce from poster)
            for c_p_tag in [evt.pub_key] + evt.p_tags:
                try:
                    ret.content = await self._as_sign.decrypt_text(encrypt_text=ret.content,
                                                                   for_pub_k=c_p_tag)
                    # if we got here then we manage to decrypt so exit
                    break
                except Exception as e:

                    pass

        return ret

    # will we output the event? This probably is mainly useful if dealing with wrapped events
    # where we may want to check again that they pass some filter after unwrapping
    def output_event(self, evt: Event) -> bool:
        return self._kinds is None or evt.kind in self._kinds

    # print the event
    async def aprint_event(self, the_client: Client, sub_id, evt: Event):
        # you probably want to override this
        aioconsole.aprint(await self.get_unwrapped_event(evt))

    # any other msgs
    @abstractmethod
    async def astatus(self, status: str):
        return

class JSONPrinter(WrappedEventPrinter):

    def __init__(self,
                 as_sign: SignerInterface = None,
                 inboxes: [Inbox] = None,
                 kinds=None,
                 encrypted_kinds: set = None
                 ):

        super().__init__(as_sign=as_sign,
                         inboxes=inboxes,
                         encrypted_kinds=encrypted_kinds)

    # outputs event in raw format
    async def aprint_event(self, the_client: Client, sub_id, evt: Event):
        # unwrap and decrypt event as required - if unable or uneeded left as is
        evt = await self.get_unwrapped_event(evt)
        if self.output_event(evt):
            evt = await self.get_decrypted_event(evt)
            await aioconsole.aprint(json.dumps(evt.event_data()))

    async def astatus(self, status: str):
        return

class ContentPrinter(WrappedEventPrinter):

    def __init__(self,
                 as_sign:SignerInterface = None,
                 inboxes: [Inbox] = None,
                 kinds=None,
                 encrypted_kinds: set = None
                 ):

        super().__init__(as_sign=as_sign,
                         inboxes=inboxes,
                         kinds=kinds,
                         encrypted_kinds=encrypted_kinds)

    # output just the content of an event
    async def aprint_event(self, the_client: Client, sub_id, evt: Event):
        # unwrap and decrypt event as required - if unable or uneeded left as is
        evt = await self.get_unwrapped_event(evt)
        if self.output_event(evt):
            evt = await self.get_decrypted_event(evt)
            content = evt.content
            await aioconsole.aprint(content)

    async def astatus(self, status: str):
        await aioconsole.aprint(status)

class FormattedEventPrinter(WrappedEventPrinter):

    def __init__(self,
                 profile_handler: ProfileEventHandler,
                 as_user: Profile = None,
                 as_sign: SignerInterface = None,
                 inboxes: [Inbox] = None,
                 show_pub_key: bool = False,
                 show_tags: [str] = None,
                 entities: bool = False,
                 nip5helper: NIP5Helper = None,
                 kinds=None,
                 encrypted_kinds=None):

        self._profile_handler = profile_handler

        self._as_user = as_user

        # styles for colouring user output
        # us
        self._as_user_style = 'bold ForestGreen'
        # someone we follow
        self._as_user_contact_style = 'ForestGreen'
        # anyone else
        self._as_user_non_contact_style = 'FireBrick bold'

        # output npub, note instead of hex for event_id and pks
        self._entities = entities

        # at end show pubkey
        self._show_pub_key = show_pub_key
        # output these tags or all if ['*']
        self._show_tags = show_tags
        if self._show_tags:
            self._show_tags = set(self._show_tags)

        # if supplied then any nip5s will be checked and colored green if good
        self._nip5helper = nip5helper
        # styles used when checking nip5s
        self._nip5_valid_style = 'ForestGreen'
        self._nip5_invalid_style = 'FireBrick bold'

        # events that we expect to be encrypted
        self._encrypted_kinds = encrypted_kinds
        if self._encrypted_kinds is None:
            self._encrypted_kinds = {Event.KIND_ENCRYPT}

        super().__init__(as_sign=as_sign,
                         inboxes=inboxes,
                         kinds=kinds,
                         encrypted_kinds=encrypted_kinds)

    async def aprint_event(self, the_client: Client, sub_id, evt: Event):
        unwrapped_evt = evt
        if self.event_needs_unwrap(evt):
            unwrapped_evt = await self.get_unwrapped_event(evt)

        if self.output_event(unwrapped_evt):
            print_formatted_text(FormattedText(await self.get_event_header(evt)))
            print_formatted_text(FormattedText(await self.get_event_content(evt, unwrapped_evt)))
            print_formatted_text(FormattedText(await self.get_event_footer(evt)))

    async def astatus(self, status: str):
        # anything thats not the event
        await aioconsole.aprint(status)

    async def _get_profile(self, key) -> Profile:
        # will error if key is not valid, doesn't break anything for us but maybe we should fix?
        ret = (await self._profile_handler.aget_profiles(pub_ks=key,
                                                         create_missing=True))[0]
        return ret

    def _is_user(self, key):
        return self._as_user is not None and self._as_user.public_key == key

    async def _is_contact(self, key):
        ret = False
        if self._as_user is not None:
            if not self._as_user.contacts_is_set():
                await self._profile_handler.aload_contacts(self._as_user)
            ret = key in self._as_user.contacts.follow_keys()

        return ret

    async def _get_pub_k_style(self, pub_k):
        ret = ''
        # no styling if no as_user
        if self._as_user:
            if self._is_user(pub_k):
                ret = self._as_user_style
            elif await self._is_contact(pub_k):
                ret = self._as_user_contact_style
            else:
                ret = self._as_user_non_contact_style

        return ret

    async def get_event_header(self,
                                     evt: Event,
                                     depth=0) -> []:
        p: Profile

        txt_arr = []
        depth_align = ''.join(['\t'] * depth)
        txt_arr.append(('', '\n%s--- ' % depth_align))
        create_p = await self._get_profile(evt.pub_key)

        style = await self._get_pub_k_style(evt.pub_key)

        name = create_p.name

        nip05_domain = None
        name_nip05match = False

        if create_p.nip05:
            nip05_split = create_p.nip05.split('@')
            if len(nip05_split) > 1:
                nip05_name = nip05_split[0]
                nip05_domain = nip05_split[1]
                if name is None and nip05_domain:
                    name = nip05_name
                name_nip05match = name.lower() == nip05_name.lower() or nip05_name == '_'

        if name is None or name.replace(' ', '') == '':
            name = util_funcs.str_tails(create_p.public_key)

        txt_arr.append((style, name))

        # nip5 info if any
        if create_p.nip05:
            nip5_style = ''

            if self._nip5helper:
                nip5_style = self._nip5_invalid_style
                try:
                    if await self._nip5helper.is_valid(create_p.nip05, create_p.public_key):
                        nip5_style = self._nip5_valid_style
                except NIP5Error as ne:
                    pass

            if name_nip05match:
                txt_arr.append((nip5_style, f'@{nip05_domain}'))
            else:
                txt_arr.append((nip5_style, f' ({create_p.nip05})'))

        txt_arr.append(('', ' ---'))

        to_list = []
        sep = False

        # this will force fetch if needed all the profiles we need
        # so that a fetch won't be made in the next loop for each p tag
        await self._profile_handler.aget_profiles(pub_ks=[pk for pk in evt.p_tags],
                                                 create_missing=True)

        for c_pk in evt.p_tags:
            style = await self._get_pub_k_style(c_pk)

            to_p = await self._get_profile(c_pk)
            if sep:
                to_list.append(('', ', '))

            to_list.append((style, to_p.display_name()))

            sep = True

        if to_list:
            txt_arr.append(('', '\n%s-> ' % depth_align, ))
            txt_arr = txt_arr + to_list

        subject_tags = evt.get_tags_value('subject')
        if subject_tags:
            txt_arr.append(('', '\n%s' % depth_align))
            txt_arr.append(('', '[subject - %s]' % ','.join(subject_tags)))

        txt_arr.append(('','\n%s' % depth_align))

        id = evt.id
        if self._entities:
            id = Entities.encode('note', id)

        txt_arr.append(('cyan', id))
        txt_arr.append(('','@'))
        txt_arr.append(('', '%s' % evt.created_at))

        # extra kind specific header
        if evt.kind == Event.KIND_CHANNEL_MESSAGE and evt.e_tags:
            txt_arr.append(('', f'\nchannel: {evt.e_tags[0]}'))

        return txt_arr

    async def get_event_footer(self,
                               evt: Event,
                               depth=0):

        txt_arr = []
        depth_align = ''.join(['\t'] * depth)

        if self._show_pub_key:
            txt_arr.append(('', f'\n{depth_align}'))
            txt_arr.append(('cyan', '-pubkey-\n'))
            out_pk = evt.pub_key
            if self._entities:
                out_pk = Entities.encode('npub', out_pk)
            txt_arr.append(('', depth_align))
            txt_arr.append((await self._get_pub_k_style(evt.pub_key), f'{out_pk}'))

        if self._show_tags:
            txt_arr.append(('', f'\n{depth_align}'))
            txt_arr.append(('cyan', f'-tags-'))

            all_tag_names = list(evt.tags.tag_names)
            all_tag_names.sort()

            for c_tag_name in all_tag_names:

                if '*' in self._show_tags or c_tag_name in self._show_tags:
                    # tag header
                    txt_arr.append(('cyan', f'\n{depth_align}{c_tag_name}'))

                    # now output values
                    for c_tag_v in evt.get_tags(c_tag_name):
                        v_style = ''
                        if c_tag_name == 'p':
                            v_style = await self._get_pub_k_style(c_tag_v[0])

                        txt_arr.append(('', f'\n{depth_align}['))
                        sep = ''
                        for i, item in enumerate(c_tag_v):
                            item_val = item
                            if self._entities:
                                try:
                                    if c_tag_name == 'p':
                                        item_val = Entities.encode('npub', item_val)
                                    elif c_tag_name == 'e':
                                        item_val = Entities.encode('note', item_val)

                                # invalid data prob?
                                except Exception as e:
                                    pass
                            if c_tag_name == 'p' and i == 0:
                                txt_arr.append((v_style, f'{item_val}'))
                            else:
                                txt_arr.append(('', f'{sep}{item_val}'))
                            sep = ','

                        txt_arr.append(('', ']'))

        # actualy do the output if any
        return txt_arr

    async def highlight_tags(self, evt: Event, default_style='', depth=0):
        replacements = {}
        arr_str = []
        ret = []
        p: Profile
        depth_align = ''.join(['\t'] * depth)

        # map tag replacements, only p tag currently
        # note this is for #[n] style to tags
        # there is also nostr:npub, etc.. maybe that is the new way? should be easy to add also...
        for i, tag_vals in enumerate(evt.tags):
            # def replacement is just grayed of the #[n] text
            r_v = {
                'text': f'#[{i}]',
                'style': 'gray'
            }

            if len(tag_vals) > 1:
                t_n = tag_vals[0]
                t_v = tag_vals[1]
                # we only do p replacements
                if t_n == 'p':
                    if Keys.is_valid_key(t_v):
                        p = await self._get_profile(t_v)

                        r_v = {
                            'text': p.display_name(),
                            'style': await self._get_pub_k_style(t_v)
                        }

                        # add replacements for npub and nostr:npub
                        if p.name:
                            npub = Entities.encode('npub', p.public_key)
                            replacements[f'nostr:{npub}'] = {
                                'text': f' {p.display_name()} ',
                                'style': await self._get_pub_k_style(t_v)
                            }

                replacements[f'#[{i}]'] = r_v

        for c_line in evt.content.splitlines():
            # ret.append(('', '\n'))
            ret.append(('', '\n'+depth_align))
            for c_word in c_line.split(' '):
                if c_word in replacements:
                    if arr_str:
                        ret.append((default_style, ' '.join(arr_str)))
                        arr_str = []

                    # TODO: cols should be based on if we follow
                    replacement = replacements[c_word]
                    ret.append((replacement['style'], replacement['text']))
                else:
                    arr_str.append(c_word)

            if arr_str:
                ret.append((default_style, ' '.join(arr_str)))
                arr_str = []

        return ret

    async def get_event_content(self, evt, unwrapped_evt):
        txt_arr = []
        print_depth = 0
        print_style = 'gray'
        decrypted_evt = await self.get_decrypted_event(unwrapped_evt)
        print_evt = evt
        content_done = False

        # FIXME - works but this logic is pretty crazy
        if self.event_needs_unwrap(evt):
            inbox_p: Profile = await self._get_profile(key=evt.pub_key)
            if evt.id != unwrapped_evt.id:
                print_depth = 1
                if self.event_needs_decrypt(unwrapped_evt):
                    txt_arr.append(('', f'\tencrypted evt in inbox({inbox_p.display_name()})-->'))
                    print_evt = decrypted_evt
                    if decrypted_evt.content != unwrapped_evt.content:
                        print_style = ''

                else:
                    txt_arr.append(('', '\tplaintext evt in inbox(%s)-->' % inbox_p.display_name()))
                    print_style = ''
                    print_evt = unwrapped_evt

                txt_arr += await self.get_event_header(print_evt, depth=print_depth)
                txt_arr += await self.highlight_tags(evt=print_evt,
                                                     default_style=print_style,
                                                     depth=print_depth)
                txt_arr += await self.get_event_footer(evt=print_evt,
                                                       depth=1)
                content_done = True

            else:
                txt_arr.append(('', f'\tunable to decrypt evt in inbox({inbox_p.display_name()})-->'))

        # basic events not via inbox
        else:
            if self.event_needs_decrypt(evt):
                print_evt = decrypted_evt
                if decrypted_evt.content != evt.content:
                    print_style = ''
            else:
                print_style = ''

        if not content_done:
            txt_arr += await self.highlight_tags(evt=print_evt,
                                                 default_style=print_style,
                                                 depth=print_depth)

        return txt_arr


from os import listdir

#Problem importer
#------------------------------------------------------------------------------
def importInst(fileName):
    file = open(fileName, 'r')
    inst = []
    for line in file:
        if not line.startswith("c") \
        and not line.startswith("p") \
        and not line.startswith("0") \
        and line.strip() \
        and not line.startswith("%") :
            line = line.split()
            line = [int(literal) for literal in line]
            inst.append(line)
    for clause in inst:
        clause.remove(0)
    file.close()
    return(inst)

#Solution Importer
#------------------------------------------------------------------------------
def importSol(fileName):
    file = open(fileName, 'r')
    sol = []
    for line in file:
        if not line.startswith("c"):
            sol = sol + line.split()
    file.close()
    sol = [value for value in sol if value not in ["v", "0"]]
    sol = [int(i) for i in sol]
    return(sol)

#SAT CHECKER
#------------------------------------------------------------------------------
def SAT(instance, solution):
    for clause in instance:
        clauseResult = False
        for literal in clause:
            if literal in solution:
                clauseResult = True
                break
        if clauseResult == False:
            return False, clause
    return True, clause

#RUN SAT CHECKER
#------------------------------------------------------------------------------
#set paths to data
inst_path = ".\\Lab-data\\Inst\\"
sol_path = ".\\Lab-data\\sols\\"
instances = [inst_path + i for i in listdir(inst_path)]
solutions = [sol_path + i for i in listdir(sol_path)]

##Simple Test Case
test_instance = [[1,-2, 3, 4], [-2, 3, -4, 1], [4, -2, 3, 1]]
test_solution = [-1,2,-3,4,5]
result, clause = SAT(test_instance, test_solution)
print("TestCase:")
print("The following clause is false:", clause)
print()

#Run all instances
for i in range(len(instances)):
    instance = importInst(instances[i])
    solution  = importSol(solutions[i])
    result, clause = SAT(instance, solution)
    print(instances[i])
    if result == True:
        print(result)
    else:
        print("The following clause is false:", clause)


#--------------------------JoyBot - Python Branch-------------------------#

import os,random,discord,pickle,json,io,contextlib,itertools
from discord.ext.tasks import asyncio
from discord.ext import commands
from discord.utils import find
from datetime import datetime
from decimal import Decimal
from shutil import copyfile
from heapq import nlargest
from pathlib import Path

#--------------------------Options-------------------------#

prefix="."
client = commands.Bot(command_prefix = prefix)
tabGoal = 25

cross = "❌"

yes = "👍"
no = "👎"

one = "1⃣"
two = "2⃣"
three = "3⃣"
four = "4⃣"
five = "5⃣"
six = "6⃣"
seven = "7⃣"
eight = "8⃣"
nine = "9⃣"
ten = "🔟"

admins_ids = [
246862123328733186,
202513952406503425,
253059354331316224
]

errorcharacter = "\u001b[31m"
resetcharacter = "\u001b[0m"

#--------------------------Variables-------------------------#

facesList={}
facesAmount={}
pollDict={}
scoreDict={}

allGifs = ["hugs","kisses","marry"]
gifsList={}
gifsAmount={}

ynpoll = [yes,no,cross]
numpoll = {

	1: one,
	2: two,
	3: three,
	4: four,
	5: five,
	6: six,
	7: seven,
	8: eight,
	9: nine,
	10: ten
}

with open('./data/token.txt', 'r') as file:
	botToken = file.read().replace('\n', '')

#--------------------------Functions-------------------------#

def inch_cm(convert):
	#CM
	try:
		if convert.isnumeric():
			inches = int(convert) / 2.54
			feet = 0
			while inches >= 13:
				feet += 1
				inches -= 12
			return str(feet)+"'"+str(round(inches))

		#Inches
		else:
			if "'" in convert:
				split = convert.split("'")
				if split[0].isnumeric():
					if split[1].isnumeric():
						inches = int(split[0]) * 12
						inches += int(split[1])
						cm = inches * 2.54
						return str(round(cm))+"cm"
					else:
						return False
				else:
					return False
			#Meters
			elif "." in convert:
				split = convert.split(".")
				if split[0].isnumeric():
					if split[1].isnumeric():
						return str(round(Decimal(convert)*100))+"cm"
					else:
						return False
				else:
					return False

			else:
				return False

	#CM again but the provided argument was a number
	except AttributeError:
		inches = int(convert) / 2.54
		feet = 0
		while inches >= 13:
			feet += 1
			inches -= 12
		return str(feet)+"'"+str(round(inches))

def stringPOP(string, pop):
	popped = ""
	for i in range(len(string)):
		if i != pop:
			popped = popped + string[i]
	return popped

#This spacifies
def spacifyFunction(text):
	endString=""
	for x in range(0,len(text)):
		endString=(endString+text[x]+" ")
	return endString

#Edit the poll dict
def pollDictMod(type, author, message):
	if type == "add":
		try:
			oldDict = pollDict[author]
		except KeyError:
			oldDict = []

		oldDict.append(message)
		pollDict[author] = oldDict

	elif type == "remove":
		try:
			oldDict = pollDict[author]
		except KeyError:
			return

		if message in oldDict:
			oldDict.remove(message)
			return True
	elif type == "list":
		return pollDict

	else:
		raise Exception(f"Invalid type of '{type}'!")

def scoreDictMod(type, author, amount=0):
	try:
		global scoreDict
		if type == "add":
			try:
				oldDict = scoreDict[author]
			except KeyError:
				oldDict = 0

			oldDict += amount
			scoreDict[author] = oldDict

		elif type == "minus":
			try:
				oldDict = scoreDict[author]
			except KeyError:
				return

			oldDict -= amount
			if oldDict <= 0:
				scoreDict.pop(author)

			else:
				scoreDict[author] = oldDict
		elif type == "one":
			try:
				oldDict = scoreDict[author]
			except KeyError:
				return 0
			return oldDict

		elif type == "list":
			return scoreDict

		else:
			raise Exception(f"Invalid type of '{type}'!")
	finally:
		with open("./data/bumpscores.pkl", "wb") as fp:
			pickle.dump(scoreDict, fp)

#Import all the faces from folder
def updateFaces(whichFolder=None):
	global facesAmount,facesList
	totalFaces=[]

	if whichFolder == None:
		print("Updating all faces...")

		facesList={}
		facesAmount={}

		for folder in os.listdir("./faces"):
			for filename in sorted(os.listdir("./faces/"+folder)):
				if filename.endswith(".png"):
					if filename[:-4].isdigit():
						totalFaces.append(filename)
					else:
						print(f"{errorcharacter}The file '{filename}' in folder '{folder}' was not named correctly!{resetcharacter}")
				else:
					print(f"{errorcharacter}The file '{filename}' in folder '{folder}' was not named correctly!{resetcharacter}")

			if len(totalFaces) != 0:
				facesList[folder] = totalFaces
				facesAmount[folder] = len(totalFaces)
				totalFaces=[]
			else:
				print(f"{errorcharacter}The folder {folder} did not have any valid pictures!{resetcharacter}")

		if facesAmount == {}:
			print(f"{errorcharacter}No faces loaded!{resetcharacter}\n")

	else:
		if os.path.exists(f"./faces/{whichFolder}"):
			print(f"Updating the '{whichFolder}' folder...")
			for filename in sorted(os.listdir("./faces/"+whichFolder)):
				if filename.endswith(".png"):
					if filename[:-4].isdigit():
						totalFaces.append(filename)
					else:
						print(f"{errorcharacter}The file '{filename}' in folder '{whichFolder}' was not named correctly!{resetcharacter}")
				else:
					print(f"{errorcharacter}The file '{filename}' in folder '{whichFolder}' was not named correctly!{resetcharacter}")

			if len(totalFaces) != 0:
				facesList[whichFolder] = totalFaces
				facesAmount[whichFolder] = len(totalFaces)
				totalFaces=[]
			else:
				print(f"{errorcharacter}The folder '{whichFolder}' did not have any valid pictures!{resetcharacter}")

		else:
			raise Exception(f"Tried to access the folder './faces/{whichFolder}, but it does not exist!'")

def updateGifs(whichGifs=None):
	global gifsList,gifsAmount
	totalGifs=[]

	if whichGifs == None:
		for gifType in allGifs:
			for filename in os.listdir(f"./gifs/{gifType}"):
				if filename.endswith(".gif"):
					if filename[:-4].isdigit():
						totalGifs.append(filename)
					else:
						print(f"{errorcharacter}The gif '{filename}' in folder '{gifType}' was not named correctly!{resetcharacter}")
				else:
					print(f"{errorcharacter}The gif '{filename}' in folder '{gifType}' was not named correctly!{resetcharacter}")

			if len(totalGifs) != 0:
				gifsList[gifType] = totalGifs
				gifsAmount[gifType] = len(totalGifs)
				totalGifs=[]
			else:
				print(f"{errorcharacter}The folder '{gifType}' did not have any valid gifs!{resetcharacter}")

	elif whichGifs in allGifs:
		for filename in os.listdir(f"./gifs/{whichGifs}"):
			if filename.endswith(".gif"):
				if filename[:-4].isdigit():
					totalGifs.append(filename)
				else:
					print(f"{errorcharacter}The gif '{filename}' in folder '{whichGifs}' was not named correctly!{resetcharacter}")
			else:
				print(f"{errorcharacter}The gif '{filename}' in folder '{whichGifs}' was not named correctly!{resetcharacter}")

		if len(totalGifs) != 0:
			gifsList[whichGifs] = totalGifs
			gifsAmount[whichGifs] = len(totalGifs)
			totalGifs=[]

		else:
			print(f"{errorcharacter}The folder '{whichGifs}' did not have any valid gifs!{resetcharacter}")
	else:
		raise Exception(f"The type '{whichGifs}' can't be used in this function! (updateGifs)")

def randomGif(whichGifs):
	if whichGifs in allGifs:
		return random.choice(gifsList[whichGifs])
	else:
		return False

#Prints a person and their total pictures nicely.
def printPerson(person,endnum):
	if len(person) > tabGoal-1:
		person="Folder name too long"

	while len(person) != tabGoal:
		person = person+" "

	print(person+endnum)

#--------------------------Events-------------------------#

#Triggers when bot starts
#Updates the presence and prints to console we've connected
@client.event
async def on_ready():
	await client.change_presence(activity=discord.Game("Just JoyBot things :)"))
	print(f"Logged in as the bot ({client.user})!")

@client.event
async def on_message(message):
	if message.content == "<@!504030703264989194>":
		if message.author.id != 504030703264989194:
			await message.channel.send("My prefix is `.` and `^`!\nType `.help` and `^help`!")
	await client.process_commands(message)
	if message.author.id == 302050872383242240:
		for embed in message.embeds:
			if "Bump done" in embed.description:
				bumpID = embed.description
				bumpID = stringPOP(bumpID,0)
				bumpID = stringPOP(bumpID,0)
				bumpID, _ = bumpID.split(">")
				scoreDictMod("add",int(bumpID),1)
				top = str(getTopDict(scoreDict, 1))
				top = stringPOP(top, 0)
				top = stringPOP(top, 0)
				top, _ = top.split(",")

				header = f"<@{bumpID}>, your bump score has been increased by one!\nType `.bumpscore` to view your current bump score!"
				if int(top) == int(bumpID):
					role = discord.utils.find(lambda r: r.id == 760719803844788235, message.guild.roles)
					member = message.guild.get_member(int(bumpID))

					if not role in member.roles:
						await message.channel.send(f"{header}\nYou also managed to get the top spot! Nice!")
						await member.add_roles(role)

						with open("./data/roletop.txt", "r") as fp:
							oldID = int(fp.read().replace('\n', ''))

						with open("./data/roletop.txt", "w") as fp:
							fp.write(str(bumpID))

						oldMember = message.guild.get_member(int(oldID))
						await oldMember.remove_roles(role)
						await message.channel.send(f"{oldMember.mention}, you've lost your top spot!")
					else:
						await message.channel.send(header)
				else:
					await message.channel.send(header)

	#testing
	if "do testbump now" == message.content:
		bumpID = message.author.id
		scoreDictMod("add",int(bumpID),0)
		top = str(getTopDict(scoreDict, 1))
		top = stringPOP(top, 0)
		top = stringPOP(top, 0)
		top, _ = top.split(",")
		header = f"<@{bumpID}>, your bump score has been increased by none (test bump!)!\nType `.bumpscore` to view your current bump score!"
		if int(top) == int(bumpID):
			role = discord.utils.find(lambda r: r.id == 760719803844788235, message.guild.roles)
			member = message.guild.get_member(bumpID)

			print(member, member.roles)
			print(type(bumpID), bumpID)

			if not role in member.roles:
				await message.channel.send(f"{header}\nYou also managed to get the top spot! Nice!")
				await member.add_roles(role)

				with open("./data/roletop.txt", "r") as fp:
					oldID = int(fp.read().replace('\n', ''))

				with open("./data/roletop.txt", "w") as fp:
					fp.write(str(bumpID))

				oldMember = message.guild.get_member(oldID)
				await oldMember.remove_roles(role)
				await message.channel.send(f"{oldMember.mention}, you've lost your top spot!")
			else:
				await message.channel.send(header)
		else:
			await message.channel.send(header)

#Triggers on any command
#Stops the error from appearing if it's any command not found error
@client.event
async def on_command_error(ctx, error):
	if isinstance(error, discord.ext.commands.errors.CommandNotFound):
		return
	raise error

#-------------------------Backwords-----------------------#

def function_backwords(passthrough):
	endString=""
	for x in passthrough:
		endString = x + endString
	return endString

@client.command(name="backwords",description="returns the string backwords!")
async def backwords(ctx, *, text):

	convert = function_backwords(text)
	if convert != "":
		await ctx.send(f"{ctx.author.mention}, here is your backwords string: {convert}")
	else:
		await ctx.send(f"{ctx.author.mention}, please provide something to return backwords!")

#-------------------------Reversecaps----------------------------#

@client.command(name="reversecaps",description="reverses the caps in the provided argument!")
async def reversecaps(ctx, *, text=None):
	if text == None:
		await ctx.send(f"{ctx.author.mention}, please suppy an argument to reversecaps!")
		return

	await ctx.send(f"{ctx.author.mention}, here is your reversecaps:\n{text.swapcase()}")

#---------------------------Evaluate-------------------------------#

@client.command(name="evaluate",description="evaluates some provided python code!",aliases=["e","eval"])
async def evaluate(ctx, *, code=None):
	if not ctx.author.id in admins_ids:
		await ctx.send(f"{ctx.author.mention}, you can't use this command!")
		return
	if code == None:
		await ctx.send(f"Please supply some code!")
		return

	str_obj = io.StringIO() #Retrieves a stream of data
	try:
		with contextlib.redirect_stdout(str_obj):
			exec(code)
	except Exception as e:
		return await ctx.send(f"{ctx.author.mention}, An exception occured!\n```{e.__class__.__name__}: {e}```")
	if str_obj.getvalue() == "":
		await ctx.send(f'{ctx.author.mention}, the run completed succesfully with no output!')
	else:
		try:
			await ctx.send(f'{ctx.author.mention}, the run completed succesfully, heres the output:\n```{str_obj.getvalue()}```')
		except discord.errors.HTTPException:
			await ctx.send(f"{ctx.author.mention}, the run completed succesfully but the output was too long to send!")

#--------------------------Bumpscore--------------------------#

@client.command(name="bumpscore", description="gets the bumpscore of you or someone else")
async def bumpscore(ctx, person : discord.Member = None):
	if person == None:
		curScore = scoreDictMod("one",ctx.author.id,0)

		if curScore == 0:
			await ctx.send("You haven't bumped disboard yet!")
		else:
			await ctx.send(f"Your current bump score is `{curScore}`")

	else:
		curScore = scoreDictMod("one",person.id)

		if curScore == 0:
			await ctx.send(f"{person} hasn't bumped disboard yet!")
		else:
			await ctx.send(f"{person}'s current bump score is `{curScore}`")

@bumpscore.error
async def bumpscore_error(ctx, error):
	if isinstance(error, commands.errors.BadArgument):
		await ctx.send("That's not a valid member!")

#-------------------------Top bumpscore----------------------------#

def getTopDict(dictionary, num=10000000000000000):
	topx = nlargest(num, dictionary, key=dictionary.get)
	returnList = []
	for key in topx:
		returnList.append((key, dictionary[key]))
	return returnList

@client.command(name="topbumpscore", description="gets the top bumpers")
async def topbumpscore(ctx):
	topbumps = getTopDict(scoreDict, 10)
	printstring=""

	message = await ctx.send("Getting the top 10 bumpers...")

	for num, x in enumerate(topbumps):
		user = await client.fetch_user(x[0])
		username = f"{user.name}#{user.discriminator}"

		if int(x[1]) == 69:
			if num >= 9:
				printstring += f"{num+1})   {username} — {x[1]} — Nice.\n"
			else:
				printstring += f"{num+1})    {username} — {x[1]} — Nice.\n"

		elif int(x[1]) == 420:
			if num >= 9:
				printstring += f"{num+1})   {username} — {x[1]} — Blaze it!.\n"
			else:
				printstring += f"{num+1})    {username} — {x[1]} — Blaze it!.\n"
		else:
			if num >= 9:
				printstring += f"{num+1})   {username} — {x[1]}\n"
			else:
				printstring += f"{num+1})    {username} — {x[1]}\n"

	if printstring == "":
		await message.edit(content = f"{ctx.author.mention}, looks like nobody has bumped disboard!\n(this is strange, contact Joyte#0001 maybe?)")
		return

	await message.edit(content = f"{ctx.author.mention}, here are the top 10 bumpers:\n```{printstring}```")

#-----------------------Bumpscore json--------------------#

@client.command(name="dictjson", description="returns a dict in json format")
async def dictjson(ctx, whichDict=None):
	if not ctx.author.id in admins_ids:
		await ctx.send(f"{ctx.author.mention}, you can't use this command!")
		return

	#i'm genuinely sorry for this code
	beforetext = "Hey, just warning you there's no use for this command as it's not stored in json anymore, it's pickled.\n"

	if whichDict == "facesList":
		message = await ctx.send(f"{beforetext}Loading dict...")
		jsonText = json.dumps(facesList, indent=4)
		await message.edit(content = f"{beforetext}Faces list dict:```json\n{jsonText}```")

	elif whichDict == "facesAmount":
		message = await ctx.send(f"{beforetext}Loading dict...")
		jsonText = json.dumps(facesAmount, indent=4)
		await message.edit(content = f"{beforetext}Faces amount dict:```json\n{jsonText}```")

	elif whichDict == "pollDict":
		message = await ctx.send(f"{beforetext}Loading dict...")
		jsonText = json.dumps(pollDict, indent=4)
		await message.edit(content = f"{beforetext}Poll owner list dict:```json\n{jsonText}```")

	elif whichDict == "scoreDict":
		message = await ctx.send(f"{beforetext}Loading dict...")
		jsonText = json.dumps(scoreDict, indent=4)
		await message.edit(content = f"{beforetext}Bump score dict:```json\n{jsonText}```")

	else:
		await ctx.send("That's not a valid dict, please choose one of these:\n`facesList`\n`facesAmount`\n`pollDict`\n`scoreDict`")

#-----------------------Bumpscore pkl--------------------#

@client.command(name="dictpkl", description="returns a dict in pkl format")
async def dictpkl(ctx, whichDict=None):
	if not ctx.author.id in admins_ids:
		await ctx.send(f"{ctx.author.mention}, you can't use this command!")
		return

	beforetext = ""
	try:
		if whichDict == "facesList":
			message = await ctx.send(f"{beforetext}Loading dict...")
			pklText = pickle.dumps(facesList)
			dictName = "Faces list dict"
			await message.edit(content = f"{beforetext}{dictName}:```json\n{pklText}```")

		elif whichDict == "facesAmount":
			message = await ctx.send(f"{beforetext}Loading dict...")
			pklText = pickle.dumps(facesAmount)
			dictName = "Faces amount dict"
			await message.edit(content = f"{beforetext}{dictName}:```json\n{pklText}```")

		elif whichDict == "pollDict":
			message = await ctx.send(f"{beforetext}Loading dict...")
			pklText = pickle.dumps(pollDict)
			dictName = "Poll owner list dict"
			await message.edit(content = f"{beforetext}{dictName}:```json\n{pklText}```")

		elif whichDict == "scoreDict":
			message = await ctx.send(f"{beforetext}Loading dict...")
			pklText = pickle.dumps(scoreDict)
			dictName = "Bump score dict"
			await message.edit(content = f"{beforetext}{dictName}:```json\n{pklText}```")

		else:
			await ctx.send("That's not a valid dict, please choose one of these:\n`facesList`\n`facesAmount`\n`pollDict`\n`scoreDict`")
	except discord.errors.HTTPException:
		await message.delete()
		split_1, split_2 = pklText[:len(pklText)//2], pklText[len(pklText)//2:]
		try:
			await ctx.send(f"{beforetext}{dictName}:```\n{split_1}```")
			await ctx.send(f"```{split_2}```")
		except discord.errors.HTTPException:
			try:
				split_again1, split_again2 = split_1[:len(split_1)//2], split_1[len(split_1)//2:]
				split_again3, split_again4 = split_2[:len(split_2)//2], split_2[len(split_2)//2:]
				await ctx.send(f"{beforetext}{dictName}:```\n{split_again1}```")
				await ctx.send(f"```{split_again2}```")
				await ctx.send(f"```{split_again3}```")
				await ctx.send(f"```{split_again4}```")
			except discord.errors.HTTPException:
				await ctx.send("Sorry, but the data is too long to send!")

#-----------------------Add/Remove bumpscore--------------------#

@client.command(name="changebumpscore", description="changes the bumpscore of a user")
async def changebumpscore(ctx, amount = None, person : discord.Member = None):
	if not ctx.author.id in admins_ids:
		await ctx.send(f"{ctx.author.mention}, you can't use this command!")
		return

	if amount == None:
		await ctx.send(f"{ctx.author.mention}, Please supply an amount to change by!")
		return

	if person == None:
		if str(amount)[0] == "-":
			amount = stringPOP(amount,0)
			if amount.isnumeric():
				scoreDictMod("minus",ctx.author.id,int(amount))
				await ctx.send(f"Changed your bump score by -{amount}")
			else:
				await ctx.send(f"{ctx.author.mention}, Please give a valid amount!")
		else:
			if amount.isnumeric():
				scoreDictMod("add",ctx.author.id,int(amount))
				await ctx.send(f"Changed your bump score by {amount}")
			else:
				await ctx.send(f"{ctx.author.mention}, Please give a valid amount!")
	else:
		if str(amount)[0] == "-":
			amount = stringPOP(amount,0)
			if amount.isnumeric():
				scoreDictMod("minus",person.id,int(amount))
				await ctx.send(f"Changed {person}'s bump score by -{amount}")
			else:
				await ctx.send(f"{ctx.author.mention}, Please give a valid amount!")
		else:
			if amount.isnumeric():
				scoreDictMod("add",person.id,int(amount))
				await ctx.send(f"Changed {person}'s bump score by {amount}")
			else:
				await ctx.send(f"{ctx.author.mention}, Please give a valid amount!")

@changebumpscore.error
async def changebumpscore_error(ctx, error):
	if isinstance(error, commands.errors.BadArgument):
		await ctx.send("That's not a valid member!")

#--------------------------Convert-------------------------#

#Number poll
@client.command(name="convert",description="Converts height!")
async def convert(ctx, text=None):
	if text == None:
		await ctx.send("Please convert either:\nFeet'inches to CM\nCM to Feet'inches\nMetres to CM")
		return

	convert = inch_cm(text)
	if convert != False:
		await ctx.send(f"{ctx.author.mention}, here is your conversion: {convert}")
	else:
		await ctx.send(f"{ctx.author.mention}, that's not a valid conversion!")

#--------------------------Random Face-------------------------#

#Get a random face and print it to discord.
@client.command(name="randomface",description="Gets a random face!")
async def randomface(ctx, person=None):
	try:
		if person == None: #Nobody specified
			printFaces = ""
			for person in facesList.keys():
				printFaces = (printFaces+"`"+person+"` **—** `"+str(facesAmount[person])+"`\n")
			await ctx.send(f"{ctx.author.mention}, Please specify a valid person!\nList:\n"+printFaces)
			return

		else:
			if person in facesList: #we got him bois!
				fileName = (str(random.choice(range(1,facesAmount[person]+1)))+".png")
				validPicture = (os.getcwd()+"/faces/"+person+"/"+fileName)

				if fileName == "69.png":
					nice = " | Nice."
				else:
					nice = ""

				with open(validPicture, 'rb') as fp:
					await ctx.send(f"{ctx.author.mention}, Here's your picture! `{fileName}`{nice}",file=discord.File(fp, fileName))

			else: #Invalid person
				printFaces = ""
				for person in facesList:
					printFaces = (printFaces+"`"+person+"` **—** `"+str(facesAmount[person])+"`\n")

				await ctx.send(f"{ctx.author.mention}, Please specify a valid person!\nList:\n"+printFaces)
				return

	except FileNotFoundError:
		message = await ctx.send(f"{ctx.author.mention}, An error occured!")
		updateFaces(person)

		if os.path.exists(f"./faces/{person}/{fileName}"):
			await message.edit(content = f"{ctx.author.mention}, An error occured, but i've managed to fix it!")
			return

		if int(fileName[:-4]) > facesAmount[person]:
			await message.edit(content = f"{ctx.author.mention}, An error occured, but i've managed to fix it!")
			return

#--------------------------Addpic-------------------------#

#Adds a picture to either the faces command or the gifs.
@client.command(name="addpic",description="Adds a picture!")
async def addpic(ctx, type=None, person=None):
	if not int(ctx.author.id) in admins_ids:
		await ctx.send(f"{ctx.author.mention}, hey you can't use this command!")
		return

	if ctx.message.attachments == []:
		await ctx.send(f"{ctx.author.mention}, please use this command with an attached file!")
		return

	if type == None:
		await ctx.send(f"{ctx.author.mention}, please give a type!\n**List:**\n`gifs`\n`faces`")
		return

	if person == None:
		if type == "faces":
			await ctx.send(f"{ctx.author.mention}, please give a person to add the face to!")
		elif type == "gifs":
			await ctx.send(f"{ctx.author.mention}, please specify which type of gif this is!\n**List:**\n`marry`\n`kisses`\n`hugs`")
		else:
			await ctx.send(f"{ctx.author.mention}, please specify argument two!")
		return

	if not person.isalpha():
		await ctx.send(f"{ctx.author.mention}, please limit your person name to alphabetical characters only!")
		return

	if type == "faces":
		for file in ctx.message.attachments:
			try:
				await file.save(f"./faces/{person}/{facesAmount[person]+1}.png")
				await ctx.send(f"{ctx.author.mention}, Added the picture to the `{person}` folder!")
			except KeyError:
				try:
					os.mkdir(f"./faces/{person}")
					await file.save(f"./faces/{person}/1.png")
					await ctx.send(f"{ctx.author.mention}, Made the `{person}` folder, and added the picture to it!")
				except FileExistsError:
					await file.save(f"./faces/{person}/1.png")
					await ctx.send(f"{ctx.author.mention}, The `{person}` folder already existed without any pictures, so i just added the picture to it!")
		updateFaces(person)
	elif type == "gifs":
		if person in allGifs:
			for file in ctx.message.attachments:
				await file.save(f"./gifs/{person}/{gifsAmount[person]+1}.png")
				await ctx.send(f"{ctx.author.mention}, Added the gif to the `{person}` folder!")

		else:
			await ctx.send(f"{ctx.author.mention}, please specify a valid gif type!\n**List:**\n`marry`\n`kisses`\n`hugs`")
	else:
		await ctx.send(f"{ctx.author.mention}, that's not a valid type!\n**List:**\n`gifs`\n`faces`")

#------------------------Addpic history------------------#

#TODO:	This command, make it scan the last {amount} messages and
#		add them all to the {person} folder. there should be an
#		optional {replace} argument, where a backup of the {person}
#		folder is made, (with the timestamp and person as the name)
#		and everything is deleted and replaced with the attachments
#		provided using addpic-history.
#		format: .addpic-history <amount> <person> [replace]
#		or maybe add arguments onto the addpic command, with the format
#		.addpic <person> [<history> <amount> <replace>]
#		which would be better, as it is only one command
#		will decide later

#@client.command(name=".addpic_history",description="Adds the picture history to a person or face")
#async def addpic_history(ctx, person=None, amount=100, replace=None):
#	messagehistory = await ctx.channel.history(limit=amount,).flatten()

#--------------------------History------------------------#

@client.command()
async def history(ctx, limit: int = 100):
	if not ctx.author.id in admins_ids:
		await ctx.send(f"{ctx.author.mention}, hey you can't use this command!")
		return

	editmsg = await ctx.send(f"{ctx.author.mention}, getting the message history...")
	messages = await ctx.channel.history(limit=limit).flatten()

	try:
		with open(f"history-{ctx.channel.id}.txt", "a") as f:
			print(f"Requested {limit} lines by {ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id})\n\n", file=f)
			for message in messages:
				print(f"#{message.channel.name} ({message.channel.id}): @{message.author.name}#{message.author.discriminator} ({message.author.id}): {message.content}", file=f)
	except ssl.SSLError:
		pass

	with open(f"history-{ctx.channel.id}.txt", "rb") as fp:
		await editmsg.edit(content = f"{ctx.author.mention}, here's the history of the channel, up to {limit}:")
		await ctx.send(file = discord.File(fp, f"history-{ctx.channel.id}.txt"))

	os.remove(f"history-{ctx.channel.id}.txt")

#---------------------Delface------------------------------#

#TODO: Convert to delpic

@client.command(name="delface",description="deletes a face")
async def delface(ctx, num, person=None):

	if not int(ctx.author.id) in admins_ids:
		await ctx.send(f"{ctx.author.mention}, hey you can't use this command!")
		return

	if person == None:
		await ctx.send(f"{ctx.author.mention}, please give a person to delete the face of!")
		return

	if not person.isalpha():
		await ctx.send(f"{ctx.author.mention}, please limit your person name to alphabetical characters only!")
		return

	if not num.isnumeric():
		await ctx.send(f"{ctx.author.mention}, please make the first argument a number!")
		return
	else:
		num = int(num)

	if num == 0:
		await ctx.send(f"{ctx.author.mention}, okay, i deleted no faces for you :)")
		return

	try:
		if num > facesAmount[person]:
			if facesAmount[person] == 1:
				await ctx.send(f"{ctx.author.mention}, {person} only has one face!")
				return
			else:
				await ctx.send(f"{ctx.author.mention}, {person} only has {facesAmount[person]} faces!")
			return

		if not os.path.exists(f"./faces/{person}/{num}.png"):
			if num == facesAmount[person]:
				await ctx.send(f"{ctx.author.mention}, this file has already been deleted, contact <@246862123328733186> to help!\ndebug info:\nperson: {person}\nnum: {num}")
				print(f"{errorcharacter}file already deleted, debug info:\nperson: {person}\nnum: {num}\nfaces list:{facesList[person]}{resetcharacter}")
				return

			await ctx.send(f"{ctx.author.mention}, this file has already been deleted, but i'll try to fix the corruption!")
			if os.path.exists(f"./faces/{person}/{facesAmount[person]}.png"):
				if num == facesAmount[person]:
					updateFaces(person)
					await ctx.send("Okay, i've fixed it!")
					return

				copyfile(f"./faces/{person}/{facesAmount[person]}.png",f"./faces/{person}/{num}.png")
				os.remove(f"./faces/{person}/{facesAmount[person]}.png")
				await ctx.send(f"Hopefully i've fixed it!")
				updateFaces(person)
				return

			else:
				await ctx.send(f"I couldn't fix it, but contact <@246862123328733186> to help!\ndebug info:\nperson: {person}\nnum: {num}")
				print(f"{errorcharacter}file already deleted, debug info:\nperson: {person}\nnum: {num}\nfaces list:{facesList[person]}{resetcharacter}")
				return

		if facesAmount[person] == 1:
			os.remove(f"./faces/{person}/{num}.png")
			os.rmdir(f"./faces/{person}")
			await ctx.send(f"{ctx.author.mention}, deleted `{person}`'s folder!")
			updateFaces(person)
			return

		if num == facesAmount[person]:
			os.remove(f"./faces/{person}/{num}.png")
			await ctx.send(f"{ctx.author.mention}, deleted `{person}`'s `{num}.png`!")
			updateFaces(person)
			return

		os.remove(f"./faces/{person}/{num}.png")
		copyfile(f"./faces/{person}/{facesAmount[person]}.png",f"./faces/{person}/{num}.png")
		os.remove(f"./faces/{person}/{facesAmount[person]}.png")

		await ctx.send(f"{ctx.author.mention}, deleted `{person}`'s `{num}.png`!")
		updateFaces(person)
		return

	except KeyError:
		await ctx.send(f"{ctx.author.mention}, that's not a valid person!")
		return
#--------------------------update all-------------------------#

@client.command(name="updateall",description="Updates all the faces")
async def updateallfaces(ctx):
	if not int(ctx.author.id) in admins_ids:
		await ctx.send(f"{ctx.author.mention}, hey you can't use this command!")
		return

	message = await ctx.send(f"{ctx.author.mention}, updating all the faces and gifs...")
	updateFaces()
	updateGifs()
	await message.edit(content = "Faces updated!")

#--------------------------Marry command-------------------------#

#TODO: finish this

"""
@client.command(name="marry", description="give someone a marry (what's a english)")
async def marry(ctx, person : discord.Member = None):
	await ctx.send("nothing :)")

@kiss.error
async def marry_error(ctx, error):
	if isinstance(error, commands.errors.BadArgument):
		await ctx.send("That's not a valid member!")
"""

#--------------------------Kiss command-------------------------#

@client.command(name="kiss", description="give someone a kiss")
async def kiss(ctx, person : discord.Member = None):
	if person == None:
		await ctx.send("I need to know who you are kissing!")
		return
	if person.id == ctx.author.id:
		await ctx.send("You can't kiss yourself!")
		return
	random_gif = randomGif("kisses")
	fileName = f"./gifs/kisses/{random_gif}"
	with open(fileName, 'rb') as fp:
		await ctx.send(f"{ctx.author.mention} has kissed {person.mention}",file=discord.File(fp, fileName))

@kiss.error
async def kiss_error(ctx, error):
	if isinstance(error, commands.errors.BadArgument):
		await ctx.send("That's not a valid member!")

#--------------------------Hug command-------------------------#

@client.command(name="hug", description="give someone a hug")
async def hug(ctx, person : discord.Member = None):
	if person == None:
		await ctx.send("I need to know who you are hugging!")
		return
	if person.id == ctx.author.id:
		await ctx.send("You can't hug yourself!")
		return
	random_gif = randomGif("hugs")
	fileName = f"./gifs/hugs/{random_gif}"
	with open(fileName, 'rb') as fp:
		await ctx.send(f"{ctx.author.mention} has hugged {person.mention}",file=discord.File(fp, fileName))

@hug.error
async def hug_error(ctx, error):
	if isinstance(error, commands.errors.BadArgument):
		await ctx.send("That's not a valid member!")

#--------------------------Polls-------------------------#

#Yes/No poll
@client.command(name="yesnopoll",description="Makes a yes no poll!",aliases=["yesnovote"])
async def yesnopoll(ctx, *, question=None):
	await ctx.message.delete()
	if question == None:
		error = await ctx.send(f"{ctx.author.mention}, Please supply arguments!\nExample:\t`.numberpoll This is the question`")
		await error.add_reaction(cross)
		pollDictMod("add",ctx.author.id,error.id)
		return

	poll = await ctx.send(f"{ctx.author.mention} wants to know:\n**Yes/No** `{question}`")
	for reaction in ynpoll:
		await poll.add_reaction(reaction)
	pollDictMod("add",ctx.author.id,poll.id)

#Spacify
@client.command(name="spacify",description="Spacifies")
async def spacify(ctx, *, text):
	spacedText = spacifyFunction(text)
	await ctx.send(f"{ctx.author.mention}, here is your message:\n{spacedText}")

#Number poll
@client.command(name="numberpoll",description="Makes a number poll!",aliases=["numbervote"])
async def numberpoll(ctx, *, text=None):
	await ctx.message.delete()
	if text == None:
		error = await ctx.send(f"{ctx.author.mention}, Please supply arguments!\nExample:\t`.numberpoll This is the question, This is answer one, This is answer two, etc`")
		await error.add_reaction(cross)
		pollDictMod("add",ctx.author.id,error.id)
		return

	split = text.split(",")

	if len(split) == 1:
		error = await ctx.send(f"{ctx.author.mention}, Please supply some answers, not just the question!\n`{text}`")
		await error.add_reaction(cross)
		pollDictMod("add",ctx.author.id,error.id)
		return

	if len(split) > 11:
		error = await ctx.send(f"{ctx.author.mention}, Please limit your poll to 10 options or less!\n`{text}`")
		await error.add_reaction(cross)
		pollDictMod("add",ctx.author.id,error.id)
		return

	popsplit = []
	for string in split:
		beforepop = string
		while string[0] == " ":
			string = stringPOP(string,0)

		while string[len(string)-1] == " ":
			string = stringPOP(string,len(string)-1)

		popsplit.append(string)

	header = popsplit[0]
	popsplit.pop(0)

	sendmessage = f"{ctx.author.mention} asks `{header}`:\n"
	curnum = 1
	for string in popsplit:
		if curnum == len(popsplit)+1:
			break
		sendmessage = sendmessage + f"{numpoll[curnum]} — {string}\n"
		curnum += 1

	poll = await ctx.send(sendmessage)

	curnum = 1
	for string in popsplit:
		if curnum == len(popsplit)+1:
			break
		await poll.add_reaction(numpoll[curnum])
		curnum += 1

	await poll.add_reaction(cross)

	pollDictMod("add",ctx.author.id,poll.id)

#Remove if x
@client.event
async def on_reaction_add(reaction, user):
	if str(reaction.emoji) == cross:
		state = pollDictMod("remove",user.id,reaction.message.id)
		if state == True:
			await reaction.message.delete()

#--------------------------------------Say----------------------------------#

@client.command(name="say", description="says something from the bot's perspective")

async def say(ctx, *, saying):
	if not ctx.author.id in admins_ids:
		await ctx.send(f"{ctx.author.mention}, Nice try Buddy.")
		return
	await ctx.message.delete()
	await ctx.send(saying)
#------------------------------Final bootstrap------------------------------------#

print(f"{resetcharacter}Booting up...")

#Update the faces
updateFaces()
if facesAmount != {}:
	print("\nLoaded faces:\n")
	for person in facesList:
		printPerson(person,str(facesAmount[person]))
	print("\n")

#Updating gifs
print("Updating gifs...")
updateGifs()
if gifsAmount != {}:
	print("\nLoaded gifs:\n")
	for gif in gifsList:
		printPerson(gif,str(gifsAmount[gif]))
	print("\n")

#Load the bumpscores
if os.path.exists("./data/bumpscores.pkl"):
	#try:
	print("Loading pickle bump scores...")
	try:
		with open('./data/bumpscores.pkl', 'rb') as fp:
			scoreDict = pickle.load(fp)
	except (EOFError, pickle.UnpicklingError):
		print(f"{errorcharacter}Pickle decoder error, the pkl file has been reset, and a backup has been made.{resetcharacter}")
		copyfile("./data/bumpscores.pkl", f"./data/bumpscores.pkl.{datetime.utcnow()}")
		os.remove("./data/bumpscores.pkl")

#------------------------------------Log into discord-------------------------------------#
print("Logging into discord...")
client.run(botToken)


from __future__ import print_function

import os, math
from pyspark.sql import SparkSession

# Map 1: extract words from a file
# input: file_name
# output: (term, doc_name), 1

def map1(doc_name):
    file = open(doc_name)
    while True:
        line = file.readline()
        if line == '':
            break
        terms = line.split()
        for term in terms:
            yield (term, doc_name), 1

# Reduce 1: calculate tf for terms in each document
# input: (term, doc_name), 1
# output: (term, doc_name), n

def reduce1(x, y):
    return x + y

# Map 2
# input: (term, doc_name), n
# output: doc_name, ([(term, n)], n)

def map2(x):
    term = x[0][0]
    doc_name = x[0][1]
    n = x[1]
    return doc_name, ([(term, n)], n)

# Reduce 2
# input: doc_name, ([(term, n)], n)
# output: doc_name, ([list of [term, n]], N)

def reduce2(x, y):
    return (x[0] + y[0], x[1] + y [1])

# Map 3
# input: doc_name, ([list of [term, n]], N)
# output: term, ([(n, doc_name, N)], n)

def map3(x):
    doc_name = x[0]
    terms = x[1][0]
    N = x[1][1]
    for item in terms:
        term = item[0]
        n = item[1]
        yield term, ([(n, doc_name, N)], 1)

# Reduce 3:
# input: term, ([(n, doc_name, N)], 1)
# output: term, ([(n, doc_name, N)], m)

def reduce3(x, y):
    return (x[0] + y[0], x[1] + y[1])

# Final map
# input: term, ([(n, doc_name, N)], m)
# output: term, (doc_name, n, N, m, tf score, idf score, tf-idf score)

def map4(x, D):
    term = x[0]
    docs = x[1][0]
    m = x[1][1]

    for item in docs:
        n = item[0]
        doc_name = item[1]
        N = item[2]

        tf = float(n) / N
        idf = math.log(float(D) / m)
        tfidf = tf * idf

        yield term, (doc_name, n, N, m, tf, idf, tfidf)

# main program
if __name__ == "__main__":

    # create spark context
    spark = SparkSession.builder.appName("TfIdf-Exercise").getOrCreate()

    # retrieve all files in the data input directory
    input_folder = "input_txt"
    input_files = [os.path.join(input_folder, file_name) for file_name in os.listdir(input_folder)]

    # get D = total number of documents
    D = len(input_files)

    # to avoid cases where a term appearing in all documents
    # idf = log(D/m) = log(1) = 0
    # we increase D by 1

    D = D + 1

    # put list of files into rdd for parallel processing
    files = spark.sparkContext.parallelize(input_files)

    # calculate terms and their number of occurrences in each document (tf)
    terms_in_docs = files.flatMap(lambda x: map1(x)).reduceByKey(lambda x, y: reduce1(x, y))

    # calculate number of occurrences of all terms in each document (N)
    docs_have_terms = terms_in_docs.map(lambda x: map2(x)).reduceByKey(lambda x, y: reduce2(x, y))

    # calculate number of documents containing specific terms (idf)
    terms_all = docs_have_terms.flatMap(lambda x: map3(x)).reduceByKey(lambda x, y: reduce3(x, y))

    # calculate tf-idf
    terms_final = terms_all.flatMap(lambda x: map4(x, D))

    # call collect() to output the result
    print(terms_final.collect())

    # stop spark context
    spark.stop()


import jwt
from rest_framework import authentication, exceptions
from django.conf import settings
from django.contrib.auth.models import User
from jwt.algorithms import get_default_algorithms

class JWTAuthentication(authentication.BaseAuthentication):

    def authenticate(self, request):
        auth_data = authentication.get_authorization_header(request)

        if not auth_data:
            return None

        prefix, token = auth_data.decode('utf-8').split(' ')
        # print(settings.JWT_SECRET_KEY)
        # print(jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[
        #       "HS256"], options={"verify_signature": False}))
        try:
            payload = jwt.decode(
                token, settings.JWT_SECRET_KEY, algorithms=["HS256"], options={
                    "verify_signature": False})

            user = User.objects.get(username=payload['username'])
            print(user)
            return (user, token)

        except jwt.DecodeError as identifier:
            raise exceptions.AuthenticationFailed(
                'Your token is invalid, login')
        except jwt.ExpiredSignatureError as identifier:
            raise exceptions.AuthenticationFailed(
                'Your token is expired, login')

        return super().authenticate(request)


#!/usr/bin/python

# -*- coding: utf-8 -*-
"""
Created on Thu Feb  5 13:08:27 2015

@author: dominic
"""

from __future__ import print_function
import sys,os
sys.path.append(os.path.abspath('../utils/'))
import allele_sim as sim
import build_panel as build
#import matplotlib.pyplot as plt

eprint = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)

## Old plotting
#plt.xlabel('Panel Size')
#plt.ylabel('Coverage')
#plt.plot(x, greedycoverage, label = 'greedy')
#plt.plot(x, randomcoverage, label = 'random')
#plt.xlim(0, x[-1] + 1)
#plt.legend(loc = 4)
#plt.show()

class CLIError(Exception):
    pass

try:
    # simulation options
    iteration_count = None
    input_path = None
    new_file_decision = "new" # Either --new-file, --append, or --overwrite

    # building options
    panel_output_path = "."
    panel_size_list = None
    random_panel_count = 0

    # shared
    allele_simulation_path = "."

    # process type
    simulate = None
    do_build = None

    i = 1
    while i < len(sys.argv):
        arg = sys.argv[i]
        n = lambda: sys.argv[i+1]

        if arg == '--simulate':
            simulate = True
        elif arg == '--build':
            do_build = True
        elif arg == '--iterations':
            iteration_count = int(n())
            i += 1
        elif arg == '--input':
            input_path = n()
            i += 1
        elif arg == '--proband-file':
            proband_file = n()
            i += 1
        elif arg == '--sim-output-dir':
            allele_simulation_path = n()
            i += 1
        elif arg == '--new-file':
            new_file_decision = "new"
        elif arg == '--append':
            new_file_decision = "append"
        elif arg == '--overwrite':
            new_file_decision = "overwrite"
        elif arg == '--allele-simulation':
            allele_simulation_path = n()
            i += 1
        elif arg == '--panel-output-dir':
            panel_output_path = n()
            i += 1
        elif arg == '--panel-size-list':
            panel_size_list = map(int, n().split(','))
            i += 1
        elif arg == '--random-points':
            random_panel_count = int(n())
            i += 1
        else:
            raise CLIError("unrecognized command-line option %s" % arg)
        i += 1
except CLIError as e:
    eprint("Command-line interface error:", e)
    sys.exit(1)
except IndexError:
    eprint("Unexpected end of command-line arguments.")
    sys.exit(1)

def check_arg(arg_name, arg):
    if arg is None:
        raise CLIError('missing mandatory argument %s' % arg_name)

if do_build is None and simulate is None:
    eprint("What are you doing with your life?")
    sys.exit(42)

try:
    # check arguments:
    if simulate:
        check_arg('iterations', iteration_count)
        check_arg('input', input_path)

    if (not simulate) and build:
        check_arg('sim-output-dir', allele_simulation_path)

    if do_build:
        check_arg('panel-output-dir', panel_output_path)
        check_arg('panel-size-list', panel_size_list)
        check_arg('random-points', random_panel_count)
		#Must be specified as None if you want all probands to be in
        check_arg('proband-file', proband_file)

    # Do the work.
    if simulate:
        results = sim.run(
                os.path.expanduser(input_path),
                os.path.expanduser(allele_simulation_path),
                iteration_count,
                new_file_decision)
        sim_results_path = results[0]
    else:
        sim_results_path = allele_simulation_path

    if do_build:
        print("Finding allele matches among individuals...")

        panel = build.panel(sim_results_path,proband_file)
        eprint("Done")

        print("Read", panel.Iterations, "simulations from file.")
        greedycoverage, randomcoverage, runtimes = panel.run(panel_output_path,
                                                             panel_size_list,
                                                             random_panel_count)
except CLIError as e:
    eprint(e)
    sys.exit(1)


"""
URL configuration for SANSKARSET project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from application import views
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('', views.index,name='index'),
    path('calender', views.calender),
    path('shop', views.shop),
    path('test', views.test),
    path('index', views.index),
    path('master', views.master),
    path('signup', views.signup),
    path('admin/', admin.site.urls),
    path('application/', include('application.urls',namespace='application')),
    path('ckeditor/', include('ckeditor_uploader.urls')),
    path('application/', include('django.contrib.auth.urls',namespace='')),
    path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]


import sys
from collections import deque

N,M= map(int,sys.stdin.readline().split()) #row,col
maps=[]
for _ in range(N):
    a=list(sys.stdin.readline().strip())
    maps.append(list(map(int,a)))

state=deque()
state.append((1,0,0))#cnt,row,col

dx=[0,0,1,-1]
dy=[1,-1,0,0]
while state:
    d,x,y=state.popleft()
    for i in range(4):
        nx=x+dx[i]
        ny=y+dy[i]
        if nx<0 or nx>=N or ny<0 or ny>=M: continue
        if maps[nx][ny]==0: continue
        if maps[nx][ny]==1 or maps[nx][ny]>d+1:
            maps[nx][ny]=d+1
            state.append((d+1,nx,ny))
print(maps[N-1][M-1])

# 다시 공부하고 해볼 것
'''def moving(x,y,table_map,load,fail):
    #동남서북
    x_move=[1,0,-1,0]
    y_move=[0,-1,0,1]
    for x_m,y_m in zip(x_move,y_move):
        if table_map[x+x_m][y+y_m]=='1' and ((x+x_m,y+y_m) not in fail):
            if (x,y) in load:
                load.remove((x,y))
                fail.append((x,y))
            else:
                load.append((x+x_m,y+y_m))
            x=x+x_m
            y=y+y_m
    return x,y,load,fail'''

'''N,M= map(int, sys.stdin.readline().split())
table_map=[]

for _ in range(N):
    table_map.append(list(sys.stdin.readline().split()))
x_move=[1,0,-1,0]
y_move=[0,1,0,-1]
x,y=0,0
load=[(0,0)]
fail=[]
while x!=N-1 or y!=M-1:
    cnt=0
    for x_m,y_m in zip(x_move,y_move):
        if x+x_m>=0 and y+y_m>=0 and x+x_m<N and y+y_m<M:
            if table_map[x+x_m][y+y_m]=='1' and ((x+x_m,y+y_m) not in load) and ((x+x_m,y+y_m) not in fail):
                load.append((x+x_m,y+y_m))
                x=x+x_m
                y=y+y_m
                break
            else: cnt+=1
        else: cnt+=1
    if cnt==4:
        load.remove((x,y))
        fail.append((x,y))
        x=load[-1][0]
        y=load[-1][1]
print(load)
print(len(load))'''


from typing import *

class Solution:
    def __init__(self) -> None:
        self.paths = []
        self.path = []

    def partition(self, s: str) -> List[List[str]]:
        # 边界情况
        # 如果为空
        if not s:
            return []
        # 如果只有一个值
        if len(s) == 1:
            return [[s]]
        self.backtracking(s, start=0)
        return self.paths

    def backtracking(self, s, start):
        # 确定边界条件
        # 由于边界条件和深度有关因此这里要考虑深度最后的情况
        # 每次循环其实就是再一次套一个循环，那么循环到最后会超过边界
        # 那么此时就是返回的时机
        if start >= len(s):
            # 这里存放所有结果
            self.paths.append(self.path[:])
            return

        # 确定回溯函数的逻辑
        # 本题循环的内容为字母字符因此这里
        # 起始位置和终止位置为字符的索引
        for i in range(start, len(s)):
            # 处理逻辑
            # 如果满足回文的情况下，我们就需要将结果进行保存了
            # 这里的回文长度同start与i有关
            # start可以回溯从1位到多位的情况
            if self.is_palindrome(s, start, i):
                # 这里将结果存入
                self.path.append(s[start:i+1])
                # 递归内容 此部分的start要缩小集合域，因此这里利用的应该是i
                self.backtracking(s, start=i+1)
                # 回溯逻辑
                # 结果进行回溯
                self.path.pop()
            else:
                continue
    # 双指针构建反转字符串查询
    def is_palindrome(self, s, left, right):
        while left <= right:
            if s[left] != s[right]:
                return False
            left += 1
            right -= 1
        return True

def main():
    pass

if __name__ == '__main__':
    sl = Solution()
    s = "aab"
    print(sl.partition(s))

8.回溯算法/一刷归档/6. 131.分割回文串.py


import pandas as pd
import time
import requests
from pyquery import PyQuery as pq

# 定义一个字母列表
Fam_List = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
            'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z']

# 定义基础网站地址
base_url = 'https://commons.wikimedia.org'

# 定义查询的HTML
middle_url = '/w/index.php?title=Category:Google_Art_Project_works_by_artist&from='
# 定义headers
headers = {'user-agent':
               'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
           }
# 定义首页的css查询路径
css_path = '#content #mw-content-text .mw-category-generated .mw-content-ltr li'
# 定义二级界面image查询路径
css_path_image = None
# 定义二级界面summary查询路径
css_path_summary = None
# 定义一个csv文件用来存放所有需要爬取的html的地址
file_name = 'Art_List.cvs'

# 定义一个写入文件的函数
def write_to_file(content):
    with open(file_name, 'a', encoding='utf-8') as f:
        f.write(content[0] + ',' + content[1] + '\n')

# 定义一个写成excel的函数
def creat_excel(file_name):
    df = pd.DataFrame(pd.read_csv(file_name, encoding='utf-8', names=['Name', 'URL']))
    # 丢弃重复的URL地址行
    df.drop_duplicates(subset='URL', keep='last', inplace=True)
    df.to_excel('Art_List.xlsx')

# 定义一个获取单页html的函数
def get_one_page(i):
    try:
        # request获取网页的响应
        url = base_url + middle_url + i
        response = requests.get(url, headers=headers)
        # 将响应的解码方式转换为自身
        print("成功进入页面")
        response.encoding = response.apparent_encoding
        # 获取网页本身的html文本
        content = response.text
        # 将获取的html文本传递给pyquery
        doc = pq(content)
        # 返回一个所有页面的生成器
        for item in doc(css_path).items():
            yield (
                i,
                base_url + (item('a').attr('href')),
            )
    except Exception as e:
        print('Error', e.args)

def main():
    for i in Fam_List:
        print('即将下载:%s页' % i)
        for content in get_one_page(i):
            print(content)
            write_to_file(content)
        time.sleep(1)
    creat_excel(file_name)

if __name__ == '__main__':
    main()


import pytest
import struct
import json
import os.path
from iotile.core.hw.hwmanager import HardwareManager
from iotile.core.dev.registry import ComponentRegistry
from iotile.core.hw.exceptions import RPCNotFoundError, RPCInvalidArgumentsError
from typedargs.exceptions import KeyValueException
from iotile.core.hw.virtual import VirtualTile

@pytest.fixture
def registry():
    reg = ComponentRegistry()
    reg.clear()
    reg.clear_components()
    path = os.path.join(os.path.dirname(__file__), 'rpc_proxy.py')
    reg.register_extension('iotile.proxy', 'virtual_tile', path)

    yield reg

@pytest.fixture
def p(tmpdir):

    path = os.path.join(os.path.dirname(__file__), 'rpc_device.py')
    config = {
        "device":
            {
                "tiles":
                    [
                        {
                            "address": 10,
                            "name": path,
                            "args": {}
                        }
                    ],
                "iotile_id": 1
            }
    }

    config_path_obj = tmpdir.join('tasks.json')
    config_path_obj.write(json.dumps(config))

    config_path = str(config_path_obj)

    hw = HardwareManager(port='virtual:tile_based@%s' % config_path)
    hw.connect_direct(1)
    tile = hw.get(10)
    yield tile
    hw.disconnect()

def test_add_rpc(registry, p):
    res1 = p.test_add_v1(1, 2)
    assert res1 == 3
    res2 = p.test_add_v2(1, 2)
    assert res2 == 3

def test_null_rpc(registry, p):
    p.test_null_v1()
    p.test_null_v2()

def test_invalid_rpc_args_length(registry, p):
    with pytest.raises(RPCInvalidArgumentsError) as e:
        p.test_invalid_arg_length_rpc_v2()
    assert e.type == RPCInvalidArgumentsError

    with pytest.raises(RPCInvalidArgumentsError):
        p.test_invalid_arg_length_rpc_v1()

def test_invalid_rpc_args(registry, p):
    with pytest.raises(RPCInvalidArgumentsError) as e:
        p.test_invalid_args_missing_rpc_v2()
    assert e.type == RPCInvalidArgumentsError

    with pytest.raises(RPCInvalidArgumentsError) as e:
        p.test_invalid_args_missing_rpc_v1()
    assert e.type == RPCInvalidArgumentsError

def test_rpc_does_not_exist(registry, p):
    with pytest.raises(RPCNotFoundError) as e:
        p.test_rpc_does_not_exist_v1()
    assert e.type == RPCNotFoundError

    with pytest.raises(RPCNotFoundError) as e:
        p.test_rpc_does_not_exist_v2()
    assert e.type == RPCNotFoundError


import math
import numpy as np

class Point:
    def __init__(self, x, y):
        self.x = round(x)
        self.y = round(y)

    def get_x(self):
        return self.x

    def get_y(self):
        return self.y

    def get(self):
        return self.x, self.y

    def set(self, x, y):
        self.x = x
        self.y = y

class Checkpoint:

    def __init__(self, x1, y1, x2, y2, col=(0, 200, 0), start=False):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

        self.col = col
        self.start = start

    def get(self):
        return self.x1, self.y1, self.x2, self.y2

    def get_x1(self):
        return self.x1

    def get_y1(self):
        return self.y1

    def get_x2(self):
        return self.x2

    def get_y2(self):
        return self.y2

    def set(self, x1, y1, x2, y2):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

    def set_col(self, col):
        self.col = col

    def get_col(self):
        return self.col

    def get_start(self):
        return self.start

def get_points(delay, x1, y1, x2, y2):
    points = []

    lengthx = x2 - x1
    lengthy = y2 - y1
    length = math.sqrt((lengthx ** 2) + (lengthy ** 2))

    for i in range(math.ceil(length / delay)):
        offsetx = (delay * lengthx) / length
        offsety = (delay * lengthy) / length

        points.append(Point(x1 + i * offsetx, y1 + i * offsety))

    return points

def give_length(ang, car, track):
    a = math.radians(ang)

    p1, p2 = make_radar(a, car, 200)

    if not find_seg1(a, track, car):
        return 200

    p3, p4 = find_seg1(a, track, car)

    if not line_intersection(p1, p2, p3, p4):
        return 200

    else:
        length = line_intersection(p1, p2, p3, p4)

    return round(length)

def find_seg(ang, track, car):

    p1, p2 = make_radar(ang, car, 200)

    plausible = []
    plaus = 0

    lent = 1000

    for pa, pointa in enumerate(track.barrier_p1):

        if intersect(p1, p2, track.barrier_p1[pa - 1], pointa):
            pointsa = track.barrier_p1[pa - 1], pointa
            plausible.append(pointsa)

    for pb, pointb in enumerate(track.barrier_p2):

        if intersect(p1, p2, track.barrier_p2[pb - 1], pointb):
            pointsb = track.barrier_p2[pb - 1], pointb
            plausible.append(pointsb)

    if plausible:
        for i, pon in enumerate(plausible):
            len1 = math.sqrt(((pon[0].x - p1.x) ** 2) + ((pon[0].y - p1.y) ** 2))
            len2 = math.sqrt(((pon[1].x - p1.x) ** 2) + ((pon[1].y - p1.y) ** 2))
            len3 = (len1 + len2) / 2

            if len3 < lent:
                lent = len3
                plaus = i

    else:
        return False

    return plausible[plaus]

def find_seg1(ang, track, car):

    p1, p2 = make_radar(ang, car, 200)

    plausible = []
    plaus = 0

    lent = 1000

    for pa, pointa in enumerate(track.barrier_p1):

        if intersect(p1, p2, track.barrier_p1[pa - 1], pointa):
            pointsa = track.barrier_p1[pa - 1], pointa
            plausible.append(pointsa)

    for pb, pointb in enumerate(track.barrier_p2):

        if intersect(p1, p2, track.barrier_p2[pb - 1], pointb):
            pointsb = track.barrier_p2[pb - 1], pointb
            plausible.append(pointsb)

    if plausible:
        for i, pon in enumerate(plausible):

            poin = np.array([p1.x, p1.y])
            lin = [np.array([pon[0].x, pon[0].y]), np.array([pon[1].x, pon[1].y])]

            length = point_to_line_dist(poin, lin)

            if length < lent:
                lent = length
                plaus = i

    else:
        return False

    return plausible[plaus]

def line_intersection(point1, point2, point3, point4):
    line1 = [[point1.get_x(), point1.get_y()], [point2.get_x(), point2.get_y()]]
    line2 = [[point3.get_x(), point3.get_y()], [point4.get_x(), point4.get_y()]]

    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])

    def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

    div = det(xdiff, ydiff)
    if div == 0:
        return False

    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div

    line_a = (point1.get_x() - x) ** 2
    line_b = (point1.get_y() - y) ** 2

    length = math.sqrt(line_a + line_b)

    return length

def make_radar(ang, car, radius):
    first = Point(car.x + car.width / 2, car.y + car.height / 2)

    second = Point(first.get_x() + math.cos(ang) * radius, first.get_y() - math.sin(ang) * radius)

    return first, second

def ccw(A, B, C):
    return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)

# Return true if line segments AB and CD intersect
def intersect(A, B, C, D):
    return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)

def point_to_line_dist(point, line):
    """Calculate the distance between a point and a line segment.

    To calculate the closest distance to a line segment, we first need to check
    if the point projects onto the line segment.  If it does, then we calculate
    the orthogonal distance from the point to the line.
    If the point does not project to the line segment, we calculate the
    distance to both endpoints and take the shortest distance.

    :param point: Numpy array of form [x,y], describing the point.
    :type point: numpy.core.multiarray.ndarray
    :param line: list of endpoint arrays of form [P1, P2]
    :type line: list of numpy.core.multiarray.ndarray
    :return: The minimum distance to a point.
    :rtype: float
    """
    # unit vector
    unit_line = line[1] - line[0]
    norm_unit_line = unit_line / np.linalg.norm(unit_line)

    # compute the perpendicular distance to the theoretical infinite line
    segment_dist = (
        np.linalg.norm(np.cross(line[1] - line[0], line[0] - point)) /
        np.linalg.norm(unit_line)
    )

    diff = (
        (norm_unit_line[0] * (point[0] - line[0][0])) +
        (norm_unit_line[1] * (point[1] - line[0][1]))
    )

    x_seg = (norm_unit_line[0] * diff) + line[0][0]
    y_seg = (norm_unit_line[1] * diff) + line[0][1]

    endpoint_dist = min(
        np.linalg.norm(line[0] - point),
        np.linalg.norm(line[1] - point)
    )

    # decide if the intersection point falls on the line segment
    lp1_x = line[0][0]  # line point 1 x
    lp1_y = line[0][1]  # line point 1 y
    lp2_x = line[1][0]  # line point 2 x
    lp2_y = line[1][1]  # line point 2 y
    is_betw_x = lp1_x <= x_seg <= lp2_x or lp2_x <= x_seg <= lp1_x
    is_betw_y = lp1_y <= y_seg <= lp2_y or lp2_y <= y_seg <= lp1_y
    if is_betw_x and is_betw_y:
        return segment_dist
    else:
        # if not, then return the minimum distance to the segment endpoints
        return endpoint_dist


from libqtile.config import Group, Key
from libqtile.command import lazy
from keys import keys, mod0, mod2
from settings import workspaces, rooms

# Workspaces and rooms
# The basic idea behind Workspaces and Rooms is to control
# DIFFERENT subsets of groups with the SAME hotkeys.
# So we can have multiple 'qwerasdf' rooms in a different workspaces.
#
# Qtile Groups are used behind the scenes, but their visibility
# is set dynamically.

def get_group_name(workspace, room):
    ''' Calculate Group name based on (workspace,room) combination.
    '''
    return '%s%s' % (room, workspace)

# Oops, time for a little hack there.
# This is a global object with information about current workspace.
# (viable as config code, not sure about client-server though)
wsp = {
    'current': workspaces[0][0], # first workspace is active by default
}
# ... and information about active group in the each workspace.
for w, _ in workspaces:
    wsp[w] = {
        'active_group': get_group_name(w, rooms[0]) # first room is active by default
    }

def get_workspace_groups(workspace):
    ''' Get list of Groups that belongs to workspace.
    '''
    return [ get_group_name(workspace, room) for room in rooms]

def to_workspace(workspace):
    ''' Change current workspace to another one.
    '''
    def f(qtile):
        global wsp

        # we need to save current active room(group) somewhere
        # to return to it later
        wsp[wsp['current']]['active_group'] = qtile.current_group.name

        # now we can change current workspace to the new one
        # (no actual switch there)
        wsp['current'] = workspace
        # and navigate to the active group from the workspace
        # (actual switch)
        qtile.groups_map[
            wsp[workspace]['active_group']
        ].cmd_toscreen(toggle = False)

        # we also need to change subset of visible groups in the GroupBox widget
        qtile.widgets_map['groupbox'].visible_groups = get_workspace_groups(workspace)
        qtile.widgets_map['groupbox'].draw()
        # You can do some other cosmetic stuff here.
        # For example, change Bar background depending on the current workspace.
        # # qtile.widgetMap['groupbox'].bar.background = 'ff0000'
    return f

def to_room(room):
    ''' Change active room to another within the current workspace.
    '''
    def f(qtile):
        global wsp
        qtile.groups_map[get_group_name(wsp['current'], room)].cmd_toscreen(toggle = False)
    return f

def window_to_workspace(workspace, room = rooms[0]):
    ''' Move active window to another workspace.
    '''
    def f(qtile):
        global wsp
        qtile.current_window.togroup(wsp[workspace]['active_group'])
    return f

def window_to_room(room):
    ''' Move active window to another room within the current workspace.
    '''
    def f(qtile):
        global wsp
        qtile.current_window.togroup(get_group_name(wsp['current'], room))
    return f

# Create individual Group for each (workspace,room) combination we have
groups = []
for workspace, hotkey in workspaces:
    for room in rooms:
        groups.append(Group(get_group_name(workspace, room)))

# Assign individual hotkeys for each workspace we have
for workspace, hotkey in workspaces:
    keys.append(Key(mod0, hotkey, lazy.function(
        to_workspace(workspace))))
    keys.append(Key(mod2, hotkey, lazy.function(
        window_to_workspace(workspace))))

# Assign shared hotkeys for each room we have.
# Decision about actual group to open is made dynamically.
for room in rooms:
    keys.append(Key(mod0, room, lazy.function(
        to_room(room))))
    keys.append(Key(mod2, room, lazy.function(
        window_to_room(room))))


cases = int(input().strip())
for case in range(cases):
    length, n = [int(i) for i in input().strip().split(" ")]
    places = [int(i) for i in input().strip().split(" ")]
    places.sort()
    result = [0, 0]
    shortest = [min(item, length - item) for item in places]
    result[0] = str(max(shortest))
    longest = [max(item, length - item) for item in places]
    result[1] = str(max(longest))
    print(" ".join(result))


from inspect import signature as sig
from functools import wraps

def typeassert(*ty_args, **ty_kwargs):
    def decorate(func):
        if not __debug__:
            return func

        fun_sig = sig(func)
        # 将签名与参数字典绑定
        bound_types = fun_sig.bind_partial(*ty_args, **ty_kwargs).arguments

        @wraps(func)
        def wrapper(*args, **kwargs):
            # 参数顺序字典
            bound_values = fun_sig.bind(*args, **kwargs)
            for name, value in bound_values.arguments.items():
                if name in bound_types:
                    if not isinstance(value, bound_types[name]):
                        raise TypeError('Argument {} must be {}'.format(name, bound_types[name]))
            return func(*args, **kwargs)
        return wrapper
    return decorate

# 限定第一个和第三个参数为str
@typeassert(str, z=str)
def add_three(x, y, z):
    print(x + str(y) + z)

# 正常情况
add_three("x", 2, "z")

# 触发异常
add_three(1, 2, 3)


from odoo import fields, models, api

class StockMove(models.Model):
    _inherit = "stock.move"

    action_reassign_visible = fields.Boolean(
        string="Shows button to reassign",
        compute='_compute_action_reassign_visible',
        readonly=True,
    )

    is_cancellable = fields.Boolean(
        compute="_compute_is_cancellable",
        help="Technical field to check for button visibility",
    )

    def _compute_action_reassign_visible(self):
        for move in self:
            visible = move.quantity_done < move.product_uom_qty \
                and not move.is_locked \
                and move.state in [
                    'confirmed',
                    'waiting',
                    'partially_available',
                    'assigned'
                ]
            move.action_reassign_visible = visible

    @api.depends('procure_method', 'state')
    def _compute_is_cancellable(self):
        for move in self:
            if move.procure_method == 'make_to_stock':
                if move.state not in ('done', 'cancel'):
                    move.is_cancellable = True
            if move.procure_method == 'make_to_order':
                if move.state in ('draft'):
                    move.is_cancellable = True
                elif move.state in ('confirmed') and not move.move_orig_ids:
                    move.is_cancellable = True

    @api.multi
    def action_confirm(self):
        """ Inputs:
                - 'draft'
            Outputs:
                - 'confirmed'
                - 'waiting'
        """
        for move in self.filtered(lambda m: m.state in ['draft']):
            move._action_confirm()

    @api.multi
    def action_assign(self):
        """ Inputs:
                - 'confirmed'
                - 'waiting'
                - 'partially_available'
            Outputs:
                - 'assigned'
                - 'partially_available'
        """
        for move in self:
            move._action_assign()

    @api.multi
    def action_reassign(self):
        """ Inputs:
                - 'assigned' --> unreserve --> 'confirmed'
                - 'partially_available' --> unreserve --> 'confirmed'
                - 'confirmed'
                - 'waiting'
                - 'partially_available'
            Outputs:
                - 'assigned'
                - 'partially_available'
        """
        for move in self:
            move._do_unreserve()
            move._action_assign()

    @api.multi
    def action_recompute_state(self):
        self._recompute_state()

    @api.multi
    def action_force_state_confirmed_to_assigned(self):
        for move in self.filtered(lambda m: m.state == 'confirmed'):
            move.write({'state': 'assigned'})

    @api.multi
    def _action_cancel_stream(self, downstream=False, upstream=False):
        def get_downstream_moves(move):
            res = move
            if move and move.move_dest_ids:
                for m in move.move_dest_ids:
                    if move.product_id.id == m.product_id.id:
                        res += get_downstream_moves(m)
            return res

        def get_upstream_moves(move):
            res = move
            if move and move.move_orig_ids:
                for m in move.move_orig_ids:
                    if move.product_id.id == m.product_id.id:
                        res += get_upstream_moves(m)
            return res

        moves_to_cancel = self.env['stock.move']
        for move in self:
            moves_to_cancel += move
            if downstream:
                moves_to_cancel += get_downstream_moves(move)
            if upstream:
                moves_to_cancel += get_upstream_moves(move)

        moves_to_cancel = moves_to_cancel.filtered(
            lambda x: x.state not in ('done', 'cancel')
        )
        if moves_to_cancel:
            moves_to_cancel._action_cancel()

    @api.multi
    def action_cancel(self):
        """ Inputs:
                - 'assigned' --> unreserve --> 'confirmed'
                - 'partially_available' --> unreserve --> 'confirmed'
                - 'confirmed'
            Outputs:
                - 'cancel'
        """
        self._action_cancel_stream()

    @api.multi
    def action_cancel_downstream(self):
        self._action_cancel_stream(downstream=True)

    @api.multi
    def action_cancel_upstream(self):
        self._action_cancel_stream(upstream=True)

    @api.multi
    def action_done(self):
        """ Inputs:
                - 'draft' --> confirm --> 'confirmed'
                - 'product_uom_qty' == 'quantity_done'
            Outputs:
                - 'done'
        """
        self._action_done()


import numpy as np
from sklearn.metrics import roc_curve,  auc
from scipy.interpolate import interp1d
from scipy.optimize import brentq

def calculate_accuracy(threshold, dist, actual_issame):
    predict_issame = np.less(1-dist, 1-threshold)
    tp = np.sum(np.logical_and(predict_issame, actual_issame))
    fp = np.sum(np.logical_and(predict_issame, np.logical_not(actual_issame)))
    tn = np.sum(np.logical_and(np.logical_not(predict_issame), np.logical_not(actual_issame)))
    fn = np.sum(np.logical_and(np.logical_not(predict_issame), actual_issame))
    tpr = 0 if (tp +fn==0) else float(tp) / float(tp +fn)
    fpr = 0 if (fp +tn==0) else float(fp) / float(fp +tn)
    acc = float(tp +tn ) /dist.shape[0]
    return tpr, fpr, acc

def eer_auc(y, y_score):
    fpr, tpr, _ = roc_curve(y, y_score, pos_label=1)
    eer = brentq(lambda x : 1. - x - interp1d(fpr, tpr)(x), 0., 1.)
    AUC = auc(fpr, tpr)
    return eer, AUC

def get_metrics(pred, gt, thre):
    gt_bool = (gt>0.5)
    _,_,ACC = calculate_accuracy(thre, pred, gt_bool)
    _, AUC = eer_auc(gt, pred)
    return AUC, ACC