import numpy as np
import os
import sounddevice as sd
import time
from django.conf import settings

# import hps_extractor as hpse
from real_time_pitch_extractor.hps_extractor import hps_pitch_detector

# General settings that can be changed by the user
sr = 44100  # sample frequency in Hz
window_size = 44100  # window size of the DFT in samples
window_step = 12000  # step size of window

def callback(indata, frames, time, status):
    """
    Callback function of the InputStream method.
    That's where the magic happens ;)
    """
    # define static variables
    if not hasattr(callback, "window_samples"):
        callback.window_samples = [0 for _ in range(window_size)]
    if not hasattr(callback, "noteBuffer"):
        callback.noteBuffer = ["1", "2"]

    if status:
        print(status)
        return
    if any(indata):
        callback.window_samples = np.concatenate(
            (callback.window_samples, indata[:, 0])
        )  # append new samples
        callback.window_samples = callback.window_samples[
            len(indata[:, 0]) :
        ]  # remove old samples

        pitch_detected, closest_pitch, closest_note, pitch_diff = hps_pitch_detector(
            callback.window_samples
        )  # extract pitch
        pitch_diff = round(pitch_diff, 1)
        callback.noteBuffer.insert(0, closest_note)  # note that this is a ringbuffer
        callback.noteBuffer.pop()

        os.system("cls" if os.name == "nt" else "clear")

        if callback.noteBuffer.count(callback.noteBuffer[0]) == len(
            callback.noteBuffer
        ):
            settings.PITCH_DETECTED = pitch_detected
            settings.CLOSEST_NOTE = closest_note
            settings.CLOSEST_PITCH = closest_pitch
            settings.PITCH_DIFF = pitch_diff
        else:
            settings.PITCH_DETECTED = 0
            settings.CLOSEST_NOTE = 0
            settings.CLOSEST_PITCH = 0
            settings.PITCH_DIFF = 0

    else:
        print("no input")

# Start the microphone input stream

def real_time(scallback):
    try:
        print("Starting HPS guitar tuner...")
        with sd.InputStream(
            channels=1, callback=callback, blocksize=window_step, samplerate=sr
        ):
            while settings.RECORD:
                scallback()
                time.sleep(0.2)

    except Exception as e:
        print(str(e))


import pandas as pd
import numpy as np
import io_data

def convt_to_one_hot(y):

    unique_items = np.unique(y)
    num_rows = len(y)
    num_cols = len(unique_items)
    one_hot = np.zeros((num_rows, num_cols)).astype(np.bool)

    for i, item in enumerate(unique_items):
        print(i, item)
        row_idx = y == item
        one_hot[row_idx, i] = 1

    print(np.sum(one_hot, axis=0))
    return one_hot * 1

def evaluate_clf(y_true, y_pred, y_pred_prob=None):
    from sklearn.metrics import accuracy_score, f1_score
    from sklearn.metrics import confusion_matrix
    from sklearn.metrics import classification_report

    acc = accuracy_score(y_true, y_pred)
    f1 = f1_score(y_true, y_pred, average='macro')
    cm = confusion_matrix(y_true, y_pred)
    rep = classification_report(y_true, y_pred)
    print(cm)
    print(rep)
    print('Acc :', acc)
    print('f1 :', f1)

if __name__ == '__main__':
    test_X, test_y = io_data.load_orig_dataset('test')
    convt_to_one_hot(test_y.iloc[:, 1])


import aiohttp
import json
import logging

logger = logging.getLogger()

async def fetch(url, headers=None, params=None):
    async with aiohttp.ClientSession() as session:
        async with session.get(url, headers=headers, params=params) as response:
            return await response.text()

async def fetch_json(url, headers=None, params=None):
    text = await fetch(url, headers=headers, params=params)
    return json.loads(text)

async def opt_webhook_send_embed(channel, embed):
    webhooks = None
    try:
        webhooks = await channel.webhooks()

    except Exception as e:
        logger.error("WH Error:", e)

    if webhooks:
        await webhooks[0].send(embed=embed, username="Ticker Bot", avatar_url="https://www.freeiconspng.com/uploads/stock-exchange-icon-png-1.png")
    else:
        await channel.send(embed=embed)


from azure.storage.filedatalake import DataLakeServiceClient

#Config parser parses the config files
from configparser import ConfigParser

config = ConfigParser()
config.read('config.ini')

storage_account_name = config['cred']['san']
storage_account_key = config['cred']['sak']
print(storage_account_name)
print(storage_account_key)

#Establishing a connection to adls2
def initialize_storage_account(storage_account_name, storage_account_key):
    try:
        global service_client

        service_client = DataLakeServiceClient(account_url="{}://{}.dfs.core.windows.net".format(
            "https", storage_account_name), credential=storage_account_key)
        print("ADLS connection established successfully")

    except Exception as e:
        print(e)

initialize_storage_account(storage_account_name, storage_account_key)

#Upload a file to a directory
def upload_file_to_directory(data_frame,filename):
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-file-system")

        directory_client = file_system_client.get_directory_client("Docker_file")
        file_client = directory_client.create_file(filename)
        print(data_frame)
        df_byte = data_frame.to_json().encode()
        print(df_byte)
        file_client.upload_data(df_byte, overwrite=True)
        # file_client.append_data(data=encrypted_data, offset=0, length=len(encrypted_data))

        # file_client.flush_data(len(file_contents))
        # logger.info("Uploaded "+filename)

    except Exception as e:
        print(e)


from matplotlib import pyplot as plt

from src.NumericalBase import NumericalBase

def gather_plot_data_depr(solutions, k, threshold=1):
    R = [[[], []] for _ in range(k)]
    C = []
    max_value = 0
    for solution in solutions:
        C.append(solution.value)
        for i, pair in enumerate(solution.root):
            R[i][0].append(pair[0])
            R[i][1].append(pair[1])
            max_value = max(solution.value, max_value)
    return R, C

def grid_plot(two_cocycles):
    size = len(two_cocycles[0].mapping)
    fig, ax = plt.subplots(size, size)
    for cocycle in two_cocycles:
        mapping = cocycle.mapping
        for i, row in enumerate(mapping):
            for j, v in enumerate(row):
                value = NumericalBase(cocycle.base).evaluate(v)
                ax[i][j].scatter(value.real, value.imag)
    plt.show()


from PyQt5.QtWidgets import QPushButton, QLabel
from PyQt5 import QtCore, QtGui, QtWidgets
from modules.view.img import img
from modules.view.Popup import Popup

def check_if_popup_needed(popup_type, distance_to_right_edge=99):
    if popup_type == 'Harvest':
        #Harvest cards don't have not any requirements to play
        return False
    if popup_type == 'PlayableStore':
        #If we're buying Playable card we need to check how close it is to end of the list
        if not distance_to_right_edge:
            return False
    if popup_type == 'too_much_resources':
        return True

    return True

def press_the_card(ClickedWholeWidget, ClickedCardWidget, Ui, Game, Popup, event):
    """Defines which actions should happened when card is pressed"""

    #If status is "USED" we shouldn't be able to press it untill player rest
    if ClickedCardWidget.Card.used == True:
        return

    #The parents of Whole Widget are ClickedCardWidget->ClickedWholeWidget->V/HBoxLayout->ScrollAreaWidgetContents->ScrollArea
    SourceScrollBox = ClickedWholeWidget.parent().parent().parent().objectName()
    if SourceScrollBox == 'PlayableStore-ScrollArea':
        popup_type = 'PlayableStore'
    elif SourceScrollBox == 'BuyableStore-ScrollArea':
        popup_type = 'BuyableStore'
    elif SourceScrollBox == 'PlayerHand-ScrollArea':
        popup_type = ClickedCardWidget.Card.card_type
    else:
        popup_type = 'unknown'

    ClickedCardWidget.Card.popup_type = popup_type

    Player = Game.CurrentPlayer

    #In case Player press another card while popup is visible
    Popup.close_popup()

    Popup.configure_popup(popup_type=popup_type, ClickedCardWidget=ClickedCardWidget)

    if check_if_popup_needed(popup_type=popup_type, distance_to_right_edge=Popup.distance_to_right_edge):
        #Show popup
        Popup.display_popup()
    else:
        Popup.action_confirmed(ClickedWholeWidget, ClickedCardWidget, Ui, Game)
        pass

    #Next steps should depends on what happened on popup


from bs4 import BeautifulSoup
import requests
import csv
from datetime import datetime
import sys

def get_job_detail(job):
    title = job.select_one('.profile').text.strip()
    company_name = job.select_one('.company_name').text.strip()
    duration = job.select('.item_body')[1].text.strip()
    last_date = job.select_one('.apply_by').select_one(".item_body").text.strip().replace("'", "")
    page_url = "https://internshala.com" + job.a['href']
    try:
        stipend = job.select_one('.stipend').text.strip()
    except AttributeError :
        stipend = 'NA'
    today = datetime.today().strftime('%d-%m-%Y')
    row = (title, company_name, stipend, duration, last_date, today , page_url)

    return row

def start_scraping(keyword='scraping') :
    rows = []
    page = 1
    #extract the job data
    while True:
        url = f'https://internshala.com/internships/keywords-{keyword}/page-{page}'
        html = requests.get(url).text
        soup = BeautifulSoup(html, 'html.parser')
        jobs = soup.select('.individual_internship')
        for job in jobs :
            row = get_job_detail(job)
            rows.append(row)
        page_number = soup.select_one('#pageNumber').text
        total_pages = soup.select_one('#total_pages').text
        if page_number == total_pages:
            break
        print("On page:", page_number, "Pages remaining:" , total_pages)
        page = page + 1

    #save the job data to csv file
    with open(keyword+'.csv', 'w', newline='', encoding='utf-8') as f :
        writer = csv.writer(f)
        writer.writerow(['Job title', 'Company', 'Stipend', 'Duration', 'Last date', 'Extract date', 'Job listing url'])
        writer.writerows(rows)

    print('Done!')

if __name__ == "__main__":
    # total arguments
    n = len(sys.argv)
    if n==2:
        keyword = sys.argv[1]
        start_scraping(keyword)
    else:
        start_scraping()


import json
import datetime

from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
import main.models as m

class DashboardConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()
        async_to_sync(self.channel_layer.group_add)('dashboard_consumer', self.channel_name)

        today = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time())
        three_days_ago = today - datetime.timedelta(days=3)
        # recently added remedial client
        recently_added_client = m.RemedialClientInfo.objects\
            .select_related("client")\
            .filter(date_created__gte=today)\
            .order_by("-date_created")\
            .values("id", "client__id", "client__first_name", "client__last_name", "health_insurance_number", "suffix", "date_created")

        recently_added_client_container = []

        for client in recently_added_client:
            recently_added_client_container.append({
                "id": client["id"],
                "client_id": client["client__id"],
                "first_name": client["client__first_name"],
                "last_name": client["client__last_name"],
                "health_insurance_number": str(client["health_insurance_number"]),
                "suffix": str(client["suffix"]),
                "date_created": datetime.datetime.strftime(client["date_created"], "%d %b %Y %H:%M")
            })

        self.send(text_data=json.dumps({
            "action_type": "init_remedial_client",
            "payload": recently_added_client_container
        }))

        # require receipt
        require_receipts = m.RemedialMedicalHistory.objects\
            .select_related("remedial_client_info","remedial_client_info__client")\
            .filter(date_created__gte=today)\
            .order_by("-date_created")\
            .values(
                "id",
                "remedial_client_info__client__id",
                "remedial_client_info__client__first_name",
                "remedial_client_info__client__last_name",
                "date_created",
                "receipt_image"
            )

        require_receipt_container = []

        for require_receipt in require_receipts:
            require_receipt_container.append({
                "id":require_receipt["id"],
                "client_id": require_receipt["remedial_client_info__client__id"],
                "first_name":require_receipt["remedial_client_info__client__first_name"],
                "last_name":require_receipt["remedial_client_info__client__last_name"],
                "date_created":datetime.datetime.strftime(require_receipt["date_created"], "%d %b %Y %H:%M"),
                "receipt_image": require_receipt["receipt_image"]
            })

        self.send(text_data=json.dumps({
            "action_type": "init_remedial_history",
            "payload": require_receipt_container
        }))

        # missing receipt
        missing_receipts = m.RemedialMedicalHistory.objects\
            .select_related("remedial_client_info","remedial_client_info__client")\
            .filter(receipt_image="",date_created__lte=today, date_created__gte=three_days_ago)\
            .order_by("-date_created")\
            .values(
                "id",
                "remedial_client_info__client__id",
                "remedial_client_info__client__first_name",
                "remedial_client_info__client__last_name",
                "date_created",
                "receipt_image"
            )

        missing_receipt_container = []

        for missing_receipt in missing_receipts:
            missing_receipt_container.append({
                "id":missing_receipt["id"],
                "client_id": missing_receipt["remedial_client_info__client__id"],
                "first_name":missing_receipt["remedial_client_info__client__first_name"],
                "last_name":missing_receipt["remedial_client_info__client__last_name"],
                "date_created":datetime.datetime.strftime(missing_receipt["date_created"], "%d %b %Y %H:%M"),
                "receipt_image": missing_receipt["receipt_image"]
            })

        self.send(text_data=json.dumps({
            "action_type": "init_missing_receipt",
            "payload": missing_receipt_container
        }))

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)('dashboard_consumer', self.channel_name)
        pass

    def receive(self, text_data):

        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        self.send(text_data=json.dumps({
            'message': message
        }))
    def send_data(self, data, type="send_data"):

        self.send(text_data=json.dumps(data))


import os
import glob
import textwrap
import pathlib as Path
from PIL import Image, ImageDraw, ImageFont

def create_contact_sheet(path, output_path):
    columns=3
    rows=4
    img_width=400
    img_extensions = ['jpg', 'png', 'jpeg', 'gif', 'bmp', 'tiff']
    images = [f for f in glob.glob(os.path.join(path, "*")) if f.split('.')[-1].lower() in img_extensions]
    font = ImageFont.truetype(r"Courier_New.ttf", 40)

    if not os.path.exists(output_path):
        os.makedirs(output_path)

    sheet_counter = 0
    margin = 80 # increased margin
    spacing = 80 # increased spacing
    for i in range(0, len(images), columns * rows):
        sheet_counter += 1
        img_list = images[i:i + columns * rows]

        sheet_width = columns * (img_width + spacing) + margin
        sheet_height = rows * (img_width + spacing) + margin # assuming square images for simplicity
        sheet = Image.new('RGB', (sheet_width, sheet_height), (255, 255, 255))
        draw = ImageDraw.Draw(sheet)

        x = y = margin # starting position

        for img_path in img_list:
            img = Image.open(img_path)
            # maintain aspect ratio
            aspect = img.size[0] / img.size[1]
            img_height = int(img_width / aspect)
            img = img.resize((img_width, img_height))

            sheet.paste(img, (x, y))

            # draw the filename below the image
            img_name = os.path.basename(img_path)
            lines = textwrap.wrap(img_name, width=18)  # wrap the text to fit within image width
            y_text = y + img_height
            line_spacing = 10 # additional space between lines in pixels

            for line in lines:
                bbox = draw.textbbox((x, y_text), line, font=font)
                width = bbox[2] - bbox[0]
                height = bbox[3] - bbox[1]
                text_x = x + (img_width - width) / 2
                draw.text((text_x, y_text), line, font=font, fill='black')
                y_text += height + line_spacing

            # move the position for the next image
            x += img_width + spacing
            if x + img_width + margin > sheet_width:
                x = margin
                y += img_height + spacing + 90 # leave room for text

        output_file_name = os.path.join(output_path, f"Endoskopiebilder_Blatt_{sheet_counter}.png")
        sheet.save(output_file_name)

    return sheet_counter

def main():
    path_in = input("Ordner, in dem sich die Fotos befinden: ")
    path_out = input("Ordner, in dem das Ergebnis gespeichert werden soll: ")

    # Uso de la función
    message = create_contact_sheet(Path.Path(path_in), Path.Path(path_out))

    print(f"{message} Kontaktbögen erstellt.")

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"An error occurred: {e}")


#loading a data from file and represent on graph
import csv
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
##from pandas import ExcelWriter
##from pandas import ExcelFile
#import xlrd
def file_loading(f):
    with open(f,'r') as csvfile:
        readCSV=csv.reader(csvfile, delimiter=',')
##        for row in readCSV:
##            print(row)
        x,y=np.loadtxt(csvfile,delimiter=',',unpack=True)
        plt.plot(x,y,label='loaded from file')
        plt.xlabel('student number')
        plt.ylabel('student growth')
        plt.title('general graph')
        plt.legend()
        plt.show()

file=input("enter the name of the file=")

file_loading(file)

##
##
##file=input("enter the file name")
##print(file_loading(file))


#! /usr/bin/env/python
# coding: utf-8

from pyspark import SparkConf, SparkContext

sconf = SparkConf().setMaster('local').setAppName('Purchase')
sc = SparkContext(conf = sconf)

lines = sc.textFile('purchases.txt')

def mapper(line):
	result = []
	items = line.strip().split("\t")
	if len(items) == 6:
		result.append((items[2], float(items[4])))
	return result

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

f = open("result", "w")

for result in lines.flatMap(mapper).reduceByKey(reducer).collect():
	f.write(result[0].encode('utf8') + "\t" + str(result[1]) + "\n")
f.close()


"""Validation tools for the datavalidator package."""
import json
import re
import html
from typing import Any, Callable, Union, Optional
from .types import RangeT, SetT, PolarT, PatternT, EnumT, XT, YT
from .exceptions import DataValidationError

class ValidationResult:
    """Data validation result.

    Each ValidationResult represents the result of a data validation attempt in a
    Validator. The ValidationResult can be queried for details on the data that was
    validated, whether it passed/failed validation, what the requirements for validation
    were, etc. A ValidationResult will evaluate to True if the validation has succeeded
    and to False if it has failed.
    """

    success: bool
    """Whether the data has been succeessfully validated or not."""

    type_: Any
    """The internal data type indicated for validation."""

    typedesc: str
    """A user-directed description of the type of data being validated."""

    constraint: Optional[Union[PatternT, RangeT, SetT, PolarT, PatternT, EnumT, bool]]
    """The constraint, if any, which was used to validate the data."""

    data: Any
    """The data itself, possibly cast.

    This is the data post-casting if force- or softcasting were used, and can be
    used to automatically ensure typecasting or type-narrowing for data storage.
    For the raw data pre-casting use the `raw_data` attribute.
    """

    rawdata: Any
    """The raw data, as it was passed to the validator.

    This is always the data as it was passed to the Validator, irrespective of
    whether forcecasting or softcasting were applied.
    """

    casting: bool
    """Whether the data in `data` was cast or not.

    Note that this does not necessarily mean that casting was necessary, e.g.
    an integer that was passed to a Validator's `vint()` method will still
    have been cast to int() and set the casting attribute to True despite
    being of type int before.
    """

    def __init__(
        self,
        success: bool,
        type_: Any,
        typedesc: str,
        constraint: Optional[
            Union[PatternT, RangeT, SetT, PolarT, PatternT, EnumT, bool]
        ],
        data: Any,
        rawdata: Any,
        casting: bool,
    ):
        """Constructs a new ValidationResult.

        Args:
            success: Whether the validaton has succeeded or failed.
            type_: The built-in data type against which validation was carried out.
            typedesc: A short user-intelligible description of the data's type.
            constraint: A constraint data type appropriate to the type of the data,
                see also the `types` submodule.
            data: The data that was evaluated (after casting if the forcecasting and/or
                softcasting options were active.)
            rawdata: The raw, uncast data as it was passed to the validation method.
            casting: Whether casting was applied to `rawdata` to yield `data`.
        """
        self.success = success
        self.type_ = type_
        self.typedesc = typedesc
        self.constraint = constraint
        self.data = data
        self.rawdata = rawdata
        self.casting = casting

    def tostring(self) -> str:
        """Returns string explanation of the data validation result."""
        polarity = "is" if self.success else "is not"
        data = self.data if self.success else self.rawdata
        string = (
            f"`{repr(data)}` {polarity} a valid {self.typedesc} of type {self.type_}."
        )
        if not self.success:
            string += f" Must match constraint: `{repr(self.constraint)}`."
        return string

    def tohtml(self) -> str:
        """Returns HTML formatted explanation of the data validation result."""
        output: list[str] = []
        output.append('
')
        data = self.data if self.success else self.rawdata
        data = html.escape(repr(data), True)
        output.append(f'{data}')
        if self.success:
            output.append('is')
        else:
            output.append('is not')
        output.append("a valid")
        typedesc = html.escape(self.typedesc, True)
        output.append(f'{typedesc}')
        output.append("of type")
        type_ = html.escape(repr(self.type_), True)
        output.append(f'{type_}.')
        if not self.success:
            output.append("Must match constraint:")
            if isinstance(self.constraint, str):
                constraint = html.escape(self.constraint, True)
            else:
                constraint = html.escape(repr(self.constraint))
            output.append(f'{constraint}')
        output.append("
")
        return " ".join(output)

    def tojson(self) -> str:
        """Returns a JSON representation of the data validation result."""
        data = self.data
        if not isinstance(self.data, (str, int, float, bool, type(None))):
            try:
                json.dumps(data)
            except TypeError:
                data = repr(self.data)

        return json.dumps(
            {
                "isvalid": self.success,
                "type": self.type_,
                "typedesc": self.typedesc,
                "constraint": self.constraint,
                "data": data,
            }
        )

    def __str__(self) -> str:
        """Returns a string representation of the data validation result."""
        return self.tostring()

    def __bool__(self) -> bool:
        """Returns True if the validation was successful, False otherwise."""
        return self.success

    def __repr__(self) -> str:
        """Returns a python-style representation of the data validation result object."""
        indent = "    "
        string = f"{__class__.__name__}(\n{indent}{repr(self.success)},\n{indent}"
        string += f"{repr(self.type_)},\n{indent}{repr(self.typedesc)},\n"
        string += f"{indent}{repr(self.constraint)},"
        return string + f"\n{indent}{repr(self.data)}\n)"

class Validator:
    """Data validation interface.

    A Validator offers a convenient interface for validating a set of data points,
    of the same or different types. The Validator will store any failed validation
    results, can optionally force casting of the data to a specific type, can be evaluated
    for success in a boolean expression, and allows for the conditional raising of a
    DataValidationError exception if any validation attempts have failed.

    A single Validator should only be used once for a closed set of data, as reuse
    will add the results to the existing Validator and always evaluate False if it has
    previously had unsuccessful validation attempts (though under some circumstances, e.g.
    the successive building of datasets with late repairs, this may be desirable).
    """

    results: list[ValidationResult]
    failed: list[ValidationResult]
    successful: list[ValidationResult]
    forcecast: bool
    ignorecase: bool

    def __init__(self, forcecast: bool = False, ignorecase: bool = False):
        """Constructs a new Validator.

        Args:
            forcecast: Whether to force casting of the data arguments to the validation
                methods to the indicated type (e.g. str for .validatestring()). This
                will set the default behaviour for validation calls, but can be
                overwritten by passing the named argument forcecast=True or
                forcecast=False on individual method calls.
                For some methods, e.g. polars and enums, casting is done by passing the
                matched value rather than typecasting.
            ignorecase: Whether to ignore case in string comparisons. If true, strings
                will be compared in all uppercase, and regular expression matches will
                be passed the IGNORECASE flag. Can be overwritten on each validation call
                by passing ignorecase=True/False. Default value: False.
        """
        self.results = []
        self.failed = []
        self.successful = []
        self.forcecast = forcecast
        self.ignorecase = ignorecase

    def __forcecast(self, overwrite: Optional[bool] = None) -> bool:
        if overwrite is not None:
            return overwrite
        return self.forcecast

    def __ignorecase(self, overwrite: Optional[bool] = None):
        if overwrite is not None:
            return overwrite
        return self.ignorecase

    def __casefolddict(self, dict_: dict[XT, YT]) -> dict[XT, YT]:
        return {self.__casefoldifstr(key): value for key, value in dict_.items()}

    def __casefoldifstr(self, x: XT) -> XT:
        if isinstance(x, str):
            return x.casefold()
        return x

    def __trycall(
        self, func: Callable[..., XT], *args: Any, **kwargs: dict[Any, Any]
    ) -> Optional[XT]:
        """Returns result of func() if possible, None if an Exception is raised."""
        try:
            return func(*args, **kwargs)
        except Exception:
            return None

    def __condcast(
        self, type_: Callable[..., XT], data: YT, overwrite: Optional[bool] = None
    ) -> Optional[Union[XT, YT]]:
        """Conditionally casts data to a type.

        Attempts to cast `data` to `type_` if, taking into account `overwrite`,
        forcecasting applies. Returns `data` itself if forcecasting doesn't apply,
        and None if forcecasting applies but `data` cannot be cast to `type_`.
        """
        if self.__forcecast(overwrite):
            return self.__trycall(type_, data)
        return data

    def __storeresult(self, result: ValidationResult):
        self.results.append(result)
        if not result:
            self.failed.append(result)
        else:
            self.successful.append(result)

    def vstr(
        self,
        typedesc: str,
        constraint: PatternT,
        data: Any,
        forcecast: Optional[bool] = None,
        ignorecase: Optional[bool] = None,
        flags: Union[re.RegexFlag, int] = 0,
    ) -> ValidationResult:
        r"""Validates a string against a regular expression pattern.

        Args:
            typedesc: An end-user intelligible description of the desired data type, e.g.
                "User ID" or "postcode".
            constraint: A regular expression to match the string against. Important: Note
                that the regular expression will implicitly be enclosed by \A and \Z to
                match the beginning and end of the string. These thus need not be
                specified in the pattern provided.
            data: The data to be validated.
            forcecast: Optional argument to overwrite the validator's default setting for
                forced casting of data arguments.
            ignorecase: Optional argument to overwrite the validator's default setting
                for case sensitivity.
            flags: Additional regex flags to be passed to re.match().
        """
        cdata = data
        try:
            if not isinstance(data, str):
                cdata = str(data)
            flags = re.IGNORECASE | flags if self.__ignorecase(ignorecase) else flags
            cmp = bool(
                re.match(r"\A{pattern}\Z".format(pattern=constraint), cdata, flags=flags)
            )
        except TypeError:
            cmp = False
        validation = ValidationResult(
            cmp,
            str,
            typedesc,
            constraint,
            self.__condcast(str, data, forcecast),
            data,
            self.__forcecast(forcecast),
        )
        self.__storeresult(validation)
        return validation

    def vint(
        self,
        typedesc: str,
        constraint: RangeT,
        data: Any,
        forcecast: Optional[bool] = None,
    ) -> ValidationResult:
        r"""Validates an integer against an inclusive range of integers or floats.

        Args:
            typedesc: An end-user intelligible description of the desired data type, e.g.
                "User ID" or "postcode".
            constraint: A two member tuple or list of integers where the first element
                represents the inclusive lower bound and the second member the inclusive
                upper bound of the permissible range of integer values. For example,
                (3, 5) would successfully validate the data inputs 3, 4, 5, but fail
                validation for 2 or 6.
            data: The data to be validated.
            forcecast: Optional argument to overwrite the validator's default setting for
                forced casting of data arguments.
        """
        cdata = data
        try:
            if not isinstance(data, int):
                cdata = int(data)
            cmp = min(constraint) <= cdata <= max(constraint)
        except (TypeError, ValueError):
            cmp = False
        validation = ValidationResult(
            cmp,
            int,
            typedesc,
            constraint,
            self.__condcast(int, data, forcecast),
            data,
            self.__forcecast(forcecast),
        )
        self.__storeresult(validation)
        return validation

    def vfloat(
        self,
        typedesc: str,
        constraint: RangeT,
        data: Any,
        forcecast: Optional[bool] = None,
    ):
        r"""Validates a float against an inclusive range of integers or floats.

        Args:
            typedesc: An end-user intelligible description of the desired data type, e.g.
                "User ID" or "postcode".
            constraint: A two member tuple or list of integers where the first element
                represents the inclusive lower bound and the second member the inclusive
                upper bound of the permissible range of integer values. For example,
                (3, 5) would successfully validate the data inputs 3, 4, 5, but fail
                validation for 2 or 6.
            data: The data to be validated.
            forcecast: Optional argument to overwrite the objects default setting for
                forced casting of data arguments.
        """
        cdata = data
        try:
            if not isinstance(data, float):
                cdata = float(data)
            cmp = min(constraint) <= cdata <= max(constraint)
        except TypeError:
            cmp = False
        validation = ValidationResult(
            cmp,
            float,
            typedesc,
            constraint,
            self.__condcast(float, data, forcecast),
            data,
            self.__forcecast(forcecast),
        )
        self.__storeresult(validation)
        return validation

    def vbool(
        self,
        typedesc: str,
        constraint: bool,
        data: Any,
        forcecast: Optional[bool] = None,
    ):
        """Validates a bool.

        Args:
            typedesc: An end-user intelligible description of the desired data type, e.g.
                "User ID" or "postcode".
            constraint: A boolean that must be matched, or None to just validate any bool.
            data: The data to be validated.
            forcecast: Optional argument to overwrite the objects default setting for
                forced casting of data arguments.
        """
        cdata = data
        try:
            if not isinstance(data, bool):
                cdata = bool(data)
            cmp = True
            if constraint is not None:
                cmp = (cdata == constraint)
        except TypeError:
            cmp = False
        validation = ValidationResult(
            cmp,
            bool,
            typedesc,
            constraint,
            self.__condcast(bool, data, forcecast),
            data,
            self.__forcecast(forcecast),
        )
        self.__storeresult(validation)
        return validation

    def vpolar(
        self,
        typedesc: str,
        constraint: PolarT,
        data: Any,
        forcecast: Optional[bool] = None,
        ignorecase: Optional[bool] = None,
    ) -> ValidationResult:
        r"""Validates a string against two sets of polar terms.

        Checkes whether data is in either of two sets of polar opposition terms. If the
        forcecast option is active, membership in the first of the two sets results in
        casting to True, membership in the second set to False, and memebership in
        neither set to None. Validation is successful if data is contained in either of
        the two sets, and unsuccessful otherwise.

        Args:
            typedesc: An end-user intelligible description of the desired data type, e.g.
                "User ID" or "postcode".
            constraint: A two member tuple or list of containers of any type (must support
                membership testing with `in`). The first member is a container of
                acceptable truthy values, the second is a container of acceptable falsy
                values. Note that python built-in boolean types True and False are always
                validated as correct.
            data: The data to be validated.
            forcecast: Optional argument to overwrite the objects default setting for
                forced casting of data arguments.
            ignorecase: Optional argument to overwrite the validator's default setting
                for case sensitivity.
        """
        cdata = data
        cval = None
        if isinstance(data, str) and self.__ignorecase(ignorecase):
            cdata = data.casefold()
            if cdata in set(map(self.__casefoldifstr, constraint[0])):
                cval = True
            elif cdata in set(map(self.__casefoldifstr, constraint[1])):
                cval = False
        else:
            if cdata in constraint[0]:
                cval = True
            elif cdata in constraint[1]:
                cval = False
        cmp = cval is not None
        validation = ValidationResult(
            cmp,
            PolarT,
            typedesc,
            constraint,
            cval if self.__forcecast(forcecast) else data,
            data,
            self.__forcecast(forcecast),
        )
        self.__storeresult(validation)
        return validation

    def venum(
        self,
        typedesc: str,
        constraint: EnumT,
        data: Any,
        forcecast: Optional[bool] = None,
        ignorecase: Optional[bool] = None,
    ) -> ValidationResult:
        """Validates a string against a key:value enumerable.

        Checks whether `data` is either contained in the keys or the values
        of the enumerable. If forcecasting is used, it casts to the *value*
        that was matched (not the key), or to None if no match was found.
        """
        cdata = data
        cfound = False
        cval = None
        if isinstance(data, str) and self.__ignorecase(ignorecase):
            cdata = data.casefold()
            cconst = self.__casefolddict(constraint)
            if cdata in cconst:
                cfound = True
                cval = cconst[cdata]
            elif cdata in cconst.values():
                cfound = True
                cval = cdata
        else:
            if cdata in constraint:
                cfound = True
                cval = constraint[cdata]
            elif cdata in constraint.values():
                cfound = True
                cval = cdata
        validation = ValidationResult(
            cfound,
            EnumT,
            typedesc,
            constraint,
            cval if self.__forcecast(forcecast) else data,
            data,
            self.__forcecast(forcecast),
        )
        self.__storeresult(validation)
        return validation

    def raiseif(self):
        """Raises a DataValidationError iff at least one validation has failed."""
        if bool(self.failed):
            raise DataValidationError(self.tostring(errorsonly=True), self)

    def tohtml(self, errorsonly: bool = False):
        """Returns a paragraph-by-paragraph HTML representation of validation attempts.

        Args:
            errorsonly: Whether to include only the errors or all validation attempts.
        """
        if errorsonly:
            return "\n".join(
                [f'
{e.tohtml()}
' for e in self.failed]
            )
        return "\n".join([f'
{e.tohtml()}
' for e in self.results])

    def tostring(self, errorsonly: bool = False):
        """Returns a line-by-line string representation of validation attempts.

        Args:
            errorsonly: Whether to include only the errors or all validation attempts.
        """
        if errorsonly:
            return "\n".join([str(e) for e in self.failed])
        return "\n".join([str(e) for e in self.results])

    def __bool__(self):
        """Returns True if no validation errors have occured so far, False otherwise."""
        return bool(self.failed)

    def __str__(self):
        """Returns a line-by-line string representation of all validation attempts."""
        return self.tostring()

    def __repr__(self):
        """Returns a python-like representation of the (empty) validator object."""
        return f"{__class__}(forcecast={self.forcecast})"


import logging
import logging.config
import argparse
import bson
import typing
import re

from arvet.config.global_configuration import load_global_config
import arvet.database.connection as dbconn
import arvet.database.image_manager as im_manager
import arvet.batch_analysis.invalidate as invalidate

def is_id(val: str) -> bool:
    """
    Is a given string a bson id. It should be a string of hex digits
    :param val:
    :return:
    """
    return re.fullmatch('[0-9a-f]+', val) is not None

def main(
        systems: typing.List[str] = None, datasets: typing.List[str] = None,
        image_collections: typing.List[str] = None,
        # simulators: typing.List[str] = None,
        # controllers: typing.List[str] = None,
        trial_results: typing.List[str] = None,
        metrics: typing.List[str] = None, metric_results: typing.List[str] = None,
        failed_trials: bool = False,
        failed_metrics: bool = False,
        incomplete_tasks: bool = False,
        mongodb_host: str = None,
        mongodb_port: int = None
):
    """
    Command line control to invalidate various objects.
    :args: Each of the different types of thing to invalidate
    :return:
    """
    if systems is None:
        systems = []
    if datasets is None:
        datasets = []
    if image_collections is None:
        image_collections = []
    # if simulators is None:
    #     simulators = []
    # if controllers is None:
    #     controllers = []
    if trial_results is None:
        trial_results = []
    if metrics is None:
        metrics = []
    if metric_results is None:
        metric_results = []

    # Sanitize arguments
    system_ids = [bson.ObjectId(oid) for oid in systems if is_id(oid)]
    system_names = [name for name in systems if not is_id(name)]
    image_collections = [bson.ObjectId(oid) for oid in image_collections if is_id(oid)]
    # controllers = [bson.ObjectId(oid) for oid in controllers if is_id(oid)]
    trial_results = [bson.ObjectId(oid) for oid in trial_results if is_id(oid)]
    metrics = [bson.ObjectId(oid) for oid in metrics if is_id(oid)]
    metric_results = [bson.ObjectId(oid) for oid in metric_results if is_id(oid)]

    # Load the configuration
    config = load_global_config('config.yml')
    if __name__ == '__main__':
        # Only configure the logging if this is the main function, don't reconfigure
        logging.config.dictConfig(config['logging'])

    # Configure the database and the image manager
    dbconn.configure(config['database'], override_host=mongodb_host, override_port=mongodb_port)
    im_manager.configure(config['image_manager'])

    # Invalidate systems
    if len(system_ids) > 0:
        logging.getLogger(__name__).info("Invalidating systems {0}".format(system_ids))
        invalidate.invalidate_systems(system_ids)
    if len(system_names) > 0:
        logging.getLogger(__name__).info("Invalidating systems {0}".format(system_names))
        invalidate.invalidate_systems_by_name(system_names)

    # Invalidate datasets by loader module
    if len(datasets) > 0:
        logging.getLogger(__name__).info("Invalidating dataset loaders {0}".format(datasets))
        invalidate.invalidate_dataset_loaders(datasets)

    # Invalidate image collections by id
    if len(image_collections) > 0:
        logging.getLogger(__name__).info("Invalidating image collection {0}".format(image_collections))
        invalidate.invalidate_image_collections(image_collections)

    # Invalidate trial results
    if len(trial_results) > 0:
        logging.getLogger(__name__).info("Invalidating trial result {0}".format(trial_results))
        invalidate.invalidate_trial_results(trial_results)

    # Invalidate metrics
    if len(metrics) > 0:
        logging.getLogger(__name__).info("Invalidating benchmark {0}".format(metrics))
        invalidate.invalidate_metrics(metrics)

    # Invalidate metric results
    if len(metric_results) > 0:
        logging.getLogger(__name__).info("Invalidating result {0}".format(metric_results))
        invalidate.invalidate_metric_results(metric_results)

    # Invalidate failed trials
    if failed_trials:
        logging.getLogger(__name__).info("Invalidating failed trials")
        invalidate.invalidate_failed_trial_results()

    # Invalidate failed metrics
    if failed_metrics:
        logging.getLogger(__name__).info("Invalidating failed metric results")
        invalidate.invalidate_failed_metric_results()

    # Remove incomplete tasks
    if incomplete_tasks:
        logging.getLogger(__name__).info("Removing incomplete tasks")
        invalidate.invalidate_incomplete_tasks()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Invalidate obsolete or incorrect data from the database.\n'
                    'Over time, data in the database will become invalid, due to new code versions, '
                    'bugs in the original implementation, or changed input. '
                    'When that happens, use this script to clear out the invalid data while retaining '
                    'other important data')
    parser.add_argument('--system', action='append',
                        help='A system id or type to invalidate. Can be a database id or a fully-qualified class')
    parser.add_argument('--dataset', action='append',
                        help='A dataset loader module to invalidate, a fully-qualified loader module name')
    parser.add_argument('--image_collection', action='append',
                        help='An image collection to invalidate, as a bson database id')
    parser.add_argument('--simulator', action='append',
                        help='An simulator to invalidate, as a bson database id or world name')
    parser.add_argument('--controller', action='append',
                        help='An controller to invalidate, as a bson database id')
    parser.add_argument('--trial_result', action='append',
                        help='A trial result to invalidate, as a bson database id')
    parser.add_argument('--metric', action='append',
                        help='A mtric to invalidate, as a bson database id')
    parser.add_argument('--metric_result', action='append',
                        help='An metric result to invalidate, as a bson database id')
    parser.add_argument('--failed_trials', action='store_true',
                        help='Also remove all failed trial results')
    parser.add_argument('--failed_metrics', action='store_true',
                        help='Also remove all failed metric results')
    parser.add_argument('--incomplete_tasks', action='store_true',
                        help='Remove all incomplete tasks, the necessary ones will be recreated by scheduling')
    parser.add_argument('--mongodb_host', default=None,
                        help='Override the mongodb hostname specified in the config file.')
    parser.add_argument('--mongodb_port', default=None,
                        help='Override the mongodb port specified in the config file.')
    args = parser.parse_args()

    main(
        systems=args.system,
        datasets=args.dataset,
        image_collections=args.image_collection,
        # simulators=args.simulator,
        # controllers=args.controller,
        trial_results=args.trial_result,
        metrics=args.metric,
        metric_results=args.metric_result,
        failed_trials=args.failed_trials,
        failed_metrics=args.failed_metrics,
        incomplete_tasks=args.incomplete_tasks,
        mongodb_host=args.mongodb_host,
        mongodb_port=args.mongodb_port
    )


"""
This script contains functions used for input and output of GELATO.

## Inputs needed for GELATO

1) Input spectra (one FITS file per spectrum)

- The log10 of the wavelengths in Angstroms, column name: "loglam"
- The spectral flux density in flam units, column name: "flux"
- The inverse variances of the data points, column name: "ivar"
- saved in `gelato_input/spectra/`

2) Input list

- a comma delimited file (ending in .csv) where each object occupies a different line.
The first item in each line is the path to the spectrum.
The second is the redshift of the spectrum.

- a FITS table (ending in .fits) where each object occupies a different entry in the table.
The table must have the column "Path" for the path to the object,
and "z" containing the redshift of the object.

- saved in `gelato_input/catalogs/`

## Notes to keep in mind
- the DESI masked pixels have ivar=0 and flux=0 so we don't need to carry the mask itself

The script consists of following functions:
    1) gelatify_spectra(specprod, survey, program, healpix, targetids)
    2) create_path_column(table)
    3) gelatify(table, output_name)
    4) divide_into_subsamples(table, n_samp, output_root)
    5) gelato_output(output_name = 'GELATO-results.fits')
    6) get_gelato_columns(table, em_line, prop = 'Flux')
    7) velocities_to_wavelengths(v, lam_0)
    8) plot_image_gelato_ha(table, index, title = None, gdir = None)

Author : Ragadeepika
Version : 2022 November 7
"""

####################################################################################################
####################################################################################################

import numpy as np
import fitsio

from astropy.io import fits
from astropy.table import Table, Column, vstack
from astropy.modeling.models import Gaussian1D

from desispec import coaddition
from desispec.io import specprod_root, read_spectra

from glob import glob
import plotting_functions as pf

import matplotlib.pyplot as plt

####################################################################################################
####################################################################################################

## Making the matplotlib plots look nicer
settings = {
    'font.size':22,
    'axes.linewidth':2.0,
    'xtick.major.size':6.0,
    'xtick.minor.size':4.0,
    'xtick.major.width':2.0,
    'xtick.minor.width':1.5,
    'xtick.direction':'in',
    'xtick.minor.visible':True,
    'xtick.top':True,
    'ytick.major.size':6.0,
    'ytick.minor.size':4.0,
    'ytick.major.width':2.0,
    'ytick.minor.width':1.5,
    'ytick.direction':'in',
    'ytick.minor.visible':True,
    'ytick.right':True
}

plt.rcParams.update(**settings)

## Output files directories

## Path to spectra
spec_out = 'input/spectra'
## Path to catalogs
cat_out = 'input/catalogs'

####################################################################################################
####################################################################################################

def gelatify_spectra(specprod, survey, program, healpix, targetids):
    """
    Function to turn a given list of spectra into GELATO-input files.
    The input spectra files are saved in input/spectra folder.

    Parameters
    ----------
    specprod : str
        Spectral Release Name

    survey : str
        Survey name for the targetes. sv1|sv2|sv3|main|special

    program : str
        Observing program for the targets. dark|bright|other|backup

    healpix : int
        Healpix number for the targets

    targetids : numpy array
        Array of targetids in a given specprod, survey, program, and healpix

    Returns
    -------
    None.
        Saves the individual spectra in GELATO-required format.
    """

    # Root directory of DESI spectra for a given specprod
    specprod_dir = specprod_root(specprod)
    hpx_dir = f'{specprod_dir}/healpix'

    # Filename & directory
    coadd_filename = f'coadd-{survey}-{program}-{healpix}.fits'
    tgt_dir = f'{hpx_dir}/{survey}/{program}/{healpix//100}/{healpix}'

    # Using desispec to read the spectra
    coadd_obj = read_spectra(f'{tgt_dir}/{coadd_filename}')
    coadd_tgts = coadd_obj.target_ids().data

    sel = np.isin(coadd_tgts,targetids)
    coadd_obj_sel = coadd_obj[sel]
    coadd_sel_tgts = coadd_obj_sel.target_ids().data

    # Combined Spectra -
    spec_combined = coaddition.coadd_cameras(coadd_obj_sel)

    lam = spec_combined.wave['brz']
    loglam = np.log10(lam)

    # Loop over targetids
    for tgt in targetids:
        ## Find the index of each targetid
        ## Get flux and ivar values and write into a fits file
        i = (coadd_sel_tgts == tgt)
        flam = spec_combined.flux['brz'][i][0]
        flam_ivar = spec_combined.ivar['brz'][i][0]
        outfile = f'{spec_out}/gspec-{survey}-{program}-{healpix}-{tgt}.fits'
        fitsio.write(outfile,[loglam,flam,flam_ivar], names=['loglam','flux','ivar'], clobber=True)

####################################################################################################
####################################################################################################

def create_path_column(table):
    """
    Function to create the "Path" column for the GELATO input file.

    Parameters
    ----------
    table : astropy table
        Table of sources that needs to be "gelatified"

    Returns
    -------
    path_column : Column
        "Path" column containing the path to the different input spectra

    """

    ## Each spectrum filename - input/spectra/gspec-{survey}-{program}-{healpix}-{targetid}.fits

    survey = table['SURVEY'].astype(str).data.astype(object)
    program = table['PROGRAM'].astype(str).data.astype(object)
    healpix = table['HEALPIX'].astype(str).data.astype(object)
    targetid = table['TARGETID'].astype(str).data.astype(object)
    spec_out = np.array(['input/spectra/gspec-']*len(table)).astype(object)
    hyphen = np.array(['-']*len(table)).astype(object)
    fits = np.array(['.fits']*len(table)).astype(object)

    path = spec_out+survey+hyphen+program+hyphen+healpix+hyphen+targetid+fits
    path_column = Column(path, name = 'Path', dtype = 'str')

    return (path_column)

####################################################################################################
####################################################################################################

def gelatify(table, output_name):
    """
    Function to create the required GELATO input files for a given table of targets.

    Parameters
    ----------
    table : astropy table
        Table of sources that needs to be "gelatified"

    output_name : str
        Name of the output catalog

    Returns
    -------
    None.
        The output catalog is saved with the given name in input/catalogs/ folder.
    """

    specprods = np.unique(table['SPECPROD'].astype(str))
    ## group by specprod
    for specprod in specprods:
        t_spec = table[table['SPECPROD'].astype(str) == specprod]

        ## group by survey
        surveys = np.unique(t_spec['SURVEY'].astype(str))
        for survey in surveys:
            t_surv = t_spec[t_spec['SURVEY'].astype(str) == survey]

            ## group by program
            programs = np.unique(t_spec['PROGRAM'].astype(str))
            for program in programs:
                t_prog = t_surv[t_surv['PROGRAM'].astype(str) == program]

                ## group by healpix
                healpixs = np.unique(t_prog['HEALPIX'])
                for hpx in healpixs:
                    t_hpx = t_prog[t_prog['HEALPIX'] == hpx]
                    tgts = t_hpx['TARGETID'].data
                    gelatify_spectra(specprod, survey, program, hpx, tgts)

    print (' ')
    print ('Done!')

    ## Create the GELATO input list with 'Path' and 'z'
    ## Initialize output table
    t_out = Table()
    path_col = create_path_column(table)
    t_out.add_column(path_col)
    t_out['z'] = table['Z']
    t_out.write(f'{cat_out}/{output_name}', overwrite = True)

####################################################################################################
####################################################################################################

def divide_into_subsamples(table, n_samp, output_root):
    """
    Function to divide the entire catalog into sub-samples to help with GELATO run.

    Parameters
    ----------
    table : astropy table
        GELATO input table that needs to be divided into sub-samples

    n_samp : int
        Number of sub-samples

    output_root : str
        Output root name for each of the sub-samples.

    Returns
    -------
    None.
        The tables for all the sub-samples are saved in input/catalogs/ folder.

    """

    for ii in range(n_samp):
        tab = table[ii::n_samp]
        tab.write(f'{cat_out}/{outpit_root}-{ii+1}.fits', overwrite = True)

####################################################################################################
####################################################################################################

def gelato_output(output_name = 'GELATO-results.fits'):
    """
    Function to combine all GELATO output files to get a single results file.
    The files should reside in Results/ folder.

    Parameters
    ----------
    output_name : str
        Path and filename for the resulting file. Default in GELATO-results.fits

    Returns
    -------
    None.
        The output file is saved at the required path.

    """

    files = glob('Results/gspec*.fits')

    tables = []

    for ii in range(len(files)):
        comps = files[ii][8:].split('-')
        filename = '-'.join(comps[:-1])+'.fits'
        targetid = comps[-2]
        parameters = fits.getdata(files[ii], 'PARAMS')

        ## Initialize Lists
        data = [targetid, filename]
        names = ['TARGETID', 'Name']
        dtype = ['>i8', np.unicode_] + [np.float64 for i in range(2*len(parameters.columns.names))]

        # Iterate over columns and add
        for n in parameters.columns.names:

            ps = parameters[n]
            ps = ps[np.invert(np.isinf(ps))]

            # Add medians
            data.append(np.nanmedian(ps))
            names.append(n)

            # Add errors
            data.append(np.nanstd(ps))
            names.append(n+'_err')

        # Append to list
        tables.append(Table(data=np.array(data),names=names,dtype = dtype))

    table = vstack(tables, join_type = 'outer')
    #if not type(table.mask) == type(None):
    #    for c in table.colnames[1:]: table[c][table.mask[c]] = np.nan

    table.write(output_name, overwrite = True)

####################################################################################################
####################################################################################################

def get_gelato_columns(table, em_line, prop = 'Flux'):
    """
    Function to output GELATO columns for a given emission line.

    Parameters
    ----------
    table : astropy table
        GELATO results table from which to grab the required columns

    em_line : str
        Required emission-line.
        For now works only for Ha, Hb, [NII], [OIII], [SII]_6716/6731, 'Ha_broad', 'Hb_broad'.

    prop : str
        Property name whose column needs to be grabbed for the given emission line.
        Default is 'Flux'

    Returns
    -------
    column : Column
        Required emission-line column.

    """
    ## Dictionary to match emission-line with the required column
    gel_dict = {'Ha':'BPT_Ha_6564.613',
                'Hb':'BPT_Hb_4862.683',
                '[NII]':'BPT_[NII]_6585.277',
                '[OIII]':'BPT_[OIII]_5008.239',
                '[SII]_6716':'BPT_[SII]_6718.294',
                '[SII]_6731':'BPT_[SII]_6732.673',
                'Ha_broad':'Broad_Ha_Broad_6564.613',
                'Hb_broad':'Broad_Hb_Broad_4862.683'}

    term = gel_dict[em_line]
    column_name = f'{term}_{prop}'
    column = table[column_name].data

    return (column)

####################################################################################################
####################################################################################################

def velocities_to_wavelengths(v, lam_0):
    """
    Function to convert from velocities (km/s) to wavelength values (in AA).
    del_lam = (v/c)*lam_0

    Parameters
    ----------
    v : array or float
        Velocities that need to be converted

    lam_0 : float
        Central wavelength for the frame of reference

    Returns
    -------
    del_lam : array or float
        Converted wavelength values
    """

    c = 3e+5 ## in km/s
    del_lam = (v/c)*lam_0

    return (del_lam)

####################################################################################################
####################################################################################################

def plot_image_gelato_ha(table, index, title = None, gdir = None):
    """
    Function to plot image-cutout and spectra+gelato model focused on the Ha+[NII] region.
    This function overplots the broad and narrow components of Ha.

    Parameters
    ----------
    table : astropy table
        The table of targets from which the object is selected.

    index : int
        Index of the object from the table that needs to be plotted.

    title : str
        Title that will go on the top of the plot. Default is None.

    gdir : str
        Path of the gelato output results files. Default is None.

    Returns
    -------
    fig : matplotlib figure
        Figure object containing the overall plot
    """
    ## Target information
    filename = table['Name'].astype(str).data[index]
    arr = filename.split('.')
    #targetid = table['TARGETID'].data[index]
    ra = table['RA'].data[index]
    dec = table['DEC'].data[index]

    gtab_file = f'{gdir}/{arr[0]}-results.fits'
    gtab = Table.read(gtab_file, hdu = 1)

    for c in gtab.columns:
        gtab[c].fill = 0.0

    gtab = gtab.filled()

    #c = 3e+5  ## in km/s
    #z = table['SSP_Redshift'].data[index]/c
    z = table['Z'].data[index]
    ## Spectra
    lam = 10**(gtab['loglam'])/(1+z)
    flam = gtab['flux']*(1+z)
    ivar = gtab['ivar']/((1+z)**2)

    model = gtab['MODEL']*(1+z)
    if ('PL' in gtab.colnames):
        cont = (gtab['SSP']+gtab['PL'])*(1+z)
    else:
        cont = gtab['SSP']*(1+z)
    residual = model - flam

    ## Gaussian components for Broad and Narow Halpha
    ha_mean = 6564.613

    ## Narrow H-alpha

    ha_amp = get_gelato_columns(table, 'Ha', prop = 'RAmp')[index]
    ha_sig = get_gelato_columns(table, 'Ha', prop = 'Dispersion')[index]

    ha_std = velocities_to_wavelengths(ha_sig, ha_mean)

    gauss_ha = Gaussian1D(amplitude = ha_amp, mean = ha_mean, stddev = ha_std)

    ## Broad H-alpha

    ha_broad_amp = get_gelato_columns(table, 'Ha_broad', prop = 'RAmp')[index]
    ha_broad_sig = get_gelato_columns(table, 'Ha_broad', prop = 'Dispersion')[index]
    ha_broad_std = velocities_to_wavelengths(ha_broad_sig, ha_mean)

    gauss_ha_broad = Gaussian1D(amplitude = ha_broad_amp, mean = ha_mean, stddev = ha_broad_std)

    ## Get image-cutout
    im = pf.get_image_cutout(ra_in = ra, dec_in = dec, cutout_size = 20)

    ## Region around [NII]+Ha
    lam_xx = np.arange(6400, 6700, 1)
    lam_ii = (lam >= 6400)&(lam <= 6700)
    cont_xx = np.median(cont[lam_ii])

    ## Plot

    fig = plt.figure(figsize = (14, 12))
    gs = fig.add_gridspec(6, 5)

    plt.suptitle(title, fontsize = 20)

    ax1 = fig.add_subplot(gs[0:2,0:2])
    ax2 = fig.add_subplot(gs[0:2,2:])
    ax3 = fig.add_subplot(gs[2:5,:])
    ax4 = fig.add_subplot(gs[5,:], sharex = ax3)

    ax1.imshow(im)
    ax1.set(xticks = [], yticks = [])

    ax2.plot(lam, flam, color = 'grey', alpha = 0.8, label = 'Flux')
    ax2.plot(lam_xx, gauss_ha(lam_xx)+cont_xx, color = 'r', label = 'Narrow H$\\alpha$')
    ax2.plot(lam_xx, gauss_ha_broad(lam_xx)+cont_xx, color = 'b', label = 'Broad H$\\alpha$')
    ax2.legend(loc = 'best', fontsize = 12)
    ax2.set(ylabel = '$F_{\lambda}$', xlim = [6440, 6680], xlabel = '$\lambda$')

    x_bounds = ax2.get_xbound()
    x_ii = (lam >= x_bounds[0])&(lam <= x_bounds[1])
    flam_ii = flam[x_ii]
    ymin_ii = min(flam_ii)-1
    ymax_ii = max(flam_ii)+1
    ax2.set_ylim([ymin_ii, ymax_ii])

    ax3.plot(lam, flam, color = 'grey', alpha = 0.8, label = 'Flux')
    ax3.plot(lam, model, color = 'k', lw = 2.0, label = 'Model')
    ax3.legend(loc = 'best', fontsize = 16)
    ax3.set(ylabel = '$F_{\lambda}$', xlim = [4600, 6900])

    x_bounds = ax3.get_xbound()
    x_ii = (lam >= x_bounds[0])&(lam <= x_bounds[1])
    flam_ii = flam[x_ii]
    ymin_ii = min(flam_ii)-1
    ymax_ii = max(flam_ii)+1
    ax3.set_ylim([ymin_ii, ymax_ii])

    ax4.scatter(lam, residual, color = 'k', s = 10, marker = '.', alpha = 0.5)
    ax4.axhline(0.0, color = 'firebrick', ls = '--', lw = 3.0)
    ax4.set_ylabel('Residual', fontsize = 16)
    ax4.set(ylim = [-7,7], xlabel = '$\lambda$')

    plt.tight_layout(rect=[0, 0, 1., 0.99])
    plt.close()

    return (fig)

####################################################################################################
####################################################################################################


import pytest
from django.test import TestCase

from rard.research.forms import WorkForm
from rard.research.models import Antiquarian, Work

pytestmark = pytest.mark.django_db

class TestWorkForm(TestCase):
    def test_fields_not_required(self):
        # allow the user to skip either and insist on the backend
        # that at least one of them is filled out
        form = WorkForm()
        self.assertFalse(form.fields["antiquarians"].required)

    def test_antiquarian_queryset(self):
        data = {
            "name": "Work Name",
            "subtitle": "Subtitle",
        }
        work = Work.objects.create(**data)

        OWNER_COUNT = 5
        OTHER_COUNT = 3
        for i in range(0, OWNER_COUNT):
            a = Antiquarian.objects.create(name="sdf", re_code="owner%d" % i)
            a.works.add(work)

        for i in range(0, OTHER_COUNT):
            a = Antiquarian.objects.create(name="sdf", re_code="other%d" % i)

        form = WorkForm(instance=work)

        self.assertEqual(form.fields["antiquarians"].initial.count(), OWNER_COUNT)


import os
import sys
import logging
import discord
from dotenv import load_dotenv
import app.actions as actions
from app.actions import PREFIX
from app.google_sheets import Google_Sheets
from app.common.helpers import get_args

load_dotenv()
try:
    google_sheet = Google_Sheets(os.environ['SPREADSHEET_ID'], 'A2', 'A11')

    try:
        google_sheet.connect_to_sheet()
        response_coord = google_sheet.get_sheet_coordinates(google_sheet.spreadsheet_id, google_sheet.sheet_range)
        google_sheet.build_houses_coordinates(response_coord)
    except ValueError as ve:
        print(f'{ve} incorrect or missing')
except KeyError as ke:
    print(
        f'''error retrieving {ke}.
            Did you set the environment variable for {ke}?''')
    print('shutting down')
    sys.exit()

logger = logging.getLogger('discord')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(
    filename='discord.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter(
    '%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
logger.addHandler(handler)

client = discord.Client()

@client.event
async def on_ready():
    # get current house roles
    msg = f'{client.user} is ready'
    print(msg)

@client.event
async def on_message(message):
    # find out if admin for protected calls later
    # TODO: might not need based on has_permissions
    if not message.content.startswith(PREFIX):
        return

    if message.author.bot or message.author == client.user:
        return

    if message.content.startswith(PREFIX + actions.POINTS):
        try:
            rg = get_args(message.content)
            points_to_add = int(rg)
        except ValueError:
            await message.channel.send(f'error: {rg} is not a number')
            return
        user_roles = message.author.roles
        username = f'{message.author.name}#{message.author.discriminator}'
        house_role = google_sheet.find_user_house(user_roles)

        if house_role is not None:
            try:
                total_points = google_sheet.add_to_sheet(
                    username, house_role, points_to_add)
                msg = f"{points_to_add} points for `{house_role['house_name']}` `{message.author.name}'s` points are now `{total_points}`!"
            except ValueError:
                msg = f'{message.author} was not found'
            except:
                print('adding points failed')
                return

            await message.channel.send(msg)
        else:
            msg = '''You are not in any current house, ask a mod to be
                placed in one\nto see current houses run -houses'''
            await message.channel.send(msg)
        return

    if message.content.startswith(PREFIX + actions.PING):
        # TODO: add real ping data
        await message.channel.send('Good Evening')
        return

    if message.content.startswith(PREFIX + actions.HOUSES):
        houses_list = ''

        for house in google_sheet.houses_info:
            houses_list += f'`{house}`\n'
        if len(houses_list) > 0:
            await message.channel.send(houses_list)
        else:
            await message.channel.send("There aren't any houses currently, ask a mod why they aren't doing there jobs")
        return

    if message.content.startswith(PREFIX + actions.BUILD_HOUSES):

        return

    if message.content.startswith(PREFIX + actions.HELP):
        available_commands = ''
        for command in actions.AVAILABLE_COMMANDS:
            available_commands += '`' + PREFIX + command + '`\n'

        help_response = '''All commands start with {} for example `-ping`
        \nAvailable commands are : \n{}\n some commands require your input
            such as a number for example:\n {}'''.format(
            PREFIX, available_commands,
            ('`' + PREFIX + actions.POINTS + ' 3`'))

        await message.channel.send(help_response)
        return

    if message.content.startswith(PREFIX):
        msg = 'command not found, run `-help` for available commands'
        await message.channel.send(msg)
        return


# -*- coding: utf-8 -*-
import pandas as pd

from main.get_result import Result
from django.db import connection

class Rank(Result):
    def get_rank(self, situation, limit=300):
        """
        指定された状況のスコアトップlimitを返す
        :param situation:
        :param limit:
        :return:
        """
        limit = str(limit)
        cursor = connection.cursor()
        cursor.execute("""
            SELECT
              interval,
               location_id,
               stay_time_avg AS stay_time_avg_score,
               num_of_people AS num_of_people_score,
               firstvisit_rate AS firstvisit_rate_score,
               foreign_rate AS foreign_rate_score,
               situation,
               score
            FROM score
            WHERE situation = '""" + situation + """'AND interval::date >= '2016-11-07'
            ORDER BY score
        """)
        features_rank = pd.DataFrame(list(self.dictfetchall(cursor)))
        features = self.get_data(db_table='features')
        features_rank = pd.merge(features_rank, features, on=['interval', 'location_id'])
        # 日付情報を付加
        features_rank['date'] = features_rank['interval'].apply(self.add_date)
        # str型にしないとjsonにした時におかしな値になる
        features_rank['interval'] = features_rank['interval'].astype(str)
        features_rank['date'] = features_rank['date'].astype(str)

        location = self.get_data(db_table='location')
        # 地点情報をmerge
        features_rank = pd.merge(features_rank, location, on=['location_id'])

        return features_rank.to_json(orient='records')


# coding: utf-8
from __future__ import annotations
from typing import Union
import os
import sys
import shutil
import __main__
from pathlib import Path
from typing import overload
from .sys_info import SysInfo

# do not import from type_var here.
# this module is used by uno_lnk.py and type_var imports uno
# PathOrStr = Union[str, os.PathLike]

# python path on mac:  /Applications/LibreOffice.app/Contents/Resources/python
#   https://ask.libreoffice.org/t/where-is-the-python-executable-embedded-in-libreoffice-on-macos/50042

PLATFORM = SysInfo.get_platform()
if PLATFORM == SysInfo.PlatformEnum.WINDOWS:
    import winreg

_INSTALL_PATH = None

def get_soffice_install_path() -> Path:
    """
    Gets the Soffice install path.

    For windows this will be something like: ``C:\\Program Files\\LibreOffice``.
    For Linux this will be something like: ``/usr/lib/libreoffice``

    Returns:
        Path: install as Path.
    """
    global _INSTALL_PATH
    if _INSTALL_PATH is not None:
        return _INSTALL_PATH
    if PLATFORM == SysInfo.PlatformEnum.WINDOWS:
        # get the path location from registery
        value = ""
        for _key in (
            # LibreOffice 3.4.5,6,7 on Windows
            "SOFTWARE\\LibreOffice\\UNO\\InstallPath",
            # OpenOffice 3.3
            "SOFTWARE\\OpenOffice.org\\UNO\\InstallPath",
        ):
            try:
                value = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, _key)
            except Exception as detail:
                value = ""
                _errMess = "%s" % detail
            else:
                break  # first existing key will do
        if value != "":
            _INSTALL_PATH = Path("\\".join(value.split("\\")[:-1]))  # drop the program
            return _INSTALL_PATH

        # failed to get path from registery. Going Manual
        soffice = "soffice.exe"
        p_sf = Path(os.environ["PROGRAMFILES"], "LibreOffice", "program", soffice)
        if p_sf.exists() is False or p_sf.is_file() is False:
            p_sf = Path(os.environ["PROGRAMFILES(X86)"], "LibreOffice", "program", soffice)
        if not p_sf.exists():
            raise FileNotFoundError(f"LibreOffice '{p_sf}' not found.")
        if not p_sf.is_file():
            raise IsADirectoryError(f"LibreOffice '{p_sf}' is not a file.")
        # drop \program\soffice.exe
        # expect C:\Program Files\LibreOffice
        _INSTALL_PATH = p_sf.parent.parent
        return _INSTALL_PATH

    elif PLATFORM == SysInfo.PlatformEnum.MAC:
        _INSTALL_PATH = Path("/Applications/LibreOffice.app/Contents/MacOS")
        return _INSTALL_PATH
    else:
        # unix
        soffice = "soffice"
        # search system path
        s = shutil.which(soffice)
        p_sf = None
        if s is not None:
            # expect '/usr/bin/soffice'
            if os.path.islink(s):
                p_sf = Path(os.path.realpath(s)).parent
            else:
                p_sf = Path(s).parent
        if p_sf is None:
            s = "/usr/bin/soffice"
            if os.path.islink(s):
                p_sf = Path(os.path.realpath(s)).parent
            else:
                p_sf = Path(s).parent
        if not p_sf.exists():
            raise FileNotFoundError(f"LibreOffice '{p_sf}' not found.")
        if not p_sf.is_file():
            raise IsADirectoryError(f"LibreOffice '{p_sf}' is not a file.")
        # drop /program/soffice
        _INSTALL_PATH = p_sf.parent.parent
        return _INSTALL_PATH

def get_soffice_path() -> Path:
    """
    Gets path to soffice

    Returns:
        Path: path to soffice
    """
    if PLATFORM == SysInfo.PlatformEnum.WINDOWS:
        return Path(get_lo_path(), "soffice.exe")
    return Path(get_lo_path(), "soffice")

def get_uno_path() -> Path:
    """
    Searches known paths for path that contains uno.py

    This path is different for windows and linux.
    Typically for Windows ``C:\\Program Files\\LibreOffice\\program``
    Typically for Linux ``/usr/lib/python3/dist-packages``

    Raises:
        FileNotFoundError: if path is not found
        NotADirectoryError: if path is not a directory

    Returns:
        Path: First found path.
    """
    if PLATFORM == SysInfo.PlatformEnum.WINDOWS:

        p_uno = Path(os.environ["PROGRAMFILES"], "LibreOffice", "program")
        if p_uno.exists() is False or p_uno.is_dir() is False:
            p_uno = Path(os.environ["PROGRAMFILES(X86)"], "LibreOffice", "program")
        if not p_uno.exists():
            raise FileNotFoundError("Uno Source Dir not found.")
        if not p_uno.is_dir():
            raise NotADirectoryError("UNO source is not a Directory")
        return p_uno
    elif PLATFORM == SysInfo.PlatformEnum.MAC:
        return Path("/Applications/LibreOffice.app/Contents/MacOS/soffice")
    else:
        p_uno = Path("/usr/lib/python3/dist-packages")
        if not p_uno.exists():
            raise FileNotFoundError("Uno Source Dir not found.")
        if not p_uno.is_dir():
            raise NotADirectoryError("UNO source is not a Directory")
        return p_uno

def get_lo_path() -> Path:
    """
    Searches known paths for path that contains LibreOffice ``soffice``.

    This path is different for windows and linux.
    Typically for Windows ``C:\\Program Files\\LibreOffice\\program``
    Typically for Linux ``/usr/bin/soffice``

    Raises:
        FileNotFoundError: if path is not found
        NotADirectoryError: if path is not a directory

    Returns:
        Path: First found path.
    """
    if PLATFORM == SysInfo.PlatformEnum.WINDOWS:
        return Path(get_soffice_install_path(), "program")

    elif PLATFORM == SysInfo.PlatformEnum.MAC:
        return Path("/Applications/LibreOffice.app/Contents/MacOS")
    else:
        # search system path
        s = shutil.which("soffice")
        p_sf = None
        if s is not None:
            # expect '/usr/bin/soffice'
            if os.path.islink(s):
                # follow link
                p_sf = Path(os.path.realpath(s)).parent
            else:
                p_sf = Path(s).parent
        if p_sf is None:
            p_sf = Path("/usr/bin/soffice")
            if p_sf.exists() is False or p_sf.is_file() is False:
                raise FileNotFoundError("LibreOffice Source Dir not found.")
            p_sf = p_sf.parent

        if not p_sf.exists():
            raise FileNotFoundError("LibreOffice Source Dir not found.")
        if not p_sf.is_dir():
            raise NotADirectoryError("LibreOffice source is not a Directory")
        return p_sf

def get_lo_python_ex() -> str:
    """
    Gets the python executable for different environments.

    In Linux this is the current python executable.
    If a virtual environment is activated then that will be the
    python executable that is returned.

    In Windows this is the ``python.exe`` file in LibreOffice.
    Typically for Windows ``C:\\Program Files\\LibreOffice\\program\\python.exe``

    Raises:
        FileNotFoundError: In Windows if ``python.exe`` is not found.
        NotADirectoryError: In Windows if ``python.exe`` is not a file.

    Returns:
        str: file location of python executable.
    """
    if PLATFORM == SysInfo.PlatformEnum.WINDOWS:
        p = Path(get_lo_path(), "python.exe")

        if not p.exists():
            raise FileNotFoundError("LibreOffice python executable not found.")
        if not p.is_file():
            raise NotADirectoryError("LibreOffice  python executable is not a file")
        return str(p)
    else:
        return sys.executable

@overload
def mkdirp(dest_dir: str) -> None:
    ...

@overload
def mkdirp(dest_dir: Path) -> None:
    ...

def mkdirp(dest_dir: Union[str, os.PathLike]) -> None:
    """
    Creates path and subpaths not existing.

    Args:
        dest_dir (str | PathLike]): PathLike object
    """
    # Python ≥ 3.5
    pth = Path(dest_dir)
    if not pth.is_absolute():
        pth = pth.resolve()
    pth.mkdir(parents=True, exist_ok=True)


from tkinter import ttk, N, W, E, S
import tkinter as tk
from enum import Enum
import threading
import logging
from utils import format_value

logger = logging.getLogger("MoonPrintGUI")

class Axes(ttk.Frame):
    def __init__(self, master: "ManualControls"):
        super().__init__(master)
        master.columnconfigure(0, weight=1)
        master.rowconfigure(0, weight=1)

        self.master = master

        self._coords = {"x": tk.DoubleVar(), "y": tk.DoubleVar(), "z": tk.DoubleVar()}

        self._create_widgets()

    def _create_widgets(self):
        ttk.Label(
            self, text="Axes", font="Roboto 10 bold"
        ).grid(column=0, row=0, sticky=(W, E))

        for i, (coord_name, coord_var) in enumerate(self._coords.items()):
            ttk.Label(self, text=coord_name.upper()).grid(column=0, row=i+1, sticky=W)
            ttk.Spinbox(
                self, textvariable=coord_var, from_=0, to=1000
            ).grid(column=1, row=i+1, sticky=(W, E))
            ttk.Scale(
                self,
                orient=tk.HORIZONTAL,
                length=200,
                from_=0,
                to=1000,
                variable=coord_var
            ).grid(column=2, row=i+1, sticky=E)

    @property
    def coords(self):
        return {k: v.get() for k, v in self._coords.items()}

    @property
    def x(self):
        return self._coords["x"].get()

    @property
    def y(self):
        return self._coords["y"].get()

    @property
    def z(self):
        return self._coords["z"].get()

class ExtruderState(Enum):
    REMOVE = -1
    IDLE = 0
    INSERT = 1

class Extruder(ttk.Frame):
    def __init__(self, master: "ManualControls"):
        super().__init__(master)
        master.columnconfigure(0, weight=1)
        master.rowconfigure(0, weight=1)

        self.master = master

        self._extruder = tk.DoubleVar()
        self._multiplier = tk.IntVar(value=10)
        self._state = ExtruderState.IDLE

        self._create_widgets()

    def _create_widgets(self):
        ttk.Label(
            self, text="Extruder", font="Roboto 10 bold"
        ).grid(column=0, row=0, sticky=(W, E))

        ttk.Label(self, text="Multiplier").grid(column=0, row=1, sticky=(W, E))
        ttk.Spinbox(
            self, textvariable=self._multiplier, from_=1, to=1000, width=5
        ).grid(column=1, row=1, sticky=(W, E), padx=5)

        self._insert_btn = ttk.Button(
            self, text="Insert Filament", command=self._insert
        )
        self._insert_btn.grid(column=2, row=1, sticky=(W, E))

        self._remove_btn = ttk.Button(
            self, text="Remove Filament", command=self._remove
        )
        self._remove_btn.grid(column=3, row=1, sticky=(W, E))

        self._stop_btn = ttk.Button(self, text="Stop", command=self._stop)
        self._stop_btn.grid(column=4, row=1, sticky=(W, E))

        self._enable()

    def _insert(self):
        self._state = ExtruderState.INSERT
        self._disable()

    def _remove(self):
        self._state = ExtruderState.REMOVE
        self._disable()

    def _stop(self):
        self._state = ExtruderState.IDLE
        self._enable()

    def _disable(self):
        self._insert_btn.config(state=tk.DISABLED)
        self._remove_btn.config(state=tk.DISABLED)
        self._stop_btn.config(state=tk.NORMAL)

    def _enable(self):
        self._insert_btn.config(state=tk.NORMAL)
        self._remove_btn.config(state=tk.NORMAL)
        self._stop_btn.config(state=tk.DISABLED)

    @property
    def extruder(self):
        return self._extruder.get()

    def run(self):
        self._extruder.set(
            self._extruder.get() + self._state.value * self._multiplier.get()
        )

class Temperature(ttk.Frame):
    def __init__(self, master: "ManualControls"):
        super().__init__(master)
        master.columnconfigure(0, weight=1)
        master.rowconfigure(0, weight=1)

        self.master = master

        self._current_temp = tk.IntVar()
        self._target_temp = tk.IntVar()

        self._create_widgets()

    def _create_widgets(self):
        ttk.Label(
            self, text="Hot End", font="Roboto 10 bold"
        ).grid(column=0, row=0, sticky=(W, E))

        ttk.Label(self, text="Current Temp.").grid(column=0, row=1, sticky=W, )
        ttk.Label(self, textvariable=self._current_temp).grid(column=1, row=1, sticky=W)

        ttk.Label(self, text="Target Temp.").grid(column=0, row=2, sticky=W)
        ttk.Spinbox(
            self, textvariable=self._target_temp, from_=0, to=300
        ).grid(column=1, row=2, sticky=(W, E))

    @property
    def current_temp(self):
        return self._current_temp.get()

    @current_temp.setter
    def current_temp(self, value: int):
        self._current_temp.set(value)

    @property
    def target_temp(self):
        return self._target_temp.get()

class Submit(ttk.Frame):
    def __init__(self, master: "ManualControls"):
        super().__init__(master)
        master.columnconfigure(0, weight=1)
        master.rowconfigure(0, weight=1)

        self.master = master

        self._submit: ttk.Button
        self._sync = tk.BooleanVar(value=False)
        self._submitting = tk.BooleanVar(value=False)
        self._submitting.trace_add("write", self._on_state_change)

        self._create_widgets()

    def _create_widgets(self):
        self._submit = ttk.Button(self, text="Submit", command=self._on_submit)
        self._submit.grid(column=0, row=0, sticky=(W, S))
        self._sync_checkbox = ttk.Checkbutton(
            self,
            text="Automatically sync with Printer",
            variable=self._sync,
            command=self._change_sync
        )
        self._sync_checkbox.grid(column=0, row=1, sticky=(W, S))

        self._progress_bar = ttk.Progressbar(
            self, orient=tk.HORIZONTAL, length=200, mode='indeterminate'
        )
        self._progress_bar.grid(column=1, row=0, sticky=E)
        self._progress_bar.grid_remove()

        self.disable()

    def _change_sync(self):
        if self._sync.get():
            self.master.on_submitting_begin()
            self._submit.config(state=tk.DISABLED)
        else:
            self._submit.config(state=tk.NORMAL)

    def _on_state_change(self, *_):
        if not self.submitting:
            self._on_submitted()

    @property
    def sync(self):
        return self._sync.get()

    @property
    def submitting(self) -> bool:
        return self._submitting.get()

    @submitting.setter
    def submitting(self, value: bool):
        self._submitting.set(value)

    def disable(self):
        self._submit.config(state=tk.DISABLED)
        self._sync_checkbox.config(state=tk.DISABLED)

    def enable(self):
        self._submit.config(state=tk.NORMAL)
        self._sync_checkbox.config(state=tk.NORMAL)

    def _on_submit(self):
        self.master.on_submitting_begin()
        self._progress_bar.grid()
        self._progress_bar.start()

        self._submitting.set(True)

    def _on_submitted(self):
        self._progress_bar.stop()
        self._progress_bar.grid_remove()

class ManualControls(ttk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        master.columnconfigure(0, weight=1)
        master.rowconfigure(0, weight=1)

        self.master = master

        self._thread = None

        self._create_widgets()

    def _quit(self):
        if self._thread is not None:
            self._thread.exit()

    def _create_widgets(self):
        self._axes = Axes(self)
        self._axes.grid(column=0, row=2, sticky=(N, W, E))

        self._extruder = Extruder(self)
        self._extruder.grid(column=0, row=3, sticky=(N, W, E))

        self._temperature = Temperature(self)
        self._temperature.grid(column=0, row=4, sticky=(N, W, E))

        ttk.Separator(self, orient=tk.HORIZONTAL).grid(column=0, row=5, sticky=(W, E))

        self._submit = Submit(self)
        self._submit.grid(column=0, row=6, sticky=(W, E, S))

        for child in self.winfo_children():
            child.grid_configure(padx=5, pady=5)

    @property
    def connection(self):
        return self.master.connection

    def on_connect(self):
        self._submit.enable()

    def on_disconnect(self):
        self._submit.disable()

    def generate_gcode(self):
        while True:
            values = self._axes.coords
            values["e"] = self._extruder.extruder

            yield "G1".ljust(5, " ") + " ".join(
                ["".join((k.upper(), format_value(v))) for k, v in values.items()]
            ) + "\n"

            yield (
                "M104".ljust(5, " ") + "S"
                + format_value(self._temperature.target_temp, 3) + "\n"
            )

    def on_submitting_begin(self):
        if self._thread is None:
            self._start()

    def _start(self):
        logger.debug("starting thread")
        self._thread = threading.Thread(target=self.run)
        self._thread.start()

    def run(self):
        serial = self.connection.serial
        gcode_generator = self.generate_gcode()
        lines_submitted = 0

        while serial.is_open:
            msg = serial.readline().strip()
            if msg == b"OK":
                if self._submit.sync or self._submit.submitting:
                    self._extruder.run()

                gcode = next(gcode_generator)

                if self._submit.sync:
                    logger.debug(f"syncing with gcode: {gcode}")
                    serial.write(gcode.encode())
                elif self._submit.submitting:
                    logger.debug(f"submitting gcode: {gcode}")
                    serial.write(gcode.encode())
                    lines_submitted += 1

                    if lines_submitted >= 2:
                        self._submit.submitting = False
                        lines_submitted = 0
            elif msg.startswith(b"T"):
                temp = int(msg[1:].strip().decode())
                self._temperature.current_temp = temp
            else:
                logger.warning(f"Received unrecognized message: {msg.decode()}")


import datetime

DEBUG = False

SUPER_USER = ['']

data = {
    datetime.datetime.now().month : {
            "hours": 0,
            "under_salary": {
                "cofe": 0,
                "food": 0
            },
            "penalties": 0,
            "bet": 0
        }
    }

dir_database = 'DataBase'

dir_api_token = 'settings/token.json'

logfile = 'Logging/logfile.txt'


"""
QPainterPath definition for TIA-LA
"""

from PyQt5 import QtCore, QtGui, QtWidgets

def run (x, y):
    icon_1 = QtWidgets.QGraphicsPathItem()
    icon_path_1 = QtGui.QPainterPath()
    p1 = QtCore.QPointF(x, y+15)
    p2 = QtCore.QPointF(x+3, y+15)
    icon_path_1.addPolygon(QtGui.QPolygonF([p1, p2]))
    icon_1.setPath(icon_path_1)
    icon_1.setPen(QtGui.QPen(QtGui.QBrush(QtCore.Qt.blue), 0.75 ))

    icon_2 = QtWidgets.QGraphicsPathItem()
    icon_path_2 = QtGui.QPainterPath()
    p1 = QtCore.QPointF(x+1, y+32)
    p2 = QtCore.QPointF(x+1, y-2)
    p3 = QtCore.QPointF(x+41, y+15)
    p4 = QtCore.QPointF(x+1, y+32)
    icon_path_2.addPolygon(QtGui.QPolygonF([p1, p2, p3, p4]))
    icon_2.setPath(icon_path_2)
    icon_2.setPen(QtGui.QPen(QtGui.QBrush(QtCore.Qt.blue), 0.75 ))
    #icon_2.setBrush(QtGui.QBrush(QtGui.QColor(QtCore.Qt.blue)))

    icon_3 = QtWidgets.QGraphicsPathItem()
    icon_path_3 = QtGui.QPainterPath()
    p1 = QtCore.QPointF(x+42.5, y+15)
    p2 = QtCore.QPointF(x+44, y+15)
    icon_path_3.addPolygon(QtGui.QPolygonF([p1, p2]))
    icon_3.setPath(icon_path_3)
    icon_3.setPen(QtGui.QPen(QtGui.QBrush(QtCore.Qt.blue), 0.75 ))

    icon_4 = QtWidgets.QGraphicsPathItem()
    icon_path_4 = QtGui.QPainterPath()
    font = QtGui.QFont("Arial", 7)
    font.setItalic(True)
    icon_path_4.addText(x+2, y+18, font, "TIA-LA")
    icon_4.setPath(icon_path_4)
    icon_4.setBrush(QtGui.QBrush(QtCore.Qt.blue))
    icon_4.setPen(QtGui.QPen(QtGui.QBrush(QtCore.Qt.blue), 0.1 ))

    icon_paths = [icon_2, icon_4]
    return icon_paths


from enum import Enum
from typing import Callable, List, Optional, Tuple

from haku.utils import abstract

class Flex(Enum):
    """Flex directives"""

    grow = 0
    fixed = 1

class Align(Enum):
    """Align directives"""

    right = 0
    center = 1
    left = 2

class Renderable:
    """Renderable item"""

    end: str = "\n"
    width: Optional[int] = None
    flex: Flex = Flex.fixed

    @staticmethod
    def from_function(w: Optional[int] = None):
        """Create a renderable from a function"""

        def decorator(cbk: Callable[[int], str]):
            """Internal decorator"""

            class R(Renderable):
                def render(self, width: int) -> str:
                    return cbk(width)

            R.width = w
            R.flex = Flex.fixed if w is not None else Flex.grow
            return R()

        return decorator

    @abstract
    def render(self, width: int) -> str:
        """Render the item"""

class Dummy(Renderable):
    """Dummy renderable"""

    width = 0

    @staticmethod
    def render(width: int) -> str:
        """Render nothing"""

        return ""

class Group(Renderable):
    """Renderable group"""

    def __init__(
        self,
        *items: Renderable,
        separator: str = "",
        sandwich: bool = False,
        width: Optional[int] = None,
        fix_flex: bool = True,
    ):
        self.items = items
        self.separator = separator
        self.width = width
        self.sandwich = sandwich
        self.fix_flex = fix_flex

        if self.sandwich:
            self.items = [Dummy, *self.items, Dummy]

    def flex(self, width: int) -> List[Tuple[Renderable, int]]:
        """Add missing width to items"""

        fixed_items = [item for item in self.items if item.flex == Flex.fixed]
        grow_items = [item for item in self.items if item.flex == Flex.grow]

        fix_width = sum([item.width for item in fixed_items])
        sep_width = len(self.separator) * (len(self.items) - 1)
        rem_width = width - fix_width - sep_width

        # calcualte the width of each item marked as growable
        growth = rem_width // len(grow_items) if len(grow_items) > 0 else 0

        # if "holes" remains due to the "low resolution" of the console,
        # repeatly apply fixtures until the holes are (mostly) evenly filled
        # between the items marked as Flex.grow
        diff = rem_width % len(grow_items) if len(grow_items) > 0 else 0
        fixtures = [0 for _ in self.items]

        if diff > 0 and self.fix_flex:
            for i in range(diff):
                fixtures[i % len(fixtures)] += 1

        fixtures = iter(fixtures)
        items = [
            (item, item.width if item.flex == Flex.fixed else growth + next(fixtures))
            for item in self.items
        ]

        return items

    def render(self, width: int):
        """Render the group"""

        items = self.flex(width)
        rendered = [item.render(width) for item, width in items]
        return self.separator.join(rendered)

class Line(Renderable):
    """Renderable line"""

    def __init__(self, pattern: str, width: Optional[int] = None):
        self.flex = Flex.grow if width is None else Flex.fixed
        self.pattern = pattern
        self.width = width

    def render(self, width: int) -> str:
        """Render the line"""

        width = self.width or width
        reps = width // len(self.pattern) + 1
        line = self.pattern * reps
        return line[:width]

class Text(Renderable):
    """Renderable text"""

    def __init__(
        self,
        text: str,
        clip: bool = True,
        expand: bool = False,
        align: Align = Align.left,
    ):
        self.text = text
        self.expand = expand
        self.clip = clip
        self.align = align

        self.width = len(text) if not expand else None
        self.flex = Flex.fixed if not expand else Flex.grow

    def render(self, width: int) -> str:
        """Render the text"""

        if self.clip and len(self.text) > width:
            self.text = self.text[: width - 3] + "..."

        items_map = {
            Align.left: Group(Text(self.text), Line(" ")),
            Align.center: Group(Line(" "), Text(self.text), Line(" ")),
            Align.right: Group(Line(" "), Text(self.text)),
        }

        return items_map[self.align].render(width) if self.expand else self.text