import pyodbc
from email.message import EmailMessage
import ssl
import smtplib

connection_string = ""
table_name = ''

def fetch_entries(conn):
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM AllMenuData WHERE Name LIKE '%chicken%' OR Name LIKE '%kiwi%'")
    rows = cursor.fetchall()
    conn.commit()
    conn.close()
    return rows

def send_text():
    email_sender = ""
    email_password = ""
    email_receiver = ''
    subject = "DiningCourtMessenger"

    em = EmailMessage()
    em['From'] = email_sender
    em['To'] = email_receiver
    em['subject'] = subject

    context = ssl.create_default_context()

    with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp:
        smtp.login(email_sender, email_password)

        for entry in entries:
            date = entry[0]
            dining_court = entry[1]
            meal = entry[2]
            food = entry[3]
            body = f"""
                    {food} is being served at {dining_court} dining court for {meal} today, {date}!
                """
            em.set_content(body)
            smtp.sendmail(email_sender, email_receiver, em.as_string())

def main():
    conn = pyodbc.connect(connection_string)
    entries = fetch_entries(conn)

    if len(entries) == 0:
        print('No matches found.')
        return

    print(entries)

    send_text()

    print('finished!')

if __name__ == "__main__":
    main()


from sklearn import datasets, svm, metrics, externals

digits = datasets.load_digits()

n_samples = len(digits.images)
data = digits.images.reshape((n_samples, -1))

classifier = svm.SVC(gamma=0.001)

classifier.fit(data[:n_samples // 2], digits.target[:n_samples // 2])

expected = digits.target[n_samples // 2:]
predicted = classifier.predict(data[n_samples // 2:])

print(metrics.classification_report(expected, predicted))
print(metrics.confusion_matrix(expected, predicted))


"""Adjust data - download deliverables, clean and upload to BigQuery."""
import csv
import json
import tempfile
from argparse import ArgumentParser

import requests
from google.cloud import bigquery

API_URI = "api.adjust.com"
ENDPOINT = "kpis"
API_VERSION = "v1"

"""The APP_TOKEN_LIST is a list of dictionaries.
Keys are the app_name, and app_token in the form:
{"app_name":"<specific app name from Adjust dash>". "app_token":"<unique Adjust app token>}"
Look here to see the apps we are tracking: https://dash.adjust.com/#/.
It is important to maintain this list in order for the script to work especially in the case of new apps
being added to track in Adjust
"""

CSV_FIELDS = [
    "date",
    "app",
    "app_token",
    "network",
    "network_token",
    "campaign",
    "campaign_token",
    "adgroup",
    "adgroup_token",
    "creative",
    "creative_token",
    "country",
    "os",
    "device",
    "clicks",
    "installs",
    "limit_ad_tracking_install_rate",
    "click_conversion_rate",
    "impression_conversion_rate",
    "sessions",
    "daus",
    "waus",
    "maus",
]

def read_json(filename: str) -> dict:
    """Read JSON file."""
    with open(filename, "r") as f:
        data = json.loads(f.read())
    return data

def write_dict_to_csv(json_data, filename):
    """Write a dictionary to a csv."""
    with open(filename, "w") as out_file:
        dict_writer = csv.DictWriter(out_file, CSV_FIELDS)
        dict_writer.writeheader()
        dict_writer.writerows(json_data)

def download_adjust_kpi_data(date, api_token, app_token):
    """Download data from Adjust - API token and APP tokens are called here."""
    start_date = date
    end_date = date

    kpis = [
        "clicks",
        "installs",
        "limit_ad_tracking_install_rate",
        "click_conversion_rate",
        "impression_conversion_rate",
        "sessions",
        "daus",
        "waus",
        "maus",
    ]
    groupings = [
        "days",
        "apps",
        "networks",
        "campaigns",
        "adgroups",
        "creatives",
        "countries",
        "os_names",
        "device_types",
    ]
    # getting overview metrics for different kpis / Deliverables
    url = f"https://api.adjust.com/kpis/v1/{app_token}"  # overview
    url_params = f"start_date={start_date}&end_date={end_date}&kpis={','.join(kpis)}&grouping={','.join(groupings)}"
    headers = {
        "Authorization": f"Token token={api_token}",
    }

    response = requests.get(url, headers=headers, params=url_params)
    if (response.status_code == 401) or (response.status_code == 400):
        print(f"***Error: {response.status_code}***")
        print(response.text)

    return response

def check_json(adjust_response_text):
    """Script will return an empty dictionary for apps on days when there is no data. Check for that here."""
    with tempfile.NamedTemporaryFile() as tmp_json:
        with open(tmp_json.name, "w") as f_json:
            f_json.write(adjust_response_text)
            try:
                query_export = read_json(f_json.name)
            except (
                ValueError
            ):  # ex. json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
                return None
    return query_export

def clean_json(query_export):
    """JSON sometimes has missing keys, need to clean up the data."""
    fields_list = []
    for date in query_export["result_set"]["dates"]:
        r_date = date["date"]
        for app in date["apps"]:
            r_app = app.get("name", "no_app_name")
            r_app_token = app.get("token", "no_app_token")
            for network in app["networks"]:
                r_network = network.get("name", "no_network_name")
                r_network_token = network.get("token", "no_network_token")
                for campaign in network["campaigns"]:
                    r_campaign = campaign.get("name", "no_campaign_name")
                    r_campaign_token = campaign.get("token", "no_campaign_token")
                    for adgroup in campaign["adgroups"]:
                        r_adgroup = adgroup.get("name", "no_ad_group_name")
                        r_adgroup_token = adgroup.get("token", "no_adgroup_token")
                        for creative in adgroup["creatives"]:
                            r_creative = creative.get("name", "no_creative_name")
                            r_creative_token = creative.get(
                                "token", "no_creative_token"
                            )
                            for country in creative["countries"]:
                                r_country = country["country"]
                                for os_name in country["os_names"]:
                                    r_os_name = os_name["os_name"]
                                    for device in os_name["device_types"]:
                                        r_device = device["device_type"]
                                        field_dict = {
                                            "date": (r_date),
                                            "app": (r_app),
                                            "app_token": (r_app_token),
                                            "network": (r_network),
                                            "network_token": (r_network_token),
                                            "campaign": (r_campaign),
                                            "campaign_token": (r_campaign_token),
                                            "adgroup": (r_adgroup),
                                            "adgroup_token": (r_adgroup_token),
                                            "creative": (r_creative),
                                            "creative_token": (r_creative_token),
                                            "country": (r_country),
                                            "os": (r_os_name),
                                            "device": (r_device),
                                            "clicks": device["kpi_values"][0],
                                            "installs": device["kpi_values"][1],
                                            "limit_ad_tracking_install_rate": device[
                                                "kpi_values"
                                            ][2],
                                            "click_conversion_rate": device[
                                                "kpi_values"
                                            ][3],
                                            "impression_conversion_rate": device[
                                                "kpi_values"
                                            ][4],
                                            "sessions": device["kpi_values"][5],
                                            "daus": device["kpi_values"][6],
                                            "waus": device["kpi_values"][7],
                                            "maus": device["kpi_values"][8],
                                        }
                                        fields_list.append(field_dict)

    return fields_list

def upload_to_bigquery(csv_data, project, dataset, date):
    """Upload the data to bigquery."""
    print("writing json to csv")

    partition = f"{date}".replace("-", "")

    with tempfile.NamedTemporaryFile() as tmp_csv:
        with open(tmp_csv.name, "w+b") as f_csv:
            write_dict_to_csv(csv_data, f_csv.name)

            client = bigquery.Client(project)

            job_config = bigquery.LoadJobConfig(
                create_disposition="CREATE_IF_NEEDED",
                write_disposition="WRITE_TRUNCATE",
                time_partitioning=bigquery.TimePartitioning(
                    type_=bigquery.TimePartitioningType.DAY,
                    field="date",
                ),
                skip_leading_rows=1,
                schema=[
                    bigquery.SchemaField("date", "DATE"),
                    bigquery.SchemaField("app", "STRING"),
                    bigquery.SchemaField("app_token", "STRING"),
                    bigquery.SchemaField("network", "STRING"),
                    bigquery.SchemaField("network_token", "STRING"),
                    bigquery.SchemaField("campaign", "STRING"),
                    bigquery.SchemaField("campaign_token", "STRING"),
                    bigquery.SchemaField("adgroup", "STRING"),
                    bigquery.SchemaField("adgroup_token", "STRING"),
                    bigquery.SchemaField("creative", "STRING"),
                    bigquery.SchemaField("creative_token", "STRING"),
                    bigquery.SchemaField("country", "STRING"),
                    bigquery.SchemaField("os", "STRING"),
                    bigquery.SchemaField("device", "STRING"),
                    bigquery.SchemaField("clicks", "INTEGER"),
                    bigquery.SchemaField("installs", "INTEGER"),
                    bigquery.SchemaField("limit_ad_tracking_install_rate", "FLOAT"),
                    bigquery.SchemaField("click_conversion_rate", "FLOAT"),
                    bigquery.SchemaField("impression_conversion_rate", "FLOAT"),
                    bigquery.SchemaField("sessions", "INTEGER"),
                    bigquery.SchemaField("daus", "INTEGER"),
                    bigquery.SchemaField("waus", "INTEGER"),
                    bigquery.SchemaField("maus", "INTEGER"),
                ],
            )

            # Table names are based on the app name seen in the Adjust dashboard"
            destination = f"{project}.{dataset}.adjust_deliverables_v1${partition}"

            job = client.load_table_from_file(f_csv, destination, job_config=job_config)

            print(
                f"Writing adjust data for all apps to {destination}. BigQuery job ID: {job.job_id}"
            )
            job.result()

def main():
    """Input data, call functions, get stuff done."""
    parser = ArgumentParser(description=__doc__)
    parser.add_argument("--date", required=True)
    parser.add_argument("--adjust_api_token", required=True)
    parser.add_argument("--adjust_app_list", required=True)
    parser.add_argument("--project", default="moz-fx-data-shared-prod")
    parser.add_argument("--dataset", default="adjust_derived")

    args = parser.parse_args()

    app_list = json.loads(args.adjust_app_list)

    data = []

    # Cycle through the apps to get the relevant kpi data
    for app in app_list:
        print(f'This is data for {app["app_name"]}')
        # Ping the Adjust URL and get a response
        json_file = download_adjust_kpi_data(
            args.date, args.adjust_api_token, app["app_token"]
        )

        query_export = check_json(json_file.text)

        if query_export is not None:
            # This section writes the tmp json data into a temp CSV file which will then be put into a BigQuery table
            adjust_data = clean_json(query_export)
            data.extend(adjust_data)
        else:
            print(f'no data for {app["app_name"]} today')

    upload_to_bigquery(data, args.project, args.dataset, args.date)

if __name__ == "__main__":
    main()


# coding=utf-8
from __future__ import absolute_import, division, print_function

from click import ClickException, argument, command, echo, option, pass_context

_SHORT_HELP = 'Update or view the {} configuration.'

def configuration_options(name, container, use, example_key, example_value, fetch_func):
    """
    Get a decorator for configuration modification options.

    :param name: name of the command
    :param container: the name of the configuration to work on
    :param use: usage string for the configuration options
    :param example_key: example update key
    :param example_value: example update value
    :param fetch_func: function to get container from the context
    :return: the decorator for the CLI function
    """

    def configuration_options_decorator(func):
        def view_configuration(context, _, value):
            """
            View the configuration options for a container. Handles
            the `--view` option.

            :param context: Click context
            :param _: command-line parameter
            :param value: whether or not to view the configuration
            """
            if not value or context.resilient_parsing:
                return

            config_container = fetch_func(context)
            max_length = max([len(key) for key in config_container])
            for key in config_container:
                echo('{:>{width}s}: {}'.format(key, config_container[key], width=max_length))

            context.exit()

        click_options = [
            command(
                name=name, short_help=_SHORT_HELP.format(container), help=_SHORT_HELP.format(container) + '''

Existing configuration options that are used to {use}
can be edited with this command.

\b
Examples:
  Update the option '{key}' option to '{value}'
  $ oct configure {name} '{key}' '{value}'
\b
  View the current {container} options
  $ oct configure {name} --view
'''.format(name=name, container=container, use=use, key=example_key, value=example_value)
            ),
            argument(
                'option',
            ),
            argument(
                'value',
            ),
            option(
                '--view',
                '-v',
                help='Print all configuration options.',
                is_flag=True,
                callback=view_configuration,
            ),
            pass_context,
        ]

        for click_option in reversed(click_options):
            func = click_option(func)

        return func

    return configuration_options_decorator

def update_configuration_option(container, option, value):
    """
    Update an option in a configuration file.

    :param container: configuration file to work on
    :param option: name of the option to update
    :param value: value to update to
    """
    if option not in container:
        raise ClickException(message='Option {} not found in configuration.'.format(option))

    # try to interpret the most common values
    if value.isdigit():
        value = int(value)
    elif value.lower() in ['true', 'yes']:
        value = True
    elif value.lower() in ['false', 'no']:
        value = False
    elif ',' in value:
        value = value.split(',')

    setattr(container, option, value)

    echo('Option {} updated to be {}.'.format(option, value))


# -*- coding: utf-8 -*-
import os
import sys
import argparse
import datetime
import pdb
import logging
import pprint
import numpy
import torch

from eznlp import auto_device
from eznlp.token import TokenSequence
from eznlp.dataset import Dataset
from eznlp.config import ConfigDict
from eznlp.model import OneHotConfig, EncoderConfig
from eznlp.model import ELMoConfig, BertLikeConfig, FlairConfig
from eznlp.model import TextClassificationDecoderConfig
from eznlp.model import ClassifierConfig
from eznlp.model.bert_like import truecase_for_bert_like, truncate_for_bert_like
from eznlp.training import Trainer, count_params, evaluate_text_classification

from utils import add_base_arguments, parse_to_args
from utils import load_data, dataset2language, load_pretrained, load_vectors, build_trainer, header_format

def parse_arguments(parser: argparse.ArgumentParser):
    parser = add_base_arguments(parser)

    group_data = parser.add_argument_group('dataset')
    group_data.add_argument('--dataset', type=str, default='imdb',
                            help="dataset name")
    group_data.add_argument('--pre_truecase', default=False, action='store_true',
                            help="whether to pre-truecase data")
    group_data.add_argument('--save_preds', default=False, action='store_true',
                            help="whether to save predictions on the test split (typically in case without ground truth)")

    group_decoder = parser.add_argument_group('decoder configurations')
    group_decoder.add_argument('--agg_mode', type=str, default='multiplicative_attention',
                               help="aggregating mode")

    return parse_to_args(parser)

def collect_TC_assembly_config(args: argparse.Namespace):
    drop_rates = (0.0, 0.05, args.drop_rate) if args.use_locked_drop else (args.drop_rate, 0.0, 0.0)

    if args.emb_dim > 0:
        vectors = load_vectors(args.language, args.emb_dim)
        ohots_config = ConfigDict({'text': OneHotConfig(field='text', min_freq=5, vectors=vectors, emb_dim=args.emb_dim, freeze=args.emb_freeze)})
    else:
        ohots_config = None

    if args.use_interm1:
        interm1_config = EncoderConfig(arch=args.enc_arch, hid_dim=args.hid_dim, num_layers=args.num_layers, in_drop_rates=drop_rates)
    else:
        interm1_config = None

    if args.use_interm2:
        interm2_config = EncoderConfig(arch=args.enc_arch, hid_dim=args.hid_dim, num_layers=args.num_layers, in_drop_rates=drop_rates)
    else:
        interm2_config = None

    if args.language.lower() == 'english' and args.use_elmo:
        elmo_config = ELMoConfig(elmo=load_pretrained('elmo'))
    else:
        elmo_config = None

    if args.language.lower() == 'english' and args.use_flair:
        flair_fw_lm, flair_bw_lm = load_pretrained('flair')
        flair_fw_config, flair_bw_config = FlairConfig(flair_lm=flair_fw_lm), FlairConfig(flair_lm=flair_bw_lm)
        if interm2_config is not None:
            interm2_config.in_proj = True
    else:
        flair_fw_config, flair_bw_config = None, None

    if args.bert_arch.lower() != 'none':
        # Uncased tokenizer for text classification
        bert_like, tokenizer = load_pretrained(args.bert_arch, args, cased=False)
        bert_like_config = BertLikeConfig(tokenizer=tokenizer, bert_like=bert_like, arch=args.bert_arch, freeze=False, paired_inputs=args.paired_inputs)
    else:
        bert_like_config = None

    return {'ohots': ohots_config,
            'intermediate1': interm1_config,
            'elmo': elmo_config,
            'flair_fw': flair_fw_config,
            'flair_bw': flair_bw_config,
            'bert_like': bert_like_config,
            'intermediate2': interm2_config, }

def build_TC_config(args: argparse.Namespace):
    drop_rates = (0.0, 0.05, args.drop_rate) if args.use_locked_drop else (args.drop_rate, 0.0, 0.0)
    decoder_config = TextClassificationDecoderConfig(agg_mode=args.agg_mode, in_drop_rates=drop_rates)
    return ClassifierConfig(**collect_TC_assembly_config(args), decoder=decoder_config)

def process_TC_data(train_data, dev_data, test_data, args, config):
    if args.pre_truecase:
        assert config.bert_like is not None
        assert not getattr(config.bert_like.tokenizer, 'do_lower_case', False)
        train_data = truecase_for_bert_like(train_data, verbose=args.log_terminal)
        dev_data   = truecase_for_bert_like(dev_data,   verbose=args.log_terminal)
        test_data  = truecase_for_bert_like(test_data,  verbose=args.log_terminal)

    # Truncate too long sentences
    if config.bert_like is not None:
        train_data = truncate_for_bert_like(train_data, config.bert_like.tokenizer, verbose=args.log_terminal)
        dev_data   = truncate_for_bert_like(dev_data,   config.bert_like.tokenizer, verbose=args.log_terminal)
        test_data  = truncate_for_bert_like(test_data,  config.bert_like.tokenizer, verbose=args.log_terminal)

    elif args.dataset in ('ChnSentiCorp', 'THUCNews_10'):
        # Too long sentences even for RNN
        for data in [train_data, dev_data, test_data]:
            for entry in data:
                if len(entry['tokens']) > 1200:
                    entry['tokens'] = entry['tokens'][:300] + entry['tokens'][-900:]

    if config.bert_like is not None and config.bert_like.paired_inputs:
        logger.info("Paired text exists, concatenating as input...")
        for data in [train_data, dev_data, test_data]:
            for entry in data:
                entry['tokens'] = entry['tokens'] + TokenSequence.from_tokenized_text([config.bert_like.tokenizer.sep_token]) + entry['paired_tokens']

    return train_data, dev_data, test_data

if __name__ == '__main__':
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
                                     fromfile_prefix_chars='@')
    args = parse_arguments(parser)

    # Use micro-seconds to ensure different timestamps while adopting multiprocessing
    timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S-%f")
    save_path =  f"cache/{args.dataset}-TC/{timestamp}"
    if not os.path.exists(save_path):
        os.makedirs(save_path)

    handlers = [logging.FileHandler(f"{save_path}/training.log")]
    if args.log_terminal:
        handlers.append(logging.StreamHandler(sys.stdout))
    logging.basicConfig(level=logging.INFO,
                        format="[%(asctime)s %(levelname)s] %(message)s",
                        datefmt="%Y-%m-%d %H:%M:%S",
                        handlers=handlers)

    logger = logging.getLogger(__name__)
    logger.info(header_format("Starting", sep='='))
    logger.info(" ".join(sys.argv))
    logger.info(pprint.pformat(args.__dict__))

    logger.info(header_format("Preparing", sep='-'))
    device = auto_device()
    if device.type.startswith('cuda'):
        torch.cuda.set_device(device)
        temp = torch.randn(100).to(device)

    train_data, dev_data, test_data = load_data(args)
    args.language = dataset2language[args.dataset]
    args.paired_inputs = ('paired_tokens' in train_data[0])

    # train_data, dev_data, test_data = train_data[:1000], dev_data[:1000], test_data[:1000]
    config = build_TC_config(args)
    train_data, dev_data, test_data = process_TC_data(train_data, dev_data, test_data, args, config)

    train_set = Dataset(train_data, config, training=True)
    train_set.build_vocabs_and_dims(dev_data)
    dev_set   = Dataset(dev_data,  train_set.config, training=False)
    test_set  = Dataset(test_data, train_set.config, training=False)

    logger.info(train_set.summary)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=args.batch_size, shuffle=True,  collate_fn=train_set.collate)
    dev_loader   = torch.utils.data.DataLoader(dev_set,   batch_size=args.batch_size, shuffle=False, collate_fn=dev_set.collate)

    logger.info(header_format("Building", sep='-'))
    model = config.instantiate().to(device)
    count_params(model)

    logger.info(header_format("Training", sep='-'))
    trainer = build_trainer(model, device, len(train_loader), args)
    if args.pdb:
        pdb.set_trace()

    torch.save(config, f"{save_path}/{config.name}-config.pth")
    def save_callback(model):
        torch.save(model, f"{save_path}/{config.name}.pth")
    trainer.train_steps(train_loader=train_loader, dev_loader=dev_loader, num_epochs=args.num_epochs,
                        save_callback=save_callback, save_by_loss=False)

    logger.info(header_format("Evaluating", sep='-'))
    model = torch.load(f"{save_path}/{config.name}.pth", map_location=device)
    trainer = Trainer(model, device=device)

    logger.info("Evaluating on dev-set")
    evaluate_text_classification(trainer, dev_set, batch_size=args.batch_size)
    logger.info("Evaluating on test-set")
    evaluate_text_classification(trainer, test_set, batch_size=args.batch_size, save_preds=args.save_preds)
    if args.save_preds:
        torch.save(test_data, f"{save_path}/test-data-with-preds.pth")

    logger.info(" ".join(sys.argv))
    logger.info(pprint.pformat(args.__dict__))
    logger.info(header_format("Ending", sep='='))


# 扩展的表格控件（QTableWidget）
# QTableWidget是QTableView的子类
# 每一个Cell（单元格）是一个QTableWidgetItem

import sys
from PyQt5.QtWidgets import (QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem, QAbstractItemView)

class TableWidgetDemo(QWidget):
    def __init__(self):
        super(TableWidgetDemo,self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget演示")
        self.resize(430, 230);
        layout = QHBoxLayout()
        tablewidget = QTableWidget()        # 创建QTableWidget对象
        tablewidget.setRowCount(4)          # 设置行数
        tablewidget.setColumnCount(3)       # 设置列数

        layout.addWidget(tablewidget)

        tablewidget.setHorizontalHeaderLabels(['姓名','年龄','籍贯'])     # 设置字段名称
        nameItem = QTableWidgetItem("小明")
        tablewidget.setItem(0,0,nameItem)

        ageItem = QTableWidgetItem("24")
        tablewidget.setItem(0,1,ageItem)

        jgItem = QTableWidgetItem("北京")
        tablewidget.setItem(0,2,jgItem)

        # 禁止编辑
        tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)       # 设置禁止编辑

        # 整行选择
        tablewidget.setSelectionBehavior(QAbstractItemView.SelectRows)
        # 调整列和行（即行与列的大小根据内容调整）
        tablewidget.resizeColumnsToContents()
        tablewidget.resizeRowsToContents()

        # 隐藏或显示水平与垂直表格字段名称
        tablewidget.horizontalHeader().setVisible(True)        # 设置水平表头是否可见
        tablewidget.verticalHeader().setVisible(False)         # 设置垂直头是否课件

        tablewidget.setVerticalHeaderLabels(["a","b"])      # 设置垂直的头名称

        # 隐藏表格线
        tablewidget.setShowGrid(True)       # 设置单元格的网格线是否可见

        self.setLayout(layout)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = TableWidgetDemo()
    example.show()
    sys.exit(app.exec_())


import os
import rospy
import rosbag
from copy import copy
import rostopic

class Recorder(object):
    def __init__(self, path, prefix, topics):
        self.path = path
        os.system('mkdir -p '+path)
        self.prefix = prefix
        self.topics= topics
        self.recorded_topics = {}
        self.publishers = {}
        self.lastMessages = {topic:None for topic in topics}
        self.bag = None

        self._latches = {}
        self._bag_latch = False
        self._not_found_topics = copy(topics)

        self.check_topics()

    def __del__(self):
        self.close_bag()

    def new_bag(self, name):
        if self.bag: raise Excpetion('First close the current bag before creating a new one')
        bag_name = self.path+'/'+name+'.bag'
        rospy.loginfo('Creating new bag : '+bag_name)
        self.bag = rosbag.Bag(bag_name, 'w')

    def close_bag(self):
        if self.bag:
            print('Closing the bag')
            while self._bag_latch:pass
            self.bag.close()
            self.bag = None
        else:
            rospy.loginfo('No bag currently open')

    def new_topic_name(self, topic):
        prefix= self.prefix
        if topic[0]!='/' and prefix[-1]!='/': prefix+='/'
        return prefix+topic

    ''' Callback for recording signals'''
    def make_callback(self, topic):
        written_topic = self.recorded_topics[topic]
        def callback(message):
            while self._latches[topic]: pass
            self._latches[topic] = True
            self.lastMessages[topic] = message
            self._latches[topic] = False
        return callback

    def check_topic(self, topic):
        message_class = rostopic.get_topic_class(topic)[0] # None if topic is not already published
        if message_class:
            self.recorded_topics[topic] = self.new_topic_name(topic)
            self._latches[topic] = False
            rospy.Subscriber(topic, message_class, self.make_callback(topic), queue_size = 10)
            self.publishers[topic] = rospy.Publisher(self.recorded_topics[topic], message_class, queue_size = 1)
            rospy.loginfo('create suscriber for '+topic)
            return True
        return False

    def check_topics(self):
        self._not_found_topics = [topic for topic in self._not_found_topics if not self.check_topic(topic)]

    def all_buffers_full(self, excepts = []):
        for topic, msg in self.lastMessages.iteritems():
            if topic not in excepts and not msg: return False
        return True

    def dump(self, topic):
        if not self.bag:
            raise Exception('Cannot dump '+topic+' : no bag opened')
        if isinstance(topic,list):
            return [self.dump(t) for t in topic]
        if topic not in self.recorded_topics:
            self.check_topic(topic)
            rospy.loginfo('Checking for '+topic+' again')
            return
        if not self.lastMessages[topic]:
            return
        while self._latches[topic]: pass
        self._latches[topic] = True
        self._bag_latch = True
        message = self.lastMessages[topic]
        try:
            self.bag.write(self.recorded_topics[topic], message)
            self.publishers[topic].publish(message)
            self.lastMessages[topic] = None
            return message
        finally:
            self._latches[topic] = False
            self._bag_latch = False

    def dump_all(self):
        return self.dump(self.topics)


import argparse
import numpy as np
import random as rnd
import librosa as lr
import os

import kissdsp.beamformer as bf
import kissdsp.filterbank as fb
import kissdsp.io as io
import kissdsp.masking as mk
import kissdsp.mixing as mx
import kissdsp.micarray as ma
import kissdsp.reverb as rb
import kissdsp.spatial as sp
import kissdsp.visualize as vz

import matplotlib.pyplot as plt

def main():

    parser = argparse.ArgumentParser()
    parser.add_argument('--speech_files', default='', type=str, help='File with list of wave files for speech.')
    parser.add_argument('--wave_duration', default=30, type=float, help='Wave file duration in second.')
    parser.add_argument('--silence_duration', default=0.5, type=float, help='Silence duration in second.')
    parser.add_argument('--sample_rate', default=16000, type=int, help='Sample rate (in samples/sec).')
    parser.add_argument('--number_sources', default=2, type=int, help='Number of speech sources.')
    parser.add_argument('--number_trials', default=10, type=int, help='Number of trials to fit speech signals in long sequence.')
    parser.add_argument('--index_start', default=1, type=int, help='Index of first feature to be generated.')
    parser.add_argument('--index_stop', default=1, type=int, help='Index of last feature to be generated.')
    parser.add_argument('--output', default='', type=str, help='Output directory to store features.')
    args = parser.parse_args()

    # Get list of files
    f = open(args.speech_files, 'r')
    paths = f.readlines()

    # Generate feature for each index
    for index in range(args.index_start, args.index_stop+1):

        # Create speech sources
        cs = []
        for source_index in range(0, args.number_sources):
            cs.append(concatenate_waves(files=paths,
                                        wave_duration=args.wave_duration,
                                        silence_duration=args.silence_duration,
                                        sample_rate=args.sample_rate,
                                        number_trials=args.number_trials))

        # Generate room
        room = configure_room(size=rnd.choice(['small', 'medium', 'large']),
                              micarray=rnd.choice(['respeaker_usb', 'respeaker_core', 'matrix_creator', 'matrix_voice', 'minidsp_uma']),
                              number_sources=args.number_sources,
                              rotate=True)

        # Create RIRs
        hs = rb.rir(room, sample_rate=args.sample_rate)

        # Split RIRs in target and interf
        hs_target = hs[[0], :, :]
        hs_interf = hs[1:, :, :]

        # Create target and interf sources
        snrs = np.random.uniform(low=-10, high=+10, size=(args.number_sources,))
        ss = np.zeros((len(cs), cs[0].shape[1]), dtype=np.float32)
        for source_index in range(0, args.number_sources):
            ss[source_index, :] = mx.pwr(cs[source_index], snrs[[source_index]])

        # Apply reverb
        xs_target = rb.conv(hs_target, ss[[0],:])
        xs_target += np.random.normal(size=xs_target.shape) * 1e-4
        xs_interf = rb.conv(hs_interf, ss[1:,:])
        xs_interf += np.random.normal(size=xs_interf.shape) * 1e-4
        xs = xs_target + xs_interf

        # STFT
        Xs = fb.stft(xs)

        # Compute time-difference-of-arrival of target speech
        tdoas = rb.tdoa(room)
        tdoa_t = tdoas[0,:]

        # Compute steering vectors using tdoa
        ws = sp.steering_tdoa(tdoa_t)

        # Perform phase-based frequency masking using the steering vector
        Zs = bf.pfm(Xs, ws)
        Ys_target = Zs[[0],:,:]
        Ys_interf = Zs[[1],:,:]

        # Go back to time-domain
        ys_target = fb.istft(Ys_target)
        ys_interf = fb.istft(Ys_interf)

        # Span full range
        vol_norm = max([ np.amax(np.abs(ys_target)),
                         np.amax(np.abs(ys_interf))])
        ys_target /= vol_norm
        ys_interf /= vol_norm

        # Apply new volume
        volume = np.random.uniform(low=-20, high=0)
        ys_target = mx.gain(ys_target, np.asarray([volume]))
        ys_interf = mx.gain(ys_interf, np.asarray([volume]))

        # Grabbing the ideal signal directly from the clean data
        ys_ideal = ss[[0],:]

        # Concatenate to a single multi-channel audio signal
        ys = np.concatenate([ ys_target, ys_interf, ys_ideal ], axis=0)

        # Save to file
        output_path = os.path.join(args.output, '%010d.wav' % index)
        io.write(ys, output_path)
        print(output_path)

def concatenate_waves(files, wave_duration, silence_duration, sample_rate, number_trials):

    wave_samples = int(wave_duration * sample_rate)
    silence_samples = int(silence_duration * sample_rate)

    s = np.zeros((1,wave_samples), dtype=np.float32)
    wave_samples_count = 0

    for trial in range(0, number_trials):

        while True:

            this_path = rnd.choice(files).rstrip()
            segment, _ = lr.load(path=this_path,
                                 sr=sample_rate,
                                 dtype=np.float32)

            segment = np.expand_dims(segment, axis=0)
            wave_samples_total = wave_samples_count + segment.shape[1]

            if wave_samples_count > 0:
                if wave_samples_total > wave_samples:
                    break
            else:
                if wave_samples_total > wave_samples:
                    segment = np.resize(segment,(1,wave_samples))
                    wave_samples_total = wave_samples

            segment_0dB = mx.pwr(segment, levels=np.asarray([0.0]))

            s[0, wave_samples_count:wave_samples_total] = segment_0dB
            wave_samples_count += segment.shape[1] + silence_samples

    return s

def configure_room(size, micarray, number_sources, margin=0.5, rotate=True):

    # Setup room dimensions

    if size == 'small':
        width = np.random.uniform(low=1, high=10, size=(1,))
        length = np.random.uniform(low=1, high=10, size=(1,))

    if size == 'medium':
        width = np.random.uniform(low=10, high=30, size=(1,))
        length = np.random.uniform(low=10, high=30, size=(1,))

    if size == 'large':
        width = np.random.uniform(low=30, high=50, size=(1,))
        length = np.random.uniform(low=30, high=50, size=(1,))

    height = np.random.uniform(low=2, high=5, size=(1,))

    box = np.concatenate([width, length, height], axis=0)

    # Choose microphone array (and rotate)

    if micarray == 'respeaker_usb':
        mics = ma.respeaker_usb()

    if micarray == 'respeaker_core':
        mics = ma.respeaker_core()

    if micarray == 'matrix_creator':
        mics = ma.matrix_creator()

    if micarray == 'matrix_voice':
        mics = ma.matrix_voice()

    if micarray == 'minidsp_uma':
        mics = ma.minidsp_uma()

    if rotate == True:

        theta_x = np.random.uniform(low=0, high=2*np.pi)
        theta_y = np.random.uniform(low=0, high=2*np.pi)
        theta_z = np.random.uniform(low=0, high=2*np.pi)

        Rx = np.asarray([[1, 0, 0],
                         [0, np.cos(theta_x), -np.sin(theta_x)],
                         [0, np.sin(theta_x), np.cos(theta_x)]])
        Ry = np.asarray([[np.cos(theta_y), 0, np.sin(theta_y)],
                         [0, 1, 0],
                         [-np.sin(theta_y), 0, np.cos(theta_y)]])
        Rz = np.asarray([[np.cos(theta_z), -np.sin(theta_z), 0],
                         [np.sin(theta_z), np.cos(theta_z), 0],
                         [0, 0, 1]])

        mics = (Rx @ Rz @ Rz @ mics.T).T

    # Define absorption

    absorption = np.random.uniform(low=0.2, high=0.8)
    alphas = absorption * np.ones(6, dtype=np.float32)

    # Position microphone array

    origin = np.concatenate([np.random.uniform(low=margin, high=box[0]-margin, size=(1,)),
                             np.random.uniform(low=margin, high=box[1]-margin, size=(1,)),
                             np.random.uniform(low=margin, high=box[2]-margin, size=(1,))], axis=0)

    # Position sources

    srcs = np.concatenate([np.random.uniform(low=margin, high=box[0]-margin, size=(number_sources, 1)),
                           np.random.uniform(low=margin, high=box[1]-margin, size=(number_sources, 1)),
                           np.random.uniform(low=margin, high=box[2]-margin, size=(number_sources, 1))], axis=1)

    # Speed of sound
    c = np.random.uniform(low=330.0, high=355.0)

    # Create room
    return rb.room(mics, box, srcs, origin, alphas, c)

if __name__ == "__main__":

    main()


import argparse

import copy
import os
import sys
import re
import tempfile
from typing import List, Dict, Union, Optional, Tuple, Sequence
from rich.text import Text
from rich import print
import numpy as np

import onnx  # type: ignore
import onnx.checker  # type: ignore
import onnx.helper  # type: ignore
import onnx.shape_inference  # type: ignore
import onnx.numpy_helper  # type: ignore
try:
    import onnxruntime as rt  # type: ignore
except ImportError:
    command = [sys.executable, '-m', 'pip', 'install', 'onnxruntime']
    print(Text(f"Installing onnxruntime by `{' '.join(command)}`, please wait for a moment..", style="bold magenta"))
    import subprocess
    subprocess.check_call(command)
    import onnxruntime as rt

import onnxsim.onnxsim_cpp2py_export as C
from . import model_info
from . import model_checking
from . import version

TensorShape = List[int]
TensorShapes = Dict[str, TensorShape]
TensorShapesWithOptionalKey = Dict[Optional[str], TensorShape]

def get_output_names(model: onnx.ModelProto) -> List[str]:
    output_names = [opt.name for opt in model.graph.output]
    return output_names

def remove_unused_output(
    model: onnx.ModelProto, unused_output: Sequence[str]
) -> onnx.ModelProto:
    unused_output_names = unused_output
    output_names = get_output_names(model)
    for unused_output_name in unused_output_names:
        if unused_output_name not in output_names:
            raise RuntimeError(
                f'The model doesn\'t have output named "{unused_output_name}"'
            )
    for graph_output in copy.deepcopy(model.graph.output):
        if graph_output.name in unused_output_names:
            model.graph.output.remove(graph_output)
    return model

def remove_initializer_from_input(model: onnx.ModelProto) -> onnx.ModelProto:
    initializer_names = [x.name for x in model.graph.initializer]
    for graph_input in copy.deepcopy(model.graph.input):
        if graph_input.name in initializer_names:
            model.graph.input.remove(graph_input)
    return model

def check_and_update_input_shapes(model: onnx.ModelProto, input_shapes: Optional[TensorShapesWithOptionalKey]) -> Optional[TensorShapes]:
    if input_shapes is None:
        return None

    def get_inputs(model: onnx.ModelProto) -> List[onnx.ValueInfoProto]:
        initializer_names = [x.name for x in model.graph.initializer]
        return [ipt for ipt in model.graph.input if ipt.name not in initializer_names]

    def get_input_names(model: onnx.ModelProto) -> List[str]:
        input_names = [ipt.name for ipt in get_inputs(model)]
        return input_names

    input_names = get_input_names(model)
    if None in input_shapes:
        if len(input_names) == 1:
            input_shapes[input_names[0]] = input_shapes[None]
            del input_shapes[None]
        else:
            raise RuntimeError(
                'The model has more than 1 inputs, please use the format "input_name:dim0,dim1,...,dimN" in --input-shape')
    for x in input_shapes:
        if x not in input_names:
            raise RuntimeError(
                'The model doesn\'t have input named "{}"'.format(x))

    return input_shapes  # type: ignore

# A very very large threshold
MAX_TENSOR_SIZE_THRESHOLD = '10GB'

def simplify(
    model: Union[str, onnx.ModelProto],
    check_n: int = 0,
    perform_optimization: bool = True,
    skip_fuse_bn: bool = False,
    overwrite_input_shapes=None,
    test_input_shapes=None,
    skipped_optimizers: Optional[List[str]] = None,
    skip_constant_folding=False,
    skip_shape_inference=False,
    input_data=None,
    dynamic_input_shape: bool = False,
    custom_lib: Optional[str] = None,
    include_subgraph: bool = False,
    unused_output: Optional[Sequence[str]] = None,
    tensor_size_threshold: str = MAX_TENSOR_SIZE_THRESHOLD,
    mutable_initializer: bool = False,
    *,
    input_shapes=None,
) -> Tuple[onnx.ModelProto, bool]:
    """
    :param model: onnx ModelProto object or file path
    :param check_n: The simplified model will be checked for `check_n` times by random inputs
    :param perform_optimization: Whether to run onnx optimizer on the model
    :param skip_fuse_bn: Skip fuse_bn_into_conv onnx optimizer
    :param overwrite_input_shapes: If the model has dynamic input shape, user must pass a fixed input shape
            for generating random inputs and checking equality.
    :param test_input_shapes: If the model has dynamic input shape, user must pass a fixed input shape
            for generating random inputs and checking equality.
    :param skipped_optimizers: Skip some specific onnx optimizers
    :param skip_constant_folding: Skip constant folding
    :param skip_shape_inference: Skip shape inference (sometimes shape inference will crash)
    :param input_data: Feed custom input data for checking if needed
    :param dynamic_input_shape: Deprecated. Not needed anymore.
    :param custom_lib: onnxruntime custom ops's shared library
    :param include_subgraph: Simplify subgraph (e.g. true graph and false graph of "If" operator) instead of only the main graph
    :param unused_output: name of unused outputs that will be eliminated from the model
    :param input_shapes: Deprecated. Please use `overwrite_input_shapes` and/or `test_input_shapes` instead.
    :return: A tuple (simplified model, success(True) or failed(False))
    """
    if dynamic_input_shape:
        print(
            Text(
                "WARNING: The argument `dynamic_input_shape=True` is not needed any more, onnxsim can now support dynamic input shapes natively, please refer to the latest documentation. An error will be raised in the future.",
                style="bold red",
            )
        )
    if input_shapes is not None:
        print(
            Text(
                "WARNING: The argument `input_shapes` is deprecated. Please use `overwrite_input_shapes` and/or `test_input_shapes` instead. An error will be raised in the future.",
                style="bold red",
            )
        )
        overwrite_input_shapes = input_shapes
        test_input_shapes = input_shapes

    if not perform_optimization:
        # None means skip all optimizers
        skipped_optimizers = None
    elif skipped_optimizers is None:
        skipped_optimizers = []

    if skip_fuse_bn and skipped_optimizers is not None:
        skipped_optimizers.append("fuse_bn_into_conv")
    if isinstance(model, str):
        model = onnx.load(model)
    if overwrite_input_shapes is None:
        overwrite_input_shapes = {}
    overwrite_input_shapes = check_and_update_input_shapes(
        model, overwrite_input_shapes)
    test_input_shapes = check_and_update_input_shapes(
        model, test_input_shapes)

    for name, input_shape in overwrite_input_shapes.items():
        for ipt in model.graph.input:
            if ipt.name == name:
                for i, dim in enumerate(ipt.type.tensor_type.shape.dim):
                    dim.dim_value = input_shape[i]
    if unused_output is not None:
        model = remove_unused_output(model, unused_output)
    if not mutable_initializer:
        model = remove_initializer_from_input(model)

    # https://stackoverflow.com/a/60708339
    def parse_size(size: str) -> int:
        units = {"B": 1, "KB": 2**10, "MB": 2**20, "GB": 2**30, "TB": 2**40}
        size = size.upper()
        if not re.match(r' ', size):
            size = re.sub(r'([KMGT]?B)', r' \1', size)
        number, unit = [string.strip() for string in size.split()]
        return int(float(number)*units[unit])

    tensor_size_threshold = parse_size(tensor_size_threshold)

    try:
        model_bytes = model.SerializeToString()
        model_opt_bytes = C.simplify(
            model_bytes,
            skipped_optimizers,
            not skip_constant_folding,
            not skip_shape_inference,
            tensor_size_threshold,
        )
        model_opt = onnx.load_from_string(model_opt_bytes)
        check_ok = model_checking.compare(
            model_opt, model, check_n, test_input_shapes, input_data, custom_lib
        )
    except ValueError:
        # large models try to convert through a temporary file
        with tempfile.TemporaryDirectory() as tmpdirname:
            onnx.save(
                copy.deepcopy(model),
                os.path.join(tmpdirname, 'model.onnx'),
                save_as_external_data=True,
            )
            check_ok = C.simplify_path(
                os.path.join(tmpdirname, 'model.onnx'),
                os.path.join(tmpdirname, 'opt.onnx'),
                skipped_optimizers,
                not skip_constant_folding,
                not skip_shape_inference,
                tensor_size_threshold,
            )
            check_ok = model_checking.compare(
                os.path.join(tmpdirname, 'opt.onnx'),
                os.path.join(tmpdirname, 'model.onnx'),
                check_n, test_input_shapes, input_data, custom_lib
            )
            model_opt = onnx.load(os.path.join(tmpdirname, 'opt.onnx'))
    return model_opt, check_ok

class PyModelExecutor(C.ModelExecutor):
    def Run(self, model_str: str, inputs_str: List[str]):
        model = onnx.ModelProto()
        model.ParseFromString(model_str)

        def deserialize_tp(tp_str):
            tp = onnx.TensorProto()
            tp.ParseFromString(tp_str)
            return tp

        input_tps = map(deserialize_tp, inputs_str)
        input_arrs = map(onnx.numpy_helper.to_array, input_tps)
        input_names = [x.name for x in model.graph.input]
        inputs = dict(zip(input_names, input_arrs))
        sess_options = rt.SessionOptions()
        sess_options.graph_optimization_level = rt.GraphOptimizationLevel(0)
        sess_options.log_severity_level = 3
        sess = rt.InferenceSession(
            model.SerializeToString(),
            sess_options=sess_options,
            providers=["CPUExecutionProvider"],
        )
        output_names = [x.name for x in sess.get_outputs()]
        run_options = rt.RunOptions()
        run_options.log_severity_level = 3
        output_arrs = sess.run(output_names, inputs, run_options=run_options)
        return [
            onnx.numpy_helper.from_array(x).SerializeToString() for x in output_arrs
        ]

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("input_model", help="Input ONNX model")
    parser.add_argument("output_model", help="Output ONNX model")
    parser.add_argument(
        "check_n",
        help="Check whether the output is correct with n random inputs",
        nargs="?",
        type=int,
        default=0,
    )
    parser.add_argument(
        "--enable-fuse-bn",
        help="This option is deprecated. Fusing bn into conv is enabled by default.",
        action="store_true",
    )
    parser.add_argument(
        "--skip-fuse-bn", help="Skip fusing batchnorm into conv.", action="store_true"
    )
    parser.add_argument(
        "--skip-optimization",
        help="Skip all ONNX optimizers or some of them. To skip all optimizers, use `onnxsim a.onnx b.onnx --skip-optimization`. To skip some of optimizers, use something like `onnxsim a.onnx b.onnx --skip-optimization fuse_bn_into_conv fuse_pad_into_pool`.",
        type=str,
        nargs="*",
    )
    parser.add_argument("--skip-constant-folding", help="Skip constant folding", action="store_true")
    parser.add_argument(
        "--input-shape",
        help="This argument has been renamed to --overwrite-input-shape, please refer to it",
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "--overwrite-input-shape",
        help='Overwrite the input shape. The format is "input_name:dim0,dim1,...,dimN" or simply "dim0,dim1,...,dimN" when there is only one input, for example, "data:1,3,224,224" or "1,3,224,224". Note: you might want to use some visualization tools like netron to make sure what the input name and dimension ordering (NCHW or NHWC) is.',
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "--test-input-shape",
        help='The input shape to generated random inputs for test, useful when the input shape is dynamic. The format is "input_name:dim0,dim1,...,dimN" or simply "dim0,dim1,...,dimN" when there is only one input, for example, "data:1,3,224,224" or "1,3,224,224". Note: you might want to use some visualization tools like netron to make sure what the input name and dimension ordering (NCHW or NHWC) is.',
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "--skip-optimizer",
        help="Deprecated. Refer to --skip-optimization",
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "--skip-shape-inference", help="Skip shape inference", action="store_true"
    )
    parser.add_argument(
        "--enable-onnxruntime-optimization",
        help="Enable ONNX Runtime's ORT_ENABLE_BASIC level optimization.",
        action="store_true",
    )
    parser.add_argument(
        "--dynamic-input-shape",
        help="Deprecated. Not needed any more.",
        action="store_true",
    )
    parser.add_argument(
        "--input-data-path",
        help='input data, The value should be "input_name1:xxx1.bin"  "input_name2:xxx2.bin ...", input data should be a binary data file.',
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "--custom-lib", help="Deprecated. Not needed any more.", type=str
    )
    parser.add_argument(
        "--include-subgraph",
        help='Experimental feature. Simplify subgraph (e.g. true graph and false graph of "If" operator) instead of only the main graph',
        action="store_true",
    )
    parser.add_argument(
        "--unused-output",
        help="Name of unused outputs that will be eliminated from the model",
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "--no-large-tensor",
        help="Some ops like Tile and ConstantOfShape can produce large tensor and make the model size much larger. Specifying this flag to skip folding these ops, with loss of some optimization chances. It can be followed with a threshold, for example, --no-large-tensor 1M or --no-large-tensor 100KB. A simple '--no-large-tensor' means '--no-large-tensor 1KB'.",
        type=str,
        const='1KB',
        default=MAX_TENSOR_SIZE_THRESHOLD,
        nargs="?",
        dest="tensor_size_threshold",
    )
    parser.add_argument(
        "--mutable-initializer",
        help="By ONNX specification, initializers can also serve as inputs. This allows users to overwrite their values during runtime, but some useful optimizations like fuse-conv-and-bn will not be applicable anymore. In almost all cases, having an initializer that is also an input is unintended (usually caused by a out-dated PyTorch). So onnxsim treats all initializers immutable to enabling all optimizations. If it is not wanted, you can specify '--mutable-initializer' to disable this behavior.",
        action="store_true",
        )
    parser.add_argument('-v', '--version', action='version', version='onnxsim ' + version.version)

    args = parser.parse_args()

    if args.enable_fuse_bn:
        print(
            Text(
                'WARNING: "--enable-fuse-bn" is not needed any more, because fuse bn is enabled by default. "--enable-fuse-bn" flag is ignored now and will raise an error in the future.',
                style="bold red",
            )
        )
    if args.dynamic_input_shape:
        print(
            Text(
                'WARNING: "--dynamic-input-shape" is not needed any more, onnxsim v0.4 now handles dynamic input shapes automatically. "--dynamic-input-shape" flag is ignored now and will raise an error in the future.',
                style="bold red",
            )
        )
    assert not (args.input_shape is not None and args.overwrite_input_shape is not None)
    if args.input_shape:
        print(
            Text(
                'WARNING: "--input-shape" is renamed to "--overwrite-input-shape". Please use it instead.',
                style="bold red",
            )
        )
        args.overwrite_input_shape = args.input_shape
    if args.include_subgraph:
        print(
            Text(
                "WARNING: subgraph optimization is not supported in v0.4 for now.",
                style="bold red",
            )
        )
    assert not (args.skip_optimizer is not None and args.skip_optimization is not None)
    if args.skip_optimizer:
        print(
            Text(
                'WARNING: "--skip-optimizer" is renamed to "--skip-optimization". Please use it instead.',
                style="bold red",
            )
        )
        args.skip_optimization = args.skip_optimizer
    if args.skip_optimization is None:
        # user doesn't specify --skip-optimization
        args.skip_optimization = []
    elif len(args.skip_optimization) == 0:
        # user specify --skip-optimization without any certain optimizer name
        # set it to None means skip all optimizations
        args.skip_optimization = None
    if args.skip_fuse_bn and args.skip_optimization is not None:
        args.skip_optimization.append("fuse_bn_into_conv")

    perform_optimization = False if args.skip_optimization is None else True

    def parse_shapes(shapes_arg):
        shapes = {}
        if shapes_arg is not None:
            for x in shapes_arg:
                if ':' not in x:
                    shapes[None] = list(map(int, x.split(',')))
                else:
                    pieces = x.split(':')
                    # for the input name like input:0
                    name, shape = ':'.join(
                        pieces[:-1]), list(map(int, pieces[-1].split(',')))
                    shapes.update({name: shape})
        return shapes

    test_input_shapes = parse_shapes(args.test_input_shape)
    overwrite_input_shapes = parse_shapes(args.overwrite_input_shape)

    if args.enable_onnxruntime_optimization:

        tmp_file = tempfile.NamedTemporaryFile()
        sess_options = rt.SessionOptions()
        # Set graph optimization level
        sess_options.graph_optimization_level = rt.GraphOptimizationLevel.ORT_ENABLE_BASIC
        # To enable model serialization after graph optimization
        sess_options.optimized_model_filepath = tmp_file.name
        _ = rt.InferenceSession(args.input_model, sess_options, providers=["CPUExecutionProvider"])

        model = onnx.load(tmp_file.name)
    else:
        model = onnx.load(args.input_model)

    if args.tensor_size_threshold == MAX_TENSOR_SIZE_THRESHOLD:
        for node in model.graph.node:
            if node.op_type in ["Tile", "ConstantOfShape"]:
                print(
                    Text(
                        'Your model contains "Tile" ops or/and "ConstantOfShape" ops. Folding these ops can make the simplified model much larger. If it is not expected, please specify "--no-large-tensor" (which will lose some optimization chances)',
                        style="bold magenta",
                    )
                )
                break

    if not args.mutable_initializer:
        initializer_names = set([x.name for x in model.graph.initializer])
        input_names = set([x.name for x in model.graph.input])
        if len(initializer_names.intersection(input_names)) > 0:
            print(
                Text(
                    'Your model contains initializers that are also inputs. This is usually caused by an out-dated PyTorch. onnxsim treats all initializers immutable to enabling all optimizations. If it is not wanted, please specify "--mutable-initializer" to disable this behavior.',
                    style="bold magenta",
                )
            )

    input_tensors = None
    if args.input_data_path is not None:
        input_tensors = {}
        for x in args.input_data_path:
            pieces = x.split(':')
            name, data = ':'.join(pieces[:-1]), pieces[-1]
            input_tensors.update({name: np.load(data)})

    print("Simplifying...")

    model_opt, check_ok = simplify(
        model,
        args.check_n,
        perform_optimization,
        False,
        overwrite_input_shapes,
        test_input_shapes,
        args.skip_optimization,
        args.skip_constant_folding,
        args.skip_shape_inference,
        input_tensors,
        False,
        args.custom_lib,
        args.include_subgraph,
        args.unused_output,
        args.tensor_size_threshold,
        args.mutable_initializer,
    )

    try:
        onnx.save(model_opt, args.output_model)
    except ValueError:
        # large models
        onnx.save(
            copy.deepcopy(model_opt),
            args.output_model,
            save_as_external_data=True,
            all_tensors_to_one_file=True,
            location=os.path.basename(args.output_model) + '.data',
        )

    if check_ok:
        print("Finish! Here is the difference:")
        model_info.print_simplifying_info(model, model_opt)
    else:
        print(
            'Check failed. Please be careful to use the simplified model, or try specifying "--skip-fuse-bn" or "--skip-optimization" (run "onnxsim -h" for details).'
        )
        print("Here is the difference after simplification:")
        model_info.print_simplifying_info(model, model_opt)
        sys.exit(1)


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

x = np.linspace(0,10,100)
y = np.sin(x)

#Contours
def f(x, y):
    return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)

x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)

X, Y = np.meshgrid(x, y)
Z = f(X, Y)
plt.contourf(X,Y,Z,20,cmap='RdGy')


import json
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--num_trial', type=int, default=0)
args = parser.parse_args()
num_trial = args.num_trial

succ = 0
for i in range(114):
    f = open("./sample/" + "{:04d}".format(i) + ".accn")
    data = f.readline()
    start_idx = int(data.split(',')[0])
    if start_idx != -1:
        succ += max(num_trial - start_idx, 0)
print(succ / 114 / num_trial)


import tkinter as tk
import tensorflow as tf
from tensorflow import keras
from litemodel import LiteModel
import chess
import chess.svg
import sys
from PIL import Image
import PIL.ImageTk as ImageTk
import cairosvg
import io
import controller

class MainWindow(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        self.board = chess.Board()
        self.columnconfigure(index=0, weight=1)
        self.rowconfigure(index=0, weight=1)
        self.canvas = tk.Canvas(self, width=400, height=400)
        self.input = tk.Entry(self)
        self.input.bind("<Return>", self.player_move)
        self.canvas.grid(row=0, column=0, sticky="nsew")
        self.input.grid(row=1, column=0, sticky="ew")
        self.imgid = None
        self.redraw()
        self.load_model()

    def load_model(self):
        model = keras.models.load_model("model-10gen.h5")
        self.model = LiteModel.from_keras_model(model)

    def player_move(self, event):
        try:
            move = chess.Move.from_uci(self.input.get())
        except:
            return
        if not self.board.is_legal(move):
            return
        self.input.delete(0, tk.END)
        self.board.push(move)
        self.redraw()
        if self.board.is_game_over():
            self.game_over(True)
            return
        self.model_move()

    def model_move(self):
        move = controller.choose_move(self.board, self.model)
        self.board.push(move)
        self.redraw()
        if self.board.is_game_over():
            self.game_over(False)

    def game_over(self, win):
        self.input.delete(0, tk.END)
        self.input.insert(0, "YOU WIN" if win else "YOU LOSE")
        self.input.configure(state="disabled")

    def redraw(self):
        if self.imgid is not None:
            self.canvas.delete(self.imgid)
        svg = chess.svg.board(self.board).encode("utf-8")
        img_data = cairosvg.svg2png(bytestring=svg)#, parent_width=self.canvas.winfo_width(), parent_height=self.canvas.winfo_height())
        image = Image.open(io.BytesIO(img_data))
        tk_image = ImageTk.PhotoImage(image)
        self.imgid = self.canvas.create_image(0, 0, anchor="nw", image=tk_image)
        self.canvas.image = tk_image

if __name__ == "__main__":
    root = tk.Tk()
    MainWindow(root).pack(side="top", fill="both", expand=True)
    root.mainloop()


import pathlib

import numpy as np

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker
import mpl_toolkits.axes_grid1.axes_divider

import util.io.fs
import util.logging

def generic(file, plot_function, font_size=20, transparent=True, caption=None, use_legend=False,
            tick_power_limit_scientific=None, tick_power_limit_scientific_x=None, tick_power_limit_scientific_y=None,
            tick_power_limit_fix=None, tick_power_limit_fix_x=None, tick_power_limit_fix_y=None,
            tick_number=None, tick_number_x=None, tick_number_y=None,
            tick_interger_only_x=False, tick_interger_only_y=False,
            tick_transform_x=None, tick_transform_y=None,
            x_min=None, x_max=None, y_min=None, y_max=None,
            overwrite=True, make_read_only=True, dpi=800, backend=None,
            invert_x_axis=False, invert_y_axis=False,
            colorbar=False, colorbar_tick_number=None, colorbar_tick_step_size=None,
            colorbar_tick_transform=None, colorbar_tick_power_limit_scientific=None):

    # check if file should be saved
    if check_file(file, overwrite=overwrite):
        # use backend
        if backend is not None:
            plt.switch_backend(backend)

        # font size
        set_global_font_size(font_size=font_size)

        # make fig
        fig = plt.figure()

        # make plot
        plot_function(fig)

        # get axes
        axes = fig.gca()

        # power limits
        set_tick_power_limit_scientific(tick_power_limit_scientific, axis='both')
        set_tick_power_limit_scientific(tick_power_limit_scientific_x, axis='x')
        set_tick_power_limit_scientific(tick_power_limit_scientific_y, axis='y')
        set_tick_power_limit_fix(tick_power_limit_fix, axis='both')
        set_tick_power_limit_fix(tick_power_limit_fix_x, axis='x')
        set_tick_power_limit_fix(tick_power_limit_fix_y, axis='y')

        # set number of ticks
        ticks_set_number(tick_number, axis='both')
        ticks_set_number(tick_number_x, axis='x')
        ticks_set_number(tick_number_y, axis='y')

        # set integer only for ticks
        ticks_set_integer_only(tick_interger_only_x, axis='x')
        ticks_set_integer_only(tick_interger_only_y, axis='y')

        # set caption
        if caption is not None:
            plt.xlabel(caption, fontsize=font_size, fontweight='bold')

        # legend
        add_legend(use_legend=use_legend, transparent=transparent)

        # set axis limits
        set_axis_limits(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)

        # invert axis
        invert_axis(x_axis=invert_x_axis, y_axis=invert_y_axis, axes=axes)

        # transform tick labels
        ticks_transform_labels(tick_transform_x=tick_transform_x, tick_transform_y=tick_transform_y, axes=axes)

        # add colorbar
        if colorbar_tick_number is None:
            colorbar_tick_number = tick_number
        if colorbar_tick_power_limit_scientific is None:
            colorbar_tick_power_limit_scientific = tick_power_limit_scientific
        add_colorbar(axes=axes, colorbar=colorbar,
                     tick_number=colorbar_tick_number, tick_step_size=colorbar_tick_step_size,
                     tick_transform=colorbar_tick_transform, tick_power_limit_scientific=colorbar_tick_power_limit_scientific)

        # save and close
        save_and_close_fig(fig, file, transparent=transparent, make_read_only=make_read_only, overwrite=overwrite, dpi=dpi)

def check_file(file, overwrite=True):
    file_exists = pathlib.Path(file).exists()
    if overwrite and file_exists:
        util.io.fs.make_writable(file)
    save = overwrite or not file_exists
    return save

def save_and_close_fig(fig, file, transparent=True, make_read_only=True, overwrite=False, dpi=800):
    # prepare file
    file = pathlib.Path(file)
    file.parent.mkdir(parents=True, exist_ok=True)
    if overwrite and file.exists():
        file.unlink()
    # plot
    fig.savefig(file, transparent=transparent, dpi=dpi, bbox_inches='tight')
    plt.close(fig)
    # make read only
    if make_read_only:
        util.io.fs.make_read_only(file)
    util.logging.debug('Plot saved to {}.'.format(file))

def set_global_font_size(font_size=None, weight='bold'):
    if font_size is not None:
        util.logging.debug(f'Setting global font size to {font_size}.')
        font_size = int(font_size)
        matplotlib.rcParams.update({'font.size': font_size})
    if weight is not None:
        util.logging.debug(f'Setting global font weight {weight}.')
        matplotlib.rcParams.update({'font.weight': weight})
        if weight == 'bold':
            matplotlib.rcParams['text.latex.preamble'] = ['\\boldmath']

def _get_axes_list(axes=None):
    if axes is None:
        axes = [plt.gca()]
    try:
        len(axes)
    except TypeError:
        axes = [axes]
    return axes

def _parse_scientific_power_limit(power_limit):
    if isinstance(power_limit, str):
        power_limit = int(power_limit)
    try:
        length = len(power_limit)
    except TypeError:
        power_limit = (-power_limit, power_limit)
    else:
        if length != 2:
            raise ValueError(f'power_limit has to be a tuple with two values but it is {power_limit}.')
    return power_limit

def set_tick_power_limit_scientific(power_limit, axis='both', axes=None):
    if power_limit is not None:
        # set axes
        axes = _get_axes_list(axes=axes)
        # set axis
        if axis is None:
            axis = 'both'
        # parse power limits
        power_limit = _parse_scientific_power_limit(power_limit)
        # apply power limit
        util.logging.debug(f'Setting tick power limit scientific for axis {axis} to {power_limit}.')
        for axes_i in axes:
            axes_i.ticklabel_format(style='scientific', axis=axis, scilimits=power_limit)

def set_tick_power_limit_fix(power_limit, axis='both', axes=None):

    class FixedOrderFormatter(matplotlib.ticker.ScalarFormatter):
        """Formats axis ticks using scientific notation with a constant order of
        magnitude"""
        def __init__(self, order_of_magnitude=0, use_offset=True, use_math_text=None):
            self._order_of_magnitude = order_of_magnitude
            matplotlib.ticker.ScalarFormatter.__init__(self, useOffset=use_offset, useMathText=use_math_text)
            self.orderOfMagnitude = order_of_magnitude

        def _set_orderOfMagnitude(self, range):
            """Over-riding this to avoid having orderOfMagnitude reset elsewhere"""
            self.orderOfMagnitude = self._order_of_magnitude

    if power_limit is not None:
        # set axes
        axes = _get_axes_list(axes=axes)
        # set axis
        if axis is None:
            axis = 'both'
        # apply power limit
        util.logging.debug(f'Setting tick power limit fix for axis {axis} to {power_limit}.')
        f = FixedOrderFormatter(power_limit)
        for axes_i in axes:
            if axis == 'y' or axis == 'both':
                axes_i.yaxis.set_major_formatter(f)
            if axis == 'x' or axis == 'both':
                axes_i.xaxis.set_major_formatter(f)

def ticks_set_number(number, axis='both', axes=None):
    if number is not None:
        number = int(number)
        # set axes
        axes = _get_axes_list(axes=axes)
        # set axis
        if axis is None:
            axis = 'both'
        # apply tick number
        for axes_i in axes:
            axes_i.locator_params(axis=axis, tight=True, nbins=number + 1, min_n_ticks=number)

def ticks_set_integer_only(integer_only, axis='both', axes=None):
    integer_only = bool(integer_only)
    if integer_only:
        # set axes
        axes = _get_axes_list(axes=axes)
        # set axis
        if axis is None:
            axis = 'both'
        # apply tick number
        for axes_i in axes:
            axes_i.locator_params(axis=axis, integer=integer_only)

def set_axis_limits(x_min=None, x_max=None, y_min=None, y_max=None):
    if x_min is not None:
        plt.xlim(left=x_min)
    if x_max is not None:
        plt.xlim(right=x_max)
    if y_min is not None:
        plt.ylim(bottom=y_min)
    if y_max is not None:
        plt.ylim(top=y_max)

def get_colors(n, colormap_name='gist_rainbow'):
    colormap = plt.get_cmap(colormap_name)
    colors = [colormap(i / (n - 1)) for i in range(n)]
    return colors

def ticks_set_number_for_locator(locator, tick_number=None):
    if tick_number is not None:
        tick_number = int(tick_number)
        # LinearLocator, LogLocator, SymmetricalLogLocator
        try:
            locator.set_params(numticks=tick_number)
        except TypeError:
            pass
        # MaxNLocator
        try:
            locator.set_params(nbins=tick_number + 1)
            locator.set_params(min_n_ticks=tick_number)
        except TypeError:
            pass

def add_colorbar(axes_image=None, axes=None, colorbar=True,
                 orientation='right', size='3%', pad='1.5%',
                 tick_number=None, tick_step_size=None, tick_transform=None, tick_power_limit_scientific=None):
    # plot colorbar
    if colorbar:
        if axes is None:
            axes = plt.gca()
        if axes_image is None:
            axes_images = axes.get_images()
            axes_image = axes_images[-1]
        # make place for colorbar with same height as plot
        axes_divider = mpl_toolkits.axes_grid1.axes_divider.make_axes_locatable(axes)
        cax = axes_divider.append_axes(orientation, size=size, pad=pad)
        # plot colorbar
        cb = plt.colorbar(axes_image, cax=cax)
        # set ticks
        if tick_number is not None and tick_step_size is not None:
            raise ValueError('Please use only tick_number or tick_step_size, not both.')
        if tick_number is not None:
            ticks_set_number_for_locator(cb.locator, tick_number=tick_number)
            cb.update_ticks()
        if tick_step_size is not None:
            tick_step_size = float(tick_step_size)
            locator = matplotlib.ticker.MultipleLocator(base=tick_step_size)
            cb.locator = locator
            cb.update_ticks()
        if tick_transform is not None and tick_power_limit_scientific is not None:
            raise ValueError('Please use only tick_transform or tick_power_limit_scientific, not both.')
        if tick_transform is not None:
            ticks = cb.ax.get_yticks()
            tick_lables = [tick_transform(tick) for tick in ticks]
            cb.ax.set_yticklabels(tick_lables)
        if tick_power_limit_scientific is not None:
            tick_power_limit_scientific = _parse_scientific_power_limit(tick_power_limit_scientific)
            cb.formatter.set_powerlimits(tick_power_limit_scientific)
            cb.update_ticks()
        return cb
    else:
        return None

def add_legend(labels=None, handles=None, use_legend=True, transparent=True):
    if use_legend:
        legend = plt.legend(labels=labels, handles=handles, loc='best')
        if legend is not None:
            legend.get_frame().set_alpha(float(not transparent))

def _v_min_max(data, percentile, significant_digits=2):
    data = data[np.isfinite(data)]
    v = np.percentile(data, percentile)
    if significant_digits is not None and v != 0:
        exp = np.log10(np.abs(v))
        exp = - np.sign(exp) * np.round(np.abs(exp)) + significant_digits
        v = np.round(v * 10**exp) * 10**-exp
    assert np.isfinite(v)
    return v

def v_min(data, percentile=1, significant_digits=2):
    return _v_min_max(data, percentile, significant_digits=significant_digits)

def v_max(data, percentile=99, significant_digits=2):
    return _v_min_max(data, percentile, significant_digits=significant_digits)

def invert_axis(x_axis=False, y_axis=False, axes=None):
    if x_axis or y_axis:
        if axes is None:
            axes = plt.gca()
        if x_axis:
            axes.invert_xaxis()
        if y_axis:
            axes.invert_yaxis()

def ticks_transform_labels(tick_transform_x=None, tick_transform_y=None, axes=None):
    if tick_transform_x or tick_transform_y:
        if axes is None:
            axes = plt.gca()

        if tick_transform_x:
            x_ticks = axes.get_xticks()
            x_tick_lables = [tick_transform_x(tick) for tick in x_ticks]
            axes.set_xticklabels(x_tick_lables)

        if tick_transform_y:
            y_ticks = axes.get_yticks()
            y_tick_lables = [tick_transform_y(tick) for tick in y_ticks]
            axes.set_yticklabels(y_tick_lables)

def tick_transform_function_exponent_notation(data, v_max=None, decimals=None):
    # calculate factor
    if v_max is not None:
        v_max_for_tick_factor = float(v_max)
    else:
        v_max_for_tick_factor = util.plot.auxiliary.v_max(data)
    tick_exponent = int(np.floor(np.log10(v_max_for_tick_factor)))
    if tick_exponent >= 0:
        tick_exponent_str = f'e\\!\\!{tick_exponent}$'
    else:
        tick_exponent_str = f'e\\!\\!-\\!\\!{-tick_exponent}$'
    tick_factor = 10**tick_exponent

    # choose decimals
    if decimals is None:
        if v_max_for_tick_factor <= 2 * tick_factor:
            decimals = 1
        else:
            decimals = 0
    else:
        decimals = int(decimals)

    # create transform function
    def tick_transform(tick):
        tick /= tick_factor
        tick = np.round(tick, decimals=decimals)
        if decimals == 0:
            tick = int(tick)
        tick_label = f'${tick}{tick_exponent_str}'
        return tick_label

    # return
    return tick_transform


import logging

from telegram.ext import CallbackContext

def delete_message(context: CallbackContext) -> None:
    """
    A job which will delete the given message
    The context (in context.job.context) is a dict that looks like this:
    {
        "chat_id": 1234,
        "message_id": 1234,
    }
    """
    logging.info("Deleting delayed message")

    chat_id = context.job.context["chat_id"]
    message_id = context.job.context["message_id"]
    logging.info(f"Chat ID: {chat_id}")
    logging.info(f"Message ID: {message_id}")

    context.bot.delete_message(chat_id, message_id)


"""add photo to diagnostics

Revision ID: 0bfc6022b536
Revises: 54b5265d2e43
Create Date: 2023-10-02 19:00:03.256724

"""
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "0bfc6022b536"
down_revision: Union[str, None] = "54b5265d2e43"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

diagnostics_photo_mapping = {
    1: "/images/get-tested/mri.png",
    2: "/images/get-tested/ct.png",
    3: "/images/get-tested/x-ray.png",
    4: "/images/get-tested/ultrasound.png",
    5: "/images/get-tested/pet.png",
    6: "/images/get-tested/blood-test.png",
    7: "/images/get-tested/urine.png",
    8: "/images/get-tested/biopsy.png",
    9: "/images/get-tested/ecg.png",
    10: "/images/get-tested/bone-density.png",
    11: "/images/get-tested/endoscopy.png",
    12: "/images/get-tested/colonoscopy.png",
}

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column("diagnostics", sa.Column("photo_url", sa.String(length=256)))
    conn = op.get_bind()

    for diagnostic_id, photo_url in diagnostics_photo_mapping.items():
        conn.execute(
            sa.text(
                "UPDATE diagnostics SET photo_url = :photo_url WHERE diagnostic_id = :diagnostic_id"
            ),
            parameters=dict(
                diagnostic_id=diagnostic_id,
                photo_url=photo_url,
            ),
        )

    op.alter_column("diagnostics", "photo_url", nullable=False)
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column("diagnostics", "photo_url")
    # ### end Alembic commands ###


import pygame

class ServeIndicator:
    def __init__(self, id, icon, x, y, colorFunc):
        self.id = id
        self.pos = (x,y)
        self.icon = icon
        self.color = (255,117,73)
        self.servingColor = (255,117,73)
        self.notServingColor = (200,199,211)
        self.colorFunc = colorFunc
        self.font = pygame.font.SysFont("lato", 150, True)
        self.bounds = self.getSurface(self.icon)

    def draw(self, surface, centralized=False):
        textSurface, self.bounds = self.getSurface(self.icon)
        pos = (self.pos[0] - self.bounds.width // 2, self.pos[1])
        surface.blit(textSurface, pos)

    def getSurface(self, text):
        color = self.color
        isNotServing = self.colorFunc(self.id)
        if isNotServing:
            color = self.notServingColor

        textSurface = self.font.render(text, False, color)
        return textSurface, textSurface.get_rect()

    def update(self):
        pass


from itertools import chain

from rest_framework import generics, permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.main.models import Announcement, News, Poll, PollChoice, UserChoice
from apps.main.serializers import (
    AnnouncementDetailSerializer,
    NewsDetailSerializer,
    NewsSerializer,
    PollDetailSerializer,
)

class ContentAPIView(generics.ListAPIView):
    serializer_class = NewsSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        news = News.objects.all()
        announcement = Announcement.objects.all()
        poll = Poll.objects.all()

        combine = list(chain(news, announcement, poll))
        sorted_data = sorted(combine, key=lambda item: item.created_at, reverse=True)

        return sorted_data

class ContentDetailAPIView(generics.RetrieveAPIView):
    queryset = None
    serializer_class = None
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        model_type = self.kwargs.get("model_type")
        pk = self.kwargs.get("pk")

        if model_type.lower() == "news":
            self.queryset = News.objects.filter(pk=pk)
            self.serializer_class = NewsDetailSerializer
        elif model_type.lower() == "announcement":
            self.queryset = Announcement.objects.filter(pk=pk)
            self.serializer_class = AnnouncementDetailSerializer
        elif model_type.lower() == "poll":
            self.queryset = Poll.objects.filter(pk=pk)
            self.serializer_class = PollDetailSerializer
        return super().get_queryset()

class CreateChoiceAPIView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, pk):
        try:
            curr_choice = PollChoice.objects.get(id=pk)
            poll = curr_choice.poll
            for choice in poll.choices.all():
                if choice.user_choice.filter(user=request.user).exists():
                    return Response(
                        {"message": "You can only answer once."},
                        status=status.HTTP_400_BAD_REQUEST,
                    )
            UserChoice.objects.create(user=request.user, choice_id=pk)
            return Response(
                {"message": "Poll choice created successfully"},
                status=status.HTTP_201_CREATED,
            )
        except PollChoice.DoesNotExist:
            return Response(
                {"message": "Poll Choice does not exist"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            return Response({"message": f"{e}"}, status=status.HTTP_400_BAD_REQUEST)


#import time
import pigpio
import utils

# GPIO Pin definitions
WING_LEFT_PIN = 25
WING_RIGHT_PIN = 8
WING_ROTATE_PIN = 24
TURRET_ROTATE_PIN = 23
LEFT_LED_PIN = 20
RIGHT_LED_PIN = 7
EYE_LED_PIN = 16

# Servo PWM width limits per servo
WING_LEFT_LIMIT = [1010, 1910] # min, max 1910
WING_RIGHT_LIMIT = [2060, 1100] # min, max 1100
WING_ROTATE_LIMIT = [1000, 1420, 1750] # min, center, max
TURRET_ROTATE_LIMIT = [1250, 1520, 1750] # min, center, max

# Variables to store and be able to read current positions
current_wing_left_pos = 0
current_wing_right_pos = 0
current_wing_rotate_pos = 0
current_turret_rotate_pos = 0

pi = None

def start_gpio():
   global pi
   pi = pigpio.pi()
   if not pi.connected:
      return -1
   # Setup GPIO for LEDs
   pi.set_mode(LEFT_LED_PIN, pigpio.OUTPUT)
   pi.set_mode(RIGHT_LED_PIN, pigpio.OUTPUT)
   pi.set_mode(EYE_LED_PIN, pigpio.OUTPUT)
   # Make sure pull-up resistors are on
   pi.set_pull_up_down(LEFT_LED_PIN, pigpio.PUD_DOWN)
   pi.set_pull_up_down(RIGHT_LED_PIN, pigpio.PUD_DOWN)
   pi.set_pull_up_down(EYE_LED_PIN, pigpio.PUD_DOWN)
   # Setp PWM frequency for gun LEDs to 10hz
   pi.set_PWM_frequency(LEFT_LED_PIN, 10)
   pi.set_PWM_frequency(RIGHT_LED_PIN, 10)
   return 0

def movement_maker(pin1, desired_pulsewidth, pin2, desired_pulsewidth_extra):
   global current_wing_left_pos
   global current_wing_right_pos
   global current_wing_rotate_pos
   global current_turret_rotate_pos
   # Check if pin and pulse inputs are valid
   if pin1 != 0 and desired_pulsewidth != 0:
      # Check if pin 2 is also a valid input, then we go into dual movement mode
      if pin2 != 0 and desired_pulsewidth_extra != 0:
         #print ("DUAL_MOVEMENT_DETECTED!")
         # We only do dual movements for the wings, so check if the right pins are our arguments
         # pin1 must be the LEFT pin and pin2 must be RIGHT pin.
         if pin1 == WING_LEFT_PIN and pin2 == WING_RIGHT_PIN:
            final_smoothed_width = 0
            final_smoothed_width_extra = 0
            # Get current position
            old_smoothed_width = current_wing_left_pos
            old_smoothed_width_extra = current_wing_right_pos
            while True:
               #time.sleep(0.001)
               # Smooth out the movement of left wing
               final_smoothed_width = (desired_pulsewidth * 0.03) + (old_smoothed_width * 0.97)
               old_smoothed_width = final_smoothed_width
               # Smooth out the movement of right wing
               final_smoothed_width_extra = (desired_pulsewidth_extra * 0.03) + (old_smoothed_width_extra * 0.97)
               old_smoothed_width_extra = final_smoothed_width_extra
               # Send out the final position
               pi.set_servo_pulsewidth(pin1, round(final_smoothed_width))
               pi.set_servo_pulsewidth(pin2, round(final_smoothed_width_extra))

               # If both servos have reached their target, break
               if round(final_smoothed_width) == desired_pulsewidth and round(final_smoothed_width_extra) == desired_pulsewidth_extra:
                  break
               #print("width1: " + str(final_smoothed_width) + " target: " + str(desired_pulsewidth))
            # Save current position
            current_wing_left_pos = round(final_smoothed_width)
            current_wing_right_pos = round(final_smoothed_width_extra)
            disable_servos()
            return 0
         else:
            # pin1 must be the LEFT and pin2 must be RIGHT!
            # Otherwise return an error
            return 1
      else:
         #print ("SINGLE_MOVEMENT_DETECTED!")
         # If only pin1 is used, we check which pin we have selected, so we know what movement we are generating
         old_smoothed_width = 0
         final_smoothed_width = 0
         if pin1 == WING_LEFT_PIN:
            old_smoothed_width = current_wing_left_pos
         elif pin1 == WING_RIGHT_PIN:
            old_smoothed_width = current_wing_right_pos
         elif pin1 == WING_ROTATE_PIN:
            old_smoothed_width = current_wing_rotate_pos
         elif pin1 == TURRET_ROTATE_PIN:
            old_smoothed_width = current_turret_rotate_pos
         else:
            # If none of the pre-defined pins are used, return an error
            return 1
         # Start a loop to slowly modify the pulse widths.
         # Keep running it until the output has reached the target.
         while round(final_smoothed_width) != desired_pulsewidth:
            #time.sleep(0.002)
            # Smooth out the movement
            final_smoothed_width = (desired_pulsewidth * 0.03) + (old_smoothed_width * 0.97)
            old_smoothed_width = final_smoothed_width
            # Send out the new position
            pi.set_servo_pulsewidth(pin1, round(final_smoothed_width))
         # Save final position
         if pin1 == WING_LEFT_PIN:
            current_wing_left_pos = round(final_smoothed_width)
         elif pin1 == WING_RIGHT_PIN:
            current_wing_right_pos = round(final_smoothed_width)
         elif pin1 == WING_ROTATE_PIN:
            current_wing_rotate_pos = round(final_smoothed_width)
         elif pin1 == TURRET_ROTATE_PIN:
            current_turret_rotate_pos = round(final_smoothed_width)
         disable_servos()
         return 0
   else:
      # welp how did you end up here...
      return 1

def move_wings(wing_selection, target_percent):
   target_width = [0,0]
   if wing_selection == utils.LEFT_WING:
      target_width[0] = utils.map(target_percent, 0, 100, WING_LEFT_LIMIT[0], WING_LEFT_LIMIT[1])
      return movement_maker(WING_LEFT_PIN, int(target_width[0]),0,0)

   elif wing_selection == utils.RIGHT_WING:
      target_width[1] = utils.map(target_percent, 0, 100, WING_RIGHT_LIMIT[0], WING_RIGHT_LIMIT[1])
      return movement_maker(WING_RIGHT_PIN, int(target_width[1]),0,0)

   elif wing_selection == utils.BOTH_WINGS:
      target_width[0] = utils.map(target_percent, 0, 100, WING_LEFT_LIMIT[0], WING_LEFT_LIMIT[1])
      target_width[1] = utils.map(target_percent, 0, 100, WING_RIGHT_LIMIT[0], WING_RIGHT_LIMIT[1])
      return movement_maker(WING_LEFT_PIN, int(target_width[0]), WING_RIGHT_PIN, int(target_width[1]))

def rotate_wings(wing_direction, target_percent):
   # Only rotate wings if they are fully extended!
   if current_wing_left_pos != WING_LEFT_LIMIT[1] and current_wing_right_pos != WING_RIGHT_LIMIT[1]:
      return 1
   if wing_direction == utils.WING_DOWN:
      target_width = utils.map(target_percent, 0, 100, WING_ROTATE_LIMIT[1], WING_ROTATE_LIMIT[0])
      return movement_maker(WING_ROTATE_PIN, int(target_width),0,0)

   elif wing_direction == utils.WING_UP:
      target_width = utils.map(target_percent, 0, 100, WING_ROTATE_LIMIT[1], WING_ROTATE_LIMIT[2])
      return movement_maker(WING_ROTATE_PIN, int(target_width),0,0)

def rotate_turret(turret_direction, target_percent):
   # Only rotate turrent if wings are fully extended!
   if current_wing_left_pos != WING_LEFT_LIMIT[1] and current_wing_right_pos != WING_RIGHT_LIMIT[1]:
      return 1
   if turret_direction == utils.TURRET_LEFT:
      target_width = utils.map(target_percent, 0, 100, TURRET_ROTATE_LIMIT[1], TURRET_ROTATE_LIMIT[0])
      return movement_maker(TURRET_ROTATE_PIN, int(target_width),0,0)

   elif turret_direction == utils.TURRET_RIGHT:
      target_width = utils.map(target_percent, 0, 100, TURRET_ROTATE_LIMIT[1], TURRET_ROTATE_LIMIT[2])
      return movement_maker(TURRET_ROTATE_PIN, int(target_width),0,0)

   elif turret_direction == utils.TURRET_FULL_ROTATE:
      target_width = utils.map(target_percent, 0, 200, TURRET_ROTATE_LIMIT[2], TURRET_ROTATE_LIMIT[0])
      return movement_maker(TURRET_ROTATE_PIN, int(target_width),0,0)

# Don't use after startup!
def home_servos():
   pi.set_servo_pulsewidth(TURRET_ROTATE_PIN, int(TURRET_ROTATE_LIMIT[1]))
   global current_turret_rotate_pos
   current_turret_rotate_pos = int(TURRET_ROTATE_LIMIT[1])
   pi.set_servo_pulsewidth(WING_ROTATE_PIN, int(WING_ROTATE_LIMIT[1]))
   global current_wing_rotate_pos
   current_wing_rotate_pos = int(WING_ROTATE_LIMIT[1])
   #.sleep(1) # wait for the wings to rotate to home position before retracting them
   pi.set_servo_pulsewidth(WING_LEFT_PIN, int(WING_LEFT_LIMIT[0]))
   pi.set_servo_pulsewidth(WING_RIGHT_PIN, int(WING_RIGHT_LIMIT[0]))
   global current_wing_left_pos
   global current_wing_right_pos
   current_wing_left_pos = int(WING_LEFT_LIMIT[0])
   current_wing_right_pos = int(WING_RIGHT_LIMIT[0])

def eye_light(brightness):
   pi.set_PWM_dutycycle(EYE_LED_PIN, brightness)

def guns_lights(brightness):
   pi.set_PWM_dutycycle(LEFT_LED_PIN, brightness)
   pi.set_PWM_dutycycle(RIGHT_LED_PIN, brightness)

def disable_servos():
   pi.set_servo_pulsewidth(WING_LEFT_PIN, 0)
   pi.set_servo_pulsewidth(WING_RIGHT_PIN, 0)
   pi.set_servo_pulsewidth(WING_ROTATE_PIN, 0)
   pi.set_servo_pulsewidth(TURRET_ROTATE_PIN, 0)

def kill_servos_and_leds():
   pi.set_servo_pulsewidth(WING_LEFT_PIN, 0)
   pi.set_servo_pulsewidth(WING_RIGHT_PIN, 0)
   pi.set_servo_pulsewidth(WING_ROTATE_PIN, 0)
   pi.set_servo_pulsewidth(TURRET_ROTATE_PIN, 0)
   pi.set_PWM_dutycycle(EYE_LED_PIN, 0)
   pi.set_PWM_dutycycle(LEFT_LED_PIN, 0)
   pi.set_PWM_dutycycle(RIGHT_LED_PIN, 0)


import numpy as np
import pandas as pand
import scipy.io
import os

def read_data(path, group, modality, frequency_range):
    params = {'delta': 0, 'theta': 1, 'alpha': 2, 'beta': 3,
              'control': 'CONT', 'preterm': 'KEKE', 'PPC': 'dbPLI',
              'AAC': 'oCC', 'active sleep': 'AS_A_dbpli.mat', 'quiet sleep': 'TA_A_dbpli.mat'}

    files = sorted(list(filter(lambda x: x.startswith(params['preterm']), os.listdir(path))))
    data = []
    for i in range(len(files)):
        data.append(read_graph(path + files[i] + '/' + params[modality] + '/' + params['active sleep'], params[frequency_range]))

    df = pand.DataFrame(data, columns = ['X', 'y'])
    return(df)

def read_graph(location, frequency_range):
    mat = scipy.io.loadmat(location)['dbPLI']
    theta_range_matrix = mat[0][frequency_range]

    return theta_range_matrix, 0


import random

from common.config import MUTATION_RATE

class SwapMutationOperator:
    """
    A mutation operator that performs swap mutation on a list of genes.
    """

    def mutate(self, genes, mutation_rate: float = MUTATION_RATE):
        """
        Mutates the given genes by swapping elements with a probability equal to the mutation_rate.

        Args:
        - genes: a list of integers representing the current tower assignment for each city
        - mutation_rate: a float representing the probability that a swap occurs between two elements in the genes list

        Returns:
        - None. The input genes list is modified in place.
        """
        for i in range(len(genes)):
            if random.random() < mutation_rate:
                j = random.randint(0, len(genes) - 1)
                genes[i], genes[j] = genes[j], genes[i]


"""
Converts the "studis_fach" pdfs to csvs using Tabula and bounding box annotations for
the tables.

"""
import argparse
import fileinput
import shutil
from pathlib import Path
from typing import Dict, List

import tabula
from tabula.template import load_template
from tqdm import tqdm

def convert_page_df(pdf_file: str, csv_file: str, options: Dict) -> None:
    tabula.convert_into(pdf_file, csv_file, guess=False,
                        output_format="csv", **options)

def convert_pdf_with_annotation(pdf_file: Path, annotation_file: Path, csv_file: Path):
    assert csv_file.suffix == ".csv"
    options = load_template(str(annotation_file))

    tmp_dir = Path(".tmp")
    tmp_dir.mkdir(exist_ok=True)
    tmp_csv_files: List[Path] = [
        tmp_dir / f"{csv_file.stem}-{i}.csv" for i in range(len(options))]

    for page_options, tmp_csv_file in zip(options, tmp_csv_files):
        convert_page_df(str(pdf_file), str(tmp_csv_file), page_options)

    with csv_file.open(mode="w") as f:
        for line in fileinput.input(tmp_csv_files):
            f.write(line)

    shutil.rmtree(".tmp")

def main(args: argparse.Namespace):
    assert args.pdfs.is_dir()
    assert args.annotations.is_dir()

    args.outdir.mkdir(parents=True, exist_ok=True)
    pdf_files = list(args.pdfs.glob("*.pdf"))
    annotation_files = list(args.annotations.glob("*.json"))

    print(f"Found {len(pdf_files)} pdfs, and {len(annotation_files)} annotations")

    for pdf_file in tqdm(pdf_files, desc="Processing pdfs"):
        annotation_file = args.annotations / f"{pdf_file.stem}.json"
        if annotation_file not in annotation_files:
            print(f"Could not find annotation for '{str(pdf_file)}'")
        else:
            csv_file = args.outdir / f"{pdf_file.stem}.csv"
            convert_pdf_with_annotation(pdf_file, annotation_file, csv_file)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)

    parser.add_argument("--pdfs",
                        type=Path,
                        help="folder with pdfs",
                        default=Path("data/pdfs/studis_fach"))
    parser.add_argument("--annotations",
                        type=Path,
                        help="folder with .json annotations",
                        default=Path("annotations/"))
    parser.add_argument("--outdir", "-o",
                        type=Path, help="Directory where csvs will be saved",
                        default=Path("data/csvs/raw"))

    args = parser.parse_args()
    import os
    print(os.listdir(args.pdfs))
    main(args)