class Solution:
    def minMoves(self, nums: List[int]) -> int:

        nums.sort()
        # This strategy only works if the array is sorted
        # Take the subarray from index 1 onwards, find its sum
        # Use that sum to subtract (the value at index 0 x the length of that subarray)
        total = sum(nums[1:])
        res = total - (nums[0] * len(nums[1:]))
        return res


import boto3
import botocore
import json
import os
from deepdiff import DeepDiff

from aws.utils import get_client, _backoff, _backoff2, key_val_search

class AbstractResource:

    def __init__(self, resource_file, resource_dir):

        # --> Directories
        self.resource_dir = resource_dir
        self.store_dir = os.path.join(self.resource_dir, 'store')

        # --> Resource File
        self.resource_file = os.path.join(self.store_dir, resource_file)
        self.resource = json.load(open(self.resource_file))

        # --> Remote file
        self.remote_dir = os.path.join(self.resource_dir, 'remote')
        self.remote_file = os.path.join(self.remote_dir, str('remote_' + resource_file))

    @property
    def prefix(self):
        from globals import prefix
        return prefix

    def save_local(self):
        with open(self.resource_file, 'w') as outfile:
            outfile.write(json.dumps(self.resource, indent=4, sort_keys=True, default=str))

    def save_remote(self):
        remote = self.resource_exists()
        if remote is None:
            remote = {}
        with open(self.remote_file, 'w') as outfile:
            outfile.write(json.dumps(remote, indent=4, sort_keys=True, default=str))

    def resource_dne(self):
        remote = self.resource_exists()
        if remote is None:
            return {}
        return None

    ################
    ### Override ###
    ################

    def load_dependencies(self):
        self.save_local()

    def resource_exists(self):
        return None

    def create_resource(self):
        return None

    def delete_resource(self):
        return None

    ###################
    ### COMPARATORS ###
    ###################
    # - Return 'False' when no remote update is needed

    def service_comparator(self, serv_1, serv_2):
        if serv_1['desiredCount'] == serv_2['desiredCount']:
            return False
        return True

    def task_definition_comparator(self, def_1, def_2):
        if 'environment' not in def_1['containerDefinitions'][0]:
            return False
        if 'environment' not in def_2['containerDefinitions'][0]:
            return False
        env1 = def_1['containerDefinitions'][0]['environment']
        env2 = def_2['containerDefinitions'][0]['environment']
        diff = DeepDiff(env1, env2)

        # --> No difference, return false
        if diff == {}:
            return False
        return True

    def listener_comparator(self, lis_1, lis_2):
        comparisons = []
        comparisons.append(
            (lis_1['Certificates'][0]['CertificateArn'] == lis_2['Certificates'][0]['CertificateArn'])
        )
        comparisons.append(
            (lis_1['DefaultActions'][0]['TargetGroupArn'] == lis_2['DefaultActions'][0]['TargetGroupArn'])
        )
        comparisons.append(
            (lis_1['LoadBalancerArn'] == lis_2['LoadBalancerArn'])
        )
        if False in comparisons:
            return True
        return False


import os
from pickletools import optimize

import mlflow
from mlflow.tracking import MlflowClient
import pytorch_lightning as pl
import torch
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torchvision import transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.optim.lr_scheduler import StepLR
from torchvision.datasets import MNIST
from torchmetrics.functional import accuracy

TRACKING_URI = "http://127.0.0.1:5000"

# mlflow serverのuriを指定
mlflow.set_tracking_uri(TRACKING_URI)

# boto3経由でminioにアクセスするための設定
os.environ["AWS_ACCESS_KEY_ID"] = "minio"
os.environ["AWS_SECRET_ACCESS_KEY"] = "password"
os.environ["MLFLOW_S3_ENDPOINT_URL"] = "http://127.0.0.1:9000"

# モデルを定義
class MNISTModel(pl.LightningModule):
    def __init__(self):
        super(MNISTModel, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.dropout1 = nn.Dropout(0.25)
        self.dropout2 = nn.Dropout(0.5)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x, train=False):
        x = self.conv1(x)# torch.Size([64, 32, 26, 26])
        x = F.relu(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout2(x)
        x = self.fc2(x)
        output = F.log_softmax(x, dim=1)
        return output

    def training_step(self, batch, batch_nb):
        x, y = batch
        logits = self(x)
        loss = F.cross_entropy(logits, y)
        pred = logits.argmax(dim=1)
        acc = accuracy(pred, y)

        # Use the current of PyTorch logger
        self.log("train_loss", loss, on_epoch=True)
        self.log("acc", acc, on_epoch=True)
        return loss

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=0.02)

    def configure_optimizers(self):
        optimizer = torch.optim.Adam(self.parameters(), lr=1e-3)
        return optimizer

def print_auto_logged_info(r):
    tags = {k: v for k, v in r.data.tags.items() if not k.startswith("mlflow.")}
    artifacts = [f.path for f in MlflowClient().list_artifacts(r.info.run_id, "model")]
    print("run_id: {}".format(r.info.run_id))
    print("artifacts: {}".format(artifacts))
    print("params: {}".format(r.data.params))
    print("metrics: {}".format(r.data.metrics))
    print("tags: {}".format(tags))

# Initialize our model
mnist_model = MNISTModel()

# Initialize DataLoader from MNIST Dataset
train_ds = MNIST(os.getcwd(), train=True,
    download=True, transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ]))
train_loader = DataLoader(train_ds, batch_size=1024)

# Initialize a trainer
trainer = pl.Trainer(max_epochs=10, progress_bar_refresh_rate=20, gpus=1)

# Auto log all MLflow entities
mlflow.pytorch.autolog()

# Train the model
with mlflow.start_run(experiment_id=1) as run:
    trainer.fit(mnist_model, train_loader)

# fetch the auto logged parameters and metrics
print_auto_logged_info(mlflow.get_run(run_id=run.info.run_id))


import os
from collections import Counter
import time
import argparse
import torch
from my_io import Vocabulary

def read_src_trg_files(opt, tag="train"):
    '''
    Read data according to the tag (train/valid/test), return a list of (src, trg, img) pairs
    '''
    if tag == "train":
        src_file = opt.train_src
    elif tag == "valid":
        src_file = opt.valid_src
    else:
        src_file = opt.test_src

    tokenized_src = []
    tokenized_trg = []
    img_fns = []

    src_lines = open(src_file, 'r').readlines()

    for line in src_lines:
        # process src and trg line
        src_words = line.strip().split('<sep>')[0].split()
        trg_words = line.strip().split('<sep>')[1].strip().split(';')  # a list of target sequences
        img_fn = line.strip().split('<sep>')[-1].strip()

        # Append the lines to the data
        tokenized_src.append(src_words)
        tokenized_trg.append(trg_words)
        img_fns.append(img_fn)

    tokenized_pairs = list(zip(tokenized_src, tokenized_trg, img_fns))
    print("Finish reading %d lines of data from %s" % (len(tokenized_src), src_file))
    return tokenized_pairs

def build_vocab(src_trg_pairs, vocab_size=40000):
    token_cnt = Counter()
    trg_class_cnt = Counter()
    for src_word_list, trg_word_lists, _ in src_trg_pairs:
        token_cnt.update(src_word_list)
        for word_list in trg_word_lists:
            token_cnt.update(word_list.split())
        trg_class_cnt.update(trg_word_lists)

    original_vocab_size = len(token_cnt)
    sorted_word2idx = sorted(token_cnt.items(), key=lambda x: x[1], reverse=True)
    trg_class_sorted_word2idx = sorted(trg_class_cnt.items(), key=lambda x: x[1], reverse=True)

    vocab = Vocabulary()
    trg_class_vocab = Vocabulary()

    special_tokens = ['<pad>', '<bos>', '<eos>', '<unk>', '<sep>']
    for word in special_tokens:
        vocab.add_word(word)

    for idx, (word, cnt) in enumerate(sorted_word2idx):
        vocab.add_word(word)
        if idx == vocab_size:
            break
    print('Filtered vocab size: %d / %d' % (vocab_size, original_vocab_size))

    trg_class_vocab.add_word('<unk>')
    for trg_class, cnt in trg_class_sorted_word2idx:
        trg_class_vocab.add_word(trg_class)

    print('Building target class vocab: %d' % len(trg_class_vocab))

    return vocab, trg_class_vocab

def extend_vocab_OOV(source_words, word2idx, vocab_size, max_unk_words=15):
    """
    Map source words to their ids, including OOV words. Also return a list of OOVs in the article.
    WARNING: if the number of oovs in the source text is more than max_unk_words, ignore and replace them as <unk>
    Args:
        source_words: list of words (strings)
        word2idx: vocab word2idx
        vocab_size: the maximum acceptable index of word in vocab
    Returns:
        ids: A list of word ids (integers); OOVs are represented by their temporary article OOV number. If the vocabulary size is 50k and the article has 3 OOVs, then these temporary OOV numbers will be 50000, 50001, 50002.
        oovs: A list of the OOV words in the article (strings), in the order corresponding to their temporary article OOV numbers.
    """
    src_oov = []
    oov_dict = {}
    for w in source_words:
        if w in word2idx and word2idx[w] < vocab_size:  # a OOV can be either outside the vocab or id>=vocab_size
            src_oov.append(word2idx[w])
        else:
            if len(oov_dict) < max_unk_words:
                # e.g. 50000 for the first article OOV, 50001 for the second...
                word_id = oov_dict.get(w, len(oov_dict) + vocab_size)
                oov_dict[w] = word_id
                src_oov.append(word_id)
            else:
                # exceeds the maximum number of acceptable oov words, replace it with <unk>
                word_id = word2idx['<unk>']
                src_oov.append(word_id)

    oov_list = [w for w, w_id in sorted(oov_dict.items(), key=lambda x: x[1])]
    return src_oov, oov_dict, oov_list

def build_dataset(src_trg_pairs, vocab, trg_class_vocab, is_test):
    examples = []
    for idx, (source, target, img) in enumerate(src_trg_pairs):
        src = [vocab(w) for w in source]
        trgs = [[vocab(w) for w in trg.split()] for trg in target]
        trgs_class = [trg_class_vocab(trg) for trg in target]
        src_oov, oov_dict, oov_list = extend_vocab_OOV(source, vocab.word2idx, len(vocab))
        trg_copies = [[oov_dict[w] if w in oov_dict else vocab(w) for w in trg.split()] for trg in target]

        if is_test:
            examples.append({'src': src, 'trg': trgs[0], 'trg_class': trgs_class[0], 'img': img, 'src_oov': src_oov,
                             'oov_list': oov_list,
                             'trg_copy': trg_copies[0], 'src_str': source, 'trg_str': target})
        else:
            examples.extend(
                [{'src': src, 'trg': trg, 'trg_class': trg_class, 'img': img, 'src_oov': src_oov, 'oov_list': oov_list,
                  'trg_copy': trg_copy, 'src_str': source, 'trg_str': target}
                 for trg, trg_class, trg_copy in zip(trgs, trgs_class, trg_copies)])
    return examples

def main(opt):
    t0 = time.time()
    # Tokenize training data, return a list of tuple
    tokenized_train_pairs = read_src_trg_files(opt, "train")

    # Build vocabulary from training src and trg
    print("Building vocabulary from training data")
    vocab, trg_class_vocab = build_vocab(tokenized_train_pairs, opt.vocab_size)
    torch.save([vocab, trg_class_vocab], open(opt.res_data_dir + '/vocab.pt', 'wb'))
    print("Src+trg word vocab_size: %d, trg class vocab_size: %d" % (len(vocab), len(trg_class_vocab)))

    train_data = build_dataset(tokenized_train_pairs, vocab, trg_class_vocab, is_test=False)
    torch.save(train_data, open(opt.res_data_dir + '/train.pt', 'wb'))

    tokenized_valid_pairs = read_src_trg_files(opt, "valid")
    valid_data = build_dataset(tokenized_valid_pairs, vocab, trg_class_vocab, is_test=False)
    torch.save(valid_data, open(opt.res_data_dir + '/valid.pt', 'wb'))

    tokenized_test_pairs = read_src_trg_files(opt, "test")
    test_data = build_dataset(tokenized_test_pairs, vocab, trg_class_vocab, is_test=True)
    torch.save(test_data, open(opt.res_data_dir + '/test.pt', 'wb'))

    print('#pairs of train data  = %d' % len(train_data))
    print('#pairs of valid data  = %d' % len(valid_data))
    print('#pairs of test data  = %d' % len(test_data))

    print('\nFinish and take %.2f seconds' % (time.time() - t0))

    emb_cmd = 'python prepare_tw_glove_emb.py -data_tag %s' % opt.data_tag
    print('\nRunning command: %s' % emb_cmd)
    os.system(emb_cmd)

    return

if __name__ == "__main__":
    data_tag = 'tw_mm_s1_tiny'
    parser = argparse.ArgumentParser(description='preprocess.py',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-data_dir', type=str, default='../data/{}'.format(data_tag), help='The source dir of the data')
    parser.add_argument('-vocab_size', type=int, default=45000, help='The source dir of the data')

    opt = parser.parse_args()
    opt.data_tag = data_tag
    opt.train_src = opt.data_dir + '/train_src.txt'
    opt.valid_src = opt.data_dir + '/valid_src.txt'
    opt.test_src = opt.data_dir + '/test_src.txt'

    processed_data_dir = '../processed_data'
    if not os.path.exists(processed_data_dir):
        os.makedirs(processed_data_dir)

    opt.res_data_dir = "{}/{}".format(processed_data_dir, data_tag)
    if not os.path.exists(opt.res_data_dir):
        os.mkdir(opt.res_data_dir)

    main(opt)

# Finish reading 42959 lines of data from ../data/tw_mm_s1/train_src.txt
# Building vocabulary from training data
# Filtered vocab size: 45000 / 48019
# Building target class vocab: 4262
# Src+trg word vocab_size: 45006, trg class vocab_size: 4262
# Finish reading 5370 lines of data from ../data/tw_mm_s1/valid_src.txt
# Finish reading 5372 lines of data from ../data/tw_mm_s1/test_src.txt
# #pairs of train data  = 57126
# #pairs of valid data  = 7185
# #pairs of test data  = 5372
#
# Finish and take 7.42 seconds


# Splitting array

# Required modules
import numpy as np
import os
from sklearn.model_selection import train_test_split

def split_data(path, percentage_training, percentage_testing):

	for arr_name in os.listdir(path):

		# Creating the path to array
		arr_path = os.path.join(path, arr_name)

		# Load the array
		arr = np.load(arr_path, allow_pickle = True)

		# If array not empty
		if arr.any():

			# Confirmation
			print(arr_name)

			# List to store all training/validation
			Training = []

			# List to store all Testing
			Testing = []

			# Looping through all bins to
			# take % of each
			Training, Testing = loop_bins(arr, Training, Testing, percentage_training, percentage_testing)

			# Name for dir
			name = arr_name.split('.npy')
			name = name[0]

			# Checking if dir exists for saving
			if os.path.isdir('/projects/mdhali/BscProjects/Stephan/Model_data/' + name):

				# Saving the training and testing sets
				np.save('/projects/mdhali/BscProjects/Stephan/Model_data/' + name + '/Training_validating/Training-Validation.npy', Training, allow_pickle = True)
				np.save('/projects/mdhali/BscProjects/Stephan/Model_data/' + name + '/Testing/Testing.npy', Testing, allow_pickle = True)

			# Create the dirs and save
			else:

				os.makedirs('/projects/mdhali/BscProjects/Stephan/Model_data/' + name + '/Training_validating/')
				np.save('/projects/mdhali/BscProjects/Stephan/Model_data/' + name + '/Training_validating/Training-Validation.npy', Training, allow_pickle = True)

				os.makedirs('/projects/mdhali/BscProjects/Stephan/Model_data/' + name + '/Testing/')
				np.save('/projects/mdhali/BscProjects/Stephan/Model_data/' + name + '/Testing/Testing.npy', Testing, allow_pickle = True)

def loop_bins(arr, Training, Testing, percentage_training, percentage_testing):

	# Loop in order of bins
	# Getting the array belonging to a bin
	# bin_arr_i[fi[],fn[]]
	for bin_arr in  arr:

		# Confirmation
		print("Bin: ", len(bin_arr[0]))

		# If there are more than 1 fragments in bin
		if len(bin_arr) > 1:

			# Splitting the fragments in the bin array into
			# percentage trainig/val and testing
			tmp_training, tmp_testing = train_test_split(bin_arr, train_size =  percentage_training, test_size = percentage_testing, random_state = 24)

			# Test
			print("Len bin: ", len(bin_arr))
			print("Len x is ", len(tmp_training))
			print("Len y is ", len(tmp_testing))

			# Appending to training and testing sets in a list to preserve bin structure
			Training.append(tmp_training)
			Testing.append(tmp_testing)

	return Training, Testing


from setuptools import setup, find_packages

VERSION = '0.0.1'
DESCRIPTION = 'LazyBugged'
LONG_DESCRIPTION = 'Python package'

setup(
        name="lazybugged",
        version=VERSION,
        author="Max Base and Tarunav.BA",
        author_email="tarunavba@gmail.com",
        description=DESCRIPTION,
        long_description=LONG_DESCRIPTION,
        packages=find_packages(),
        install_requires=[],
        keywords=['python', 'first package'],
        classifiers= [
            "Development Status :: 3 - Alpha",
            "Intended Audience :: Education",
            "Programming Language :: Python :: 2",
            "Programming Language :: Python :: 3",
            "Operating System :: MacOS :: MacOS X",
            "Operating System :: Microsoft :: Windows",
        ]
)


from rest_framework.response import Response
from .models import Rating, User
from django.http import JsonResponse
from django.forms.models import model_to_dict
import json

from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def rate(request):
    try:
        data = json.loads(request.body)

        user_id = data.get('user_id')
        game_id = data.get('game_id', None)
        player_id = data.get('player_id', None)
        team_id = data.get('team_id', None)
        rating_value = data.get('rating')
        comment = data.get('comment', None)

        if sum([bool(game_id), bool(player_id), bool(team_id)]) != 1:
            return JsonResponse({"error": "You must rate a game, player or team, but not more than 1 at the time."}, status=400)

        if not (0 <= int(rating_value) <= 10):
            return JsonResponse({"error": "Rating must be between 0 and 10."}, status=400)

        user = User.objects.get(pk=user_id)
        rating= Rating.objects.filter(user=user, game_id=game_id, player_id=player_id, team_id=team_id).first()
        if rating:
            rating.rating = rating_value
            if comment:
                rating.comment = comment
        else:

            rating = Rating.objects.create(user=user, game_id=game_id, player_id=player_id, team_id=team_id, rating=rating_value, comment=comment)
        # rating = Rating.objects.update_or_create(user=user, game_id=game_id, player_id=player_id, team_id=team_id, rating=rating_value)[0]
        rating.save()

        return JsonResponse({"message": "Rating successfully created."}, status=201)
    except Exception as e:
         return JsonResponse({"error": e}, status=400)

def get_ratings(request):
    try:
        target_type = request.GET.get('type')
        target_id = request.GET.get('id')

        if target_type not in ['player', 'game', 'team']:
            return JsonResponse([])
        ratings = Rating.objects.filter(**{f'{target_type}_id': target_id})
        if len(ratings.all())>0:
            ratings_list = [model_to_dict(rating) for rating in ratings]
            total = sum(rating['rating'] for rating in ratings_list) /len(ratings_list)
            return JsonResponse({'total':total}, safe=False)
        else:
              return JsonResponse({"error": 'No  ratings for this id'}, status=404)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)
@csrf_exempt
def get_user_rating(request):
    try:
        target_type = request.GET.get('type')
        target_id = request.GET.get('id')
        user_id = request.GET.get('user_id')
        if target_type not in ['player', 'game', 'team']:
            return JsonResponse({'error': 'no type provided'}, status=400)
        else:
            rate_type = switch_target(target_type)
            kwargs = {
                '{}'.format(rate_type): target_id,
                'user_id': user_id
            }
            rating = Rating.objects.filter(**kwargs).first()

            if rating:
                return JsonResponse({'id':rating.id,'rating': rating.rating, 'comment':rating.comment})
            else:
                return JsonResponse({'error': 'No rating found'},status=404)

    except Exception as e:
        return JsonResponse({"error":  str(e)}, status=400)

def switch_target(target):
    return {'player': 'player_id',
            'game':'game_id',
            'team':'team_id'}.get(target, None)


import pathlib
import re
from typing import List

def read_inputs(input_file: str) -> List[List[str]]:
    with pathlib.Path(input_file).open() as fp:
        lines = fp.readlines()

    regex = re.compile(r"([a-g]+)")

    return [
        ["".join(sorted(string)) for string in regex.findall(line)] for line in lines
    ]

def part1() -> int:
    # 19 minutes
    result = 0

    lines = read_inputs("input.txt")
    for line in lines:
        test_signals, answers = line[:10], line[10:]

        mapping = {}

        for test_signal in test_signals:
            if len(test_signal) == 7:
                mapping[test_signal] = 8
            elif len(test_signal) == 2:
                mapping[test_signal] = 1
            elif len(test_signal) == 4:
                mapping[test_signal] = 4
            elif len(test_signal) == 3:
                mapping[test_signal] = 7

        result += sum(1 for answer in answers if answer in mapping)

    return result

def part2() -> int:
    # 57 minutes
    """
    Lets think of the signals as indexes...

    0000
    1  2
    1  2
    3333
    4  5
    4  5
    6666

    From this, we know the following information:
    (*) Denotes we can deduce this without question, from Part 1 in some way

    (*) For Value 1: 2,5            2
    For Value 2: 0,2,3,4,6          5
    For Value 3: 0,2,3,5,6          5
    (*) For Value 4: 1,2,3,5        4
    For Value 5: 0,1,3,5,6          5
    For Value 6: 0,1,3,4,5,6        6
    (*) For Value 7: 0,2,5          3
    (*) For Value 8: 0,1,2,3,4,5,6  7
    For Value 9: 0,1,2,3,5,6        6
    For Value 0: 0,1,2,4,5,6        6
    """

    g_total = 0

    lines = read_inputs("input.txt")
    for line in lines:
        test_signals, answers = line[:10], line[10:]

        """
        We will reduce down the mapping as we eliminate invalid entries
        until we have only one entry per slot
        """
        mapping = {i: {"a", "b", "c", "d", "e", "f", "g"} for i in range(0, 6 + 1)}

        """
        If we engage with the signals in size order, we can deduce components
        as we iterate through
        """
        test_signals = sorted(test_signals, key=len)

        """
        Number 1 - Two Digits
        We know that Index 2 and 5 can only be one of two values
        """
        mapping[2] = set(sorted(test_signals[0]))
        mapping[5] = mapping[2]

        """
        Number 7 - Three Digits
        We know that Seven marks off Indexes 0, 2, and 5. Index 2 and 5 were established above
        This means we definitively know what Index 0 is
        """
        mapping[0] = set(sorted(test_signals[1])) - mapping[2]

        """
        Number 4 - Four Digits
        Since we know Indexes 2 and 5 were 50/50 set above, we have two possible solutions
        for Indexes 1 and 3 now.
        """
        mapping[1] = set(sorted(test_signals[2])) - mapping[2]
        mapping[3] = mapping[1]

        """
        Number 2, 3, or 5 - Five Digits
        These numbers all share their top, middle, and bottom segments, so we can deduce a bit
        We know the top (Index 0) from 7 earlier, so we can now 50/50
        the middle (Index 3) and bottom (Index 6) values
        """
        top_middle_bottom = (
            set(sorted(test_signals[3]))
            .intersection(set(sorted(test_signals[4])))
            .intersection(set(sorted(test_signals[5])))
        )
        mapping[3] = top_middle_bottom - mapping[0]
        mapping[6] = mapping[3]

        """
        Number 6, 9, or 0 - Six Digits
        We can determine the middle (Index 3) from the 0 case, as it will be the only
        number in 6 and 9 that don't have all top, middle, and bottom set and has only
        the middle value (Index 3) turned off.
        Since we now know the top and middle, we also implicitly know the bottom (Index 6) value.
        """
        for i in range(6, 8 + 1):
            if (
                top_middle_bottom.intersection(set(sorted(test_signals[i])))
                != top_middle_bottom
            ):
                mapping[3] = {"a", "b", "c", "d", "e", "f", "g"} - set(
                    sorted(test_signals[i])
                )
                mapping[6] -= mapping[3]
                break

        """
        Going back a bit, from the 4 case, we now know what Index 1 is, since we definitively
        know the right segments and the mid point.
        """
        mapping[1] -= mapping[3] - mapping[2] - mapping[5]

        """
        Since we know what Indices 0, 1, 3, and 6 definitively are,
        and what Indices 2 and 5 are (50/50), we know what Index 4 is by deduction.
        """
        mapping[4] = (
            mapping[4] - mapping[0] - mapping[1] - mapping[3] - mapping[6] - mapping[2]
        )

        """
        Finally, we can determine what Indices 2 and 5 are by reading the number 5 from the input.
        The number 5 will be the only number in 2, 3, and 5 that doesn't have Index 1 set.
        Once we have the 5 case, we can finish our decoding by establishing what Indices 2 and 5
        definitively are.
        """
        # We can determine 2/5 positioning from the "five" signal, as it will have 1 and 5
        for i in range(3, 5 + 1):
            if list(mapping[1])[0] in set(sorted(test_signals[i])):
                mapping[5] = (
                    set(sorted(test_signals[i]))
                    - mapping[0]
                    - mapping[3]
                    - mapping[6]
                    - mapping[1]
                )
                mapping[2] -= mapping[5]
                break

        """
        Now we need to construct a mapping between signals and the final results
        """
        top = list(mapping[0])[0]
        top_left = list(mapping[1])[0]
        top_right = list(mapping[2])[0]
        mid = list(mapping[3])[0]
        bottom_left = list(mapping[4])[0]
        bottom_right = list(mapping[5])[0]
        bottom = list(mapping[6])[0]

        decoded_operations = {
            tuple(sorted(top_right + bottom_right)): "1",
            tuple(sorted(top + top_right + mid + bottom_left + bottom)): "2",
            tuple(sorted(top + top_right + mid + bottom_right + bottom)): "3",
            tuple(sorted(top_left + top_right + mid + bottom_right)): "4",
            tuple(sorted(top + top_left + mid + bottom_right + bottom)): "5",
            tuple(
                sorted(top + top_left + mid + bottom_left + bottom_right + bottom)
            ): "6",
            tuple(sorted(top + top_right + bottom_right)): "7",
            tuple(
                sorted(
                    top
                    + top_left
                    + top_right
                    + mid
                    + bottom_left
                    + bottom_right
                    + bottom
                )
            ): "8",
            tuple(
                sorted(top + top_left + top_right + mid + bottom_right + bottom)
            ): "9",
            tuple(
                sorted(top + top_left + bottom_left + bottom + bottom_right + top_right)
            ): "0",
        }

        """
        The row has been fully decoded, lets get what 4 digit number we add to running total
        """
        answer_value_string = ""
        for digit in line[10:]:
            answer_value_string += decoded_operations[tuple(sorted(digit))]

        g_total += int(answer_value_string)

    return g_total

def main() -> None:
    print(part1())
    print(part2())

if __name__ == "__main__":
    main()


import datetime as d
print("-------------------------------------------------------",end = '\n')
print("-------------------------------------------------------",end = '\n')
print("             HEALTH MANAGEMENT SYSTEM                ")
print("-------------------------------------------------------",end = '\n')
print("-----------------------------------------------------1--",end = '\n')
print("  What would you like to lock diet or retrieve diet  ")
print("  Press 1 for Locking diet  \n  Press 2 for retrieving diet  ")
s = int(input())
if(s == 1):
    print("  Enter Name To Whom Diet would You Like To Lock: ")
else:
    print("  Enter Name To Whom Diet would You Like To Retrieve: ")
print("   1  Rohan \n   2  Harry \n   3  Mohan ")

Name = input()

def Date():
    x = d.datetime.now()
    return x

def Set():
    if(Name == 'Rohan'):
        print("Please lock the diet of Rohan")
        with open('Rohan.txt','w+') as f:
            r = input()
            f.write(r)
        print("Diet of Rohan is locked in file ")
    elif(Name == 'Mohan'):
        print("Please lock the diet of Mohan")
        with open('Mohan.txt','w+') as f:
            m = input()
            f.write(m)
        print("Diet of Rohan is locked in file ")
    elif(Name == 'Harry'):
        print("Please lock the diet of Harry")
        with open('Harry.txt','w+') as f:
            h = input()
            f.write(h)
        print("Diet of Rohan is locked in file ")
def Get():
    if (Name == 'Rohan'):
        with open('Rohan.txt', 'r+') as f:
            print([Date()],":",f.read())
    elif (Name == 'Mohan'):
        with open('Mohan.txt', 'r+') as f:
            print([Date()],":",f.read())
    elif (Name == 'Harry'):
        with open('Harry.txt', 'r+') as f:
            print([Date()],":",f.read())

if(s == 1):
    Set()

else:
    Get()


#mendapatkan daftar direktori dengan pathlib.Path.iterdir()

from pathlib import Path
import traceback as tb

folder = r"E:\TESTING"

try:
    dirPath = Path(folder)
    listDir = dirPath.iterdir()
except BaseException as be:
    be = tb.format_exc()
    print("Error: ", be)
else:
    print("Listdir: ", listDir)
    print("Entri: ")
    for entri in listDir:
        print("-> ", entri)


from main import get_temperature
from unittest import mock
from unittest.mock import patch
import pytest

'''
Valores a serem utilizados no teste off-line

lat = -14.235004
lng = -51.92528
temperature = 62
expected = 16

'''

'''
Função que define a chave que queremos acessar no JSON da Requisição
e atribui um valor especifico como temperatura.
'''

def temperature_json(temp):
    temperature = {"currently": {"temperature": temp}}
    return temperature

'''
Função para manipular e simular valor padrao como resposta da API
utilizando a biblioteca unittest.mock

Recebe como parametro:
    -Latitude
    -Longitude
    -Temperatura
'''

# definindo o tipo da requisição(get) e o local
@mock.patch('main.requests.get')  # Iniciando o patch para ciração do mock
@pytest.mark.parametrize(
    "lat, lng, temp, expected",
    [(-91, -51.92528, 62, 16)]
)
def test_get_temperature_by_lat_lng(mock_requests, lat, lng, temp, expected):

    # verificando se a temperatura é valida
    if (type(temp) == int) or type(temp) == float:

        # Chamando a função que define os dados do JSON
        expected_result = temperature_json(temp)
    # Inserindo o valor desejado no Json da requisição
        mock_requests.return_value.json.return_value = expected_result

        result = get_temperature(lat, lng)
    # result = mock(lat, lng, temp)

    # Verificando  se o resultado obtido na requisição off-line
    # é igual ao esperado
        assert expected == result

    # caso a temperatura seja invalida
    else:
        assert ("Temp", temp) == "Invalid temperatures"

'''
Função para testar valores fora do range da latitude e longitude
'''

@pytest.mark.parametrize(
    "lat, lng",
    [(-104, -20), (91, -95), (-200, 300)]
)
def test_range_lat_lng_out(lat, lng):
    expected = {'code': 400, 'error': 'The given location is invalid.'}

    response = get_temperature(lat, lng)

    assert response == expected

'''
Função para testar valores que nao são validos
'''

@pytest.mark.parametrize(
    "lat, lng",
    [('Test', 60), (False, -95), (None, None)]
)
def test_range_lat_lng_type(lat, lng):
    expected = {'code': 400, 'error': 'Poorly formatted request'}

    response = get_temperature(lat, lng)
    print(response)
    assert response == expected


from nltk.corpus import movie_reviews
from pymongo import MongoClient

client = MongoClient()
db = client.twitter

all_tweets = db.all
maga_tweets = db.maga

def movie_words(sentiment):
    sentiment_words = movie_reviews.fileids(sentiment)
    words = {}
    for file_id in sentiment_words:
        for word in movie_reviews.words(file_id):
            words[word.lower()] = True

    return words

neg_dict = movie_words('neg')
pos_dict = movie_words('pos')

def evaluate(tweet):

    words = tweet.split()

    negfeats = []
    posfeats = []
    neutfeats = []
    counter = 0
    for word in words:
        pre = counter
        if word in neg_dict:
            negfeats.append(word.lower())
            counter += 1

        if word in pos_dict:
            posfeats.append(word.lower())
            counter += 1

        # Some words may not be contained in the Movie Reviews corpus.
        # These words must be stored some way for later analysis.
        # The length must also be subtracted from the processed words
        # in order to get an accurate percentage of negative vs positive
        if counter == pre:
            neutfeats.append(word)

    # determined the number of words processed
    processed_count = len(words) - len(neutfeats)

    percent_neg = 0
    percent_pos = 0

    # determine the % negative
    if len(negfeats) > 0:
        percent_neg = len(negfeats) / processed_count

    # determine the % positive
    if len(posfeats) > 0:
        percent_pos = len(posfeats) / processed_count

    if percent_neg > percent_pos:
        return('neg')
    elif percent_neg == percent_pos:
        return('neut')
    else:
        return('pos')

all_total_processed=0
all_total_neg=0
all_total_pos=0
all_total_neut=0

for tweet in all_tweets.find():
    if 'text' in tweet:
        sentiment = evaluate(tweet['text'])
        all_total_processed += 1
        if 'neg' == sentiment:
            all_total_neg += 1
        elif 'pos' == sentiment:
            all_total_pos += 1
        else:
            all_total_neut += 1

print("[ALL] total processed: " + str(all_total_processed))
print("[ALL] total neg: " + str(all_total_neg))
print("[ALL] total pos: " + str(all_total_pos))
print("[ALL] total neut: " + str(all_total_neut))

maga_total_processed=0
maga_total_neg=0
maga_total_pos=0
maga_total_neut=0

for tweet in maga_tweets.find():
    if 'text' in tweet:
        sentiment = evaluate(tweet['text'])
        maga_total_processed += 1
        if 'neg' == sentiment:
            maga_total_neg += 1
        elif 'pos' == sentiment:
            maga_total_pos += 1
        else:
            maga_total_neut += 1

print("[MAGA] total processed: " + str(maga_total_processed))
print("[MAGA] total neg: " + str(maga_total_neg))
print("[MAGA] total pos: " + str(maga_total_pos))
print("[MAGA] total neut: " + str(maga_total_neut))


from wifi import detect_networks, attack
from network_interface import monitor_mode, managed_mode

# 2.4Ghz are 1-13 and 5Ghz are > 36
CHANNELS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56, 60, 64]
WIFI_INTERFACE = "wlp4s0"

monitor_mode(WIFI_INTERFACE)
acces_points = detect_networks(WIFI_INTERFACE, CHANNELS)

for i, ap in enumerate(acces_points):
    print(f"{i}: {ap}")

option = input("Select which network to deauthenticate and hit return: ")
acces_point = acces_points[int(option)]
answer = input(f"Are you sure you want to deauthenticate {acces_point.ssid}? Y/n ")
if not answer == "Y":
    exit(0)

attack(WIFI_INTERFACE, answer)
managed_mode(WIFI_INTERFACE)


from __future__ import print_function

import ast

__all__ = ['AstNodeWrapper', 'DygraphToStaticAst', 'StaticAnalysisVisitor']

class NodeVarType(object):
    """
    Enum class of python variable types. We have to know some variable types
    during compile time to transfer AST. For example, a string variable and a
    tensor variable in if clause may lead to different conversion from dygraph
    to static graph.
    """
    UNKNOWN = 0  # Reserve for AST nodes have not known the type
    STATEMENT = 1  # For nodes representing statement (non-variable type)
    PADDLE_DYGRAPH_API = 2
    PADDLE_CONTROL_IF = 3
    PADDLE_CONTROL_WHILE = 4
    PADDLE_CONTROL_FOR = 5

    NONE = 100
    INT = 101
    FLOAT = 102
    STRING = 103
    TENSOR = 104

class AstNodeWrapper(object):
    """
    Wrapper for python ast.node. We need a node wrapper because ast.node
    doesn't store all required information when we are transforming AST.
    We should collect additional information which the actual transformation
    needs.
    """

    def __init__(self, node):
        self.node = node
        self.parent = None
        self.children = []
        self.node_var_type = NodeVarType.UNKNOWN

class StaticAnalysisVisitor(object):
    """
    A class that does static analysis
    """

    def __init__(self, ast_root=None):
        if ast_root is not None:
            self.run(ast_root)

    def run(self, ast_root):
        self.node_wrapper_root = None
        self.ancestor_wrappers = []
        self.node_to_wrapper_map = {}
        self.dfs_visit(ast_root)

    def dfs_visit(self, node):
        # AST reuses some ast.nodes, such as Param node of expr_context
        if node not in self.node_to_wrapper_map:
            cur_wrapper = AstNodeWrapper(node)
            self.node_to_wrapper_map[node] = cur_wrapper
        else:
            cur_wrapper = self.node_to_wrapper_map[node]

        if self.node_wrapper_root is None:
            self.node_wrapper_root = cur_wrapper

        if len(self.ancestor_wrappers) != 0:
            last_wrapper = self.ancestor_wrappers[-1]
            last_wrapper.children.append(cur_wrapper)
            cur_wrapper.parent = last_wrapper

        self.ancestor_wrappers.append(cur_wrapper)
        for child in ast.iter_child_nodes(node):
            self.dfs_visit(child)
        self.ancestor_wrappers.pop()
        return cur_wrapper.node_var_type

    def get_node_wrapper_root(self):
        return self.node_wrapper_root

    def get_node_to_wrapper_map(self):
        return self.node_to_wrapper_map

class DygraphToStaticAst(ast.NodeTransformer):
    """
    Main class to transform Dygraph to Static Graph
    """

    def get_static_ast(self, root):
        # save root for some analysis may need global AST
        self.root = root
        self.static_analysis_root = StaticAnalysisVisitor(
            root).get_node_wrapper_root()
        self.transfer_from_node_type(self.static_analysis_root)
        return self.static_analysis_root

    def transfer_from_node_type(self, node):
        print("Not implemented")


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Nov 26 13:41:17 2019

@author: huyuemei
"""

import numpy as np
import pandas as pd
from scipy.stats import norm
import matplotlib.pylab as plt
from matplotlib.ticker import PercentFormatter

def DynamicHedge(stepN,paths=50000,S0=100,sigma=0.2,r=0.05,T=1/12,K=100):
    def simulate_Brownian_Motion(paths, steps, T):
        deltaT = T/steps
        t = np.linspace(0, T, steps+1)
        X = np.c_[np.zeros((paths, 1)),
                  np.random.randn(paths, steps)]
        return t, np.cumsum(np.sqrt(deltaT) * X, axis=1)
    def BlackScholesCall(S, K, r, sigma, T):
        d1 = (np.log(S/K)+(r+sigma**2/2)*T) / (sigma*np.sqrt(T))
        d2 = d1 - sigma*np.sqrt(T)
        return S*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)

#    S0=100
#    sigma=0.2
#    r=0.05
#    T=1/12
#    K=100

    #simulate Brownian motion
    t, x = simulate_Brownian_Motion(paths, stepN, T)

    #generate stock price
    St=S0*np.exp((r-0.5*sigma**2)*t+sigma*x)
    error=0

    #calculate option premium received in t=0
    c0=BlackScholesCall(S0,K,r,sigma,T)

    #calculate option payoff at t=T
    option_payoff=np.maximum(St[:,-1]-K,0)

    #calculate dynamic hedging total P&L
    for i in range(0,stepN):
        d1 = (np.log(St[:,i]/K)+(r+sigma**2/2)*(T-t[i])) / (sigma*np.sqrt(T-t[i])) #from standing point to strike point
        d2 = d1 - sigma*np.sqrt(T-t[i]) #for i, num of d1:50000
        Phi_all=norm.cdf(d1) #50000
        PsiBt_all= -K*np.exp(-r*(T-t[i]))*norm.cdf(d2)
        port_v_t=Phi_all*St[:,i]+PsiBt_all
        port_v_T=Phi_all*St[:,i+1]+PsiBt_all*np.exp(r*(t[i+1]-t[i]))

        error+=port_v_T-port_v_t

    #total error
    total_error=error+c0-option_payoff

    return total_error,c0

hedge21 = DynamicHedge(21)[0]
hedge84 = DynamicHedge(84)[0]

#plot
fig1=plt.figure(figsize=(12,5))
fig1.add_subplot(111, frameon=False)
plt.tick_params(labelcolor='none', top=False, bottom=False,
                left=False, right=False)
plt.xlabel('measured volatility')

ax1=fig1.add_subplot(121)
ax1.hist(hedge21,weights=np.ones(len(hedge21))/len(hedge21),
         bins=np.linspace(-2,2,35))
ax1.yaxis.set_major_formatter(PercentFormatter(1))
#ax1.set_xlabel('measured volatility')
ax1.set_ylabel('Frequency (out of 100%)')
ax1.set_title('Dynamic Hedge error for N=21')
ax1.yaxis.grid(color='gray', linestyle='dashed')

ax2=fig1.add_subplot(122,sharey=ax1)
ax2.hist(hedge84,weights=np.ones(len(hedge84))/len(hedge84),
         bins=np.linspace(-2,2,35))
ax2.set_title('Dynamic Hedge error for N=84')
ax2.yaxis.grid(color='gray', linestyle='dashed')
plt.tight_layout()

plt.savefig('hedgeerror.png')
plt.show()

#statistics
mean=[]
std=[]
std_ratio=[]
c0=DynamicHedge(21)[1]
for i in [hedge21,hedge84]:
    mean.append(np.mean(i))
    std.append(np.std(i,ddof=1))
    std_ratio.append(np.std(i,ddof=1)/c0)

data=pd.DataFrame(index=['N=21','N=84'])
data['Mean P&L']=np.array(mean).round(3)
data['Standard Dev. of P&L']=np.array(std).round(2)
data['StDev of P&L as a % of option premium']=np.array(std_ratio).round(4)*100
print(data)

Stochastic Modelling in Finance/part4.py


import os
from dotenv import load_dotenv
import datetime
import pandas as pd
import numpy as np
import plotly.graph_objs as go
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import requests
import yfinance as yf
import dash_bootstrap_components as dbc
from dash.exceptions import PreventUpdate
from flask import Flask
from flask_cors import CORS

load_dotenv()

AA_API_KEY = os.environ.get('AA_API_KEY')
FRED_API_KEY = os.environ.get('FRED_API_KEY')

def fetch_sp500_data():
    sp500_ticker = '^GSPC'
    sp500 = yf.Ticker(sp500_ticker)
    df = sp500.history(period='max')
    df = df.reset_index()[['Date', 'Close']].set_index('Date')
    df.index = pd.to_datetime(df.index).tz_localize(None)

    df.columns = ['5. adjusted close']
    return df

class AlphaVantageLimitReachedException(Exception):
    pass

def fetch_stock_data(ticker):
    base_url = 'https://www.alphavantage.co/query?'
    function = 'TIME_SERIES_DAILY_ADJUSTED'
    datatype = 'json'

    url = f"{base_url}function={function}&symbol={ticker}&apikey={AA_API_KEY}&datatype={datatype}"
    try:
        response = requests.get(url)
        response.raise_for_status()
    except requests.exceptions.HTTPError as err:
        if '5 calls per minute' in response.text.lower():
            raise AlphaVantageLimitReachedException('API request limit reached. Please try again in a minute.')
        else:
            raise err

    data = response.json()

    if 'Error Message' in data or 'Note' in data:
        # notify user that api limit reached and then set a timer for 1 minute and send request again
        print(data[0])
        return None

    df = pd.DataFrame(data['Time Series (Daily)']).T
    df.index = pd.to_datetime(df.index).tz_localize(None)
    df = df.sort_index(ascending=True)
    df = df.astype(float)

    return df

def fetch_company_overview(ticker):
    base_url = 'https://www.alphavantage.co/query?'
    function = 'OVERVIEW'

    url = f"{base_url}function={function}&symbol={ticker}&apikey={AA_API_KEY}"
    try:
        response = requests.get(url)
        response.raise_for_status()
    except requests.exceptions.HTTPError as err:
        if '5 calls per minute' in response.text.lower():
            raise AlphaVantageLimitReachedException('API request limit reached. Please try again in a minute.')
        else:
            raise err
    data = response.json()

    return data

def fetch_earnings_data(ticker):
    base_url = 'https://www.alphavantage.co/query?'
    function = 'EARNINGS'

    url = f"{base_url}function={function}&symbol={ticker}&apikey={AA_API_KEY}"
    print(url)
    try:
        response = requests.get(url)
        response.raise_for_status()
    except requests.exceptions.HTTPError as err:
        if '5 calls per minute' in response.text.lower():
            raise AlphaVantageLimitReachedException('API request limit reached. Please try again in a minute.')
        else:
            raise err
    data = response.json()

    return data['quarterlyEarnings']

def fetch_risk_free_rate(fred_api_key, start_date, end_date):
    base_url = "https://api.stlouisfed.org/fred/series/observations"
    series_id = "GS10"  # 10-year US Treasury Yield
    file_type = "json"

    url = f"{base_url}?series_id={series_id}&api_key={fred_api_key}&file_type={file_type}&sort_order=asc&start_date={start_date}&end_date={end_date}"
    response = requests.get(url)
    data = response.json()

    df = pd.DataFrame(data['observations'])
    df['date'] = pd.to_datetime(df['date'])
    df['value'] = df['value'].astype(float)
    df.set_index('date', inplace=True)
    df = df[df['value'] != -1]

    return df

def calculate_moving_averages(df, window):
    return df['5. adjusted close'].rolling(window=window).mean()

def normalize_data(df, column):
    return (df[column] - df[column].min()) / (df[column].max() - df[column].min())
# Create a Flask app and wrap the Dash app with it
server = Flask(__name__)
CORS(server)
app = dash.Dash(__name__, server=server, external_stylesheets=[dbc.themes.BOOTSTRAP]) # type: ignore

# Update the app.layout to be a function
def serve_layout():
    return dbc.Container([
    dbc.Row([
        dbc.Col(html.H1('Stock Market Dashboard', className='text-center mb-4'), width=12),
        dbc.Col(html.Sub('API request limit: 5 per min - Refresh if hangs', className='text-center mb-4'), width=12)

    ]),
    dbc.Row([
        dbc.Col(
            dbc.InputGroup([
                dbc.Input(id='stock-ticker', type='text', value='MSFT', placeholder='Enter a stock ticker'),
                dbc.Button('Submit', id='submit-button', color='primary', className='ms-2')
            ], className='mb-4'),
            width=12
        )
    ]),
    dbc.Row([
        dbc.Col(
            dcc.Loading(
                id="stock-price-loading",
                type="circle",
                children=[dcc.Graph(id='stock-price-graph')],
            ),
            width=12,
        )
    ]),
    dbc.Row([
        dbc.Col(
            dcc.Loading(
                id="company-overview-loading",
                type="circle",
                children=[html.Div(id='company-overview')],
            ),
            width=12,
        )
    ]),
    # Add the following row to display the risk-free rate chart at the bottom
    dbc.Row([
        dbc.Col(
            dcc.Graph(id='risk-free-rate-graph', style={'height': '400px'})
        ),
    ]),
   ], fluid=True)

app.layout = serve_layout

@app.callback(
    [Output('stock-price-graph', 'figure'),
     Output('company-overview', 'children'),
     Output('risk-free-rate-graph', 'figure'),
     Output('submit-button', 'disabled')],
    [Input('submit-button', 'n_clicks')],
    [dash.dependencies.State('stock-ticker', 'value')],
    prevent_initial_call=True)

def update_graph_and_financials(n_clicks, stock_ticker):
    # Disable the submit button
    disabled = True

    if n_clicks == 0:
        return go.Figure(), None, go.Figure(), disabled

    try:
        # Fetch stock data
        df = fetch_stock_data(stock_ticker)
        # Handle the case where the API request limit is reached

        if df is None:
            return go.Figure(), None, go.Figure(), disabled
    except AlphaVantageLimitReachedException as e:
        return go.Figure(), html.P(str(e)), go.Figure(), disabled
    except Exception as e:
        # Handle other exceptions here
        return go.Figure(), None, go.Figure(), disabled

    # Fetch S&P 500 data
    sp500_df = fetch_sp500_data()

    # Calculate stock returns and S&P 500 returns
    df['returns'] = df['5. adjusted close'].pct_change()
    sp500_df['returns'] = sp500_df['5. adjusted close'].pct_change()

    # Calculate stock beta
    covariance = df['returns'].cov(sp500_df['returns'])
    sp500_variance = sp500_df['returns'].var()
    beta = covariance / sp500_variance # type: ignore
    # Get the company overview, earnings data, and risk-free rate
    company_overview = fetch_company_overview(stock_ticker)
    earnings_data = fetch_earnings_data(stock_ticker)
    start_date = '2019-11-01'
    end_date = df.index.max().strftime("%Y-%m-%d")
    risk_free_rate_df = fetch_risk_free_rate(
        FRED_API_KEY, '1980-01-01', '2023-02-28')

    # Calculate the moving averages and plot the stock
    df['SMA10'] = calculate_moving_averages(df, 10)
    df['SMA50'] = calculate_moving_averages(df, 50)

    # Calculate daily risk-free rate
    daily_risk_free_rate = (
        1 + risk_free_rate_df['value'].mean() / 100) ** (1 / 252) - 1

    # Calculate CAPM daily expected returns
    df['capm_expected_returns'] = daily_risk_free_rate + \
        beta * (sp500_df['returns'] - daily_risk_free_rate)

    trace_close = go.Scatter(
        x=df.loc[start_date:end_date].index,
        y=df.loc[start_date:end_date, '5. adjusted close'],
        mode='lines',
        name='Close',
        yaxis='y1'
    )
    trace_sma10 = go.Scatter(
        x=df.loc[start_date:end_date].index,
        y=df.loc[start_date:end_date, 'SMA10'],
        mode='lines',
        name='SMA10',
        yaxis='y1'
    )
    trace_sma50 = go.Scatter(
        x=df.loc[start_date:end_date].index,
        y=df.loc[start_date:end_date, 'SMA50'],
        mode='lines',
        name='SMA50',
        yaxis='y1'
    )

    # Find earnings dates
    earnings_dates = [pd.to_datetime(earning['fiscalDateEnding'])
                    for earning in earnings_data]
    earnings_dates = [date for date in earnings_dates if date in df.index]
    latest_earning_date = pd.to_datetime(earnings_data[0]['fiscalDateEnding'])

    earnings_trace = go.Scatter(
        x=[latest_earning_date, latest_earning_date],
        y=[df['5. adjusted close'].min(), df['5. adjusted close'].max()],
        mode='lines',
        name='Earnings',
        line=dict(color='red', width=2, dash='dash'),
        yaxis='y1'
    )

    trace_capm_expected_returns = go.Scatter(
        x=df.loc[start_date:end_date].index,
        y=df.loc[start_date:end_date, 'capm_expected_returns'],
        mode='lines',
        name='CAPM Expected Returns',
        yaxis='y2'
    )

    layout = go.Layout(
        title=f'{stock_ticker} Stock Price and CAPM Expected Returns',
        yaxis=dict(
            title='Price',
            side='left',
            showgrid=False,
            zeroline=False
        ),
        yaxis2=dict(
            title='CAPM Expected Returns',
            side='right',
            showgrid=False,
            zeroline=False,
            overlaying='y'
        ),
        legend=dict(
            x=0.01,
            y=0.99,
            bgcolor='rgba(255, 255, 255, 0.5)',
            bordercolor='rgba(0, 0, 0, 0.5)'
        ),
        hovermode="x unified",
        shapes=[
                # Vertical line for each earnings date
                dict(
                    type='line',
                    x0=date,
                    x1=date,
                    y0=0,
                    y1=1,
                    xref='x',
                    yref='paper',
                    line=dict(
                        color='red',
                        width=1,
                        dash='dot'
                    )
                ) for date in earnings_dates
            ]
        )

    # Display company overview
    company_overview_elements = [
        html.H3('Company Overview:'),
        dbc.Row([
            dbc.Col(html.P(f"Name: {company_overview['Name']}"), width=6),
            dbc.Col(html.P(f"Industry: {company_overview['Industry']}"), width=6),
        ]),
        dbc.Row([
            dbc.Col(html.P(f"Sector: {company_overview['Sector']}"), width=6),
            dbc.Col(html.P(f"Latest Financial Report Date: {company_overview['LatestQuarter']}"), width=6),
        ]),
        dbc.Row([
            dbc.Col(html.P(f"Revenue (TTM): ${float(company_overview['RevenueTTM']):,.0f}"), width=6),
            dbc.Col(html.P(f"Gross Profit (TTM): ${float(company_overview['GrossProfitTTM']):,.0f}"), width=6),
        ]),
        dbc.Row([
            dbc.Col(html.P(f"Market Capitalization: ${float(company_overview['MarketCapitalization']):,.0f}"), width=6),
            dbc.Col(html.P(f"EBITDA (TTM): ${float(company_overview['EBITDA']):,.0f}"), width=6),
        ]),
        dbc.Row([
            dbc.Col(html.P(f"PE Ratio (TTM): {company_overview['PERatio']}"), width=6),
            dbc.Col(html.P(f"Dividend Yield: {company_overview['DividendYield']}"), width=6),
        ]),
        html.P(f"Description: {company_overview['Description']}"),
    ]

    # Plot risk-free rate
    risk_free_trace = go.Scatter(
        x=risk_free_rate_df.index,
        y=risk_free_rate_df['value'],
        mode='lines',
        name='Risk-Free Rate'
    )
    risk_free_layout = go.Layout(
        title='Risk-Free Rate (10-Year US Treasury Yield)',
        yaxis=dict(
            title='Yield'
        )
    )
    # Enable the submit button after processing
    disabled = False
    return (
        go.Figure(data=[trace_close, trace_sma10, trace_sma50,
                earnings_trace, trace_capm_expected_returns], layout=layout),
        company_overview_elements,
        go.Figure(data=[risk_free_trace], layout=risk_free_layout),
        disabled
    )

if __name__ == '__main__':
    app.run_server(debug=True)


def ordena(lista, maior):
    """
    Ordena 'lista' do maior para o menor, caso 'maior' seja True; caso
    contrário, ordena do menor para o maior. Essa função modifica a lista
    passada por parâmetro.

    Parâmetros: lista de números ou strings e um booleano.
    Retorna: nada.
    """
    if maior == True:
        lista.sort(reverse = True)
    else:
        lista.sort(reverse = False)

def moda(lista):
    """
    Encontra a moda de 'lista', isto é, o valor que mais se repete; em caso de
    empate, retorne o que aparece primeiro.

    Parâmetros: lista de strings.
    Retorna: a moda de 'lista'.
    """
    lista_repeticao = [0 for _ in range(len(lista))]

    for i in lista:
        for n in range(len(lista)):
            if lista[n] == i:
                lista_repeticao[n] += 1

    indice_moda = 0
    for i in range(len(lista_repeticao)):
        if lista_repeticao[i]>lista_repeticao[indice_moda]:
            indice_moda = i

    moda = lista[indice_moda]
    return moda

def mediana(valores):
    """
    Encontra a mediana de 'valores', isto é, o valor que ocupa a posição
    central da lista ordenada. Quando a lista tem tamanho par,
    definimos a mediana como o valor da primeira posição na segunda
    metada da lista ordenada.

    Parâmetros: lista de floats.
    Retorna: a mediana de 'valores'.
    """

    maior = False
    ordena(valores,maior)

    if len(valores)%2 == 0:
        indice = (len(valores)//2)
    else:
        indice = (len(valores)-1)//2

    mediana = valores[indice]
    return mediana

def media(valores):
    """
    Encontra a média de 'valores'.

    Parâmetros: lista de floats.
    Retorna: a média de 'valores'.
    """
    soma = 0
    num_elementos = len(valores)
    for elemento in valores:
        soma+=elemento
    media = soma/num_elementos
    return media

def media_ponderada(valores, pesos):
    """
    Encontra a média ponderada de 'valores'.

    Parâmetros: listas de floats.
    Retorna: a média ponderada de 'valores'.
    """
    soma_valores = 0
    soma_pesos = 0
    for i in range(len(valores)):
        soma_valores += (pesos[i]*valores[i])
        soma_pesos += pesos[i]

    media_ponderada = soma_valores / soma_pesos
    return media_ponderada

def filtrar_entre(valores, menor, maior):
    """
    Cria uma lista com os números de 'valores' que estejam no intervalo
    ['menor', 'maior') (o primeiro intervalo é fechado e o segundo é aberto).

    Parâmetros: lista de floats e os limites.
    Retorna: a lista filtrada.
    """
    lista_filtrada = []
    for elemento in valores:
        if (elemento >= menor and elemento < maior):
            lista_filtrada.append(elemento)

    return lista_filtrada

def filtrar_caracteristica(lista, caracteristica, alvo):
    """
    Cria uma lista com os elementos de 'lista' cuja posição em 'caracteristica'
    seja igual a 'alvo'. Por exemplo, com a entrada abaixo, retornaríamos
    ['Alemanha', 'Portugal']:
    lista = ['Brasil', 'Alemanha', 'Angola', 'Portugal']
    caracteristica = ['América do Sul', 'Europa', 'África', 'Europa']
    alvo = 'Europa'

    Parâmetros: listas de números ou strings e um valor alvo.
    Retorna: a lista com a característica filtrada.
    """
    lista_c_filtrada = []
    for i in range(len(caracteristica)):
        if caracteristica[i]==alvo:
            lista_c_filtrada.append(lista[i])

    return lista_c_filtrada

def histograma(valores, intervalos):
    """
    Cria uma lista com as frequências do histograma de 'valores', divididas nas
    classes conforme a lista 'intervalos'. Por exemplo, se temos [10, 20, 30]
    como intervalos, devemos obter as frequências dos intervalos [10, 20) e [20,
    30).

    Parâmetros: listas de números.
    Retorna: lista de frequência do histograma.
    """
    num_intervalos = len(intervalos)-1
    frequencia_histograma = [0 for _ in range(num_intervalos)]

    for i in range(num_intervalos):
        min = intervalos[i]
        max = intervalos[i+1]
        for elemento in valores:
            if (elemento >= min and elemento < max):
                frequencia_histograma[i]+=1

    return frequencia_histograma

def maiores_k(valores, k):
    """
    Cria uma lista com os 'k' maiores números de 'valores'.

    Parâmetros: lista de inteiros e um número inteiro.
    Retorna: lista com os 'k' maiores números.
    """
    maior = True
    ordena(valores,maior)
    lista_maiores_k = valores[:k]
    return lista_maiores_k

def menores_k(valores, k):
    """
    Cria uma lista com os 'k' menores números de 'valores'.

    Parâmetros: lista de inteiros e um número inteiro.
    Retorna: lista com os 'k'menores números.
    """
    maior = False
    ordena(valores,maior)
    lista_maiores_k = valores[:k]
    return lista_maiores_k

def remove_duplicatas(lista):
    """
    Cria uma lista removendo todos os elementos duplicados de 'lista', mantendo
    a ordem relativa original. Use somente listas, for/while e variáveis
    simples para implementar essa função.

    Parâmetros: listas de strings.
    Retorna: 'lista' sem duplicatas.
    """

    lista_remove_duplicatas = []

    for i in range(len(lista)):
        if lista[i] not in lista_remove_duplicatas:
            lista_remove_duplicatas.append(lista[i])

    return lista_remove_duplicatas


import os
import re
import sys
import time

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QIcon, QPainter, QColor, QFont
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox

import method
import port
from sscom import Ui_MainWindow

class MyMainForm(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyMainForm, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle("智能消火栓配置软件")                 # 设置窗体标题
        self.setWindowIcon(QIcon(r'.\imgs\title2.ico'))         # 设置窗体图标
        self.loopSendBox.setEnabled(False)                      # 锁定定时发送
        self.init_port()                                        # 初始化调用init_port()函数
        self.init_config()                                      # 初始化调用init_config()函数
        self.init_send_show()                                   # 初始化调用init_send_show()函数
        # self.init_file()                                        # 初始化调用init_file()函数

    # 串口开关控件初始化
    def init_port(self):
        self.myCOM = None
        self.get_port_list_loop = QTimer(self)                                        # 定义一个定时串口刷新器
        self.get_port_list_loop.timeout.connect(self.get_port_list_btn)               # 定时串口刷新器绑定get_port_list_btn函数
        self.portItemsBox.addItems(method.get_port_list())                            # 获取可以串口列表并添加到串口号下拉框
        self.get_port_list_loop.start(2000)                                           # 打开定时串口刷新器，设置每隔2000毫秒接收一次
        self.getPortBtn.clicked.connect(self.get_port_list_btn)                       # 刷新串口按钮绑定get_port_list_btn函数
        self.openPortBtn.setChecked(False)                                            # 设置打开串口按钮初始值为关闭
        self.openPortBtn.toggled.connect(lambda: self.btn_port(self.openPortBtn))     # 打开串口按钮绑定槽函数
        self.closePortBtn.setChecked(True)                                            # 设置关闭串口按钮初始值为打开
        self.closePortBtn.toggled.connect(lambda: self.btn_port(self.closePortBtn))   # 关闭串口按钮绑定槽函数

    # 串口配置控件初始化
    def init_config(self):
        self.baudBox.addItems(method.get_baud_rate())           # 波特率下拉框添加选项
        self.baudBox.setCurrentIndex(12)                        # 波特率下拉框设置初始索引
        self.baudBox.activated.connect(self.config_port)        # 波特率下拉框绑定config_port函数
        self.byteBox.addItems(method.get_byte_size())           # 数据位下拉框添加选项
        self.byteBox.setCurrentIndex(3)                         # 数据位下拉框设置初始索引
        self.byteBox.activated.connect(self.config_port)        # 数据位下拉框绑定config_port函数
        self.stopBitBox.addItems(method.get_stop_bits())        # 停止位下拉框添加选项
        self.stopBitBox.activated.connect(self.config_port)     # 停止位下拉框绑定config_port函数
        self.parityBox.addItems(method.get_parity())            # 校验位下拉框添加选项
        self.parityBox.activated.connect(self.config_port)      # 校验位下拉框绑定config_port函数
        self.flowCtrBox.addItems(method.get_flow_ctr())         # 流控制下拉框添加选项
        self.flowCtrBox.activated.connect(self.config_port)     # 流控制下拉框绑定config_port函数
        self.DTRBox.stateChanged.connect(self.set_dtr)          # DTR复选框绑定set_dtr函数
        self.RTSBox.stateChanged.connect(self.set_rts)          # RTS复选框绑定set_rts函数

    # 收发数据控件初始化
    def init_send_show(self):
        self.showDataEdit.setReadOnly(True)                               # 设置显示数据文本框为只读
        self.timer_show = QTimer(self)                                    # 定义一个定时接收器
        self.timer_show.timeout.connect(self.show_data)                   # 定时接收器绑定show_data函数
        self.HEXShowBox.setChecked(False)                                 # HEX显示复选框设置为不选中
        self.HEXShowBox.stateChanged.connect(lambda: self.show_type())    # HEX显示复选框绑定show_type函数
        self.HEXShowBox.setEnabled(False)                                 # 锁定HEX显示复选框
        self.HEXSendBox.setEnabled(False)                                 # 锁定HEX发送复选框
        self.timer_send = QTimer(self)                                    # 定义一个定时发送器
        self.timer_send.timeout.connect(self.send_data_loop)              # 定时发送器绑定send_data_loop函数
        self.loopSendBox.toggled.connect(self.send_data_loop)             # 定时发送复选框绑定send_data_loop函数
        self.sendDataBtn.clicked.connect(self.send_data)                  # 发送数据按钮绑定send_data函数
        self.clearWinBtn.clicked.connect(self.clear_win)                  # 清除窗口按钮绑定clear_win函数
        self.all_send_bytes = 0                                           # 定义一个总发送字节数
        self.all_get_bytes = 0                                            # 定义一个总接收字节数
        self.getConfigBtn.clicked.connect(self.get_config)                # 获取参数按钮绑定get_config函数
        self.setConfigBtn.clicked.connect(self.set_config)                # 写入参数按钮绑定set_config函数

    # 获取和写入参数控件初始化
    def init_file(self):
        self.getConfigBtn.clicked.connect(self.get_config)    # 获取参数按钮绑定get_config函数
        # self.fileDialog = QFileDialog                           # 定义一个打开文件弹窗
        self.setConfigBtn.clicked.connect(self.set_config)    # 写入参数按钮绑定set_config函数

    # 更新串口列表按钮
    def get_port_list_btn(self):
        port_list_temp = method.get_port_list()
        num = len(port_list_temp)
        current_port_list = self.portItemsBox.count()
        if num != current_port_list:
            self.portItemsBox.clear()
            self.portItemsBox.addItems(port_list_temp)                # 获取可以串口列表并添加到串口号下拉框
            # self.closePortBtn.setChecked(True)                        # 设置关闭串口按钮初始值为打开

    # 打开串口
    def open_port(self):
        try:
            currentport = re.findall(r"(\d+)", self.portItemsBox.currentText())
            self.myCOM = port.COM("COM" + currentport[0])             # 实例化一个自定义的port类
            self.myCOM.open_port()                                    # 打开串口
            self.portItemsBox.setEnabled(False)                       # 锁定串口下拉框
            # self.baudBox.setEnabled(False)                            # 锁定波特率下拉框
            # self.byteBox.setEnabled(False)                            # 锁定数据位下拉框
            # self.stopBitBox.setEnabled(False)                         # 锁定停止位下拉框
            # self.parityBox.setEnabled(False)                          # 锁定校验位下拉框
            # self.flowCtrBox.setEnabled(False)                         # 锁定流控制下拉框
            if not self.myCOM.port_state:                             # 如果串口不可用，弹窗报错
                port_error_msgBox = QMessageBox(QMessageBox.Information, '串口打开错误', '串口无法打开，当前串口已被占用或其他错误！！'
                                                                                '请选择其他串口或检查串口设置是否正确。')
                port_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                port_error_msgBox.exec_()
                self.closePortBtn.setChecked(True)                    # 选择关闭按钮
                self.portItemsBox.setEnabled(True)                    # 解除串口下拉框锁定
                # self.baudBox.setEnabled(True)                         # 解除波特率下拉框锁定
                # self.byteBox.setEnabled(True)                         # 解除数据位下拉框锁定
                # self.stopBitBox.setEnabled(True)                      # 解除停止位下拉框锁定
                # self.parityBox.setEnabled(True)                       # 解除校验位下拉框锁定
                # self.flowCtrBox.setEnabled(True)                      # 解除流控制下拉框锁定
            self.DTRBox.setChecked(self.myCOM.ser.dtr)                # 获取DTR
            self.RTSBox.setChecked(self.myCOM.ser.rts)                # 获取RTS
            self.timer_show.start(2)                                  # 打开接收定时器，设置每隔2毫秒接收一次
            port_info1 = self.myCOM.ser.port + '已打开' + ' [' + str(self.myCOM.ser.baudrate) + '-' + str(self.myCOM.ser.bytesize) + \
                        '-' + str(self.myCOM.ser.stopbits) + '-' + self.myCOM.ser.parity + ']'
            port_info2 = ' [CTS=' + str(self.myCOM.ser.getCTS()) + ' DSR=' + str(self.myCOM.ser.getDSR()) + ' RI=' + \
                         str(self.myCOM.ser.getRI()) + ' CD=' + str(self.myCOM.ser.getCD()) + ']'
            self.portInfolabel.setText(port_info1 + port_info2)       # 显示已打开串口信息
        except Exception as e:
            print(e)

    # 关闭串口
    def close_port(self):
        try:
            self.timer_show.stop()                                      # 关闭接收定时器
            self.myCOM.close_port()                                     # 关闭串口
            self.DTRBox.setChecked(False)
            self.RTSBox.setChecked(False)
            self.portItemsBox.setEnabled(True)                          # 解除串口下拉框锁定
            # self.baudBox.setEnabled(True)                               # 解除波特率下拉框锁定
            # self.byteBox.setEnabled(True)                               # 解除数据位下拉框锁定
            # self.stopBitBox.setEnabled(True)                            # 解除停止位下拉框锁定
            # self.parityBox.setEnabled(True)                             # 解除校验位下拉框锁定
            # self.flowCtrBox.setEnabled(True)                            # 解除流控制下拉框锁定
            self.portInfolabel.setText(self.myCOM.ser.port + '已关闭')
        except Exception as e:
            print(e)

    # 打开还是关闭串口
    def btn_port(self, btn):
        if btn.text() != '打开串口':
            return None
        if btn.isChecked():
            self.open_port()                     # 打开串口
            self.loopSendBox.setEnabled(True)    # 解除定时发送复选框锁定
            return None
        self.close_port()                    # 关闭串口
        self.loopSendBox.setEnabled(False)   # 锁定定时发送复选框

    # 配置串口
    def config_port(self):
        try:
            self.myCOM.set_baud_rate(self.baudBox.currentText())       # 设置波特率
            self.myCOM.set_byte_size(self.byteBox.currentText())       # 设置数据位
            self.myCOM.set_stop_bits(self.stopBitBox.currentText())    # 设置停止位
            self.myCOM.set_parity(self.parityBox.currentText())        # 设置校验位
            self.myCOM.set_flow_ctr(self.flowCtrBox.currentText())     # 设置流控制
            port_info1 = self.myCOM.ser.port + '已打开' + ' [' + str(self.myCOM.ser.baudrate) + '-' + str(self.myCOM.ser.bytesize) + \
                        '-' + str(self.myCOM.ser.stopbits) + '-' + self.myCOM.ser.parity + ']'
            port_info2 = ' [CTS=' + str(self.myCOM.ser.getCTS()) + ' DSR=' + str(self.myCOM.ser.getDSR()) + ' RI=' + \
                         str(self.myCOM.ser.getRI()) + ' CD=' + str(self.myCOM.ser.getCD()) + ']'
            self.portInfolabel.setText(port_info1 + port_info2)       # 显示已打开串口信息
        except Exception as e:
            print(e)

    # 设置 DTR
    def set_dtr(self):
        try:
            self.myCOM.set_dtr(self.DTRBox.isChecked())
        except Exception as e:
            print(e)

    # 设置 RTS
    def set_rts(self):
        try:
            self.myCOM.set_rts(self.RTSBox.isChecked())
        except Exception as e:
            print(e)

    # 发送数据
    def send_data(self):
        try:
            if self.closePortBtn.isChecked():    # 如果关闭串口按钮选定，不能发送数据
                send_data_error_msgBox = QMessageBox(QMessageBox.Information, '发送数据错误', '无法发送数据，请先打开串口！')
                send_data_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                send_data_error_msgBox.exec_()
                return None
            data = self.sendDataEdit.text()  + "\r\n"      # 从发送数据输入框获取要发送的数据
            send_bytes = 0                       # 定义一个发送字节数
            # if self.HEXSendBox.isChecked():  # 如果HEX发送复选框选定，发送HEX格式数据
            #     if not method.check_hex(data) or len(''.join(data.split())) % 2 != 0:
            #         hex_error_msgBox = QMessageBox(QMessageBox.Information, '发送数据错误', '输入hex格式错误! '
            #                                                                             '请按照如“01 23 .. ..” 格式输入')
            #         hex_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
            #         hex_error_msgBox.exec_()
            #     else:
            #         send_bytes = self.myCOM.send_data(method.hex_byte(data))  # 获取发送字节数
            # else:
            send_bytes = self.myCOM.send_data(data.encode())                  # 获取发送字节数
            self.all_send_bytes += send_bytes                                 # 统计发送总字节数
            self.sendBytelabel.setText('S:' + str(self.all_send_bytes))
        except Exception as e:
            print(e)

    # 定时发送
    def send_data_loop(self):
        if self.loopSendBox.isChecked():                             # 如果定时发送复选框被选定，定时发送数据
            self.setTimeEdit.setReadOnly(True)                       # 定时发送输入框设置只读
            if self.setTimeEdit.text() == '':
                return None
            self.timer_send.start(int(self.setTimeEdit.text()))      # 如果定时发送输入框输入不为空，设置间隔时间
            self.send_data()
            return None
        self.timer_send.stop()                                       # 停止定时发送定时器
        self.setTimeEdit.setReadOnly(False)                          # 取消定时发送输入框设置只读

    # 读取并显示数据
    def show_data(self):
        data = self.myCOM.get_data()                   # 获取接收到的数据
        try:
            if data is not None:                       # 如果接收到的数据不为空，选择字符串显示还是HEX显示
                # if self.HEXShowBox.isChecked():
                #     self.showDataEdit.insertPlainText(method.str_hex(data))
                # else:
                self.showDataEdit.insertPlainText(data)
                self.all_get_bytes += len(data)        # 统计接收总字节数
                self.getBytelabel.setText('R:' + str(self.all_get_bytes))
                text_cursor = self.showDataEdit.textCursor()   # 获取光标位置
                text_cursor.movePosition(text_cursor.End)
                self.showDataEdit.setTextCursor(text_cursor)   # 设置光标位置
        except Exception as e:
            print(e)

    # 读取（更新）参数
    def get_config(self):
        try:
            if self.closePortBtn.isChecked():    # 如果关闭串口按钮选定，则要先打开串口
                get_config_open_error_msgBox = QMessageBox(QMessageBox.Information, '获取参数失败', '无法获取参数，请先打开串口！')
                get_config_open_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                get_config_open_error_msgBox.exec_()
                return None
            current_data = self.showDataEdit.toPlainText()
            data_sn = re.findall(r'data\ sn\ =\ (.*)', current_data)
            data_sample_duty = re.findall(r'data\ sample_duty\ =\ (.*)', current_data)
            data_upload_duty = re.findall(r'data\ upload_duty\ =\ (.*)', current_data)
            data_ip = re.findall(r'mqtt\ address:(.*)', current_data)
            data_port = re.findall(r'mqtt\ port:(.*)', current_data)
            if len(data_sn[-1]) < 6 or len(data_ip[-1]) < 7 or len(data_ip[-1]) > 15 or int(data_port[-1]) < 0 or int(data_port[-1]) > 65536:
                get_config_error_msgBox = QMessageBox(QMessageBox.Information, '获取参数异常', '请检查接收的数据是否完整或正常，可尝试复位或者重启板子后再次获取！')
                get_config_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                get_config_error_msgBox.exec_()
            # 将接收框内经过筛选后的信息分别写入对应输入框内
            self.snNameEdit.setText(data_sn[-1])
            self.IPaddressEdit.setText(data_ip[-1])
            self.portNumberEdit.setText(data_port[-1])
            self.sampleDutyEdit.setText(data_sample_duty[-1])
            self.uploadDutyEdit.setText(data_upload_duty[-1])
        except Exception as e:
            print(e)

    # 写入参数
    def set_config(self):
        try:
            # 判断哪个框是否为空以及按照要求输入
            if self.closePortBtn.isChecked():    # 如果关闭串口按钮选定，则要先打开串口
                set_config_open_error_msgBox = QMessageBox(QMessageBox.Information, '写入参数失败', '无法写入参数，请先打开串口！')
                set_config_open_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                set_config_open_error_msgBox.exec_()
                return None
            data_sn_input = self.snNameEdit.text() if len(self.snNameEdit.text()) != 0 else self.snNameEdit.placeholderText()
            data_ip_input = self.IPaddressEdit.text() if len(self.IPaddressEdit.text()) != 0 else self.IPaddressEdit.placeholderText()
            data_port_input = self.portNumberEdit.text() if len(self.portNumberEdit.text()) != 0 else self.portNumberEdit.placeholderText()
            data_sampleDuty_input = self.sampleDutyEdit.text() if len(self.sampleDutyEdit.text()) != 0 else self.sampleDutyEdit.placeholderText()
            data_uploadDuty_input = self.uploadDutyEdit.text() if len(self.uploadDutyEdit.text()) != 0 else self.uploadDutyEdit.placeholderText()
            if len(data_sn_input) < 6 or len(data_ip_input) < 7 or len(data_ip_input) > 15 or int(data_port_input) < 0 or int(data_port_input) > 65535 or int(data_sampleDuty_input) < 20 or int(data_sampleDuty_input) > 65535 or int(data_uploadDuty_input) < 20 or int(data_uploadDuty_input) > 4294967295:
                set_config_error_msgBox = QMessageBox(QMessageBox.Information, '参数异常', '请检查输入框内所需要写入的参数是否完整或符合框内提示的要求，然后再次尝试！')
                set_config_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                set_config_error_msgBox.exec_()
                return None
            # 拼接成所要的格式之后 TODO 要在收到相应信号时再发送
            set_config_todo_msgBox = QMessageBox.question(self.centralwidget, '写入参数', '请先对板子进行复位或者重启！然后请确认是否开始写入！',
                                                          QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if set_config_todo_msgBox == QMessageBox.No:
                return None
            config_setting_msgBox = QMessageBox(QMessageBox.Information, '写入中', '写入参数中，请勿关闭，稍后会返回写入结果...')
            config_setting_msgBox.setWindowIcon(QIcon(r'.\imgs\title1.ico'))
            config_setting_msgBox.setStandardButtons(QMessageBox.Ok)
            config_setting_msgBox.button(QMessageBox.Ok).setVisible(False)
            config_setting_msgBox.button(QMessageBox.Ok).animateClick(2500)
            config_setting_msgBox.exec_()
            self.clear_win()
            config_flag = self.myCOM.send_data("S".encode())
            time.sleep(0.5)
            data_config = "Y@" + data_sn_input + "@" + data_ip_input + ":" + data_port_input + "," + data_sampleDuty_input + "," + data_uploadDuty_input
            print("data_config: " + data_config)
            send_bytes = self.myCOM.send_data(data_config.encode())
            self.all_send_bytes += send_bytes + config_flag
            self.sendBytelabel.setText('S:' + str(self.all_send_bytes))
            time.sleep(0.5)
            self.show_data()
            current_data = self.showDataEdit.toPlainText()
            print("current_data = " + current_data)
            if current_data.find("First power on") == -1:
                config_success_msgBox = QMessageBox(QMessageBox.Information, '写入失败', '写入参数失败！请检查上一步是否对板子进行复位或重启后及时点击yes开始写入！')
                config_success_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
                config_success_msgBox.exec_()
                return None
            config_success_msgBox = QMessageBox(QMessageBox.Information, '写入成功', '写入参数成功！可重启板子或者触发事件唤醒板子之后再点获取参数进行验证！')
            config_success_msgBox.setWindowIcon(QIcon(r'.\imgs\title1.ico'))
            config_success_msgBox.exec_()
        except Exception as e:
            print(e)

    # 选择显示类型，str 还是 hex
    def show_type(self):
        # if self.HEXShowBox.isChecked():
        #     text_data = self.showDataEdit.toPlainText()
        #     hex_data = method.str_hex(text_data)
        #     self.showDataEdit.setPlainText(hex_data)
        # else:
        text_data = self.showDataEdit.toPlainText()
        data = method.hex_str(text_data)
        self.showDataEdit.setPlainText(data)
        text_cursor = self.showDataEdit.textCursor()
        text_cursor.movePosition(text_cursor.End)
        self.showDataEdit.setTextCursor(text_cursor)

    # 打开文件
    def open_file(self):
        try:
            file_name, file_type = self.fileDialog.getOpenFileName(self, '打开文件', os.getcwd(),
                                                                   'All Files(*);;Text Files(*.txt)')
            if file_name != '':
                self.fileNameEdit.setText(file_name)
                with open(file_name, 'r', encoding='utf-8') as f:
                    for line in f:
                        self.showDataEdit.insertPlainText(line)
        except Exception as e:
            print(e)

    # 发送文件
    def send_file(self):
        if self.closePortBtn.isChecked():
            send_file_error_msgBox = QMessageBox(QMessageBox.Information, '发送文件错误', '无法发送文件，请先打开串口！')
            send_file_error_msgBox.setWindowIcon(QIcon(r'.\imgs\error.ico'))
            send_file_error_msgBox.exec_()
        try:
            if self.fileNameEdit.text() != '':
                file_data = self.showDataEdit.toPlainText()
                send_bytes = self.myCOM.send_data(file_data.encode())
        except Exception as e:
            print(e)

    # 清除窗口
    def clear_win(self):
        self.showDataEdit.clear()
        self.sendBytelabel.setText('S:0')
        self.getBytelabel.setText('R:0')

if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWin = MyMainForm()
    myWin.show()
    sys.exit(app.exec_())


import matplotlib.pyplot as plt
#A
x = float()
y = float()
Xdots = []
Ydots = []
for x in range(-50, 50):
    if x < 1:
        Xdots.append(x)
        y = x + 2
        Ydots.append(y)
    if x >= 1:
        Xdots.append(x)
        y = (x**2) - 6*x + 10
        Ydots.append(y)
plt.plot(Xdots, Ydots, 'g.-')
plt.show()


import pymongo
import os
from urllib.parse import urlparse

class __MongoUtil():
  def __init__(self):
    self.mongodbUri = os.environ.get('MONGODB_URI')
    if not self.mongodbUri:
      raise Exception()
    self.client = pymongo.MongoClient(self.mongodbUri)
    self.dbname = urlparse(self.mongodbUri).path[1:]
  def collection(self, collectionName):
      return self.client[self.dbname][collectionName]

__sharedInstance = __MongoUtil()

def mongoUtil():
  return __sharedInstance

def loadDslFunctions(dslFunctions):
  def mongoGet(container, *args):
    collectionName = args[0].rawArg()
    # print("collectionName", collectionName)
    collection = mongoUtil().collection(collectionName)
    try:
      result = list(collection.find())
      return result, None
    except Exception as e:
      return None, e

  dslFunctions["mongoGet"] = mongoGet

  def mongoInsert(container, *args):
    collectionName = args[0].rawArg()
    obj, err = args[1].evaluate(container)
    if err != None :
      return None, err

    collection = mongoUtil().collection(collectionName)
    try:
      res = collection.insert_one(obj)
      return res, None
    except Exception as e:
      return None, e

  dslFunctions["mongoInsert"] = mongoInsert

  def mongoReplace(container, *args):
    collectionName = args[0].rawArg()
    obj, err = args[1].evaluate(container)
    if err != None:
      return None, err

    collection = mongoUtil().collection(collectionName)
    res = collection.replace_one({"_id": obj["_id"]}, obj)
    return res, None

  dslFunctions["mongoReplace"] = mongoReplace


import sys
import threading
from os.path import abspath
from os import system
from math import floor, ceil
from time import sleep
from itertools import zip_longest

import pygame
from pygame.display import init
from pygame.locals import *
from grid import Grid
from pygame_textinput import TextInput

if system("python -m pip install pygame-button") != 0:
    print(
"""ERROR: tried to install pygame-button module via

python -m pip install pygame-button

This module is necessary for the program to run
Please check your internet connection and try again, or run the command yourself"""
)
    input("Press enter to exit...")
    sys.exit()

from pygame_button import Button
pygame.init()

BLUE  = (0, 0, 255)
RED   = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

BG = WHITE
FG = BLACK

CLOCK = pygame.time.Clock()
FPS = 30

SCREEN = pygame.display.set_mode((100, 100), FULLSCREEN)
pygame.display.set_caption("Animated addition")
font_obj = pygame.font.Font("calibri.ttf", 24)

"""-------------------------------------------------Define Functions"""
delay = 1 #Delay between animation steps

def clear_and_draw_baseline():
    "Draw the base line and operator"
    grid.clear(BG)
    for i in range(grid.x_blocks):
        grid.draw("H", i, bottom_line)

    grid.draw("+", 0, bottom_line - 1)

force_stop = False
def addition_manager():
    global force_stop

    force_stop = True
    sleep(0.06)
    force_stop = False

    for i in addition():
        for j in range(int(delay / 0.05)):
            sleep(0.05)
            if force_stop:
                return()

def addition():
    "The part that actually adds"
    clear_and_draw_baseline()

    top_num = int(top_input.get_text().strip())
    bottom_num = int(bottom_input.get_text().strip())
    top_num, bottom_num = sorted( #Longest number on top
        (top_num, bottom_num),
        key=(lambda x: len(str(x))),
        reverse=True
    )

    sum_string = str(top_num + bottom_num)
    top_list = list(str(top_num))
    bottom_list = list(str(bottom_num))

    #The animation runs right to left, so the lists are reversed
    top_list.reverse()
    bottom_list.reverse()
    right_side = grid.x_blocks - 1

    #bottom_line is the sum line
    #bottom_line - 1 is the bottom addend
    #bottom_line - 2 is the top addend
    #bottom_line - 3 is the carry row

    #Draw the numbers then wait
    grid.draw(str(bottom_num), right_side, bottom_line - 1)
    grid.draw(str(top_num), right_side, bottom_line - 2)
    pygame.display.flip(); yield

    move = 0
    carry_sum = 0
    for (top, bottom) in zip_longest(top_list, bottom_list, fillvalue="0"):
        r_sum = int(top) + int(bottom) + carry_sum #Add column
        if r_sum > 9: #If there's a carry

            #Draw the ones part
            grid.draw(r_sum - 10, right_side - move, bottom_line)
            pygame.display.update(); yield

            #Draw the carry digit
            carry_sum = 1
            grid.draw("1b", right_side - move - 1, bottom_line - 3)

        else: #If there's no carry

            #Draw the sum as normal
            grid.draw(r_sum, right_side - move, bottom_line)
            carry_sum = 0

        pygame.display.update(); yield True
        move += 1

    #If there's a carry digit left, draw it on the bottom line
    if carry_sum == 1:
        grid.draw(1, right_side - move, bottom_line)

    pygame.display.update(); yield

    #Highlight the sum green once the animation is complete
    n = 0
    for x in range(right_side - len(sum_string), right_side):
        grid.draw(sum_string[n] + "r", x + 1, bottom_line)
        n += 1

    yield

"""-----------------------------------------------------Define input"""
if True: #I'm sick of seeing these blocks of code. if True does the job
    top_input = TextInput(
        font_family=abspath("calibri.ttf"),
        max_string_length=10,
        initial_string="23",
        cursor_color=BG
        )
    top_input.update(pygame.event.get())

    bottom_input = TextInput(
        font_family=abspath("calibri.ttf"),
        max_string_length=10,
        initial_string="79",
        cursor_color=BG
    )
    bottom_input.update(pygame.event.get())
    focus = "" #Empty is no focus

"""--------------------------------------------------Define the grid"""
if True:
    SCREEN.fill(BG)

    WIDTH = SCREEN.get_width()
    HEIGHT = SCREEN.get_height()

    divider = ceil(0.7 * WIDTH)

    #making_grid.md for explanation
    rect_height = floor(divider * 4/3)
    if rect_height > HEIGHT:
        rect_width = HEIGHT * 3/4
        rect_height = HEIGHT
    else:
        rect_width = divider

    #Change the eights to any power of 2 to change the size.
    #8 is perfect though
    grid_width = int(divider // (rect_width // 8))
    grid_height = int(HEIGHT // (rect_height // 8))

    grid = Grid(SCREEN, divider, HEIGHT, grid_width, grid_height, FG, 1)
    grid.update()

    #------Make the divider line
    #The right edge of the grid, adjusted to match the difference of
    # the grid and ceil(0.7 * WIDTH) Added 40 pixels of margin. 20 left side, 20 right side
    divider = divider // grid_width * grid_width + 40

    pygame.draw.line(
        SCREEN,
        RED,
        (divider, 0),
        (divider, HEIGHT),
        grid.thickness * 2 + 1
    )

    #Line beneath numbers. Right side.
    Z = HEIGHT // 4 #Fuckit im calling Z.
    pygame.draw.line(
        SCREEN,
        BLACK,
        (divider, Z),
        (WIDTH, Z),
        2
    )
    bottom_line = grid.y_blocks - 2
    clear_and_draw_baseline()

"""-------------------------------Input box coords and define button"""
if True:
    padding = 10
    top_coords = [divider + padding, 10]
    bottom_coords = [divider + padding, Z / 4 + 10]

    button_width = WIDTH - divider - 2 * padding
    button_height = Z - ceil(0.6 * Z) - padding
    addition_button = Button(
        (divider + padding, ceil(0.6 * Z), button_width, button_height),
        BLUE,
        (lambda: threading.Thread(target=addition_manager, daemon=True).start()),
        text="+",
        **{"font": font_obj, "hover_color": (0, 0, 127)}
    )

while True:

    """------Event loop"""
    events = pygame.event.get()
    for event in events:
        #Fill just the button area, don't redraw everything if you don't have to
        SCREEN.fill(WHITE, (divider + padding, 0, WIDTH, Z))

        keys = pygame.key.get_pressed()
        if event.type == QUIT or keys[K_ESCAPE]:
            pygame.quit()
            sys.exit()

        """Focus on the box and fix the cursor color"""
        if event.type == pygame.MOUSEBUTTONUP:
            pos = pygame.mouse.get_pos()
            #It stores the surface, not the rect, so this ugly thing is used to get one
            t_rect = top_input.get_surface().get_rect().move(*top_coords)
            b_rect = bottom_input.get_surface().get_rect().move(*bottom_coords)

            if t_rect.collidepoint(pos):
                focus = "top"
                top_input.set_cursor_color(FG)
                bottom_input.set_cursor_color(FG)

            elif b_rect.collidepoint(pos):
                focus = "bottom"
                top_input.set_cursor_color(FG)
                bottom_input.set_cursor_color(FG)

            else:
                focus = ""
                top_input.set_cursor_color(BG)
                top_input.update(events)
                bottom_input.set_cursor_color(BG)
                bottom_input.update(events)

        addition_button.check_event(event)

    addition_button.update(SCREEN)

    """Input handling outside loop"""
    if focus == "top":
        top_input.update(events)

    elif focus == "bottom":
        bottom_input.update(events)

    #If the string is empty, put a space.
    #Otherwise the surface will be 1x1 and can't be clicked on again

    for boxy_boi in (top_input, bottom_input):
        if len(boxy_boi.input_string) == 0:
            boxy_boi.input_string = "   "

    SCREEN.blit(top_input.get_surface(), top_coords)
    SCREEN.blit(bottom_input.get_surface(), bottom_coords)

    pygame.draw.rect(SCREEN, BLACK, (
        (top_coords[0] - 5, top_coords[1] - 5),
        (top_input.get_surface().get_size()[0] + 10, top_input.get_surface().get_size()[1] + 10)
    ), 2)

    pygame.draw.rect(SCREEN, BLACK, (
        (bottom_coords[0] - 5, bottom_coords[1] - 5),
        (bottom_input.get_surface().get_size()[0] + 10, bottom_input.get_surface().get_size()[1] + 10)
    ), 2)

    pygame.display.flip()
    CLOCK.tick(FPS)


import logging
from fastapi import FastAPI, HTTPException
from typing import Optional, List
import traceback
from pydantic import BaseModel, ValidationError
import base64
import tcpclient
import tools
from tools import HOST, PORT
from messaging import MessageType
import asyncio
import aiohttp
import time

app = FastAPI()

IMAGE_URL = 'http://tchaikovsky.company:8001/v1/faces'

class BaseRequest(BaseModel):
    data: List[str] = []
    distance_threshold: Optional[float] = 0.75
    limit: Optional[int] = 5

class RequestImages(BaseRequest):
    pass

class RequestEmbeddings(BaseRequest):
    pass

class SearchResult(BaseModel):
    detail: Optional[str] = ""
    face_ids: List[str] = []
    distances: List[float] = []

class EmbeddingsResponse(BaseModel):
    detail: Optional[str] = ""
    result: List[SearchResult] = []

class ImagesResponse(BaseModel):
    detail: Optional[str] = ""
    result: List[List[SearchResult]] = []

class RequestDetect(BaseModel):
    type: Optional[str] = 'imagebase64'
    value: str

class FaceEmbedding(BaseModel):
    version: Optional[str]
    data: str

class FaceElement(BaseModel):
    embedding: FaceEmbedding
    rect: Optional[List[int]]
    image: Optional[str]

class FacesResponse(BaseModel):
    status: Optional[str] = ""
    reply: List[FaceElement]

async def do_post(session, data, url, headers):
    async with session.post(url, data=data, headers=headers) as response:
        return await response.text()

@app.post("/images", response_model=ImagesResponse)
async def find_images(payload: RequestImages):
    async with aiohttp.ClientSession() as session:
        headers = {"content-type": "application/json"}
        tasks = []
        async for _, img_b64 in tools.aiter(payload.data):
            img_req = RequestDetect(value=img_b64).json()
            task = do_post(session, img_req, IMAGE_URL, headers)
            tasks.append(task)

        start_time = time.time()
        faces = await asyncio.gather(*tasks)
        print('RequestDetect', time.time() - start_time)

    request = RequestEmbeddings(distance_threshold=payload.distance_threshold,
                                limit=payload.limit)
    counts = [0]
    for text in faces:
        try:
            resp = FacesResponse.parse_raw(text)
            counts.append(counts[-1] + len(resp.reply))
        except ValidationError:
            logging.error(traceback.format_exc())
            counts.append(counts[-1])
            continue

        for face in resp.reply:
            request.data.append(face.embedding.data)

    start_time = time.time()
    embeddings = await find_embeddings(request)
    print('RequestEmbeddings', time.time() - start_time)

    respone = ImagesResponse()
    if embeddings['detail']:
        raise HTTPException(status_code=500, detail=embeddings['detail'])

    for left, right in zip(counts[:-1], counts[1:]):
        img_faces = embeddings['result'][left:right]
        if len(img_faces) == 0:
            img_faces = [SearchResult(detail='face detection ValidationError')]
        respone.result.append(img_faces)

    return respone

@app.post("/embeddings", response_model=EmbeddingsResponse)
async def find_embeddings(payload: RequestEmbeddings):
    nrows = len(payload.data)
    try:
        payload.data = tools.bytes_concat(map(base64.b64decode, payload.data))
    except:
        logging.error(traceback.format_exc())
        response = EmbeddingsResponse(detail='incorrect base64 encoding')
        return response

    data = payload.dict()
    data['nrows'] = nrows
    response = tcpclient.send(HOST, PORT, data, MessageType.FRONTEND)
    if response['detail']:
        raise HTTPException(status_code=500, detail=response['detail'])

    return response