"""
The single task procedure (execute_singleTask) involves the presentation of visual stimuli and the recording
of the participant's response (reading out loud).
The dual task procedure (execute_dualTask) involves the presentation of visual stimuli, along with math problems
and a moving-dots task, and records the participant's response (reading out loud) and
their response to the math problem and moving-dots task.
"""

# Import necessary libraries
from psychopy import prefs
# Set the audio library preference
prefs.hardware['audioLib'] = ['ptb', 'sounddevice', 'pygame', 'pyo']
# Now, import sound
from psychopy import sound, core, event, visual
import time
import datetime
import sounddevice as sd
from scipy.io.wavfile import write
import random
from dualtask_configuration import append_result_to_csv
import os
import numpy as np

# single task procedure
def execute_singleTask(window, results, subj_path_rec, stimuli, task_name, werKommt, fixation, item, rec_seconds,
                       fs, participant_info, base_filename):
    """
    Execute the single task procedure.

    This function takes a set of stimuli and a task name and runs the single task experiment.
    It presents the stimuli visually and records the participants' verbal responses.

    Args:
        window: The display window.
        results: A list to store the results.
        subj_path_rec: The path for the subject's recording.
        stimuli: The stimuli to be presented.
        task_name: The name of the task.
        werKommt: The "werKommt" stimulus.
        fixation: The fixation point stimulus.
        item: The item stimulus.
        rec_seconds: The recording duration in seconds.
        fs: The sample rate for the recording.
        participant_info: The participant's information.
        base_filename: The filename for the result CSV file.
    """
    # Initialize start time and format it into string
    start_time = time.time()
    start_time_str = datetime.datetime.fromtimestamp(start_time).strftime('%H:%M:%S')

    # Iterate through each stimulus in the provided stimuli
    for x in range(len(stimuli)):
        task = task_name  # store the task name

        # Present the "werKommt" stimulus, draw it, flip window, wait for 1 sec, and flip window again
        werKommt.name = 'werKommt'  # Naming the TextStim to find it in the log-file
        werKommt.draw()  # Draw stimulus to window
        window.flip()  # Flip window to make drawn stimulus visible
        core.wait(1.0)  # Wait for 1 sec
        window.flip()  # Flip window to clear stimulus

        # Repeat the same process for fixation stimulus
        fixation.name = 'fixation'
        fixation.draw()
        window.flip()
        core.wait(1.0)
        window.flip()

        # Set item TextStim and pic ImageStim based on the current stimulus in the iteration
        stimulus = stimuli.loc[x]['item']
        item.setText(stimulus)
        item.name = 'item_' + str(stimuli.loc[x]['ID'])  # Naming the TextStim to find it in the log-file

        # Start recording the participant's verbal response
        responseRecord = sd.rec(int(rec_seconds * fs), samplerate=fs, channels=1)

        # Present item and pic for 350 frames
        for frame in range(350):
            item.draw()  # Draw item
            window.flip()  # Flip window to make drawn items visible

        # Stop the recording after the presentation is over
        sd.stop()

        # Save the recording as a WAV file
        write(os.path.join(subj_path_rec, 'dualtask_' + participant_info['subject'] + '_' + task_name + '_' +
                           "{:02d}".format(x + 1) + '_' + str(stimuli.loc[x]['ID']) + '.wav'), fs, responseRecord)
        # naming the recording wav file to find it in the log-file
        responseRecordName = 'dualtask_' + participant_info['subject'] + '_' + task_name + '_' + \
                             "{:02d}".format(x + 1) + '_' + str(stimuli.loc[x]['ID']) + '.wav'

        # Calculate and format end time and duration
        end_time = time.time()
        end_time_str = datetime.datetime.fromtimestamp(end_time).strftime('%H:%M:%S')
        duration = end_time - start_time
        hours, remainder = divmod(duration, 3600)
        minutes, seconds = divmod(remainder, 60)
        duration_str = '{:02d}:{:02d}:{:02d}'.format(int(hours), int(minutes), int(seconds))

        # Prepare the result dictionary
        results.append({
            'task': task,
            'main_trial': "{:02d}".format(x + 1),
            'phase': 'practice' if task_name.startswith('practice') else 'test',
            'stimulus_id': stimuli.loc[x]['ID'],
            'stimulus': stimuli.loc[x]['item'],
            'stimulus_rec': responseRecordName,
            'dot_direction': 'NA',
            'dot_1st_frame': 'NA',
            'dot_last_frame': 'NA',
            'dot_response_key': 'NA',
            'dot_response_accuracy': 'NA',
            'beep_sequence':'NA',
            'beep_count_trials': 'NA',
            'beep_count_deviant_trials': 'NA',
            'beep_count_normal_trials': 'NA',
            'beep_count_number_selection': 'NA',
            'beep_count_index_correct_count': 'NA',
            'beep_count_response': 'NA',
            'beep_count_response_accuracy': 'NA',
            'start_time': start_time_str,
            'end_time': end_time_str,
            'duration': duration_str,
        })
        # Append the result to the CSV file
        append_result_to_csv(results[-1], base_filename, participant_info)

# dual task procedure
def execute_dualTask_beep_count_dots(window, results, base_filename, subj_path_rec, stimuli, task_name, werKommt,
                                     fixation, item, prompt, feedback, fs, rec_seconds, movementDirections,
                                     responseList, dots, arrows, arrows_small, number_prompts, participant_info):
    """
    Executes a dual-task experiment where the participant is asked to count beeps and track moving dots.
    The participant's responses are recorded for analysis.

    Parameters:
    window : object
        The window object where all the visual stimuli are drawn.
    results : list
        A list to hold the results of the experiment.
    base_filename : str
        The base filename for all output files.
    subj_path_rec : str
        The path to save the recordings of the participant's responses.
    stimuli : DataFrame
        A DataFrame containing the stimuli for the experiment.
    task_name : str
        The name of the task to be executed.
    werKommt : object
        Text stimulus object for drawing.
    fixation : object
        Shape stimulus object for drawing.
    item : object
        Text stimulus object for drawing the main task.
    prompt : object
        Text stimulus object for drawing instructions.
    feedback : object
        Text stimulus object for drawing feedback.
    fs : int
        The sampling frequency for recording.
    rec_seconds : int
        The duration of recording in seconds.
    movementDirections : list
        List of possible movement directions for the dots.
    responseList : list
        List of possible responses from the participant.
    dots : object
        Dot stimulus object for drawing.
    arrows : list
        List of arrow stimulus objects for drawing.
    arrows_small : list
        List of small arrow stimulus objects for drawing.
    number_prompts : list
        List of number prompt stimulus objects for drawing.
    participant_info : dict
        Dictionary containing information about the participant.

    Returns:
    None
    """

    # List of random numbers, same order for every participant
    if task_name == 'practice_beep_count_dots':
        random.seed(424)  # Set a random seed for reproducibility - here to get same list of nrs for practice
    elif task_name == 'test_beep_count_dots':
        random.seed(6667)  # Set a random seed for reproducibility - here to get same list of nrs for test

    # Initialize start time and start_time_str
    start_time = time.time()
    start_time_str = datetime.datetime.fromtimestamp(start_time).strftime('%H:%M:%S')

    # Beep configuration
    beep_duration = 0.2  # Duration of each beep in seconds

    # Iterate over stimuli
    for x in range(len(stimuli)):
        task = task_name

        # Generate a random start and end frame for the main task stimulus presentation
        start_offset = random.randint(300, 601)  # random number between 300 and 600
        end_offset = start_offset + 350

        # Randomly select a movement direction and a start/end frame for the moving dots
        movement = random.choice(movementDirections)
        rand1stFrame = random.randint((start_offset+15), (start_offset+50))
        randLastFrame = rand1stFrame + 250

        # naming the TextStim to find it in the log-file
        werKommt.name = 'werKommt'
        werKommt.draw()
        window.flip()
        core.wait(1.0)
        window.flip()

        # naming the ShapeStim to find it in the log-file
        fixation.name = 'fixation'
        fixation.draw()
        window.flip()
        core.wait(1.0)
        window.flip()

        stimulus = stimuli.loc[x]['item']
        item.setText(stimulus)
        # naming the TextStim to find it in the log-file
        item.name = 'item_' + str(stimuli.loc[x]['ID'])

        # start recording the participant response - reading out loud the stimulus
        responseRecord = sd.rec(int(rec_seconds * fs), samplerate=fs, channels=1)
        beep_type = 'NA'
        beep_counter = 0
        beep_sequence = []  # List to store the beep sounds played within the current main trial
        beep_count_results = []
        is_row_added = False  # Flag variable to track if a row has been added for the condition

        for frame in range(1200):  # Loop for 1200 frames
            # Check if it's time to play a beep sound
            if frame % 35 == 0 and frame >= 49:

                # If the first 3 beeps have not yet been generated, they should be normal
                if beep_counter < 3:
                    beep_sound = sound.Sound('C', octave=5, secs=beep_duration)
                    beep_type = 'normal'
                else:
                    # Ensure that there are at least 3 deviants in the sequence
                    deviants_so_far = beep_sequence.count('deviant')
                    remaining_beeps = 22 - beep_counter  # assuming you have 23 beeps in total

                    if deviants_so_far < 3 and remaining_beeps <= (3 - deviants_so_far):
                        beep_sound = sound.Sound('A', octave=5, secs=beep_duration)
                        beep_type = 'deviant'
                    else:
                        # Calculate the probability of generating a deviant beep
                        if deviants_so_far >= 3:
                            deviant_prob = min(0.5, deviants_so_far / (beep_counter - 3))
                        else:
                            deviant_prob = 0.5

                        if np.random.rand() < deviant_prob:
                            beep_sound = sound.Sound('A', octave=5, secs=beep_duration)
                            beep_type = 'deviant'
                        else:
                            beep_sound = sound.Sound('C', octave=5, secs=beep_duration)
                            beep_type = 'normal'

                # Play the beep sound if it is not in the item presentation or dot presentation phase
                if not start_offset - 10 <= frame < end_offset + 10:
                    beep_sound.play()

                    # Increment the beep counter
                    beep_counter += 1

                    # Append the beep sound to the current trial's beep sequence
                    beep_sequence.append(beep_type)

                    # Record end time and duration
                    end_time = time.time()
                    end_time_str = datetime.datetime.fromtimestamp(end_time).strftime('%H:%M:%S')
                    duration = end_time - start_time
                    hours, remainder = divmod(duration, 3600)
                    minutes, seconds = divmod(remainder, 60)
                    duration_str = '{:02d}:{:02d}:{:02d}'.format(int(hours), int(minutes), int(seconds))

                    # Append the data of the current trial to the results
                    beep_count_results.append({
                        'task': task_name,
                        'phase':'practice' if task_name.startswith('practice') else 'test',
                        'main_trial': "{:02d}".format(x + 1),
                        'beep_count_trial': beep_counter,
                        'beep_count_stimulus': beep_type,
                        'presentation': 'dual' if start_offset <= frame < end_offset else 'single',
                        'start_time': start_time_str,
                        'end_time': end_time_str,
                        'duration': duration_str,
                    })

                # Add a row for frames when item is shown
                if start_offset - 10 <= frame < end_offset + 10:

                    # Record end time and duration
                    end_time = time.time()
                    end_time_str = datetime.datetime.fromtimestamp(end_time).strftime('%H:%M:%S')
                    duration = end_time - start_time
                    hours, remainder = divmod(duration, 3600)
                    minutes, seconds = divmod(remainder, 60)
                    duration_str = '{:02d}:{:02d}:{:02d}'.format(int(hours), int(minutes), int(seconds))

                    # Append the data of the current trial to the results if a row hasn't been added already
                    if not is_row_added:
                        beep_count_results.append({
                        'task': task_name,
                        'phase':'practice' if task_name.startswith('practice') else 'test',
                        'main_trial': "{:02d}".format(x + 1),
                        'beep_count_trial': 'pause for ' + str(end_offset-start_offset) + 'frames',
                        'beep_count_stimulus': 'none',
                        'presentation': 'name_coordinate and dots',
                        'start_time': start_time_str,
                        'end_time': end_time_str,
                        'duration': duration_str,
                    })
                    is_row_added = True

            # reading aloud primary task
            if start_offset <= frame < end_offset:  # This means that if the current frame is between start_offset and end_offset, it is the time to show the main task
                item.draw()  # Drawing the name of the image on the screen

                if frame == start_offset:  # If we are at the start of the primary task
                    # Start recording the participant's spoken response
                    responseRecord = sd.rec(int(rec_seconds * fs), samplerate=fs, channels=1)

                if frame == end_offset - 1:  # If we are at the end of the primary task
                    # Stop recording the participant's spoken response
                    sd.stop()
                    # Save the spoken response as a .wav file
                    write(os.path.join(subj_path_rec,
                                       'dualtask_' + participant_info['subject'] + '_' + task_name + '_' +
                                       "{:02d}".format(x + 1) + '_' + str(stimuli.loc[x]['ID']) + '.wav'),
                          fs, responseRecord)

            if rand1stFrame <= frame < randLastFrame:  # Present dots for subset of frames
                dots.dir = movement
                dots.draw()

            window.flip()

        for result in beep_count_results:
            append_result_to_csv(result, base_filename, participant_info, type='beep_count')

        # Reset the flag for the next trial
        is_row_added = False

        core.wait(2)

        # show first response screen
        prompt.setText('Drücken Sie den Richtungs-Pfeil auf der Tastatur,\n in die sich die Punkte bewegt haben.')
        prompt.pos = (0, -0.6)
        prompt.size = 0.12
        prompt.draw()

        # these are the arrows pointing at 0,90,180,270 degrees - resembling movement direction of dots
        for arrow in arrows:
            arrow.draw()
        window.flip()

        # wait for a response - allowed are the key buttons on the keypad
        arrowKey = event.waitKeys(keyList=responseList)
        # compare input arrow key with movement direction to check accuracy
        if responseList.index(arrowKey[0]) == movementDirections.index(movement):
            dot_Accuracy = 'correct'
            feedback.setText('Korrekt!')
            feedback.setColor('black')
            feedback.draw()
        else:
            dot_Accuracy = 'incorrect'
            feedback.setText('Inkorrekt!')
            feedback.setColor('black')
            feedback.draw()
        window.flip()

        core.wait(2)

        number_selection, correct_index = select_and_replace_number(beep_sequence.count('deviant'))

        # now number_prompts have been created and are of the same length as number_selection
        for i, arrow in zip(range(len(number_selection)), arrows_small):
            number_prompts[i].text = str(number_selection[i])
            number_prompts[i].draw()
            arrow.draw()

        # show second response screen
        prompt.setText('Wieviele hohe Töne haben Sie gehört?\n Drücken Sie den entsprechenden Pfeil auf der Tastatur.')
        prompt.pos = (0, -0.75)
        prompt.size = 0.12
        prompt.draw()

        window.flip()

        # wait for a response - allowed are the key buttons on the keypad
        arrowKey_number = event.waitKeys(keyList=responseList)
        # compare input arrow key with movement direction to check accuracy
        if responseList.index(arrowKey_number[0]) == correct_index:
            numbers_accuracy = 'correct'
            feedback.setText('Korrekt!')
            feedback.setColor('black')
            feedback.draw()
        else:
            numbers_accuracy = 'incorrect'
            feedback.setText('Inkorrekt!')
            feedback.setColor('black')
            feedback.draw()
        window.flip()

        core.wait(2)

        # Define a file name for the response record
        responseRecordName = 'dualtask_' + participant_info['subject'] + '_' + task_name + '_' + \
                             "{:02d}".format(x + 1) + '_' + str(stimuli.loc[x]['ID']) + '.wav'

        # Record end time and duration
        end_time = time.time()
        end_time_str = datetime.datetime.fromtimestamp(end_time).strftime('%H:%M:%S')
        duration = end_time - start_time
        hours, remainder = divmod(duration, 3600)
        minutes, seconds = divmod(remainder, 60)
        duration_str = '{:02d}:{:02d}:{:02d}'.format(int(hours), int(minutes), int(seconds))

        # Filter single presentation trials
        count_results_trials_single = [r for r in beep_count_results if
                             'presentation' in r and r['presentation'] == 'single']

        beep_count_trials = len(count_results_trials_single)

        # Filter deviants in single presentation trials
        count_results_trials_single_deviant = [r for r in beep_count_results if
                               'presentation' and 'beep_count_stimulus' in r and
                                        r['presentation'] == 'single' and
                                        r['beep_count_stimulus'] == 'deviant']

        # Calculate the number of single deviant presentation trials
        beep_count_trials_deviant = len(count_results_trials_single_deviant)

        # Calculate the number of single normal presentation trials
        beep_count_trials_normal = beep_count_trials - beep_count_trials_deviant

        beep_count_results.clear()  # Clear beep_results after writing to CSV

        # Prepare the result dictionary
        results.append({
            'task': task,
            'main_trial': "{:02d}".format(x + 1),
            'phase': 'practice' if task_name.startswith('practice') else 'test',
            'stimulus_id': stimuli.loc[x]['ID'],
            'stimulus': stimuli.loc[x]['item'],
            'stimulus_rec': responseRecordName,
            'dot_direction': movement,
            'dot_1st_frame': rand1stFrame,
            'dot_last_frame': randLastFrame,
            'dot_response_key': arrowKey,
            'dot_response_accuracy': dot_Accuracy,
            'beep_sequence':str(beep_sequence),
            'beep_count_trials': beep_count_trials,
            'beep_count_deviant_trials': beep_count_trials_deviant,
            'beep_count_normal_trials': beep_count_trials_normal,
            'beep_count_number_selection': str(number_selection),
            'beep_count_index_correct_count': correct_index,
            'beep_count_response': str(arrowKey_number[0]),
            'beep_count_response_accuracy': numbers_accuracy,
            'start_time': start_time_str,
            'end_time': end_time_str,
            'duration': duration_str,
        })
        # Append the result to the CSV file
        append_result_to_csv(results[-1], base_filename, participant_info)

# Display instructions consecutively
def execute_task(window, task_name, participant_info, stimuli, werKommt, fixation, item, prompt,
                 feedback, fs, rec_seconds, movementDirections, responseList, dots, arrows, arrows_small,
                 number_prompts, dual_task=False):
    """
    Executes a task for a participant based on the task_name and type (single or dual).
    It sets up paths for recording and results, checks the task name to call the appropriate
    task execution function, and displays end-of-practice instructions.

    Parameters:
    window : object
        The window object where all the visual stimuli are drawn.
    task_name : str
        The name of the task to be executed.
    participant_info : dict
        Dictionary containing information about the participant.
    stimuli : DataFrame
        A DataFrame containing the stimuli for the experiment.
    werKommt : object
        Text stimulus object for drawing.
    fixation : object
        Shape stimulus object for drawing.
    item : object
        Text stimulus object for drawing the main task.
    prompt : object
        Text stimulus object for drawing instructions.
    feedback : object
        Text stimulus object for drawing feedback.
    fs : int
        The sampling frequency for recording.
    rec_seconds : int
        The duration of recording in seconds.
    movementDirections : list
        List of possible movement directions for the dots.
    responseList : list
        List of possible responses from the participant.
    dots : object
        Dot stimulus object for drawing.
    arrows : list
        List of arrow stimulus objects for drawing.
    arrows_small : list
        List of small arrow stimulus objects for drawing.
    number_prompts : list
        List of number prompt stimulus objects for drawing.
    dual_task : bool, optional
        Whether the task to be executed is a dual task or a single task (default is False).

    Returns:
    None

    Notes:
    Depending on the task_name and whether the task is a dual task, different tasks are executed.
    These are: 'practice_number_dots', 'test_number_dots', 'practice_number_beep_press',
    'test_number_beep_press', 'practice_beep_count_dots', 'test_beep_count_dots', and
    'practice_single'. For each task, an end-of-practice instruction is displayed. Directories for storing the
    results and recordings are created if they do not already exist. The results of the task are saved to a
    results file, and a base filename is generated for the task.
    """

    # Initialize an empty list to hold the results
    results = []
    # Import the end-of-practice instructions
    from dualtask_instructions import instructPracticeSingleTaskEnd,  instructPracticeDualTask_beep_count_dots_End

    # path setup results per participant
    # Define the path in results for each subject
    subj_path_results = os.path.join('results', participant_info['subject'])
    # Create the directory if it doesn't exist
    if not os.path.exists(subj_path_results):
        os.makedirs(subj_path_results)
    # generate the base_filename based on task_name and phase
    base_filename = os.path.join(subj_path_results,
                            f"{task_name}_{participant_info['subject']}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}")

    # path setup recordings per participant
    # Define the path in recordings for each subject
    subj_path_rec = os.path.join('recordings', participant_info['subject'])
    # Create the directory if it doesn't exist
    if not os.path.exists(subj_path_rec):
        os.makedirs(subj_path_rec)

    # Execute the task and save the result
    if dual_task:
        if task_name == 'practice_beep_count_dots':
            execute_dualTask_beep_count_dots(window, results, base_filename, subj_path_rec, stimuli, task_name, werKommt,
                                             fixation, item, prompt, feedback, fs, rec_seconds, movementDirections,
                                             responseList, dots, arrows, arrows_small, number_prompts, participant_info)
            display_text_and_wait(instructPracticeDualTask_beep_count_dots_End, window)
        if task_name == 'test_beep_count_dots':
            execute_dualTask_beep_count_dots(window, results, base_filename, subj_path_rec, stimuli, task_name, werKommt,
                                             fixation, item, prompt, feedback, fs, rec_seconds, movementDirections,
                                             responseList, dots, arrows, arrows_small, number_prompts, participant_info)
    else:
        execute_singleTask(window, results, subj_path_rec, stimuli, task_name, werKommt, fixation, item, rec_seconds,
                           fs, participant_info, base_filename)
        if task_name == 'practice_single':
            display_text_and_wait(instructPracticeSingleTaskEnd, window)

def display_and_wait(element, window):
    """
    Displays a given screen element, flips the window, and then waits for any key press.

    Args:
        element: A psychopy visual element (TextStim, ImageStim, etc.) to be displayed on the screen.
        window: A psychopy.visual.Window object where the element will be displayed.

    Returns:
        keys: List of the keys that were pressed while waiting.
    """
    # Draw the provided element and flip window
    element.draw()
    window.flip()
    # Wait for any key to be pressed to display the next screen
    keys = event.waitKeys(keyList=['return'])

    return keys

def display_text_and_wait(text_string, window):
    """
    This function creates a TextStim object from a provided string, then draws it and waits for any key press.

    Args:
        text_string: The string to be displayed.
        window: The window to draw on.

    Returns:
        keys: A list of the keys that were pressed.
    """
    text_stim = visual.TextStim(window, text=text_string, color='black', wrapWidth=2)

    return display_and_wait(text_stim, window)

def select_and_replace_number(given_number):
    """
    Based on the given number, this function generates three random numbers that are within the
    same tens or ones range. It then combines these numbers with the given number,
    and randomly shuffles them to produce a list of four numbers.

    Parameters:
    given_number : int
        The base number based on which three other numbers are generated.

    Returns:
    numbers : list of int
        List of four numbers where one of the numbers is the given_number.
    replace_index : int
        The index at which the given_number is placed in the list.
    """

    # Check the range of the given_number
    if given_number < 10:
        range_start = given_number - 2
        range_end = given_number + 2
    else:
        num_str = str(given_number)
        range_start = int(num_str[:-1] + '0')
        range_end = int(num_str[:-1] + '9')

    # Create a list of all numbers in the range except the given_number
    range_without_given = [i for i in range(range_start, range_end + 1) if i != given_number]

    # Randomly select 3 numbers from this list
    numbers = random.sample(range_without_given, 3)

    # Add the given_number to the list and shuffle
    numbers.append(given_number)
    random.shuffle(numbers)

    # Get the index of the given_number
    replace_index = numbers.index(given_number)

    return numbers, replace_index


import requests

with open('token.txt', 'r') as file:
    token = file.read()

def ya_create_folder(token, name):
    url = "https://cloud-api.yandex.net/v1/disk/resources"
    headers = {'Content-Type': 'application/json', 'Authorization': f'OAuth {token}'}
    params = {"path": name, "overwrite": "true"}
    req = requests.put(url, headers=headers, params=params)
    return req.status_code

if __name__ == '__main__':
    print(ya_create_folder('token', "New_folder"))


from __future__ import annotations
from pydantic import BaseModel
from typing import Tuple, Callable, Iterable
from functools import partial
from itertools import chain, islice
import torch
from datastream.tools import repeat_map_chain
from datastream import Dataset

class MergeSampler(BaseModel, torch.utils.data.Sampler):
    samplers: Tuple[torch.utils.data.Sampler, ...]
    datasets: Tuple[Dataset, ...]
    ns: Tuple[int, ...]
    length: int
    from_mapping: Callable[[int], Tuple[int, int]]
    merged_samplers: Iterable

    class Config:
        arbitrary_types_allowed = True
        allow_mutation = False

    def __init__(self, samplers, datasets, ns):
        BaseModel.__init__(
            self,
            samplers=samplers,
            datasets=datasets,
            ns=ns,
            length=MergeSampler.merged_samplers_length(samplers, ns),
            from_mapping=Dataset.create_from_concat_mapping(datasets),
            merged_samplers=MergeSampler.merge_samplers(samplers, datasets, ns),
        )

    def __len__(self):
        return self.length

    def __iter__(self):
        return islice(self.merged_samplers, self.length)

    @staticmethod
    def merged_samplers_length(samplers, ns):
        return min([len(sampler) / n for sampler, n in zip(samplers, ns)]) * sum(ns)

    @staticmethod
    def merge_samplers(samplers, datasets, ns):
        to_mapping = Dataset.create_to_concat_mapping(datasets)

        def batch(iterable, n):
            while True:
                yield [next(iterable) for _ in range(n)]

        index_batch = zip(
            *[
                batch(
                    map(
                        partial(to_mapping, dataset_index),
                        repeat_map_chain(iter, sampler),
                    ),
                    n,
                )
                for dataset_index, (sampler, n) in enumerate(zip(samplers, ns))
            ]
        )

        return chain.from_iterable(chain.from_iterable(index_batch))

    def weight(self, index):
        dataset_index, inner_index = self.from_mapping(index)
        return self.samplers[dataset_index].weight(inner_index)

    def update_weights_(self, function):
        for sampler in self.samplers:
            sampler.update_weights_(function)

    def update_example_weight_(self, weight, index):
        dataset_index, inner_index = self.from_mapping(index)
        self.samplers[dataset_index].update_example_weight_(weight, inner_index)

    def sample_proportion(self, proportion):
        return MergeSampler(
            [sampler.sample_proportion(proportion) for sampler in self.samplers],
            self.datasets,
            self.ns,
        )

    def state_dict(self):
        return dict(samplers=[sampler.state_dict() for sampler in self.samplers])

    def load_state_dict(self, state_dict):
        for sampler, state_dict in zip(self.samplers, state_dict["samplers"]):
            sampler.load_state_dict(state_dict)


import discord
from redbot.core import commands

class ORFcog(commands.Cog):
    """ORF cog"""

    async def _manage_pins(self, message: discord.Message):
        pins = await message.channel.pins()
        try:
            for pin in pins:
                if message.author.id == pin.author.id:
                    await pin.unpin()
            await message.pin()
        except discord.Forbidden:
            await message.channel.send("Ich benötige die 'Manage Messages' Berechtigung, um die Nachricht zu pinnen")

    @commands.Cog.listener()
    async def on_message(self, message: discord.Message):
        author = message.author
        if author.bot and author.name == "ORF LIVE-SPEZIAL HEUTE":
            await self._manage_pins(message)


from urllib import request
import re #正则表达式去除网页标签
# from fake_useragent import UserAgent
from bs4 import BeautifulSoup as bs   # 其中bs为BeautifulSoup的别名
import xlwt

bname_list = []#书名列表
author_list = []#作者
grade_list = []#评分
click_list = []#点击量

#正则表达式去除网页标签
def RemoveLabel(html):
    pre=re.compile(('>(.*)<'))
    return''.join(pre.findall(html))

def crawler(index,x):
    # 爬取数据
    url = "http://www.en8848.com.cn/fiction/fiction/gerneral/"+index

    headers = {
        # "USER-Agent":UserAgent(verify_ssl=False).random
    }
    req = request.Request(url, headers=headers)
    resp = request.urlopen(req)
    html_data = resp.read().decode()#整个网页

    soup = bs(html_data, "html.parser")  # 构建一个解析器

    nowplay = soup.find_all("div", class_="yd-book-item yd-book-item-pull-left")

    global bname_list,author_list,grade_list,click_list
    n=0
    for item in nowplay:
        bname_dict = []
        author_dict = []
        grade_dict = []
        click_dict = []
        IsGrade = True#评分和点击量的标签相同，用来做判断
        match="-"#加入日期的标签与评分和点击量相同，但是不需要，所以做筛选
        n = n + 1
        for name_item in item.find_all('h2'):
            if (x == 1):
                if (n >= 31):
                    break
            else:
                if (n >= 14):
                    break
            bname_dict = RemoveLabel(str(name_item))
            bname_list.append(bname_dict)

        for dl_item in item.find_all('div', class_="author-container"):
            if (x == 1):
                if (n >= 31):
                    break
            else:
                if (n >= 14):
                    break
            for author in dl_item.find_all("dd"):
                author_dict = RemoveLabel(str(author))
                author_list.append(author_dict)

        for dd_item in item.find_all('div', class_="price-container"):
            if (x == 1):
                if (n >= 31):
                    break
            else:
                if (n >= 14):
                    break
            for price in dd_item.find_all("dd"):
                if (IsGrade):
                    grade_dict = RemoveLabel(str(price))
                    if(match in grade_dict):
                        pass
                    else:
                        grade_list.append(grade_dict)
                        IsGrade = False
                else:
                    click_dict = RemoveLabel(str(price))
                    if (match in click_dict):
                        pass
                    else:
                        click_list.append(click_dict)
                        IsGrade = True

def excel():
    global  bname_list,author_list,grade_list,click_list
    workbook=xlwt.Workbook(encoding='utf-8')
    worksheet=workbook.add_sheet('book',cell_overwrite_ok=True)
    for i in range(0,4):
        for j in range(0,len(bname_list)+1):
            if(i==0):
                if(j==0):
                    worksheet.write(j, i, '书名')
                else:
                    worksheet.write(j, i,bname_list[j-1])
            elif(i==1):
                if (j == 0):
                    worksheet.write(j, i, '作者')
                else:
                    worksheet.write(j, i, author_list[j - 1])
            elif(i==2):
                if (j == 0):
                    worksheet.write(j, i, '评分')
                else:
                    worksheet.write(j, i, grade_list[j - 1])
            else:
                if (j == 0):
                    worksheet.write(j, i, '点击量')
                else:
                    worksheet.write(j, i, click_list[j - 1])
    workbook.save('book.xls')

crawler("index.html",1)
crawler("index_2.html",2)
excel()


# Imports
import pygame
from pygame import *

# Screen Dimensions

WIN_WIDTH = 1400
WIN_HEIGHT = 1000

# Screen Defaults

DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 32
FLAGS = 0

# Spritesheet

spritesheet = pygame.image.load("resources/graphics/item_objects.png")

# Main Function

def main():
    pygame.init()
    screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
    pygame.display.set_caption("Sprite Mapper")
    x = 0
    y = 0
    w = 100
    h = 50
    pygame.key.set_repeat(1, 30)
    f = open("test.txt", "w")
    f.close()

    while 1:
        for e in pygame.event.get():
            if e.type == QUIT: raise SystemExit("QUIT")
            if e.type == KEYDOWN and e.key == K_ESCAPE:
                raise SystemExit("ESCAPE")
            if e.type == KEYDOWN and e.key == K_DOWN:
                y = y - 1
            if e.type == KEYDOWN and e.key == K_UP:
                y = y + 1
            if e.type == KEYDOWN and e.key == K_RIGHT:
                x = x - 1
            if e.type == KEYDOWN and e.key == K_LEFT:
                x = x + 1
            if e.type == KEYDOWN and e.key == K_g:
                y = y - 10
            if e.type == KEYDOWN and e.key == K_t:
                y = y + 10
            if e.type == KEYDOWN and e.key == K_h:
                x = x - 10
            if e.type == KEYDOWN and e.key == K_f:
                x = x + 10
            if e.type == KEYDOWN and e.key == K_w:
                h = h - 1
            if e.type == KEYDOWN and e.key == K_s:
                h = h + 1
            if e.type == KEYDOWN and e.key == K_a:
                w = w - 1
            if e.type == KEYDOWN and e.key == K_d:
                w = w + 1
            if e.type == KEYDOWN and e.key == K_SPACE:
                name = input("Name of Sprite?")

                f = open("test.txt", "a")

                savedcode = ("character = Surface((") + str(w) + (",") + str(h) + ("),pygame.SRCALPHA)") + ("\n")
                f.write(savedcode)

                savedcode = ("character.blit(spritesheet,(") + str(x) + (",") + str(y) + ("))") + ("\n")
                f.write(savedcode)

                savedcode = ("character = pygame.transform.scale(character, (") + str(w) + (",") + str(h) + ("))") + (
                    "\n")
                f.write(savedcode)

                savedcode = name + (" = character") + ("\n") + ("\n")
                f.write(savedcode)

                f.close()

        screen.fill(Color("#2E722A"))

        character = Surface((w, h))
        character.fill(Color("#4CCCFF"))

        character.blit(spritesheet, (x, y))
        character = pygame.transform.scale(character, (w * 8, h * 8))
        screen.blit(character, (0, 0))

        pygame.display.update()

if __name__ == "__main__":
    main()


import functools

from rest_framework import status
from rest_framework.request import Request
from rest_framework.response import Response

def require_param(name: str, data_type: type):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            assert len(args) >= 2, "At least two arguments are expected: view and request"
            request: Request = args[1]
            is_http_get = request.method == "GET"
            if is_http_get:
                data = request.query_params
            else:
                data = request.data
            if name not in data:
                error = "Parameter is required"
                detail = {"expected_location": "query params" if is_http_get else "body"}
            else:
                value = data[name]
                if not isinstance(value, data_type):
                    error = "Parameter has wrong type"
                    detail = {
                        "expected": data_type.__name__,
                        "actual": type(value).__name__,
                    }
                else:
                    if not value:
                        error = "Invalid value"
                        detail = {"msg": "A value is expected but it is empty, undefined or zero"}
                    else:
                        error = None
                        detail = None

            if error:
                return Response(
                    {"error": error, "parameter": name, "detail": detail}, status=status.HTTP_400_BAD_REQUEST
                )
            return func(*args, **kwargs)

        return wrapper

    return decorator


"""     Phần 1: Sử dụng thư viện Turtle để setup  đồng hồ
B1: import turtle
          B2: Create a screen/window
          B3: Create a pen tool to draw
B4: Tạo mặt đồng hồ
B5: Thêm chi tiết cho mặt đồng hồ (tạo vặt giờ tương ứng với các con số trên đồng hồ và vặt giây tương ứng với từng nấc kim giờ chạy)
B6: tạo kim Giờ, Phút, Giây

 Phần 2: tạo kim Giờ,Phút, Giây làm đồng hồ hoạt động
B7: làm 3 kim Giờ Phút Giây hoạt động như 1 đồng hồ bình thường
B9: thêm chút chỉnh sửa màu mè theo phong cách của bạn và hoàn chỉnh """

import turtle
import time
from datetime import date, datetime

# Create a window/screen
wn = turtle.Screen()
wn.title("Turtle clock by Epamelis")
wn.setup(width = 600, height = 600)
wn.bgcolor("black")
wn.tracer(0)
# tracer is a function turn on/off the animation and it goes with wn.update()

# Create a pen to draw
pen = turtle.Turtle()
pen.speed(0)
pen.pensize(3)
pen.hideturtle()
pen.color("white")

def clock(h, m, s, pen):
          pen.up()
          pen.goto(0,260)
          pen.setheading(180)
          pen.color("white")
          pen.down()
          pen.circle(180) #180 is the radius

          # Create 12 numbers around  the clock
          num =0
          for i in range(12):
                    num+=1
                    pen.up()
                    pen.setheading(-30*(i+3)+75)
                    pen.forward(100)
                    pen.write(str(num), align="center", font=("Arial", 12, "normal"))

          # Create the hour lines
          pen.goto(0,80)
          pen.setheading(90)
          for i in range(12):
                    pen.forward(160)
                    pen.down()
                    pen.forward(10)
                    pen.up()
                    pen.goto(0,80)
                    pen.rt(30)

          # Create the second lines
          # There are 5 dashes between each number 5x12 = 60. The second hand stop at these dashes
          for i in range(60):
                    pen.forward(175)
                    pen.down()
                    pen.forward(4)
                    pen.up()
                    pen.goto(0,80)
                    pen.rt(6)

          # Create Hour hand
          pen.setposition(0,80)
          pen.setheading(90)
          pen.pensize(4)
          pen.color("yellow")
          angle = (h/12)*360
          pen.rt(angle)
          pen.down()
          pen.forward(95)

          # Create Minute hand
          pen.setposition(0,80)
          pen.setheading(90)
          pen.pensize(4)
          pen.color("blue")
          angle = (m/60)*360
          pen.rt(angle)
          pen.down()
          pen.forward(160)

          # Create Second hand
          pen.setposition(0,80)
          pen.setheading(90)
          pen.pensize(2)
          pen.color("red")
          angle = (s/60)*360
          pen.rt(angle)
          pen.down()
          pen.forward(175)

          #Create Name inside the clock
          pen.up()
          pen.color("orange")
          pen.setpos(0,-30)
          pen.write("Epamelis", align="center", font=("Arial", 15, "italic"))

#Create an infinite loop to Get Time and pass it in the User Define Function
while True:
          h = int(time.strftime("%I"))
          m = int(time.strftime("%M"))
          s = int(time.strftime("%S"))
          pen.up()
          pen.color("orange")
          today = date.today()
          today_now = today.strftime("%B, %d, %Y")
          pen.goto(0,-175)
          pen.write("Today is: " + today_now, align="center", font=("Arial", 15, "italic"))

          duisplay_time = datetime.now()
          time_now  = duisplay_time.strftime("%H %p %M minutes %S seconds")
          pen.goto(0,-220)
          pen.write("Up time:" + time_now, align="center", font=("Arial", 15, "italic"))
          clock(h, m, s, pen)

          wn.update()
          time.sleep(1)
          pen.clear()

wn.mainloop()


import numpy as np
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import Dataset
from torchvision.datasets import ImageFolder

from utils.conf import base_path
from PIL import Image
import os
from datasets.utils.validation import get_train_val
from datasets.utils.incremental_dataset import IncrementalDataset, store_masked_loaders, getfeature_loader
from datasets.utils.incremental_dataset import get_previous_train_loader
from datasets.transforms.denormalization import DeNormalize
from argparse import Namespace
import torchvision.models as models

class MyImagenet(ImageFolder):
    """
    Defines Tiny Imagenet as for the others pytorch datasets.
    """
    def __init__(self, root: str, transform: transforms=None,
                target_transform: transforms=None) -> None:
        super(MyImagenet, self).__init__(
            root, transform)
        self.attributes = []
        self.trans = []
        self.transform = transform
        self.target_transform = target_transform
        self.not_aug_transform = transforms.Compose([transforms.Resize(224), transforms.ToTensor()])
        self.data = [s[0] for s in self.samples]

    def set_att(self, att_name, att_data, att_transform=None):
        self.attributes.append(att_name)
        self.trans.append(att_transform)
        setattr(self, att_name, att_data)

    def get_att_names(self):
        return self.attributes

    def __getitem__(self, index):
        path, target = self.data[index], self.targets[index]

        # path, target = self.data[index]
        img = self.loader(path)
        original_img = img.copy()
        not_aug_img = self.not_aug_transform(original_img)

        if self.transform is not None:
            img = self.transform(img)

        if self.target_transform is not None:
            target = self.target_transform(target)

        ret_tuple = (img, target)
        for i, att in enumerate(self.attributes):
            att_data = getattr(self, att)[index]

            trans = self.trans[i]
            if trans:
                att_data = trans(att_data)

            ret_tuple += (att_data,)

        return ret_tuple

    def __len__(self) -> int:
        return len(self.data)

class SequentialImagenet(IncrementalDataset):

    NAME = 'seq-imagenet'
    SETTING = 'class-il'
    N_CLASSES_PER_TASK = 20
    N_TASKS = 10
    TRANSFORM = None

    def __init__(self, args: Namespace) -> None:
        """
        Initializes the train and test lists of dataloaders.
        :param args: the arguments which contains the hyperparameters
        """
        self.nc = 1000
        self.nt = 10
        self.n_channel = 3
        self.n_imsize1 = 64
        self.n_imsize2 = 64
        super(SequentialImagenet, self).__init__(args)

        self.train_transform = transforms.Compose([
            transforms.Resize(68),
            transforms.CenterCrop(64),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])
        self.test_transform = transforms.Compose([
            transforms.Resize(68),
            transforms.CenterCrop(64),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])

    def get_data_loaders(self):

        train_dataset = MyImagenet('../../dataset/imagenet/' + 'train', transform=self.train_transform)
        test_dataset = MyImagenet('../../dataset/imagenet/' + 'val', transform=self.test_transform)

        # train_dataset = MyTinyImagenet(base_path() + 'TINYIMG',
        #                          train=True, download=True, transform=self.train_transform)
        # if self.args.validation:
        #     train_dataset, test_dataset = get_train_val(train_dataset,
        #                                             self.test_transform, self.NAME)
        # else:
        #     test_dataset = TinyImagenet(base_path() + 'TINYIMG',
        #                 train=False, download=True, transform=self.test_transform)

        train, test = getfeature_loader(train_dataset, test_dataset, setting=self)
        return train, test

    def get_transform(self):
        transform = transforms.Compose(
            [transforms.ToPILImage(), self.train_transform])
        return transform


import config
from ext import glob
from typing import Union
from typing import Optional
from objects.score import Score
from objects.beatmap import Beatmap
from objects.score import BanchoScore

ONLINE_PLAYS = dict[str, list[dict]]
OSU_API_BASE = 'https://osu.ppy.sh/api'

status_to_db = {
    1: 'ranked',
    2: 'approved',
    3: 'qualified',
    4: 'loved'
}

"""Map ranking types"""
NOTSUBMITTED = -1
PENDING = 0
UPDATEAVALIABLE = 1
RANKED = 2
APPROVED = 3
QUALIFIED = 4
LOVED = 5

"""Leaderboard types"""
LOCAL   = 0
TOP     = 1
MODS    = 2
FRIENDS = 3
COUNTRY = 4

FROM_API_TO_SERVER_STATUS = {
    4: LOVED,
    3: QUALIFIED,
    2: APPROVED,
    1: RANKED,
    0: PENDING,
    -1: PENDING, # wip
    -2: PENDING  # graveyard
}

STARTING_LB_FORMAT = (
    "{rankedstatus}|false|{mapid}|{setid}|{num_of_scores}\n0\n"
    "[bold:0,size:20]{artist_unicode}|{title_unicode}\n10.0\n"
)
SCORE_FORMAT = (
    "{score_id}|{username}|{score}|"
    "{maxcombo}|{count50}|{count100}|"
    "{count300}|{countmiss}|{countkatu}|"
    "{countgeki}|{perfect}|{enabled_mods}|{user_id}|"
    "{num_on_lb}|{time}|{replay_available}"
)
VALID_LB_STATUESES = (LOVED, QUALIFIED, RANKED, APPROVED)

SCORE = Union[Score, BanchoScore]
class Leaderboard:
    def __init__(self) -> None:
        self.scores: list[SCORE] = []
        self.bmap: Optional[Beatmap] = None
        self.personal_score: Optional[Score] = None

    @property
    def lb_base_fmt(self) -> Optional[bytes]:
        if not self.bmap:
            return

        return STARTING_LB_FORMAT.format(
            rankedstatus = FROM_API_TO_SERVER_STATUS[self.bmap.approved],
            mapid = self.bmap.beatmap_id,
            setid = self.bmap.beatmapset_id,
            num_of_scores = len(self.scores),
            artist_unicode = self.bmap.artist_unicode or self.bmap.artist,
            title_unicode = self.bmap.title_unicode or self.bmap.title
        ).encode()

    @property
    def as_binary(self) -> bytes:
        if not self.bmap:
            return b'0|false'

        r = FROM_API_TO_SERVER_STATUS[self.bmap.approved]
        if r not in VALID_LB_STATUESES:
            return f'{r}|false'.encode()

        if not self.lb_base_fmt:
            return f'{r}|false'.encode()

        buffer = bytearray()
        buffer += self.lb_base_fmt

        if self.personal_score:
            if self.personal_score not in self.scores:
                num_on_lb = 101
            else:
                num_on_lb = self.scores.index(self.personal_score) + 1

            self.personal_score.score = int(self.personal_score.pp or 0)
            buffer += SCORE_FORMAT.format(
                **self.personal_score.as_leaderboard_score,
                num_on_lb = num_on_lb
            ).encode()
            buffer += b'\n'
        else:
            buffer += b'\n'

        len_scores = len(self.scores)
        for idx, s in enumerate(self.scores):
            idx += 1
            buffer += SCORE_FORMAT.format(
                **s.as_leaderboard_score,
                num_on_lb = idx
            ).encode()
            if idx != len_scores:
                buffer += b'\n'

        return bytes(buffer)

    @classmethod
    async def from_offline(
        cls, filename: str, mods: int,
        mode: int, rank_type: int,
        set_id: int, md5: str
    ) -> 'Leaderboard':
        lb = cls()

        bmap = Beatmap()
        bmap.approved = 3
        bmap.title_unicode = bmap.title = ''
        bmap.artist_unicode = bmap.artist = ''
        bmap.beatmap_id = bmap.beatmapset_id = 0

        lb.bmap = bmap
        scores: list[Union[Score, BanchoScore]] = []

        lb.scores = scores
        if (
            not glob.player or
            not glob.current_profile
        ):
            return lb

        key = 'qualified_plays'

        _player_scores: Optional[ONLINE_PLAYS] = \
        glob.current_profile['plays'][key]

        if not _player_scores:
            return lb

        if md5 not in _player_scores:
            return lb

        player_scores = _player_scores[md5].copy()

        player_scores.sort(key = lambda s: s['score'], reverse = True)

        if rank_type == MODS:
            player_scores = [x for x in player_scores if x['mods'] & mods]
            if not player_scores:
                return lb

            player_score = Score.from_dict(player_scores[0])
        else:
            player_score = Score.from_dict(player_scores[0])

        lb.scores.append(player_score)
        lb.scores.sort(key = lambda s: int(s.score), reverse = True)

        if lb.scores.index(player_score) == 100:
            lb.scores.remove(player_score)

        lb.personal_score = player_score

        return lb

    @classmethod
    async def from_bancho(
        cls, filename: str, mods: int,
        mode: int, rank_type: int,
        set_id: int, md5: str
    ) -> 'Leaderboard':
        lb = cls()

        lb.bmap = bmap = await Beatmap.from_md5(md5)
        if not bmap:
            return lb

        ranked_status = FROM_API_TO_SERVER_STATUS[bmap.approved]
        if ranked_status not in VALID_LB_STATUESES:
            return lb

        if config.osu_api_key:
            params = {
                'k': config.osu_api_key,
                'b': bmap.beatmap_id,
                'limit': 100
            }
            if rank_type == MODS:
                params['mods'] = mods

            async with glob.http.get(
                url = f'{OSU_API_BASE}/get_scores',
                params = params
            ) as resp:
                scores: list[SCORE] = [
                    BanchoScore(x) for x in await resp.json()
                ]
        else:
            scores: list[SCORE] = []

        lb.scores = scores
        if (
            not glob.player or
            not glob.current_profile
        ):
            return lb

        key = f'{status_to_db[bmap.approved]}_plays'

        _player_scores: Optional[ONLINE_PLAYS] = \
        glob.current_profile['plays'][key]

        if not _player_scores:
            return lb

        if md5 not in _player_scores:
            return lb

        player_scores = _player_scores[md5]

        player_scores.sort(key = lambda s: s['score'], reverse = True)

        if rank_type == MODS:
            player_scores = [x for x in player_scores if x['mods'] == mods]
            if not player_scores:
                return lb

            player_score = Score.from_dict(player_scores[0])
        else:
            player_score = Score.from_dict(player_scores[0])

        lb.scores.append(player_score)
        lb.scores.sort(key = lambda s: int(s.score), reverse = True)

        if lb.scores.index(player_score) == 100:
            lb.scores.remove(player_score)

        lb.personal_score = player_score

        return lb


import pygame

class Attract():
    '''
    The entire attract scene and all of its core funtions
    '''
    def __init__(self):
        self.run = True
        self.game_start = False
        self.framerate = 60
        self.clock = pygame.time.Clock()

    def eventHandler(self):
        '''
        Handles game events in attract mode.
        Returns states if needed.
        '''
        if pygame.event.get(pygame.QUIT):
            self.run = False

    def attractLoop(self, screen):
        '''
        The attract loop.
        Has the warning screen, title screen, demo play.
        '''
        while self.run:
            # First, we should start our loop with the event manager
            events = self.eventHandler()

            # Now, let's make sure that the game is locked to a framerate.
            self.clock.tick(self.framerate)

            # The last thing we want to do in our loop here is update the screen.
            pygame.display.update()


import sys
from collections import deque

input = sys.stdin.readline

n = int(input())
queue = deque(enumerate(map(int, input().split())))

ans = []

while queue:
    index, note = queue.popleft()
    ans.append(index + 1)

    if note > 0:
        queue.rotate(-(note - 1))
    elif note < 0:
        queue.rotate(-note)

print(" ".join(map(str, ans)))


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

find_mappings_verbs_file_name = 'find_mappings_verbs_cosine_sim.txt'

def read_verbs_cosine_sim(filename, bins, labels):
    triplets = set()
    file_object = open(filename, 'r')
    for line in file_object:
        cos_sim, v1, v2 = float(line.split(';')[0]), line.split(';')[1], line.split(';')[2][:-1]
        if (cos_sim, v1, v2) not in triplets and (cos_sim, v2, v1) not in triplets:
            triplets.add((cos_sim, v1, v2))

    verbs, scores = [], []
    for triple in triplets:
        verbs.append(triple[1] + ';' + triple[2])
        scores.append(triple[0])

    df1 = {'verbs': verbs, 'scores': scores}
    df1 = pd.DataFrame(df1, columns=['verbs', 'scores'])
    df1['binned'] = pd.cut(df1['scores'], bins, labels=labels)

    results = []
    for i in range(labels[0], int(labels[-1]) + 1):
        results.append(df1[(df1['binned'] == i)])
    print(results)

def plot_graph(x, y):
    plt.plot(x, y)
    for a, b in zip(x, y):
        plt.text(a, b, str(b))

    plt.xlabel('cosine similarity thresholds')
    plt.ylabel('verbs similarity accuracy')

    plt.show()

if __name__ == '__main__':
    bins = [0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8]
    labels = [1, 2, 3, 4, 5, 6, 7, 8]
    read_verbs_cosine_sim(find_mappings_verbs_file_name, bins, labels)

    # values derived from ../paper_experiments_results/cosine_similarity_threshold_figure4/samples_for_verbs_cosine_threshold.csv
    thresholds_x_axis = [0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8]
    acc_y_axis = [0.533, 0.6, 0.867, 0.867, 0.867, 1.0, 1.0, 1.0, 1.0]
    plot_graph(np.array(thresholds_x_axis), np.array(acc_y_axis))


import logging
import unittest
from omero_ext import xmlrunner

class OmeTestLoader(object):

    def __init__(self, args):
        self.__args = args

    def loadTestsFromModule(self, *args):
        if hasattr(self, "already_called"):
            raise Exception("Already called")
        load = unittest.defaultTestLoader.loadTestsFromName
        suite = unittest.TestSuite()
        for arg in self.__args:
            suite.addTest(load(arg))
        self.already_called = True
        return suite

def ome_test_main(args):
    logging.basicConfig(level=logging.WARN)
    unittest.main(
        testRunner=xmlrunner.XMLTestRunner(verbose=True, output='target/reports'),
        testLoader = OmeTestLoader(args))


import os
import parse
import re
from collections import defaultdict
import pprint

def get_subfamily(dirname):
    r = parse.parse('{}- Subfamily', dirname)
    if r is not None:
        return r[0].strip().lower()
    else:
        raise RuntimeError('unable to get subfamily name from %r' % dirname)

def get_tribe(dirname):
    r = parse.parse('{}- Tribe', dirname)
    if r is not None:
        return r[0].strip().lower()
    else:
        return dirname.strip().lower()

def get_genus(filename):
    s = filename.replace('Copy of ', '')
    idx_first_nonletter = re.search('[^a-zA-Z]', s).start()
    return s[:idx_first_nonletter].lower()

def get_image_stacks(path):
    res = {}
    for d in os.listdir(path):
        dpath = os.path.join(path, d)
        if os.path.isdir(dpath) and 'stack' in d.lower():
            res['dir'] = d
            files_per_genus = defaultdict(list)
            files = os.listdir(dpath)
            for fn in files:
                if fn.endswith('.tif'):
                    genus = get_genus(fn)
                    files_per_genus[genus].append(fn)
            res['files'] = dict(files_per_genus)
    return res

def build_tree(dirname):
    tree = {}
    for subfamily_dir in os.listdir(dirname):
        subfamily_path = os.path.join(dirname, subfamily_dir)
        if os.path.isdir(subfamily_path):
            subfamily = get_subfamily(subfamily_dir)
            tree[subfamily] = {'dir': subfamily_dir}
            tribes = {}
            for tribe_dir in os.listdir(subfamily_path):
                tribe_path = os.path.join(subfamily_path, tribe_dir)
                if os.path.isdir(tribe_path):
                    tribe = get_tribe(tribe_dir)
                    tribes[tribe] = {'dir': tribe_dir}

                    genus = {'image_stack': get_image_stacks(tribe_path)}
                    tribes[tribe]['genus'] = genus

            tree[subfamily]['tribes'] = tribes

    return tree

if __name__ == '__main__':
    pp = pprint.PrettyPrinter(indent=2)
    pp.pprint(build_tree('../../data/official_data'))


"""
data.py - Data handling of graphs
"""
import torch
import torch_sparse
import copy
from typing import Optional, Any, Tuple, Callable, Dict

class Data:
    """
    The `Data` class encodes graph data.

    A `Data` object can be seen as a `dict`, with its key-value pairs representing
    various graph features.
    """

    def __init__(self,
                 x: Optional[torch.Tensor] = None,
                 edge_index: Optional[torch.LongTensor] = None,
                 edge_attr: Optional[torch.Tensor] = None,
                 y: Optional[torch.Tensor] = None,
                 **kwargs):
        self.node_feature_set: set[str] = set()
        self.edge_feature_set: set[str] = set()
        self.edge_index_set: set[str] = set()
        self.graph_feature_set: set[str] = set()
        self.require_slice_set: set[str] = set()
        self.borrow_slice_dict: Dict[str, str] = {}

        if x is not None:
            self.node_feature_set.add('x')
            self.x = x
        if edge_index is not None:
            self.edge_index_set.add('edge_index')
            self.edge_index = edge_index
        if edge_attr is not None:
            self.edge_feature_set.add('edge_attr')
            self.edge_attr = edge_attr
        if y is not None:
            self.graph_feature_set.add('y')
            self.y = y

        self.batch_level = -1

        # maintain a dict for __cat_dim__() and __inc__() calls
        self.cat_dim_dict: Dict[str, int] = {}
        self.inc_dict: Dict[str, int] = {}

        self.__dict__.update(kwargs)

    def __setattr__(self, __name: str, __value: Any) -> None:
        self.__dict__[__name] = __value

    def __delattr__(self, __name: str) -> None:
        del self.__dict__[__name]

    def __repr__(self) -> str:
        feature_list = []
        for k in self.__dict__:
            v = self.__dict__[k]
            if isinstance(v, torch.Tensor):
                feature_list.append('='.join((k, repr(list(v.shape)))))
            elif k not in {'batch_level', 'inc_dict', 'cat_dim_dict', 'borrow_slice_dict'} \
                    and '_set' not in k:
                feature_list.append('='.join((k, repr(v))))
        return self.__class__.__name__ + '(' + ', '.join(feature_list) + ')'

    def keys(self):
        return self.__dict__.keys()

    def __iter__(self):
        return self.__dict__.__iter__()

    @property
    def num_nodes(self) -> int:
        if hasattr(self, 'x'):
            return self.x.shape[0]
        if hasattr(self, 'edge_index'):
            return int(self.edge_index.max() + 1)
        raise AttributeError("No attribute num_nodes!")

    @property
    def num_edges(self) -> int:
        if hasattr(self, 'edge_attr'):
            return self.edge_attr.shape[0]
        if hasattr(self, 'edge_index'):
            return self.edge_index.shape[1]
        raise AttributeError("No attribute num_edges!")

    @property
    def num_node_features(self) -> int:
        if hasattr(self, 'x'):
            return self.x.shape[1]
        return 0

    @property
    def num_edge_features(self) -> int:
        if hasattr(self, 'edge_attr'):
            try:
                return self.edge_attr.shape[1]
            except IndexError:
                return 1
        return 0

    @property
    def shape(self) -> Tuple[int, int]:
        """
        Returns the shape of the adjacency matrix.
        """
        return (self.num_nodes, self.num_nodes)

    def is_undirected(self) -> bool:
        index_t, value_t = torch_sparse.transpose(index=self.edge_index,
                                                  value=torch.ones(
                                                      self.num_edges),
                                                  m=self.num_nodes, n=self.num_nodes)
        index, value = torch_sparse.coalesce(index=self.edge_index,
                                             value=torch.ones(self.num_edges),
                                             m=self.num_nodes, n=self.num_nodes)
        return bool(torch.all(index == index_t))

    def is_directed(self) -> bool:
        return not self.is_undirected()

    def has_self_loops(self) -> bool:
        src, tgt = self.edge_index
        return bool(torch.any(src - tgt == 0))

    def coalesce(self, reduce: str = 'add'):
        """
        If there are duplicate columns in `edge_index`, coalesce them via `reduce`.
        """
        edge_attr = self.edge_attr if hasattr(self, 'edge_attr') else None
        edge_index = self.edge_index if hasattr(self, 'edge_index') else None
        self.edge_index, self.edge_attr = torch_sparse.coalesce(
            edge_index, edge_attr, *self.shape, op=reduce)
        if self.edge_attr is None:
            del self.edge_attr

    def __cat_dim__(self, attr: str) -> int:
        """
        The `__cat_dim__` tells a dataloader the dimension on which a tensor-type
        graph feature `attr` should be concatenated.
        """
        if attr in set.union(self.node_feature_set,
                             self.edge_feature_set,
                             self.graph_feature_set):
            return 0
        elif attr in self.edge_index_set:
            return 1
        return self.cat_dim_dict[attr]

    def __inc__(self, attr: str) -> int:
        """
        The `__inc__` tells a dataloader the offset to add on a tensor-type graph
        feature `attr`.
        """
        if attr in set.union(self.node_feature_set,
                             self.edge_feature_set,
                             self.graph_feature_set):
            return 0
        elif attr in self.edge_index_set:
            return self.num_nodes
        return self.inc_dict[attr]

    def apply(self, func: Callable) -> "Data":
        """
        Apply `func` to all tensor-type graph features.
        """

        out = self.__new__(self.__class__)
        for k in self.__dict__:
            val = self.__dict__[k]
            if isinstance(val, torch.Tensor):
                out.__dict__[k] = func(val)
            else:
                out.__dict__[k] = val
        return out

    def to(self, *args, **kwargs) -> "Data":
        return self.apply(lambda x: x.to(*args, **kwargs))

    def clone(self) -> "Data":
        out = self.apply(lambda x: x.clone())
        for k in out.__dict__:
            val = out.__dict__[k]
            if not isinstance(val, torch.Tensor):
                out.__dict__[k] = copy.copy(val)
        return out

    def __set_tensor_attr__(self, name: str,
                            value: torch.Tensor,
                            collate_type: str,
                            cat_dim: Optional[int] = None,
                            slicing: bool = False,
                            use_slice: Optional[str] = None):
        """
        An extension to `__setattr__`, which allows auto-batching of new
        tensor-type attributes.

        `collate_type` should be one of `'node_feature'`, `'edge_feature'`,
        `'edge_index'`, `'graph_feature'`, `'auto_collate'` or `'no_collate'`.

        When `slicing` is set `True`, an additional slice vector for the feature
        will be added. This should only happen when `collate_type='edge_index'` or
        `collate_type='auto_collate'`.

        When `use_slice` is not `None`, we will use an already existing slicing
        vector to slice the current tensor-type attribute, instead of creating a
        new slicing vector.
        """
        assert collate_type in {'node_feature', 'edge_feature', 'graph_feature',
                                'edge_index', 'auto_collate', 'no_collate'
                                }, "Invalid collate type!"

        self.__setattr__(name, value)

        if collate_type == 'no_collate':
            return

        if collate_type in {'node_feature', 'edge_feature', 'graph_feature'}:
            assert slicing == False, "Can't customize slicing method when collate_type is "
            "node_feature, edge_feature or graph_feature!"
            self.__dict__[collate_type+'_set'].add(name)
            return

        if collate_type == 'edge_index':
            self.edge_index_set.add(name)
        elif collate_type == 'auto_collate':
            assert cat_dim is not None, "Auto collating receives NoneType cat_dim!"
            self.cat_dim_dict[name] = cat_dim
            self.inc_dict[name] = 0

        if slicing:
            if use_slice is None:
                self.require_slice_set.add(name)
            else:
                self.borrow_slice_dict[name] = use_slice

    def __del_tensor_attr__(self, name: str):
        """
        An extension to `__delattr__`, which automatically handles the deletion of
        tensor features (e.g. deletion of slicing vectors).
        """
        self.node_feature_set.discard(name)
        self.edge_index_set.discard(name)
        self.edge_feature_set.discard(name)
        self.graph_feature_set.discard(name)
        self.require_slice_set.discard(name)
        if name in self.borrow_slice_dict.keys():
            del self.borrow_slice_dict[name]

        keys = {key for key in self.__dict__ if key.startswith(name)}
        for key in keys:
            del self.cat_dim_dict[key]
            del self.inc_dict[key]
            del self.__dict__[key]


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Sep 21 14:54:02 2019

@author: emil
"""
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d
from utils import matrix_from_rpy
from matplotlib import animation

plt.close('all')

class Visualizer(object):
    def __init__(self, system):
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111, projection='3d')
        self.ax.set_xlabel('X')
        self.ax.set_ylabel('Y')
        self.ax.set_zlabel('Z')
        self.ax.set_title('Simulation')
        self.ax.set_xlim(-5, 5)
        self.ax.set_ylim(-5, 5)
        self.ax.set_zlim(0, 3)
        self.system = system
        self.line1, =  self.ax.plot([], [], [], c = 'b', lw=2)
        self.line2, =  self.ax.plot([], [], [], c = 'r', lw=2)

    def update_drawing(self, N):
        U = self.system.get_optimal_gain()
        pos, rpy = self.system.iterate(U = U)
        # print(pos,rpy)
        self.draw_drone(pos, rpy)
        return [self.line1, self.line2]

    def draw_drone(self, position, rpy):

        R = matrix_from_rpy(rpy)
        size = 0.2
        fl = R @ np.array([[ 1, 0,0]]).T * size + position
        fr = R @ np.array([[0,-1,0]]).T * size + position
        bl = R @ np.array([[0, 1,0]]).T * size + position
        br = R @ np.array([[-1,0,0]]).T * size + position
        # self.ax.scatter(position[0],position[1],position[2])
        self.line1.set_data([fl[0,0],br[0,0]],[fl[1,0],br[1,0]])
        self.line1.set_3d_properties([fl[2,0],br[2,0]])
        self.line2.set_data([fr[0,0],bl[0,0]],[fr[1,0],bl[1,0]])
        self.line2.set_3d_properties([fr[2,0],bl[2,0]])
        # self.ax.scatter(position[0],position[1],position[2])

    def animate(self, time):
        print(round(time/self.system.delta))
        self.ani = animation.FuncAnimation(self.fig, self.update_drawing,
                                      frames = round(time/self.system.delta),
                                      interval = round(self.system.delta*1000),
                                      repeat_delay = 500, blit=True)
        plt.show()

    def clear(self):
        # self.ax.clear()
        # pass
        self.update_drawing()

if __name__ == '__main__':
    pos = np.array([[0,1,2]]).T
    rpy = np.array([[0.1,0.1,1]]).T

    sim = Visualizer(1)
    sim.draw_drone(pos, rpy)


from img_processing import convert_to_grayscale
import pickle
import numpy as np
import cv2

pca = pickle.load(open("pca_cyrillic.sav", 'rb'))
scaler = pickle.load(open("scaler_cyrillic.sav", 'rb'))

desired_output_size = (50, 50)
img = cv2.imread("SHA_UPPER.png", cv2.IMREAD_UNCHANGED)

converted_img = img[:, :, 0]
converted_img = cv2.resize(converted_img[:, :], dsize=desired_output_size, interpolation=cv2.INTER_CUBIC)
features = np.reshape(converted_img, (desired_output_size[0] * desired_output_size[1]))

features = scaler.transform(features.reshape(1, -1))
features = pca.transform(features.reshape(1, -1))

loaded_model = pickle.load(open("SVC.sav", 'rb'))
result = loaded_model.predict(features.reshape(1, -1))
print(result)


"""ex02_15.py"""
from threading import Thread

class SimpleThread(Thread):
    def __init__(self):
        super(SimpleThread, self).__init__()
        self._callback = None
        self.args = None

    def callback(self, func, *args, **kwargs):
        self._callback = func
        self.args = args
        self.kwargs = kwargs

    def run(self):
        if self._callback:
            self._callback(*self.args, **self.kwargs)

class SimpleWGet:
    def __init__(self):
        import requests
        from requests.adapters import HTTPAdapter
        from urllib3.util.retry import Retry

        self.session = requests.Session()
        retry = Retry(connect=3, backoff_factor=0.5)
        adapter = HTTPAdapter(max_retries=retry)
        self.session.mount('http://', adapter)
        self.session.mount('https://', adapter)

    def get(self, url):
        r = self.session.get(url)
        filename = url.split('/')[-1]

        with open(filename, 'wb') as f:
            f.write(r.content)

def main():
    OPENCV_SIMPLE_DATA = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/"
    urls = [
        OPENCV_SIMPLE_DATA + "ml.png",
        OPENCV_SIMPLE_DATA + "home.jpg",
        OPENCV_SIMPLE_DATA + "butterfly.jpg",
        OPENCV_SIMPLE_DATA + "baboon.jpg",
        OPENCV_SIMPLE_DATA + "lena.jpg",
        OPENCV_SIMPLE_DATA + "lena.jpg"
    ]

    wget = SimpleWGet()

    for url in urls:
        th = SimpleThread()
        th.callback(wget.get, url)
        th.start()

    th.join()

if __name__ == '__main__':
    main()

{'requests': 'requests', 'HTTPAdapter': 'requests.adapters.HTTPAdapter', 'Retry': 'urllib3.util.retry.Retry'}


#! /usr/bin/env python

import advent_of_code as adv

MOVES = {
    'R': (1, 0),
    'L': (-1, 0),
    'U': (0, 1),
    'D': (0, -1),
}

def line_positions(moves):
    x, y = 0, 0
    for move in moves.split(','):
        direction = move[0]
        steps = int(move[1:])
        dx, dy = MOVES[direction]
        for i in range(steps):
            x += dx
            y += dy
            yield (x, y)

test_data = (
    ("R8,U5,L5,D3\nU7,R6,D4,L4", 6, 30),
    ("R75,D30,R83,U83,L12,D49,R71,U7,L72\nU62,R66,U55,R34,D71,R55,D58,R83", 159, 610),
    ("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51\nU98,R91,D20,R16,D67,R40,U7,R15,U6,R7", 135, 410),
)
for lines, expect_part_one, _ in test_data:
    line1, line2 = (set(line_positions(line)) for line in lines.splitlines())
    closest = min(map(lambda p: sum(map(abs, p)), line1.intersection(line2)))
    assert closest == expect_part_one, f"{closest} != {expect_part_one}"

data = adv.input(3, lambda x: x)

line1, line2 = (set(line_positions(line)) for line in data.splitlines())
closest = min(map(lambda p: sum(map(abs, p)), line1.intersection(line2)))

print("Part one:", closest)

for lines, _, expect_part_two in test_data:
    line1, line2 = map(list, (line_positions(line) for line in lines.splitlines()))
    delay = min(
        line1.index(intersection) + line2.index(intersection) + 2  # because first step is index 0
        for intersection in set(line1).intersection(set(line2))
    )
    assert delay == expect_part_two, f"{delay} != {expect_part_two}"

line1, line2 = map(list, (line_positions(line) for line in data.splitlines()))
delay = min(
    line1.index(intersection) + line2.index(intersection) + 2  # because first step is index 0
    for intersection in set(line1).intersection(set(line2))
)

print("Part two:", delay)


"""
Tests for pandabase.

Note that the working directory is must be project_root_dir; when running tests from (for example) PyCharm,
the default may be project_root_dir/tests. In Pycharm this default can be set from Run/Debug Configurations:Templates

see, e.g.:
https://pythonspot.com/python-database-postgresql/
for postgres configuration commands to run postgres tests (use command 'pytest --run-postgres')
"""
import pytest

import pandabase as pb
from pandabase.helpers import *

import pytz

UTC = pytz.utc
LA_TZ = pytz.timezone('America/Los_Angeles')  # test timezone

bad_df = pd.DataFrame(index=[2, 2], data=['x', 'y'])
bad_df.index.name = 'bad_index'

bad_df2 = pd.DataFrame(index=[1, None], columns=['a'], data=['x', 'y'])
bad_df2.index.name = 'bad_index'

@pytest.mark.parametrize('df, how', [
    (bad_df, 'create_only'),
    (bad_df2, 'create_only'),
    (pd.Series([1, 2, 3]), 'create_only'),
    (pd.DataFrame(index=[1, 2, 3], columns=['a'], data=['x', 'y', 'a']), 'fake_mode'),
])
def test_invalid_inputs(df, how, empty_db):
    """all these inputs are invalid"""
    with pytest.raises(ValueError):
        pb.to_sql(df, table_name='blank', con=empty_db, how=how)

def test_get_sql_dtype_from_db(simple_df, empty_db):
    """test that datatype extraction functions work in pandas and SQL"""
    df = simple_df
    table = pb.to_sql(df,
                      table_name='sample',
                      con=empty_db,
                      how='create_only')

    for col in table.columns:
        if col.primary_key:
            # different syntax for index
            assert_sqla_types_equivalent(get_column_dtype(col, 'sqla'), get_column_dtype(df.index, 'sqla'))
            continue
        assert_sqla_types_equivalent(get_column_dtype(col, 'sqla'), get_column_dtype(df[col.name], 'sqla'))

def assert_sqla_types_equivalent(type1, type2):
    """weak equality test"""
    assert str(type1) == str(type2)

def test_get_sql_dtype_from_db_nans(simple_df_with_nans, empty_db):
    """test that datatype extraction functions work as expected"""
    df = simple_df_with_nans
    table = pb.to_sql(df,
                      table_name='sample',
                      con=empty_db,
                      how='create_only')

    for col in table.columns:
        if col.primary_key:
            # different syntax for index
            assert_sqla_types_equivalent(get_column_dtype(col, 'sqla'), get_column_dtype(df.index, 'sqla'))
            continue
        assert_sqla_types_equivalent(get_column_dtype(col, 'sqla'), get_column_dtype(df[col.name], 'sqla'))

@pytest.mark.parametrize('actually_do', [True, False])
def test_add_column_to_database(pandabase_loaded_db, actually_do, constants):
    """possibly add new column to db"""
    name = 'a_new_column'
    col = sqa.Column(name, primary_key=False, type_=Integer, nullable=True)
    if actually_do:
        pb.sql._add_columns_to_db(col, table_name=constants.TABLE_NAME, con=pandabase_loaded_db)
    df = pb.read_sql(table_name=constants.TABLE_NAME, con=pandabase_loaded_db)

    if actually_do:
        assert name in df.columns
        assert is_integer_dtype(df[name])
    else:
        assert name not in df.columns

def test_drop_table(pandabase_loaded_db):
    names = pb.util.get_db_table_names(pandabase_loaded_db)
    for name in names:
        assert pb.has_table(pandabase_loaded_db, table_name=name)
        pb.util.drop_db_table(con=pandabase_loaded_db, table_name=name)
        assert not pb.has_table(pandabase_loaded_db, table_name=name)

def test_get_tables(pandabase_loaded_db, constants):
    names = pb.util.get_db_table_names(pandabase_loaded_db)
    assert len(names) == 1
    assert names[0] == constants.TABLE_NAME

def test_get_columns(pandabase_loaded_db, simple_df, constants):
    cols = pb.util.get_table_column_names(pandabase_loaded_db, constants.TABLE_NAME)
    assert len(cols) == len(simple_df.columns) + 1
    assert constants.SAMPLE_INDEX_NAME in [col.name for col in cols]

def test_describe_db(pandabase_loaded_db, constants):
    desc = pb.util.describe_database(pandabase_loaded_db)
    assert len(desc) == 1  # 1 table in sample db
    assert desc[constants.TABLE_NAME]['min'] == 0  # min
    assert desc[constants.TABLE_NAME]['max'] == 5  # max
    assert desc[constants.TABLE_NAME]['count'] == 6  # count